diff --git a/.env b/.env
index cdbb61806..21ce00ac1 100644
--- a/.env
+++ b/.env
@@ -25,4 +25,7 @@ VITE_APP_DEFAULT_LOGIN_USERNAME = admin
VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
# 租户开关
-VUE_APP_TENANT_ENABLE=false
\ No newline at end of file
+VUE_APP_TENANT_ENABLE=false
+
+# 腾讯地图key
+VITE_APP_TENCENT_MAP_KEY= EE5BZ-SMO63-I6G3P-OL4UN-BE34O-QCBOS
\ No newline at end of file
diff --git a/.env.local b/.env.local
index 1a84e6942..aa7982fa5 100644
--- a/.env.local
+++ b/.env.local
@@ -4,7 +4,7 @@ NODE_ENV=development
VITE_DEV=true
# 请求路径
-VITE_BASE_URL='http://localhost:48080'
+VITE_BASE_URL='http://www.woyaoshouchong.asia:48080'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
VITE_UPLOAD_TYPE=client
diff --git a/.env.test b/.env.test
index b4bef42a8..3436f5776 100644
--- a/.env.test
+++ b/.env.test
@@ -1,10 +1,10 @@
# 测试环境:只在打包时使用
NODE_ENV=production
-VITE_DEV=false
+VITE_DEV=true
# 请求路径
-VITE_BASE_URL='http://www.woyaoshouchong.asia:48080'
+VITE_BASE_URL='http://www.woyaoshouchong.asia:88'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
VITE_UPLOAD_TYPE=client
@@ -13,16 +13,16 @@ VITE_UPLOAD_TYPE=client
VITE_API_URL=/admin-api
# 是否删除debugger
-VITE_DROP_DEBUGGER=true
+VITE_DROP_DEBUGGER=false
# 是否删除console.log
-VITE_DROP_CONSOLE=true
+VITE_DROP_CONSOLE=false
# 是否sourcemap
-VITE_SOURCEMAP=false
+VITE_SOURCEMAP=true
# 打包路径
-VITE_BASE_PATH=/admin-ui-vue3/
+VITE_BASE_PATH=/
# 输出路径
VITE_OUT_DIR=dist-test
diff --git a/src/api/crm/followup/index.ts b/src/api/crm/followup/index.ts
index 8071f7284..a3bc03c25 100644
--- a/src/api/crm/followup/index.ts
+++ b/src/api/crm/followup/index.ts
@@ -35,6 +35,16 @@ export const FollowUpRecordApi = {
return await request.get({ url: `/crm/follow-up-record/page`, params })
},
+ // 查询单条跟进记录
+ getFollowUpRecord: async (id: number) => {
+ return await request.get({ url: `/crm/follow-up-record/get?id=${id}` })
+ },
+
+ // 更新跟进记录
+ updateFollowUpRecord: async (data: FollowUpRecordVO) => {
+ return await request.put({ url: `/crm/follow-up-record/update`, data })
+ },
+
// 新增跟进记录
createFollowUpRecord: async (data: FollowUpRecordVO) => {
return await request.post({ url: `/crm/follow-up-record/create`, data })
diff --git a/src/views/crm/backlog/components/ContractRemindList.vue b/src/views/crm/backlog/components/ContractRemindList.vue
index 0cacf3597..4957949d0 100644
--- a/src/views/crm/backlog/components/ContractRemindList.vue
+++ b/src/views/crm/backlog/components/ContractRemindList.vue
@@ -49,17 +49,6 @@
-
-
-
- {{ scope.row.businessName }}
-
-
-
编辑
-
+
diff --git a/src/views/crm/customer/components/FileForm.vue b/src/views/crm/customer/components/FileForm.vue
index d5aa4cca2..b2f7bd4e3 100644
--- a/src/views/crm/customer/components/FileForm.vue
+++ b/src/views/crm/customer/components/FileForm.vue
@@ -47,11 +47,19 @@ const uploadRef = ref()
const props = defineProps<{ customerId: number }>()
-const { uploadUrl, httpRequest } = useUpload(undefined, 'crm_consumer', props.customerId )
+const { uploadUrl, httpRequest } = useUpload(undefined, 'crm_consumer', Number(props.customerId))
+// 保证 data.value.categoryId 始终为最新 customerId
+watch(() => props.customerId, (val) => {
+ if (val && Number(val) >= 0) {
+ data.value.categoryId = Number(val)
+ } else {
+ data.value.categoryId = undefined
+ }
+})
/** 打开弹窗 */
-const open = (customerId: number) => {
- data.value.categoryId = customerId
+const open = (customerId: number | string) => {
+ data.value.categoryId = customerId && Number(customerId) > 0 ? Number(customerId) : undefined
dialogVisible.value = true
resetForm()
}
@@ -66,7 +74,7 @@ const resetForm = () => {
const handleFileChange = (file) => {
data.value.path = file.name
data.value.category = 'crm_consumer'
- data.value.categoryId = props.customerId
+ data.value.categoryId = Number(props.customerId)
}
/** 提交上传 */
diff --git a/src/views/crm/customer/components/FileList.vue b/src/views/crm/customer/components/FileList.vue
index b04d1dc77..a8190a50b 100644
--- a/src/views/crm/customer/components/FileList.vue
+++ b/src/views/crm/customer/components/FileList.vue
@@ -101,7 +101,7 @@
link
type="danger"
@click="handleDelete(scope.row.id)"
- v-hasPermi="['infra:file:delete']"
+ v-hasPermi="['crm:customer:file:delete']"
>
删除
diff --git a/src/views/crm/customer/detail/index.vue b/src/views/crm/customer/detail/index.vue
index f64d1fa0d..93e65fb9b 100644
--- a/src/views/crm/customer/detail/index.vue
+++ b/src/views/crm/customer/detail/index.vue
@@ -8,7 +8,7 @@
>
编辑
-
+
转移
@@ -108,13 +93,13 @@ const formData = ref({
const formRules = reactive({
type: [{ required: true, message: '跟进类型不能为空', trigger: 'change' }],
content: [{ required: true, message: '跟进内容不能为空', trigger: 'blur' }],
- nextTime: [{ required: true, message: '下次联系时间不能为空', trigger: 'blur' }],
contactIds: [{ required: true, message: '联系人不能为空', trigger: 'change' }]
})
const formRef = ref() // 表单 Ref
const contactOptions = ref([])
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
watch(
() => formData.value.bizId,
@@ -123,19 +108,38 @@ watch(
// 获取客户下联系人
const res = await ContactApi.getContactPageByCustomer({ customerId: val, pageSize: 100 })
contactOptions.value = res.list || []
+ // 自动选中第一个联系人
+ if (contactOptions.value.length > 0) {
+ formData.value.contactIds = [contactOptions.value[0].id]
+ } else {
+ formData.value.contactIds = []
+ }
} else {
contactOptions.value = []
+ formData.value.contactIds = []
}
},
{ immediate: true }
)
/** 打开弹窗 */
-const open = async (bizType: number, bizId: number) => {
+const open = async (bizType: number, bizId: number, id?: number) => {
dialogVisible.value = true
resetForm()
formData.value.bizType = bizType
formData.value.bizId = bizId
+ if (id) {
+ formType.value = 'update'
+ formLoading.value = true
+ try {
+ const data = await FollowUpRecordApi.getFollowUpRecord(id)
+ Object.assign(formData.value, data)
+ } finally {
+ formLoading.value = false
+ }
+ } else {
+ formType.value = 'create'
+ }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
@@ -156,7 +160,15 @@ const submitForm = async () => {
contactIds,
businessIds: (formData.value.businesses || []).map((item: any) => item.id)
} as unknown as FollowUpRecordVO
- await FollowUpRecordApi.createFollowUpRecord(data)
+
+ if (formType.value === 'create') {
+ await FollowUpRecordApi.createFollowUpRecord(data)
+ message.success(t('common.createSuccess'))
+ } else {
+ await FollowUpRecordApi.updateFollowUpRecord(data)
+ message.success(t('common.updateSuccess'))
+ }
+
message.success(t('common.createSuccess'))
dialogVisible.value = false
emit('success')
diff --git a/src/views/crm/followup/index.vue b/src/views/crm/followup/index.vue
index fbd0990fc..e50dbc508 100644
--- a/src/views/crm/followup/index.vue
+++ b/src/views/crm/followup/index.vue
@@ -15,6 +15,7 @@
:timestamp="formatDate(item.createTime)"
placement="top"
>
+ 跟进人:{{ item.creatorName }}
跟进时间
{{ formatDate(item.createTime) }}
@@ -65,11 +66,12 @@
- 跟进人:{{ item.creatorName }}
-
+
+ 编辑
+
- 删除
-
+ 删除
+
{{ item.creatorName ? item.creatorName[0] : '' }}
@@ -129,8 +131,13 @@ const getList = async () => {
/** 添加/修改操作 */
const formRef = ref>()
-const openForm = () => {
- formRef.value?.open(props.bizType, props.bizId)
+// 支持编辑和新增
+const openForm = (type: 'create' | 'update' = 'create', id?: number) => {
+ if (type === 'update' && id) {
+ formRef.value?.open(props.bizType, props.bizId, id)
+ } else {
+ formRef.value?.open(props.bizType, props.bizId)
+ }
}
/** 删除按钮操作 */
@@ -264,4 +271,24 @@ watch(
vertical-align: middle;
}
}
+
+ .followup-update-btn {
+ color: #7492f3 !important;
+ font-weight: bold;
+ font-size: 14px;
+ transition: background 0.2s, color 0.2s;
+ border: none;
+ background: #fef0f0;
+ border-radius: 4px;
+ padding: 2px 10px;
+ margin: 10px 0 0 0;
+ &:hover {
+ background: #fef0f0;
+ color: #c0392b !important;
+ }
+ .icon {
+ font-size: 15px;
+ vertical-align: middle;
+ }
+}
diff --git a/src/views/crm/permission/components/TransferForm.vue b/src/views/crm/permission/components/TransferForm.vue
index 43d5af74b..a97abbbca 100644
--- a/src/views/crm/permission/components/TransferForm.vue
+++ b/src/views/crm/permission/components/TransferForm.vue
@@ -36,13 +36,6 @@
-
-
- 联系人
- 商机
- 合同
-
-
确 定
@@ -87,6 +80,10 @@ const open = async (bizId: number) => {
dialogTitle.value = getDialogTitle()
resetForm()
formData.value.id = bizId
+ // 客户转移时默认勾选联系人和合同
+ if (props.bizType === BizTypeEnum.CRM_CUSTOMER) {
+ formData.value.toBizTypes = [BizTypeEnum.CRM_CONTACT, BizTypeEnum.CRM_CONTRACT, BizTypeEnum.CRM_CUSTOMER]
+ }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
// 老负责人负责方式
@@ -154,6 +151,10 @@ const getDialogTitle = () => {
const resetForm = () => {
formRef.value?.resetFields()
formData.value = {} as TransferReqVO
+ // 客户转移时默认勾选联系人和合同
+ if (props.bizType === BizTypeEnum.CRM_CUSTOMER) {
+ formData.value.toBizTypes = [BizTypeEnum.CRM_CONTACT, BizTypeEnum.CRM_CONTRACT]
+ }
}
onMounted(async () => {
// 获得用户列表
diff --git a/src/views/crm/stationsite/index.vue b/src/views/crm/stationsite/index.vue
index 0c47bb583..f6e51a537 100644
--- a/src/views/crm/stationsite/index.vue
+++ b/src/views/crm/stationsite/index.vue
@@ -90,7 +90,7 @@
-
+
删除
+
+ 获取经纬度
+
@@ -118,6 +125,10 @@
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
+
+
+
+
@@ -153,6 +164,10 @@ const queryParams = reactive({
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
const areaList = ref([])
+const mapDialogVisible = ref(false)
+const tencentLbsUrl = ref('')
+const currentRow = ref(null)
+const TENCENT_MAP_KEY = import.meta.env.VITE_APP_TENCENT_MAP_KEY // 你的腾讯地图key
/** 查询列表 */
const getList = async () => {
@@ -212,11 +227,60 @@ const handleExport = async () => {
}
}
-/** 初始化 **/
+/** 地图弹窗相关操作 */
+const openMapDialog = (row: any) => {
+ currentRow.value = row
+ // 拼接带初始点的 url
+
+ let url = `https://apis.map.qq.com/tools/locpicker?type=1&key=${TENCENT_MAP_KEY}&referer=myapp`
+ if (row.locationLat && row.locationLng) {
+ url += `&coord=${row.locationLat},${row.locationLng}`
+ }
+ tencentLbsUrl.value = url
+ mapDialogVisible.value = true
+}
+
+watch(mapDialogVisible, (val) => {
+ if (!val) {
+ tencentLbsUrl.value = '' // 关闭时重置,避免缓存
+ }
+})
+
+// 选择经纬度回填
+function selectAddress(loc: any): void {
+ if (loc.latlng && loc.latlng.lat) {
+ currentRow.value.locationLat = loc.latlng.lat
+ }
+ if (loc.latlng && loc.latlng.lng) {
+ currentRow.value.locationLng = loc.latlng.lng
+ }
+ mapDialogVisible.value = false
+ // 可选:刷新表格数据
+ getList()
+}
+
+// 初始化腾讯地图监听
+const initTencentLbsMap = () => {
+ // @ts-ignore
+ window.selectAddress = selectAddress
+ window.addEventListener(
+ 'message',
+ function (event) {
+ let loc = event.data
+ if (loc && loc.module === 'locationPicker') {
+ // @ts-ignore
+ window.selectAddress(loc)
+ }
+ },
+ false
+ )
+}
+
onMounted(() => {
getList()
AreaApi.getAreaTree().then(res => {
areaList.value = res
})
+ initTencentLbsMap()
})
\ No newline at end of file