feat: 操作日志系统 + 日期修复 + 协同去重 + 主题持久化 + 计划自动完成
本次累积提交包含以下功能: 1. 修复:el-date-picker 缺失 value-format 导致日期保存偏移(8处) 2. 新增:协同拜访 visit_group_id 去重机制(完整副本 + 周报合并 + 亮灯去重) 3. 新增:计划自动完成(创建计划检测已有拜访 / 编辑拜访触发) 4. 新增:拜访表单选择客户后自动预填过期计划内容 5. 新增:移动端主题切换按钮 + 后端持久化 6. 新增:操作日志系统 (audit_logs),全覆盖 8 个模块 CRUD 7. 新增:PC端「我的数据」支局长/领导视角客户经理列 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,15 @@
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { todayStr } from '@/utils'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { getMgrStyle } from '@/utils/managerColor'
|
||||
import api from '@/api/index'
|
||||
import { compressImage } from '@/utils/image'
|
||||
import ImagePreview from '@/components/ImagePreview.vue'
|
||||
import EditLogPanel from '@/components/EditLogPanel.vue'
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const activeTab = ref('visits')
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -238,6 +242,17 @@ function onDialogTimeChange(val: [string, string] | null) {
|
||||
form.value.time_range = val ? val.join('-') : ''
|
||||
}
|
||||
|
||||
async function onDialogCustomerChange(customerId: string) {
|
||||
// Only pre-fill for new visit creation (not edit)
|
||||
if (dialogMode.value !== 'create' || dialogType.value !== 'visit' || !customerId) return
|
||||
try {
|
||||
const planRes = await api.get(`/customers/${customerId}/overdue-plans`)
|
||||
if (planRes.data?.plan?.plan_content) {
|
||||
form.value.communication_content = planRes.data.plan.plan_content
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Quick status change
|
||||
async function quickStatusChange(type: string, row: any, newStatus: string) {
|
||||
try {
|
||||
@@ -295,6 +310,11 @@ const notesByDate = computed(() => {
|
||||
<el-table-column prop="customer_name" label="客户" width="130">
|
||||
<template #default="{ row }"><el-link type="primary" :underline="false" @click="openEdit('visit', row)">{{ row.customer_name }}</el-link></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="auth.isDirector || auth.isLeader" label="客户经理" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :style="getMgrStyle(row.manager_name || '', false)" effect="dark" size="small" disable-transitions>{{ row.manager_name || '-' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="visit_method" label="方式" width="60" />
|
||||
<el-table-column prop="time_range" label="时间" width="100" />
|
||||
<el-table-column label="照片" width="60">
|
||||
@@ -326,6 +346,11 @@ const notesByDate = computed(() => {
|
||||
</h4>
|
||||
<el-table :data="items" stripe size="small" v-column-resize>
|
||||
<el-table-column prop="category" label="分类" width="100"><template #default="{ row }"><el-tag size="small">{{ row.category }}</el-tag></template></el-table-column>
|
||||
<el-table-column v-if="auth.isDirector || auth.isLeader" label="客户经理" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag :style="getMgrStyle(row.manager_name || '', false)" effect="dark" size="small" disable-transitions>{{ row.manager_name || '-' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="工作内容" min-width="250" show-overflow-tooltip>
|
||||
<template #default="{ row }"><el-link type="primary" :underline="false" @click="openEdit('note', row)">{{ row.content }}</el-link></template>
|
||||
</el-table-column>
|
||||
@@ -447,13 +472,13 @@ const notesByDate = computed(() => {
|
||||
<el-dialog v-model="dialogVisible" :title="(dialogMode === 'create' ? '新建' : '编辑') + ' ' + typeLabel(dialogType)" width="520px">
|
||||
<el-form label-position="top" v-if="form">
|
||||
<el-form-item v-if="['visit','plan','mini','key'].includes(dialogType)" label="客户单位">
|
||||
<el-select v-model="form.customer_id" filterable remote :remote-method="(q: string) => loadCustomers(q)" placeholder="搜索选择客户" style="width:100%">
|
||||
<el-select v-model="form.customer_id" filterable remote :remote-method="(q: string) => loadCustomers(q)" placeholder="搜索选择客户" style="width:100%" @change="onDialogCustomerChange">
|
||||
<el-option v-for="c in customers" :key="c.id" :label="c.name" :value="c.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="dialogType === 'visit'" label="日期"><el-date-picker v-model="form.visit_date" type="date" style="width:100%" /></el-form-item>
|
||||
<el-form-item v-if="dialogType === 'note'" label="日期"><el-date-picker v-model="form.note_date" type="date" style="width:100%" /></el-form-item>
|
||||
<el-form-item v-if="dialogType === 'plan'" label="计划拜访时间"><el-date-picker v-model="form.plan_date" type="date" style="width:100%" /></el-form-item>
|
||||
<el-form-item v-if="dialogType === 'visit'" label="日期"><el-date-picker v-model="form.visit_date" type="date" value-format="YYYY-MM-DD" style="width:100%" /></el-form-item>
|
||||
<el-form-item v-if="dialogType === 'note'" label="日期"><el-date-picker v-model="form.note_date" type="date" value-format="YYYY-MM-DD" style="width:100%" /></el-form-item>
|
||||
<el-form-item v-if="dialogType === 'plan'" label="计划拜访时间"><el-date-picker v-model="form.plan_date" type="date" value-format="YYYY-MM-DD" style="width:100%" /></el-form-item>
|
||||
<template v-if="dialogType === 'visit'">
|
||||
<el-form-item label="拜访方式">
|
||||
<div class="method-grid">
|
||||
|
||||
Reference in New Issue
Block a user