<template>
|
<view class="account-detail">
|
<PageHeader :title="pageTitle" @back="goBack" />
|
|
<view class="detail-body">
|
<view class="detail-card">
|
<view class="section-header">
|
<text class="section-title">基本信息</text>
|
<text class="section-subtitle">请填写商机的基础资料</text>
|
</view>
|
|
<up-form
|
ref="formRef"
|
:rules="rules"
|
:model="form"
|
label-width="90"
|
@submit="onSubmit"
|
>
|
<!-- 基本信息 -->
|
<up-form-item label="客户名称" prop="customerName" :required="isAddOrEdit">
|
<up-input
|
v-model="form.customerName"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入客户名称"
|
/>
|
</up-form-item>
|
|
<up-form-item label="省份" prop="province">
|
<up-input
|
v-model="form.province"
|
readonly
|
:disabled="isDetail || isAddOperation"
|
placeholder="点击选择省份"
|
@click="onProvinceClick"
|
/>
|
<template #right>
|
<up-icon
|
name="arrow-right"
|
@click="onProvinceClick"
|
></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="城市" prop="city">
|
<up-input
|
v-model="form.city"
|
readonly
|
:disabled="isDetail || isAddOperation"
|
placeholder="点击选择城市"
|
@click="onCityClick"
|
/>
|
<template #right>
|
<up-icon
|
name="arrow-right"
|
@click="onCityClick"
|
></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="商机来源" prop="businessSource">
|
<up-input
|
v-model="form.businessSource"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入商机来源"
|
/>
|
</up-form-item>
|
|
<up-form-item label="行业" prop="industry">
|
<up-input
|
v-model="form.industry"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入行业"
|
/>
|
</up-form-item>
|
|
<up-form-item label="主营产品" prop="mainProducts">
|
<up-input
|
v-model="form.mainProducts"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入主营产品"
|
/>
|
</up-form-item>
|
|
<up-form-item label="主营业务收入" prop="mainBusinessRevenue">
|
<up-input
|
v-model="form.mainBusinessRevenue"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入主营业务收入"
|
/>
|
</up-form-item>
|
|
<up-form-item label="客户规模" prop="customerScale">
|
<up-input
|
v-model="form.customerScale"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入客户规模"
|
/>
|
</up-form-item>
|
|
<up-form-item label="信息化现状" prop="informationState">
|
<up-textarea
|
v-model="form.informationState"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入信息化现状"
|
autoHeight
|
:maxlength="300"
|
count
|
/>
|
</up-form-item>
|
|
<up-form-item label="状态" prop="status" required @click="onStatusClick">
|
<up-input
|
v-model="form.status"
|
readonly
|
:disabled="isDetail"
|
placeholder="点击选择状态"
|
@click="onStatusClick"
|
/>
|
<template #right>
|
<up-icon
|
name="arrow-right"
|
@click="onStatusClick"
|
></up-icon>
|
</template>
|
</up-form-item>
|
|
<up-form-item label="合同金额(元)" prop="contractAmount" :required="isAddOrEdit">
|
<up-input
|
v-model="form.contractAmount"
|
:disabled="isDetail || isAddOperation"
|
placeholder="请输入合同金额"
|
/>
|
</up-form-item>
|
|
<!-- 描述信息 -->
|
<view class="section-header section-header-inner">
|
<text class="section-title">描述信息</text>
|
<text class="section-subtitle">补充商机说明,便于后续跟进</text>
|
</view>
|
<up-form-item label="拜访记录" prop="description" :required="isAddOrAddOperation">
|
<up-textarea
|
v-model="form.description"
|
:disabled="isDetail"
|
:placeholder="renovationPlaceholder"
|
autoHeight
|
count
|
maxlength="500"
|
/>
|
</up-form-item>
|
|
<up-form-item label="改造内容" prop="renContent">
|
<up-textarea
|
v-model="form.renContent"
|
:disabled="isDetail || isAddOperation"
|
:placeholder="renContentPlaceholder"
|
autoHeight
|
maxlength="500"
|
/>
|
</up-form-item>
|
|
<up-form-item label="付款描述" prop="paymentDescription">
|
<up-textarea
|
v-model="form.paymentDescription"
|
:disabled="isDetail || isAddOperation"
|
placeholder="是否垫资?企业是否开票?企业是否分补贴或额外出钱?"
|
autoHeight
|
count
|
maxlength="500"
|
/>
|
</up-form-item>
|
|
<!-- 附件材料 -->
|
<view class="section-header section-header-inner">
|
<text class="section-title">附件材料</text>
|
<text class="section-subtitle" v-if="!isDetail">支持多文件上传</text>
|
</view>
|
|
<view v-if="!isDetail" class="upload-wrap">
|
<up-upload
|
:fileList="uploadFileList"
|
@afterRead="afterRead"
|
@delete="deleteUpload"
|
name="attachments"
|
multiple
|
:maxCount="10"
|
:previewImage="false"
|
>
|
<view class="upload-btn">
|
<up-icon name="plus" size="18" color="#667085"></up-icon>
|
<text class="upload-text">上传附件</text>
|
</view>
|
</up-upload>
|
</view>
|
|
<view v-if="existingFiles.length" class="existing-files">
|
<view class="existing-title">已上传</view>
|
<view v-for="f in existingFiles" :key="f.id || f.url" class="existing-item">
|
<text class="file-name">{{ f.name || getFileName(f.url) }}</text>
|
<view class="file-actions">
|
<u-button size="mini" type="primary" plain @click="downloadFile(f)">下载</u-button>
|
<u-button
|
v-if="!isDetail"
|
size="mini"
|
type="error"
|
plain
|
@click="removeExistingFile(f)"
|
>
|
删除
|
</u-button>
|
</view>
|
</view>
|
</view>
|
|
<!-- 录入信息 -->
|
<view class="section-header section-header-inner">
|
<text class="section-title">录入信息</text>
|
</view>
|
<up-form-item label="录入人" prop="entryPerson" required>
|
<up-input
|
v-model="form.entryPerson"
|
:disabled="true"
|
/>
|
</up-form-item>
|
|
<up-form-item label="录入日期" prop="entryDate" required @click="onEntryDateClick">
|
<up-input
|
v-model="form.entryDate"
|
readonly
|
:disabled="isDetail"
|
placeholder="点击选择日期"
|
@click="onEntryDateClick"
|
/>
|
<template #right>
|
<up-icon
|
name="arrow-right"
|
@click="onEntryDateClick"
|
></up-icon>
|
</template>
|
</up-form-item>
|
|
<!-- 历史记录 -->
|
<view v-if="changeHistory.length" class="change-history-section">
|
<view class="history-title">变更记录</view>
|
<view v-for="item in changeHistory" :key="item.id" class="history-item">
|
<view class="history-header">
|
<text class="history-status">{{ getStatusText(item.status) }}</text>
|
<text class="history-operator">{{ item.operator }}</text>
|
</view>
|
<view class="history-time">{{ item.timestamp }}</view>
|
<view v-if="item.description" class="history-desc">
|
{{ item.description }}
|
</view>
|
</view>
|
</view>
|
|
<!-- 底部按钮 -->
|
<view v-if="!isDetail" class="footer-btns">
|
<u-button class="cancel-btn" @click="goBack">取消</u-button>
|
<u-button class="save-btn" type="primary" @click="onSubmit" :loading="loading">保存</u-button>
|
</view>
|
</up-form>
|
</view>
|
</view>
|
|
<!-- 省份选择 -->
|
<up-action-sheet
|
:show="showProvincePicker"
|
:actions="provinceActionList"
|
title="选择省份"
|
@select="onProvinceSelect"
|
@close="showProvincePicker = false"
|
/>
|
|
<!-- 城市选择 -->
|
<up-action-sheet
|
:show="showCityPicker"
|
:actions="cityActionList"
|
title="选择城市"
|
@select="onCitySelect"
|
@close="showCityPicker = false"
|
/>
|
|
<!-- 状态选择 -->
|
<up-action-sheet
|
:show="showStatusPicker"
|
:actions="statusActionList"
|
title="选择状态"
|
@select="onStatusSelect"
|
@close="showStatusPicker = false"
|
/>
|
|
<!-- 日期选择 -->
|
<up-popup :show="showDatePicker" mode="bottom" @close="showDatePicker = false">
|
<up-datetime-picker
|
:show="true"
|
v-model="pickerDateValue"
|
mode="date"
|
@confirm="onDateConfirm"
|
@cancel="showDatePicker = false"
|
/>
|
</up-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, computed } from 'vue'
|
import { onLoad } from '@dcloudio/uni-app'
|
import dayjs from 'dayjs'
|
import PageHeader from '@/components/PageHeader.vue'
|
import useUserStore from '@/store/modules/user'
|
import {
|
addOpportunity,
|
updateOpportunity,
|
addDescription,
|
getProvinceList,
|
getCityList,
|
delCommonFile
|
} from '@/api/salesManagement/opportunityManagement.js'
|
import { getToken } from '@/utils/auth'
|
import config from '@/config.js'
|
|
const userStore = useUserStore()
|
|
const formRef = ref(null)
|
const loading = ref(false)
|
const operationType = ref('add')
|
const renovationPlaceholder = '请输入拜访记录'
|
const renContentPlaceholder = '1.标准化:\n2.定制化:\n3.外采:'
|
|
// 附件上传
|
const uploadFileList = ref([]) // up-upload 绑定列表
|
const tempFileIds = ref([]) // 提交给后端的临时文件ID
|
const existingFiles = ref([]) // 已上传的附件(详情/编辑反显)
|
|
const getFileName = (url) => {
|
try {
|
if (!url) return ''
|
return decodeURIComponent(url.split('/').pop())
|
} catch (e) {
|
return url || ''
|
}
|
}
|
|
const isImageUrl = (url) => /\.(png|jpe?g|gif|bmp|webp)$/i.test(url || '')
|
|
const toAbsoluteUrl = (url) => {
|
if (!url) return ''
|
if (/^https?:\/\//i.test(url)) return url
|
return config.baseUrl.replace(/\/$/, '') + (url.startsWith('/') ? url : `/${url}`)
|
}
|
|
// 只下载到手机(不预览)
|
const downloadFile = (file) => {
|
const url = toAbsoluteUrl(file?.url)
|
if (!url) return
|
|
// H5 直接打开链接触发下载
|
if (typeof window !== 'undefined' && window?.open) {
|
window.open(url, '_blank')
|
return
|
}
|
|
uni.showLoading({ title: '下载中...' })
|
uni.downloadFile({
|
url,
|
success: (res) => {
|
if (res.statusCode !== 200) {
|
uni.hideLoading()
|
uni.showToast({ title: '下载失败', icon: 'none' })
|
return
|
}
|
uni.saveFile({
|
tempFilePath: res.tempFilePath,
|
success: () => {
|
uni.hideLoading()
|
uni.showToast({ title: '已下载到本地', icon: 'success' })
|
},
|
fail: () => {
|
uni.hideLoading()
|
uni.showToast({ title: '保存失败', icon: 'none' })
|
}
|
})
|
},
|
fail: () => {
|
uni.hideLoading()
|
uni.showToast({ title: '下载失败', icon: 'none' })
|
}
|
})
|
}
|
|
const uploadSingle = (fileObj) => {
|
return new Promise((resolve, reject) => {
|
const filePath = fileObj?.url || fileObj?.tempFilePath || fileObj?.path
|
if (!filePath) {
|
reject(new Error('未找到可上传的文件'))
|
return
|
}
|
uni.uploadFile({
|
url: config.baseUrl + '/file/upload',
|
filePath,
|
name: 'file',
|
formData: { type: 9 },
|
header: { Authorization: 'Bearer ' + getToken() },
|
success: (res) => {
|
try {
|
const data = JSON.parse(res.data || '{}')
|
if (data.code === 200) {
|
resolve(data.data)
|
} else {
|
reject(new Error(data.msg || '上传失败'))
|
}
|
} catch (e) {
|
reject(e)
|
}
|
},
|
fail: (err) => reject(err)
|
})
|
})
|
}
|
|
const afterRead = async (event) => {
|
const files = Array.isArray(event.file) ? event.file : [event.file]
|
for (const f of files) {
|
const item = {
|
url: f.url,
|
name: f.name,
|
status: 'uploading',
|
message: '上传中...'
|
}
|
const idx = uploadFileList.value.length
|
uploadFileList.value.push(item)
|
try {
|
uni.showLoading({ title: '上传中...' })
|
const uploaded = await uploadSingle(f)
|
uni.hideLoading()
|
uploadFileList.value[idx] = {
|
...uploadFileList.value[idx],
|
status: 'success',
|
message: '',
|
url: uploaded?.url || uploadFileList.value[idx]?.url,
|
name: uploaded?.name || uploadFileList.value[idx]?.name,
|
tempId: uploaded?.tempId
|
}
|
if (uploaded?.tempId) {
|
tempFileIds.value.push(uploaded.tempId)
|
}
|
} catch (e) {
|
uni.hideLoading()
|
uploadFileList.value[idx] = {
|
...uploadFileList.value[idx],
|
status: 'failed',
|
message: '上传失败'
|
}
|
uni.showToast({ title: '上传失败', icon: 'none' })
|
}
|
}
|
}
|
|
const deleteUpload = (event) => {
|
const index = event?.index
|
if (index === undefined || index === null) return
|
const removed = uploadFileList.value[index]
|
uploadFileList.value.splice(index, 1)
|
if (removed?.tempId) {
|
const pos = tempFileIds.value.findIndex(id => String(id) === String(removed.tempId))
|
if (pos > -1) tempFileIds.value.splice(pos, 1)
|
}
|
}
|
|
const removeExistingFile = (file) => {
|
if (!file?.id) {
|
existingFiles.value = existingFiles.value.filter(f => f !== file)
|
return
|
}
|
uni.showModal({
|
title: '提示',
|
content: '确定删除该附件吗?',
|
success: async (res) => {
|
if (!res.confirm) return
|
try {
|
const resp = await delCommonFile([file.id])
|
if (resp.code === 200) {
|
uni.showToast({ title: '删除成功', icon: 'success' })
|
existingFiles.value = existingFiles.value.filter(f => f.id !== file.id)
|
} else {
|
uni.showToast({ title: resp.msg || '删除失败', icon: 'none' })
|
}
|
} catch (e) {
|
uni.showToast({ title: '删除失败', icon: 'none' })
|
}
|
}
|
})
|
}
|
|
const form = ref({
|
id: undefined,
|
status: '',
|
province: '',
|
city: '',
|
customerName: '',
|
industry: '',
|
informationState: '',
|
mainBusinessRevenue: '',
|
customerScale: '',
|
mainProducts: '',
|
businessSource: '',
|
contractAmount: '',
|
description: '',
|
renContent: '',
|
paymentDescription: '',
|
entryPerson: userStore.nickName,
|
entryDate: dayjs().format('YYYY-MM-DD'),
|
businessDescription: [],
|
businessCommonFiles: []
|
})
|
|
const rules = {
|
customerName: [
|
{ required: true, message: '请输入客户名称', trigger: ['blur', 'change'] }
|
],
|
status: [
|
{ required: true, message: '请选择状态', trigger: ['blur', 'change'] }
|
],
|
contractAmount: [
|
{ required: true, message: '请输入合同金额', trigger: ['blur', 'change'] }
|
],
|
description: [
|
{ required: true, message: '请输入拜访记录', trigger: ['blur', 'change'] }
|
],
|
entryPerson: [
|
{ required: true, message: '请输入录入人', trigger: ['blur', 'change'] }
|
],
|
entryDate: [
|
{ required: true, message: '请选择录入日期', trigger: ['blur', 'change'] }
|
]
|
}
|
|
// 状态 / 省市选项
|
const statusOptions = [
|
{ value: '新建', label: '新建' },
|
{ value: '项目跟踪', label: '项目跟踪' },
|
{ value: '放弃', label: '放弃' },
|
{ value: '合同签约', label: '合同签约' },
|
{ value: '备案申报', label: '备案申报' },
|
{ value: '项目交付', label: '项目交付' },
|
{ value: '项目验收', label: '项目验收' },
|
{ value: '项目回款', label: '项目回款' },
|
{ value: '回补贴', label: '回补贴' }
|
]
|
|
const provinceOptions = ref([])
|
const cityOptions = ref([])
|
const selectedProvinceId = ref(null)
|
|
const statusActionList = computed(() =>
|
statusOptions.map(item => ({
|
name: item.label,
|
value: item.value
|
}))
|
)
|
|
const provinceActionList = computed(() =>
|
provinceOptions.value.map(item => ({
|
name: item.name,
|
value: item.id
|
}))
|
)
|
|
const cityActionList = computed(() =>
|
cityOptions.value.map(item => ({
|
name: item.name,
|
value: item.id
|
}))
|
)
|
|
const showProvincePicker = ref(false)
|
const showCityPicker = ref(false)
|
const showStatusPicker = ref(false)
|
const showDatePicker = ref(false)
|
const pickerDateValue = ref(Date.now())
|
|
const changeHistory = ref([])
|
|
const isDetail = computed(() => operationType.value === 'detail')
|
const isAddOperation = computed(() => operationType.value === 'addOperation')
|
const isAddOrEdit = computed(() => ['add', 'edit'].includes(operationType.value))
|
const isAddOrAddOperation = computed(() => ['add', 'addOperation'].includes(operationType.value))
|
|
const pageTitle = computed(() => {
|
switch (operationType.value) {
|
case 'add':
|
return '新建商机'
|
case 'edit':
|
return '编辑商机'
|
case 'addOperation':
|
return '添加拜访记录'
|
case 'detail':
|
default:
|
return '商机详情'
|
}
|
})
|
|
const goBack = () => {
|
uni.navigateBack()
|
}
|
|
const getStatusText = (status) => {
|
const map = statusOptions.reduce((acc, cur) => {
|
acc[cur.value] = cur.label
|
return acc
|
}, {})
|
return map[status] || status || '未知'
|
}
|
|
// 加载省份
|
const loadProvinces = async () => {
|
try {
|
const res = await getProvinceList()
|
provinceOptions.value = res.data || res.records || []
|
} catch (e) {
|
console.error('获取省份列表失败:', e)
|
}
|
}
|
|
// 根据省份加载城市
|
const loadCitiesByProvinceId = async (provinceId) => {
|
if (!provinceId) {
|
cityOptions.value = []
|
return
|
}
|
try {
|
const res = await getCityList({ provinceId })
|
cityOptions.value = res.data || res.records || []
|
} catch (e) {
|
console.error('获取城市列表失败:', e)
|
}
|
}
|
|
// 省份 / 城市选择
|
const onProvinceClick = () => {
|
if (isDetail.value || isAddOperation.value) return
|
showProvincePicker.value = true
|
}
|
|
const onProvinceSelect = async (e) => {
|
selectedProvinceId.value = e.value
|
const target = provinceOptions.value.find(p => p.id === e.value)
|
form.value.province = target ? target.name : e.name
|
// 重置城市并加载城市列表
|
form.value.city = ''
|
await loadCitiesByProvinceId(e.value)
|
showProvincePicker.value = false
|
}
|
|
const onCityClick = () => {
|
if (isDetail.value || isAddOperation.value) return
|
if (!selectedProvinceId.value) {
|
uni.showToast({
|
title: '请先选择省份',
|
icon: 'none'
|
})
|
return
|
}
|
showCityPicker.value = true
|
}
|
|
const onCitySelect = (e) => {
|
const target = cityOptions.value.find(c => c.id === e.value)
|
form.value.city = target ? target.name : e.name
|
showCityPicker.value = false
|
}
|
|
// 状态选择
|
const onStatusClick = () => {
|
if (isDetail.value) return
|
showStatusPicker.value = true
|
}
|
|
const onStatusSelect = (e) => {
|
form.value.status = e.value
|
showStatusPicker.value = false
|
}
|
|
// 录入日期选择
|
const onEntryDateClick = () => {
|
if (isDetail.value) return
|
showDatePicker.value = true
|
}
|
|
const onDateConfirm = (e) => {
|
const val = e.value || e
|
form.value.entryDate = dayjs(val).format('YYYY-MM-DD')
|
showDatePicker.value = false
|
}
|
|
// 生成变更记录
|
const generateChangeHistory = (row) => {
|
const history = []
|
if (row.businessDescription && Array.isArray(row.businessDescription)) {
|
row.businessDescription.forEach((item, index) => {
|
history.push({
|
id: item.id || index,
|
timestamp: item.entryDate || item.updateTime || item.createTime,
|
operator: item.entryPerson || '系统',
|
status: item.status,
|
description: item.description
|
})
|
})
|
}
|
changeHistory.value = history
|
}
|
|
// 提交表单
|
const onSubmit = () => {
|
console.log('onSubmit called, isDetail:', isDetail.value, 'operationType:', operationType.value)
|
console.log('formRef.value:', formRef.value)
|
if (isDetail.value) {
|
console.log('Blocked: isDetail is true')
|
return
|
}
|
if (!formRef.value) {
|
console.log('Blocked: formRef is null')
|
return
|
}
|
|
console.log('Starting validation...')
|
formRef.value.validate().then(async () => {
|
loading.value = true
|
try {
|
let api
|
let params
|
console.log('Submitting with operationType:', operationType.value)
|
console.log('Form data:', form.value)
|
|
if (operationType.value === 'add') {
|
api = addOpportunity
|
params = {
|
...form.value,
|
type: 9,
|
tempFileIds: tempFileIds.value
|
}
|
} else if (operationType.value === 'edit') {
|
api = updateOpportunity
|
params = {
|
...form.value,
|
type: 9,
|
tempFileIds: tempFileIds.value
|
}
|
} else if (operationType.value === 'addOperation') {
|
api = addDescription
|
params = {
|
status: form.value.status,
|
description: form.value.description,
|
paymentDescription: form.value.paymentDescription,
|
entryPerson: form.value.entryPerson,
|
entryDate: form.value.entryDate,
|
type: 9,
|
businessOpportunityId: form.value.id,
|
tempFileIds: tempFileIds.value
|
}
|
}
|
|
const res = await api(params)
|
if (res.code === 200) {
|
uni.showToast({
|
title: '操作成功',
|
icon: 'success'
|
})
|
setTimeout(() => {
|
goBack()
|
}, 500)
|
} else {
|
uni.showToast({
|
title: res.msg || '操作失败',
|
icon: 'none'
|
})
|
}
|
} catch (e) {
|
console.error('商机操作失败:', e)
|
uni.showToast({
|
title: '操作失败,请重试',
|
icon: 'none'
|
})
|
} finally {
|
loading.value = false
|
}
|
}).catch((err) => {
|
console.log('Validation failed:', err)
|
uni.showToast({
|
title: '请检查表单填写是否正确',
|
icon: 'none'
|
})
|
})
|
}
|
|
onLoad(async () => {
|
// 读取操作类型和数据
|
const type = uni.getStorageSync('opportunityOperationType') || 'add'
|
operationType.value = type
|
console.log('onLoad - operationType set to:', operationType.value)
|
|
// 加载省份列表
|
await loadProvinces()
|
|
const raw = uni.getStorageSync('opportunityData')
|
let row = null
|
|
// 兼容多种存储形式:字符串 / 对象 / null
|
if (raw) {
|
try {
|
if (typeof raw === 'string') {
|
row = JSON.parse(raw)
|
} else if (typeof raw === 'object') {
|
row = raw
|
}
|
} catch (e) {
|
console.error('解析商机数据失败:', e)
|
row = null
|
}
|
}
|
|
if (row && typeof row === 'object' && !Array.isArray(row)) {
|
try {
|
// 保留已有字段,避免覆盖未在表单中编辑的字段
|
form.value = Object.assign({}, form.value, row)
|
|
// 兼容后端返回 null,避免组件内部读 length 报错
|
const nullToEmpty = (v) => (v === null || v === undefined ? '' : v)
|
form.value.status = nullToEmpty(form.value.status)
|
form.value.province = nullToEmpty(form.value.province)
|
form.value.city = nullToEmpty(form.value.city)
|
form.value.customerName = nullToEmpty(form.value.customerName)
|
form.value.businessSource = nullToEmpty(form.value.businessSource)
|
form.value.industry = nullToEmpty(form.value.industry)
|
form.value.mainProducts = nullToEmpty(form.value.mainProducts)
|
form.value.mainBusinessRevenue = nullToEmpty(form.value.mainBusinessRevenue)
|
form.value.customerScale = nullToEmpty(form.value.customerScale)
|
form.value.informationState = nullToEmpty(form.value.informationState)
|
form.value.contractAmount = form.value.contractAmount !== null && form.value.contractAmount !== undefined ? String(form.value.contractAmount) : ''
|
form.value.description = nullToEmpty(form.value.description)
|
form.value.renContent = nullToEmpty(form.value.renContent)
|
form.value.paymentDescription = nullToEmpty(form.value.paymentDescription)
|
form.value.entryPerson = nullToEmpty(form.value.entryPerson)
|
form.value.entryDate = nullToEmpty(form.value.entryDate)
|
form.value.businessDescription = Array.isArray(form.value.businessDescription) ? form.value.businessDescription : []
|
form.value.businessCommonFiles = Array.isArray(form.value.businessCommonFiles) ? form.value.businessCommonFiles : []
|
|
// 反显附件
|
existingFiles.value = form.value.businessCommonFiles
|
uploadFileList.value = []
|
tempFileIds.value = []
|
|
// 反显省份和城市
|
if (row.province) {
|
const provinceMatch = provinceOptions.value.find(p => p.name === row.province || String(p.id) === String(row.province))
|
if (provinceMatch) {
|
selectedProvinceId.value = provinceMatch.id
|
form.value.province = provinceMatch.name
|
await loadCitiesByProvinceId(provinceMatch.id)
|
if (row.city) {
|
const cityMatch = cityOptions.value.find(c => c.name === row.city || String(c.id) === String(row.city))
|
form.value.city = cityMatch ? cityMatch.name : row.city
|
}
|
} else {
|
form.value.province = row.province
|
form.value.city = row.city || ''
|
}
|
} else {
|
form.value.province = ''
|
form.value.city = row.city || ''
|
}
|
|
if (!form.value.entryPerson) {
|
form.value.entryPerson = userStore.nickName
|
}
|
if (!form.value.entryDate) {
|
form.value.entryDate = dayjs().format('YYYY-MM-DD')
|
}
|
generateChangeHistory(row)
|
} catch (e) {
|
console.error('处理商机数据失败:', e)
|
}
|
} else {
|
// 新建模式默认录入信息
|
form.value.entryPerson = userStore.nickName
|
form.value.entryDate = dayjs().format('YYYY-MM-DD')
|
existingFiles.value = []
|
uploadFileList.value = []
|
tempFileIds.value = []
|
}
|
})
|
</script>
|
|
<style scoped lang="scss">
|
@import '@/styles/sales-common.scss';
|
|
.account-detail {
|
min-height: 100vh;
|
background: #f8f9fa;
|
padding-bottom: 80px;
|
}
|
|
.detail-body {
|
padding: 12px 12px 0;
|
}
|
|
.detail-card {
|
background: #ffffff;
|
border-radius: 16px;
|
box-shadow: 0 8px 24px rgba(15, 35, 52, 0.06);
|
padding: 8px 16px 16px;
|
}
|
|
.section-header {
|
margin: 8px 0 4px;
|
}
|
|
.section-header-inner {
|
margin-top: 18px;
|
}
|
|
.section-title {
|
font-size: 15px;
|
font-weight: 600;
|
color: #1f2933;
|
}
|
|
.section-subtitle {
|
margin-left: 8px;
|
font-size: 12px;
|
color: #9ca3af;
|
}
|
|
.footer-btns {
|
display: flex;
|
gap: 12px;
|
padding: 20px;
|
}
|
|
.cancel-btn {
|
flex: 1;
|
}
|
|
.save-btn {
|
flex: 1;
|
}
|
|
.change-history-section {
|
padding: 16px 20px 0 20px;
|
}
|
|
.history-title {
|
font-size: 14px;
|
font-weight: 600;
|
color: #333;
|
margin-bottom: 8px;
|
}
|
|
.history-item {
|
background: #ffffff;
|
border-radius: 8px;
|
padding: 12px;
|
margin-bottom: 8px;
|
}
|
|
.history-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 4px;
|
}
|
|
.history-status {
|
font-size: 13px;
|
color: #2979ff;
|
font-weight: 500;
|
}
|
|
.history-operator {
|
font-size: 12px;
|
color: #999;
|
}
|
|
.history-time {
|
font-size: 12px;
|
color: #999;
|
margin-bottom: 4px;
|
}
|
|
.history-desc {
|
font-size: 13px;
|
color: #333;
|
line-height: 1.5;
|
}
|
|
.upload-wrap {
|
padding: 6px 0 2px;
|
}
|
|
.upload-btn {
|
height: 40px;
|
padding: 0 12px;
|
border-radius: 10px;
|
background: #f2f4f7;
|
display: inline-flex;
|
align-items: center;
|
gap: 6px;
|
}
|
|
.upload-text {
|
font-size: 13px;
|
color: #475467;
|
}
|
|
.existing-files {
|
margin-top: 10px;
|
}
|
|
.existing-title {
|
font-size: 12px;
|
color: #98a2b3;
|
margin-bottom: 6px;
|
}
|
|
.existing-item {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 10px 0;
|
border-top: 1px solid #f2f4f7;
|
gap: 10px;
|
}
|
|
.file-name {
|
flex: 1;
|
font-size: 13px;
|
color: #1f2933;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
|
.file-actions {
|
display: flex;
|
gap: 6px;
|
}
|
|
// 表单细节优化
|
:deep(.u-form) {
|
.u-form-item__body {
|
padding: 6px 0;
|
}
|
}
|
|
:deep(.u-form-item__label) {
|
font-size: 13px;
|
color: #6b7280;
|
}
|
|
:deep(.u-input__content__field) {
|
font-size: 14px;
|
}
|
|
:deep(.u-textarea__field) {
|
font-size: 14px;
|
}
|
</style>
|