feat(用户主页): 添加客户信息自动填充功能

新增从URL参数获取wecom_id并自动填充客户信息的功能,优化表单数据回显逻辑。同时简化文件上传和提交逻辑,移除冗余代码。
This commit is contained in:
2026-03-17 19:26:30 +08:00
parent a22526c820
commit 2f2152b008

View File

@@ -215,10 +215,9 @@
</div>
</n-config-provider>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { reactive, ref, onMounted } from 'vue' // 👉 增加 onMounted
import { useRoute, useRouter } from 'vue-router'
import {
useMessage, zhCN, dateZhCN, NConfigProvider, NCard, NSpace, NGrid, NGridItem,
NForm, NFormItem, NInput, NInputNumber, NDatePicker, NUpload, NUploadDragger,
@@ -231,10 +230,10 @@ import {
CreateOutline, CloseOutline, CheckmarkCircleOutline
} from '@vicons/ionicons5'
// 👉 引入你项目中的请求封装文件
import http from '@/utils/request'
const router = useRouter()
const route = useRoute() // 👉 用于获取 URL 中的参数
const message = useMessage()
const formRef = ref(null)
@@ -255,152 +254,118 @@ const formData = reactive({
signatureFileList: []
})
// 表单校验规则
const rules = {
analystName: [{ required: true, message: '请输入分析师姓名', trigger: 'blur' }],
parentName: [{ required: true, message: '请输入家长姓名', trigger: 'blur' }],
parentPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
transactionAmount: [{ required: true, type: 'number', message: '请输入成交金额', trigger: 'blur' }],
}
// 预览功能
const handlePreview = (file) => {
// 优先预览从服务端获取到的 url
if (file.url) {
window.open(file.url)
} else if (file.file) {
const url = URL.createObjectURL(file.file)
window.open(url)
}
}
// 退出登录
const logout = () => {
localStorage.clear()
message.info('已退出系统')
// router.push('/login')
}
// 取消
const handleCancel = () => {
message.info('操作已取消')
}
// 👉 自定义文件上传逻辑
const customUpload = async ({ file, data, headers, onFinish, onError, onProgress }) => {
// 1. 构造 FormData 对象
const uploadData = new FormData()
// naive-ui 的 file 是一个包装对象,原生的 File 对象在 file.file 属性中
uploadData.append('file', file.file)
// 如果组件传入了额外数据,也追加进去
if (data) {
Object.keys(data).forEach((key) => {
uploadData.append(key, data[key])
})
}
// 👉 新增:初始化获取数据的方法
const fetchCustomerInfo = async () => {
// 优先从 URL 获取 wecom_id如果没有则使用你提供的测试 ID
const wecom_id = route.query.wecom_id || 'wmcr-ECwAAzKclEfIKNcVgOdxD-TcqLg'
const d = message.loading('正在加载客户信息...')
try {
// 2. 调用封装好的上传接口 (注意:这里直接写 /v1/material/upload前提是 utils/request.js 中配置好了 /api 等前缀)
const res = await http.post('/v1/material/upload', uploadData, {
headers: {
'Content-Type': 'multipart/form-data',
...headers
},
// 监听上传进度,并在页面上显示绿色进度条
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
onProgress({ percent: percentCompleted })
}
})
const res = await http.get('/v1/customer/get_customers_info', { wecom_id })
// 3. 上传成功后处理数据
// 根据你 utils/request.js 的响应拦截器逻辑,`res` 已经是接口的 `data` 部分了
// 这里把后台返回的 url 赋值给当前文件对象,便于后续图片预览及提交时抽取数据
file.url = res.url
file.object_name = res.object_name // 保留一下 object_name以防后台提交需要用到
// 👉 数据映射回显
formData.parentName = res.customerName
formData.parentPhone = res.customerPhone
formData.analystName = res.analystName
// 4. 通知组件上传成功
onFinish()
// 数组取第一个
formData.analystDepartment = res.analystDepartmentName?.[0] || ''
formData.analystSupervisor = res.analystDepartmentLeader?.[0] || ''
// 金额转数字
formData.transactionAmount = res.dealAmount ? Number(res.dealAmount) : null
// 日期字符串转时间戳
formData.transactionDate = res.dealDate ? new Date(res.dealDate).getTime() : null
formData.analystNotes = res.notes || ''
// 👉 回显付款截图 (将 URL 数组转为 Upload 组件需要的对象格式)
if (res.proofOfPayment && Array.isArray(res.proofOfPayment)) {
formData.paymentFileList = res.proofOfPayment.map((url, index) => ({
id: 'init-' + index,
name: '已上传凭证-' + (index + 1),
status: 'finished',
url: url
}))
}
message.success('客户资料已自动填充')
} catch (error) {
console.error('上传失败:', error)
// 5. 通知组件上传失败,变红
onError()
message.error(`${file.name} 上传失败,请重试`)
console.error('获取初始信息失败:', error)
message.error('无法获取客户基础信息')
} finally {
d.destroy()
}
}
// 👉 提交表单数据
// 👉 修改后的提交方法
// 👉 页面加载时执行
onMounted(() => {
fetchCustomerInfo()
})
// --- 以下是之前的逻辑,保持不变 ---
// 自定义上传逻辑
const customUpload = async ({ file, onFinish, onError, onProgress }) => {
const uploadData = new FormData()
uploadData.append('file', file.file)
try {
const res = await http.post('/v1/material/upload', uploadData, {
onUploadProgress: (p) => onProgress({ percent: Math.round((p.loaded * 100) / p.total) })
})
file.url = res.url
onFinish()
} catch (e) {
onError()
message.error('上传失败')
}
}
// 最终提交逻辑
const handleSubmit = () => {
formRef.value?.validate(async (errors) => {
if (!errors) {
// 1. 检查文件上传状态
const allFiles = [
...formData.documentFileList,
...formData.paymentFileList,
...formData.signatureFileList
]
const isUploading = allFiles.some(file => file.status === 'uploading')
const hasError = allFiles.some(file => file.status === 'error')
if (isUploading) return message.warning('文件正在上传中,请稍候')
if (hasError) return message.error('部分文件上传失败,请处理后再提交')
// 2. 构造符合后端接口要求的 JSON 对象
// 注意接口字段为下划线命名且文件URL要求为字符串
const submitPayload = {
// 从缓存获取 wecom_id (如果没有则传空字符串或从路由获取)
wecom_id: localStorage.getItem('wecom_id') || 'default_user',
// 文件 URL 处理:取第一个,或者用逗号拼接(根据你接口单数命名的理解,通常传第一个或拼接)
wecom_id: route.query.wecom_id || 'wmcr-ECwAAvBzsjWQ6RQZuXmxzXHMLiQ',
payment_image_url: formData.paymentFileList.map(f => f.url).filter(Boolean).join(','),
signature_image_url: formData.signatureFileList.map(f => f.url).filter(Boolean).join(','),
attachment_file_url: formData.documentFileList.map(f => f.url).filter(Boolean).join(','),
// 基础文本信息
analyst_supervisor: formData.analystSupervisor,
analyst_department: formData.analystDepartment,
analyst_name: formData.analystName,
parent_name: formData.parentName,
parent_phone: formData.parentPhone,
parent_id_card: formData.parentIdCard,
// 指导周期
guidance_period: formData.guidancePeriod,
// 特殊处理:金额转为字符串
transaction_amount: String(formData.transactionAmount || '0'),
// 特殊处理:日期转为 YYYY-MM-DD 字符串
transaction_date: formData.transactionDate
? new Date(formData.transactionDate).toISOString().split('T')[0]
: ''
transaction_date: formData.transactionDate ? new Date(formData.transactionDate).toISOString().split('T')[0] : '',
transaction_amount: String(formData.transactionAmount || ''),
guidance_period: formData.guidancePeriod
}
const d = message.loading('正在提报材料...', { duration: 0 })
const d = message.loading('提交中...')
try {
// 3. 调用提交接口
// 你的 request.js baseURL 已经包含 /api所以这里写相对路径
const res = await http.post('/v1/material/submit', submitPayload)
d.destroy()
await http.post('/v1/material/submit', submitPayload)
message.success('提报成功!')
console.log('提交结果:', res)
// 4. 提交成功后的操作:比如跳转或重置表单
// router.push('/success')
} catch (error) {
} catch (e) {
console.error(e)
} finally {
d.destroy()
// 错误提示已在 request.js 拦截器处理,这里可以做特定逻辑
console.error('提报失败', error)
}
} else {
message.error('请完善表单必填项')
}
})
}
const handlePreview = (file) => {
window.open(file.url)
}
const logout = () => { localStorage.clear(); message.info('已退出'); }
const handleCancel = () => { message.info('已取消'); }
const rules = {
analystName: [{ required: true, message: '必填', trigger: 'blur' }],
parentName: [{ required: true, message: '必填', trigger: 'blur' }],
parentPhone: [{ required: true, message: '必填', trigger: 'blur' }],
}
</script>
<style scoped>