feat: 导入增强 + 客户合并 + 同伴自定义 + 性能优化
=== 导入系统全面增强 === - 5 Sheet 完整导入:拜访/计划/商机/要客/纪要 - 同访人智能解析:中英文逗号/顿号/分号 → 系统用户UUID + 外部人员TEXT - 客户自动创建:Excel中不存在的客户自动入库 - 模糊名称匹配:去空格 + 包含关系纠错 - manager_id 修正:导入时用客户分配的经理(非Excel列/非导入人) - 模板更新:Sheet5「客户经理」→「填报人」,同访人示例含逗号分隔 === 客户合并功能 === - PUT 改名碰撞检测 → 409 + 合并预览 - GET merge-preview / POST merge 端点 - 事务级迁移:Visit/WorkPlan/MiniBusiness/KeyVisit/联系人/分配 - 去重逻辑:联系人(name+phone)、分配(manager+role) - last_visit_date 取最大值 === 拜访记录完善 === - visits 新增 companion_names TEXT[] 列 - 同访人支持自定义输入(外部人员),el-select allow-create - 移动端 VisitForm + PC端 ManagerWorkspace 统一 - 周报「客户经理」→「相关人员」(创建人+同访人) - 纪要「客户经理」→「填报人」 - 删除拜访后重新计算 customer.last_visit_date === 客户选择放开 === - 客户经理可看到全部客户(不再限自己分配的) - 拜访时客户下拉返回全量 === 前端性能优化 === - Element Plus 按需加载 (unplugin-vue-components + unplugin-element-plus) - 周报照片URL并行请求 (Promise.all) - onMounted 三路并行 (loadReport + dropdowns + AI) - nginx Cache-Control: immutable for /assets/ - Google Fonts preconnect hints - 图片上传前 Canvas 压缩 (max 1920px, quality 0.8) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { todayStr } from '@/utils'
|
||||
import { visitsApi } from '@/api/visits'
|
||||
import { customersApi } from '@/api/customers'
|
||||
import { uploadApi } from '@/api/upload'
|
||||
import { compressImage } from '@/utils/image'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import ImagePreview from '@/components/ImagePreview.vue'
|
||||
import EditLogPanel from '@/components/EditLogPanel.vue'
|
||||
@@ -121,8 +122,10 @@ async function handlePhotoUpload(event: Event) {
|
||||
const previewUrl = URL.createObjectURL(file)
|
||||
photoPreviews.value.push(previewUrl)
|
||||
try {
|
||||
const res = await uploadApi.getPresignedUrl(file.name, file.type || 'image/jpeg')
|
||||
await uploadApi.uploadFile(res.data.upload_url, file)
|
||||
// Compress before upload to reduce storage & transfer
|
||||
const compressed = await compressImage(file, { maxPixels: 1920, quality: 0.8 })
|
||||
const res = await uploadApi.getPresignedUrl(compressed.name, compressed.type || 'image/jpeg')
|
||||
await uploadApi.uploadFile(res.data.upload_url, compressed)
|
||||
uploadedPhotos.value.push(res.data.object_key)
|
||||
form.value.photos = [...uploadedPhotos.value]
|
||||
} catch (e: any) {
|
||||
@@ -154,15 +157,28 @@ function removePhoto(index: number) {
|
||||
form.value.photos = [...uploadedPhotos.value]
|
||||
}
|
||||
|
||||
function splitCompanions(values: string[]) {
|
||||
// UUIDs → companions (system users), non-UUID strings → companion_names (external)
|
||||
const uuidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
||||
const sys: string[] = []; const ext: string[] = []
|
||||
for (const v of values) {
|
||||
if (uuidRe.test(v)) sys.push(v)
|
||||
else if (v.trim()) ext.push(v.trim())
|
||||
}
|
||||
return { companions: sys, companion_names: ext }
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.value.customer_id) { ElMessage.warning('请选择客户'); return }
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const { companions, companion_names } = splitCompanions(form.value.companions || [])
|
||||
const body = { ...form.value, companions, companion_names }
|
||||
if (isEdit.value) {
|
||||
await visitsApi.update(route.params.id as string, form.value)
|
||||
await visitsApi.update(route.params.id as string, body)
|
||||
ElMessage.success('记录已更新')
|
||||
} else {
|
||||
await visitsApi.create(form.value)
|
||||
await visitsApi.create(body)
|
||||
ElMessage.success('拜访记录已提交')
|
||||
}
|
||||
router.push('/m')
|
||||
@@ -269,9 +285,9 @@ async function handleDelete() {
|
||||
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="form-label">同访人员</span>
|
||||
<span class="form-label">同访人员 <span class="form-label-hint">可输入外部人员</span></span>
|
||||
</template>
|
||||
<el-select v-model="form.companions" multiple filterable placeholder="选择同访人员" style="width:100%">
|
||||
<el-select v-model="form.companions" multiple filterable allow-create default-first-option placeholder="选择或输入姓名(可多选)" style="width:100%">
|
||||
<el-option v-for="m in managers" :key="m.id" :label="m.name" :value="m.id" :disabled="m.id === auth.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
Reference in New Issue
Block a user