上传
This commit is contained in:
16
src/api/clue/followRecord.js
Normal file
16
src/api/clue/followRecord.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 查询(精简)列表
|
||||||
|
export const getFollowList = async (params) => {
|
||||||
|
return await request.get({ url: '/admin-api/crm/clue-follow-record/list', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
export const createFollow = async (data) => {
|
||||||
|
return await request.post({ url: '/admin-api/crm/clue-follow-record/create', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
export const deleteFollow = async (id) => {
|
||||||
|
return await request.delete({ url: '/admin-api/crm/clue-follow-record/delete?id=' + id })
|
||||||
|
}
|
||||||
@@ -34,3 +34,7 @@ export const deleteClue = async (id) => {
|
|||||||
export const getClueCount = async () => {
|
export const getClueCount = async () => {
|
||||||
return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' })
|
return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getOpearateRecord = async (params) => {
|
||||||
|
return await request.get({ url: '/admin-api/crm/clue-operate-record/list', params })
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ const dialogStyle = computed(() => {
|
|||||||
height: unref(dialogHeight)
|
height: unref(dialogHeight)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -66,6 +68,7 @@ const dialogStyle = computed(() => {
|
|||||||
draggable
|
draggable
|
||||||
lock-scroll
|
lock-scroll
|
||||||
v-bind="getBindValue"
|
v-bind="getBindValue"
|
||||||
|
@close="emit('close')"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
|
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px" @close="destroyMap">
|
||||||
<el-tabs v-model="tabName">
|
<el-tabs v-model="tabName">
|
||||||
<el-tab-pane label="线索信息" name="info">
|
<el-tab-pane label="线索信息" name="info">
|
||||||
<Form ref="formRef" v-loading="formLoading" :rules="rules" isCol :schema="formSchema" />
|
<Form ref="formRef" v-loading="formLoading" :rules="rules" isCol :schema="formSchema" />
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
:disabled="!row.editable"
|
:disabled="!row.editable"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in userOptions"
|
v-for="item in props.userOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.nickname"
|
:label="item.nickname"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
@@ -92,9 +92,9 @@
|
|||||||
|
|
||||||
<script setup name="DialogClue">
|
<script setup name="DialogClue">
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { getSimpleUserList as getUserOption } from '@/api/system/user'
|
|
||||||
import { getPlaceList } from '@/api/school/place'
|
import { getPlaceList } from '@/api/school/place'
|
||||||
import * as ClueApi from '@/api/clue'
|
import * as ClueApi from '@/api/clue'
|
||||||
|
import { getDiyFieldList } from '@/api/clue/clueField'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||||
import ImgPostion from '@/assets/imgs/flag/flag_red.png'
|
import ImgPostion from '@/assets/imgs/flag/flag_red.png'
|
||||||
@@ -112,6 +112,9 @@ const appStore = useAppStore()
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
schema: {
|
schema: {
|
||||||
type: Array
|
type: Array
|
||||||
|
},
|
||||||
|
userOptions: {
|
||||||
|
type: Array
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -156,7 +159,6 @@ const rules = {
|
|||||||
const tabName = ref('info')
|
const tabName = ref('info')
|
||||||
|
|
||||||
const followList = ref([])
|
const followList = ref([])
|
||||||
const userOptions = ref([])
|
|
||||||
|
|
||||||
const areaValue = ref('')
|
const areaValue = ref('')
|
||||||
const areaList = ref([])
|
const areaList = ref([])
|
||||||
@@ -167,10 +169,14 @@ const defaultLatLng = ref({
|
|||||||
})
|
})
|
||||||
const info = ref({})
|
const info = ref({})
|
||||||
|
|
||||||
|
const diyFieldArr = ref([])
|
||||||
|
|
||||||
const open = async (type, id) => {
|
const open = async (type, id) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
tabName.value = 'info'
|
||||||
dialogTitle.value = type == 'create' ? '新增线索' : '修改线索'
|
dialogTitle.value = type == 'create' ? '新增线索' : '修改线索'
|
||||||
formType.value = type
|
formType.value = type
|
||||||
|
resetForm()
|
||||||
// 修改时,设置数据
|
// 修改时,设置数据
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
@@ -185,6 +191,13 @@ const open = async (type, id) => {
|
|||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
followList.value = []
|
||||||
|
address.value = []
|
||||||
|
defaultLatLng.value = {
|
||||||
|
lat: 31.86119,
|
||||||
|
lng: 117.283042
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
|
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -194,8 +207,17 @@ const open = async (type, id) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
info.value.address = undefined
|
||||||
|
info.value.lat = undefined
|
||||||
|
info.value.lng = undefined
|
||||||
|
info.value.followUsers = []
|
||||||
|
info.value.diyParams = {}
|
||||||
|
}
|
||||||
|
|
||||||
const placeList = ref([])
|
const placeList = ref([])
|
||||||
function getSchoolPlace() {
|
function getSchoolPlace() {
|
||||||
getPlaceList().then((data) => {
|
getPlaceList().then((data) => {
|
||||||
@@ -203,6 +225,8 @@ function getSchoolPlace() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef.value) return
|
if (!formRef.value) return
|
||||||
@@ -220,10 +244,12 @@ async function handleSave() {
|
|||||||
params.lng = defaultLatLng.value.lng
|
params.lng = defaultLatLng.value.lng
|
||||||
params.followUsers = [...followList.value]
|
params.followUsers = [...followList.value]
|
||||||
params.diyParams = {}
|
params.diyParams = {}
|
||||||
debugger
|
diyFieldArr.value.map((it) => {
|
||||||
for (const key in info.value.diyParams) {
|
params.diyParams[it.field] = undefined
|
||||||
if (Object.hasOwnProperty.call(info.value.diyParams, key)) {
|
})
|
||||||
params.diyParams[key] = params.key
|
for (const key in params.diyParams) {
|
||||||
|
if (Object.hasOwnProperty.call(params, key)) {
|
||||||
|
params.diyParams[key] = params[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
@@ -270,13 +296,15 @@ function initMap(data) {
|
|||||||
lng: data.lng,
|
lng: data.lng,
|
||||||
lat: data.lat
|
lat: data.lat
|
||||||
}
|
}
|
||||||
addmark(data.lng, data.lat, AMap)
|
|
||||||
}
|
}
|
||||||
dialogMap.value = new AMap.Map('dialogMap', {
|
dialogMap.value = new AMap.Map('dialogMap', {
|
||||||
zoom: 12,
|
zoom: 12,
|
||||||
zooms: [2, 22],
|
zooms: [2, 22],
|
||||||
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
|
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
|
||||||
})
|
})
|
||||||
|
if (data.lng || data.lat) {
|
||||||
|
addmark(data.lng, data.lat, AMap)
|
||||||
|
}
|
||||||
AutoComplete.value = new AMap.AutoComplete({
|
AutoComplete.value = new AMap.AutoComplete({
|
||||||
city: '全国'
|
city: '全国'
|
||||||
})
|
})
|
||||||
@@ -375,10 +403,19 @@ function currentSelect(val) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
function destroyMap() {
|
||||||
getUserOption().then((data) => {
|
dialogMap.value = null
|
||||||
userOptions.value = data
|
aMap.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiyList() {
|
||||||
|
getDiyFieldList().then((data) => {
|
||||||
|
diyFieldArr.value = data
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDiyList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -39,55 +39,26 @@
|
|||||||
<el-tab-pane label="跟进记录" name="followRecord">
|
<el-tab-pane label="跟进记录" name="followRecord">
|
||||||
<el-button class="mb-10px" type="primary" @click="addFollow">添加跟进记录</el-button>
|
<el-button class="mb-10px" type="primary" @click="addFollow">添加跟进记录</el-button>
|
||||||
<el-timeline>
|
<el-timeline>
|
||||||
<el-timeline-item timestamp="2024-04-01" placement="top">
|
<el-timeline-item
|
||||||
|
v-for="item in followRecordList"
|
||||||
|
:key="item.recordId"
|
||||||
|
:timestamp="item.operateDate"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between" style="align-items: center">
|
<div>
|
||||||
<div class="flex align-baseline">
|
<b class="text-18px" style="line-height: 36px">{{ item.operateUserName }}</b>
|
||||||
<b class="text-18px">张三</b>
|
|
||||||
<span class="text-14 ml-10px">2024-04-01 09:00:00</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" plain @click="updateFollow()">修改</el-button>
|
|
||||||
<el-button type="danger" plain>删除</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>{{ followContent }}</div>
|
<div>{{ item.centent }}</div>
|
||||||
<div class="flex mt-10px" style="align-items: center">
|
<div class="flex mt-10px" style="align-items: center">
|
||||||
<div class="flex" style="color: #666; align-items: center">
|
<div class="flex" style="color: #666; align-items: center">
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
<Icon icon="ep:clock" class="mr-5px" />
|
||||||
<span>本次跟进时间:2024-04-05 10:57</span>
|
<span>本次跟进时间:{{ item.followTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex ml-50px" style="color: #666; align-items: center">
|
<div class="flex ml-50px" style="color: #666; align-items: center">
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
<Icon icon="ep:clock" class="mr-5px" />
|
||||||
<span>下次跟进时间:2024-04-10 10:00</span>
|
<span>下次跟进时间:{{ item.nextFollowTime }}</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
|
||||||
<div>
|
|
||||||
<div class="flex justify-between" style="align-items: center">
|
|
||||||
<div class="flex align-baseline">
|
|
||||||
<b class="text-18px">李四</b>
|
|
||||||
<span class="text-14 ml-10px">2024-02-01 09:00:00</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" plain>修改</el-button>
|
|
||||||
<el-button type="danger" plain>删除</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>{{ followContent2 }}</div>
|
|
||||||
<div class="flex mt-10px" style="align-items: center">
|
|
||||||
<div class="flex" style="color: #666; align-items: center">
|
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
|
||||||
<span>本次跟进时间:2024-02-05 10:57</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex ml-50px" style="color: #666; align-items: center">
|
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
|
||||||
<span>下次跟进时间:2024-02-10 10:00</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -96,74 +67,33 @@
|
|||||||
</el-timeline>
|
</el-timeline>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="详细信息" name="infoDetail">
|
<el-tab-pane label="详细信息" name="infoDetail">
|
||||||
<Descriptions :data="info" :schema="schema" :columns="2" />
|
<Descriptions :data="info" :schema="showSchema" :columns="2" />
|
||||||
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool"
|
<div v-if="appStore.getAppInfo?.instanceType == 1">
|
||||||
>展示场地</el-checkbox
|
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool">
|
||||||
>
|
展示场地
|
||||||
<div id="dialogMap" class="mt-20px" style="height: 400px; width: 100%"></div>
|
</el-checkbox>
|
||||||
|
<div id="dialogMap" class="mt-20px" style="height: 400px; width: 100%"></div>
|
||||||
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="操作记录" name="operateRecord">
|
<el-tab-pane label="操作记录" name="operateRecord">
|
||||||
<el-timeline>
|
<el-timeline>
|
||||||
<el-timeline-item timestamp="2024-04-01" placement="top">
|
<el-timeline-item
|
||||||
|
v-for="item in operateRecordList"
|
||||||
|
:key="item.recordId"
|
||||||
|
:timestamp="item.operateDate"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||||
<div style="color: #666">
|
<div style="color: #666">
|
||||||
<div class="pt-5px">
|
<div class="pt-5px">
|
||||||
<span>操作人:机器人1号</span>
|
<span>操作人:{{ item.operateUserName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-5px pb-5px">
|
<div class="pt-5px pb-5px">
|
||||||
<span>成交线索</span>
|
<span>{{ item.centent }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex" style="align-items: center">
|
<div class="flex" style="align-items: center">
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
<Icon icon="ep:clock" class="mr-5px" />
|
||||||
<span>操作时间:2024-04-05 10:57</span>
|
<span>操作时间:{{ item.followTime }}</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
|
||||||
<div style="color: #666">
|
|
||||||
<div class="pt-5px">
|
|
||||||
<span>操作人:机器人2号</span>
|
|
||||||
</div>
|
|
||||||
<div class="pt-5px pb-5px">
|
|
||||||
<span>修改意向状态为:高意向</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex" style="align-items: center">
|
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
|
||||||
<span>操作时间:2024-04-1 10:57</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
|
||||||
<div style="color: #666">
|
|
||||||
<div class="pt-5px">
|
|
||||||
<span>操作人:机器人1号</span>
|
|
||||||
</div>
|
|
||||||
<div class="pt-5px pb-5px">
|
|
||||||
<span>跟进线索</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex" style="align-items: center">
|
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
|
||||||
<span>操作时间:2024-04-1 10:57</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-timeline-item>
|
|
||||||
<el-timeline-item timestamp="2024-02-01" placement="top">
|
|
||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
|
||||||
<div style="color: #666">
|
|
||||||
<div class="pt-5px">
|
|
||||||
<span>操作人:机器人2号</span>
|
|
||||||
</div>
|
|
||||||
<div class="pt-5px pb-5px">
|
|
||||||
<span>创建线索</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex" style="align-items: center">
|
|
||||||
<Icon icon="ep:clock" class="mr-5px" />
|
|
||||||
<span>操作时间:2024-04-1 10:57</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -177,47 +107,50 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import * as ClueApi from '@/api/clue'
|
import * as ClueApi from '@/api/clue'
|
||||||
|
import * as FollowApi from '@/api/clue/followRecord'
|
||||||
|
import { getPlaceList } from '@/api/school/place'
|
||||||
|
|
||||||
import DialogFollow from './DialogFollow.vue'
|
import DialogFollow from './DialogFollow.vue'
|
||||||
import ImgFlag from '@/assets/imgs/flag/position_blue.png'
|
|
||||||
import AMapLoader from '@amap/amap-jsapi-loader'
|
import AMapLoader from '@amap/amap-jsapi-loader'
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
|
||||||
|
import ImgPostion from '@/assets/imgs/flag/position_blue.png'
|
||||||
|
import FlagRed from '@/assets/imgs/flag/flag_red.png'
|
||||||
|
import FlagYellow from '@/assets/imgs/flag/flag_yellow.png'
|
||||||
|
import FlagPurple from '@/assets/imgs/flag/flag_purple.png'
|
||||||
|
import FlagGreen from '@/assets/imgs/flag/flag_green.png'
|
||||||
|
import FlagBlue from '@/assets/imgs/flag/flag_blue.png'
|
||||||
|
import FlagBlack from '@/assets/imgs/flag/flag_black.png'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const show = ref(false)
|
const show = ref(false)
|
||||||
const info = ref(null)
|
const info = ref(null)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const schema = ref([
|
const props = defineProps({
|
||||||
{
|
schema: {
|
||||||
field: 'name',
|
type: Array
|
||||||
label: '线索名称'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'contact',
|
|
||||||
label: '联系方式'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'supplier',
|
|
||||||
label: '意向状态'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'supplier',
|
|
||||||
label: '创建时间'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'purchaseCount',
|
|
||||||
label: '诉求',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'remark',
|
|
||||||
label: '备注',
|
|
||||||
isEditor: true,
|
|
||||||
span: 2
|
|
||||||
}
|
}
|
||||||
])
|
})
|
||||||
|
|
||||||
const followContent = ``
|
const showSchema = computed(() => {
|
||||||
|
const arr = [
|
||||||
const followContent2 = ``
|
{
|
||||||
|
field: 'requirement',
|
||||||
|
label: '诉求',
|
||||||
|
span: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
span: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return [...props.schema, ...arr]
|
||||||
|
})
|
||||||
|
|
||||||
const followList = ref([
|
const followList = ref([
|
||||||
{
|
{
|
||||||
@@ -234,17 +167,41 @@ const followList = ref([
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const followRecordList = ref([])
|
||||||
|
const operateRecordList = ref([])
|
||||||
|
|
||||||
// 地图相关
|
// 地图相关
|
||||||
const dialogMap = ref(null)
|
const dialogMap = ref(null)
|
||||||
const aMap = ref(null)
|
const aMap = ref(null)
|
||||||
|
|
||||||
async function open(id) {
|
async function open(id) {
|
||||||
try {
|
try {
|
||||||
info.value = await ClueApi.getClue(id)
|
FollowApi.getFollowList({ clueId: id }).then((data) => {
|
||||||
|
followRecordList.value = data.map((item) => ({
|
||||||
|
operateUserName: item.operateUserName,
|
||||||
|
centent: item.centent,
|
||||||
|
operateDate: formatDate(item.operateTime),
|
||||||
|
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm'),
|
||||||
|
nextFollowTime: formatDate(item.nextFollowTime)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
ClueApi.getOpearateRecord({ clueId: id }).then((data) => {
|
||||||
|
operateRecordList.value = data.map((item) => ({
|
||||||
|
operateUserName: item.operateUserName,
|
||||||
|
centent: item.centent,
|
||||||
|
operateDate: formatDate(item.operateTime),
|
||||||
|
followTime: formatDate(item.operateTime, 'YYYY-MM-DD HH:mm')
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
const data = await ClueApi.getClue(id)
|
||||||
|
info.value = { ...data, ...data.diyParams }
|
||||||
show.value = true
|
show.value = true
|
||||||
if (!dialogMap.value) {
|
infoIndex.value = 'followRecord'
|
||||||
|
|
||||||
|
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
initMap()
|
getSchoolPlace()
|
||||||
|
initMap(info.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -252,53 +209,89 @@ async function open(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initMap() {
|
const placeList = ref([])
|
||||||
|
function getSchoolPlace() {
|
||||||
|
getPlaceList().then((data) => {
|
||||||
|
placeList.value = data.placeList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultLatLng = ref({
|
||||||
|
lat: 31.86119,
|
||||||
|
lng: 117.283042
|
||||||
|
})
|
||||||
|
|
||||||
|
function initMap(data) {
|
||||||
AMapLoader.load({
|
AMapLoader.load({
|
||||||
key: '2ffb0e2ea90b1df0b8be48ed66e18fc8', //设置您的key
|
key: '2ffb0e2ea90b1df0b8be48ed66e18fc8', //设置您的key
|
||||||
version: '2.0'
|
version: '2.0'
|
||||||
}).then((AMap) => {
|
}).then((AMap) => {
|
||||||
aMap.value = AMap
|
aMap.value = AMap
|
||||||
|
if (data.lng || data.lat) {
|
||||||
|
defaultLatLng.value = {
|
||||||
|
lng: data.lng,
|
||||||
|
lat: data.lat
|
||||||
|
}
|
||||||
|
}
|
||||||
dialogMap.value = new AMap.Map('dialogMap', {
|
dialogMap.value = new AMap.Map('dialogMap', {
|
||||||
zoom: 12,
|
zoom: 12,
|
||||||
zooms: [2, 22],
|
zooms: [2, 22],
|
||||||
center: [117.283042, 31.86119]
|
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
|
||||||
})
|
})
|
||||||
|
if (data.lng || data.lat) {
|
||||||
|
addmark(data.lng, data.lat, AMap)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const showSchool = ref(false)
|
const showSchool = ref(false)
|
||||||
const schoolMarkers = ref([])
|
const schoolMarkers = ref([])
|
||||||
|
|
||||||
function handleShowSchool() {
|
function handleShowSchool() {
|
||||||
if (showSchool.value) {
|
if (showSchool.value) {
|
||||||
let marker1 = new aMap.value.Marker({
|
const flagMap = {
|
||||||
map: dialogMap.value,
|
red: FlagRed,
|
||||||
position: [117.258001, 31.895216],
|
yellow: FlagYellow,
|
||||||
label: {
|
purple: FlagPurple,
|
||||||
content: '慧安驾校桃花社区训练基地',
|
green: FlagGreen,
|
||||||
direction: 'left'
|
blue: FlagBlue,
|
||||||
},
|
black: FlagBlack
|
||||||
icon: ImgFlag,
|
}
|
||||||
// extData: element,
|
schoolMarkers.value = []
|
||||||
clickable: true
|
for (let i = 0; i < placeList.value.length; i++) {
|
||||||
})
|
const place = placeList.value[i]
|
||||||
let marker2 = new aMap.value.Marker({
|
const marker = new aMap.value.Marker({
|
||||||
map: dialogMap.value,
|
map: dialogMap.value,
|
||||||
position: [117.286731, 31.902396],
|
position: [place.lng, place.lat],
|
||||||
label: {
|
label: {
|
||||||
content: '(皖西)瑞星驾校总校(D)',
|
content: place.name,
|
||||||
direction: 'left'
|
direction: 'left'
|
||||||
},
|
},
|
||||||
icon: ImgFlag,
|
icon: flagMap[place.flagColor || 'red'],
|
||||||
// extData: element,
|
extData: place,
|
||||||
clickable: true
|
clickable: true
|
||||||
})
|
})
|
||||||
schoolMarkers.value = [marker1, marker2]
|
schoolMarkers.value.push(marker)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dialogMap.value.remove(schoolMarkers.value)
|
dialogMap.value.remove(schoolMarkers.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let marker = ref(null)
|
||||||
|
function addmark(lat, lng, AMap) {
|
||||||
|
marker.value && removeMarker()
|
||||||
|
marker.value = new AMap.Marker({
|
||||||
|
position: new AMap.LngLat(lat, lng),
|
||||||
|
zoom: 13,
|
||||||
|
icon: ImgPostion
|
||||||
|
})
|
||||||
|
dialogMap.value.add(marker.value)
|
||||||
|
dialogMap.value.setCenter([lat, lng], '', 500)
|
||||||
|
}
|
||||||
|
function removeMarker() {
|
||||||
|
dialogMap.value.remove(marker.value)
|
||||||
|
}
|
||||||
|
|
||||||
const infoIndex = ref('followRecord')
|
const infoIndex = ref('followRecord')
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
@@ -309,9 +302,6 @@ const followRef = ref()
|
|||||||
function addFollow() {
|
function addFollow() {
|
||||||
followRef.value.open('create', null)
|
followRef.value.open('create', null)
|
||||||
}
|
}
|
||||||
function updateFollow() {
|
|
||||||
followRef.value.open('update', { nextFollowTime: '2024-04-01 12:12' })
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroyMap() {
|
function destroyMap() {
|
||||||
dialogMap.value = null
|
dialogMap.value = null
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
// import { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
// import * as MailAccountApi from '@/api/system/mail/account'
|
|
||||||
|
|
||||||
// 表单校验
|
|
||||||
export const rules = reactive({
|
|
||||||
name: [required],
|
|
||||||
phone: [required],
|
|
||||||
source: [required],
|
|
||||||
intentionState: [required]
|
|
||||||
})
|
|
||||||
|
|
||||||
// const userList = await MailAccountApi.getSimpleMailAccountList()
|
|
||||||
const userList = []
|
|
||||||
|
|
||||||
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
|
|
||||||
const crudSchemas = reactive([
|
|
||||||
{
|
|
||||||
label: '线索名称',
|
|
||||||
field: 'name',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '联系方式',
|
|
||||||
field: 'contact',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '线索位置',
|
|
||||||
field: 'address',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
isForm: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '线索来源',
|
|
||||||
field: 'resource',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
search: {
|
|
||||||
component: 'Select',
|
|
||||||
api: () => userList,
|
|
||||||
componentProps: {
|
|
||||||
optionsAlias: {
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'Select',
|
|
||||||
api: () => userList,
|
|
||||||
componentProps: {
|
|
||||||
optionsAlias: {
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '意向状态',
|
|
||||||
field: 'intention',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
table: {
|
|
||||||
fixed: 'left'
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
component: 'Select',
|
|
||||||
api: () => userList,
|
|
||||||
componentProps: {
|
|
||||||
optionsAlias: {
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'Select',
|
|
||||||
api: () => userList,
|
|
||||||
componentProps: {
|
|
||||||
optionsAlias: {
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '跟进人员',
|
|
||||||
field: 'userId',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
isForm: false,
|
|
||||||
search: {
|
|
||||||
component: 'Select',
|
|
||||||
api: () => userList,
|
|
||||||
componentProps: {
|
|
||||||
optionsAlias: {
|
|
||||||
labelField: 'name',
|
|
||||||
valueField: 'id'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '下次跟进时间',
|
|
||||||
field: 'nextTime',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
isForm: false,
|
|
||||||
formatter: dateFormatter,
|
|
||||||
detail: {
|
|
||||||
dateFormat: 'YYYY-MM-DD'
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'daterange',
|
|
||||||
format: 'YYYY-MM-DD',
|
|
||||||
valueFormat: 'YYYY-MM-DD',
|
|
||||||
startPlaceholder: '下次跟进时间',
|
|
||||||
endPlaceholder: '下次跟进时间'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '诉求',
|
|
||||||
field: 'need',
|
|
||||||
isTable: true,
|
|
||||||
form: {
|
|
||||||
component: 'Input',
|
|
||||||
componentProps: {
|
|
||||||
type: 'textarea'
|
|
||||||
},
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '最新跟进时间',
|
|
||||||
field: 'latestFollowTime',
|
|
||||||
isTable: true,
|
|
||||||
isForm: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '创建时间',
|
|
||||||
field: 'createTime',
|
|
||||||
isSearch: true,
|
|
||||||
isTable: true,
|
|
||||||
table: {
|
|
||||||
fixed: 'left'
|
|
||||||
},
|
|
||||||
formatter: dateFormatter,
|
|
||||||
detail: {
|
|
||||||
dateFormat: 'YYYY-MM-DD'
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'daterange',
|
|
||||||
format: 'YYYY-MM-DD',
|
|
||||||
valueFormat: 'YYYY-MM-DD',
|
|
||||||
startPlaceholder: '创建时间',
|
|
||||||
endPlaceholder: '创建时间'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'date',
|
|
||||||
format: 'YYYY-MM-DD',
|
|
||||||
valueFormat: 'YYYY-MM-DD',
|
|
||||||
placeholder: '创建时间'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '跟进记录',
|
|
||||||
field: 'followRecord',
|
|
||||||
isTable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '备注',
|
|
||||||
field: 'remark',
|
|
||||||
isTable: true,
|
|
||||||
form: {
|
|
||||||
component: 'Editor',
|
|
||||||
colProps: {
|
|
||||||
span: 24
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<el-tabs v-model="queryType" size="small">
|
<el-tabs v-model="queryType" size="small" @tab-change="getTableList">
|
||||||
<el-tab-pane label="全部" name="0" />
|
<el-tab-pane label="全部" name="0" />
|
||||||
<el-tab-pane name="1">
|
<el-tab-pane name="1">
|
||||||
<template #label>
|
<template #label>
|
||||||
@@ -118,10 +118,11 @@
|
|||||||
<DialogClue
|
<DialogClue
|
||||||
v-if="!loading"
|
v-if="!loading"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
:userOptions="userOptions"
|
||||||
:schema="allSchemas.formSchema"
|
:schema="allSchemas.formSchema"
|
||||||
@sucess="getTableList"
|
@sucess="getTableList"
|
||||||
/>
|
/>
|
||||||
<DrawerClue ref="drawerRef" />
|
<DrawerClue v-if="!loading" ref="drawerRef" :schema="allSchemas.formSchema" />
|
||||||
<DialogSuccess ref="successRef" />
|
<DialogSuccess ref="successRef" />
|
||||||
<DialogFollow ref="followRef" />
|
<DialogFollow ref="followRef" />
|
||||||
</div>
|
</div>
|
||||||
@@ -133,6 +134,7 @@ import DialogClue from './Comp/DialogClue.vue'
|
|||||||
import DrawerClue from './Comp/DrawerClue.vue'
|
import DrawerClue from './Comp/DrawerClue.vue'
|
||||||
import DialogSuccess from './Comp/DialogSuccess.vue'
|
import DialogSuccess from './Comp/DialogSuccess.vue'
|
||||||
import DialogFollow from './Comp/DialogFollow.vue'
|
import DialogFollow from './Comp/DialogFollow.vue'
|
||||||
|
import { getSimpleUserList as getUserOption } from '@/api/system/user'
|
||||||
|
|
||||||
import { removeNullField } from '@/utils'
|
import { removeNullField } from '@/utils'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
@@ -240,8 +242,11 @@ async function makeCall(phone) {
|
|||||||
function handleSuccess(row) {
|
function handleSuccess(row) {
|
||||||
successRef.value.open(row)
|
successRef.value.open(row)
|
||||||
}
|
}
|
||||||
|
const userOptions = ref([])
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
getUserOption().then((data) => {
|
||||||
|
userOptions.value = data
|
||||||
|
})
|
||||||
getSearchCount()
|
getSearchCount()
|
||||||
getCurdSchemas()
|
getCurdSchemas()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,9 +27,9 @@
|
|||||||
<el-tab-pane label="常规设置" :name="40" v-if="checkPermi(['clue:setting:general-setting'])">
|
<el-tab-pane label="常规设置" :name="40" v-if="checkPermi(['clue:setting:general-setting'])">
|
||||||
<GeneralSet v-if="tabIndex == 40" />
|
<GeneralSet v-if="tabIndex == 40" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="消息通知" :name="50" v-if="checkPermi(['mall:setting:prod'])">
|
<!-- <el-tab-pane label="消息通知" :name="50" v-if="checkPermi(['mall:setting:prod'])">
|
||||||
<MsgSend v-if="tabIndex == 50" />
|
<MsgSend v-if="tabIndex == 50" />
|
||||||
</el-tab-pane>
|
</el-tab-pane> -->
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,7 +40,7 @@ import FieldOrder from './Comp/FieldOrder.vue'
|
|||||||
import ClueSource from './Comp/ClueSource.vue'
|
import ClueSource from './Comp/ClueSource.vue'
|
||||||
// import ClueGet from './Comp/ClueGet.vue'
|
// import ClueGet from './Comp/ClueGet.vue'
|
||||||
// import ClueSend from './Comp/ClueSend.vue'
|
// import ClueSend from './Comp/ClueSend.vue'
|
||||||
import MsgSend from './Comp/MsgSend.vue'
|
// import MsgSend from './Comp/MsgSend.vue'
|
||||||
import GeneralSet from './Comp/GeneralSet.vue'
|
import GeneralSet from './Comp/GeneralSet.vue'
|
||||||
import { checkPermi } from '@/utils/permission'
|
import { checkPermi } from '@/utils/permission'
|
||||||
|
|
||||||
|
|||||||
@@ -15,16 +15,16 @@
|
|||||||
<el-tab-pane label="常规设置" :name="4" v-if="checkPermi(['mall:setting:general'])">
|
<el-tab-pane label="常规设置" :name="4" v-if="checkPermi(['mall:setting:general'])">
|
||||||
<GeneralSet v-if="tabIndex == 4" />
|
<GeneralSet v-if="tabIndex == 4" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="消息通知" :name="9" v-if="checkPermi(['mall:setting:msg'])">
|
<!-- <el-tab-pane label="消息通知" :name="9" v-if="checkPermi(['mall:setting:msg'])">
|
||||||
<MsgSend v-if="tabIndex == 9" />
|
<MsgSend v-if="tabIndex == 9" />
|
||||||
</el-tab-pane>
|
</el-tab-pane> -->
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import GeneralSet from './Comp/GeneralSet.vue'
|
import GeneralSet from './Comp/GeneralSet.vue'
|
||||||
import FieldProduct from './Comp/FieldProduct.vue'
|
import FieldProduct from './Comp/FieldProduct.vue'
|
||||||
import MsgSend from './Comp/MsgSend.vue'
|
// import MsgSend from './Comp/MsgSend.vue'
|
||||||
import CategorySet from './Comp/CategorySet.vue'
|
import CategorySet from './Comp/CategorySet.vue'
|
||||||
import BrandSet from './Comp/BrandSet.vue'
|
import BrandSet from './Comp/BrandSet.vue'
|
||||||
import SupplierSet from './Comp/SupplierSet.vue'
|
import SupplierSet from './Comp/SupplierSet.vue'
|
||||||
|
|||||||
Reference in New Issue
Block a user