莳松crm管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ss-crm-manage-web/src/views/Clue/Pool/Comp/DialogClue.vue

431 lines
12 KiB

11 months ago
<template>
9 months ago
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px" @close="destroyMap">
11 months ago
<el-tabs v-model="tabName">
<el-tab-pane label="线索信息" name="info">
9 months ago
<Form ref="formRef" v-loading="formLoading" :rules="rules" isCol :schema="formSchema" />
11 months ago
</el-tab-pane>
<el-tab-pane label="跟进信息" name="follow">
<el-button type="primary" @click="handleAppendFollow">新增跟进人</el-button>
<el-table :data="followList">
<el-table-column label="跟进人">
<template #default="{ row }">
9 months ago
<el-select
v-model="row.userId"
placeholder="选择跟进人"
filterable
:disabled="!row.editable"
>
11 months ago
<el-option
9 months ago
v-for="item in props.userOptions"
9 months ago
:key="item.id"
:label="item.nickname"
:value="item.id"
11 months ago
/>
</el-select>
</template>
</el-table-column>
9 months ago
<el-table-column prop="followTime" label="下次跟进时间">
11 months ago
<template #default="{ row }">
9 months ago
<el-date-picker
v-model="row.followTime"
type="date"
placeholder="选择日期时间"
:disabled="!row.editable"
/>
11 months ago
</template>
</el-table-column>
<el-table-column label="操作" width="80">
9 months ago
<template #default="{ row, $index }">
<Icon
v-if="row.editable"
icon="ep:remove-filled"
class="text-red-500"
@click="handleRemove($index)"
/>
11 months ago
</template>
</el-table-column>
</el-table>
</el-tab-pane>
9 months ago
<el-tab-pane v-if="appStore.getAppInfo?.instanceType == 1" label="位置信息" name="map">
11 months ago
<div class="flex justify-between">
<el-select
v-model="areaValue"
filterable
clearable
remote
style="width: 350px"
reserve-keyword
placeholder="请输入关键词"
:remote-method="remoteMethod"
@change="currentSelect"
>
<el-option
v-for="item in areaList"
:key="item.id"
:label="item.name"
:value="item.name"
class="one-text"
>
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.district }}</span>
</el-option>
</el-select>
9 months ago
<el-checkbox v-model="showSchool" :label="true" @change="handleShowSchool">
展示场地
</el-checkbox>
11 months ago
</div>
<div id="dialogMap" class="mt-20px" style="height: 400px; width: 100%"></div>
9 months ago
<div class="flex items-center mt-10px mb-10px">
<div class="w-100px">线索位置</div>
<el-input v-model="address" placeholder="请输入线索位置" clearable />
</div>
11 months ago
</el-tab-pane>
</el-tabs>
<template #footer>
<span>
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="handleSave"> </el-button>
</span>
</template>
10 months ago
</Dialog>
11 months ago
</template>
9 months ago
<script setup name="DialogClue">
import { useAppStore } from '@/store/modules/app'
import { getPlaceList } from '@/api/school/place'
import * as ClueApi from '@/api/clue'
9 months ago
import { getDiyFieldList } from '@/api/clue/clueField'
11 months ago
import { formatDate } from '@/utils/formatTime'
import AMapLoader from '@amap/amap-jsapi-loader'
import ImgPostion from '@/assets/imgs/flag/flag_red.png'
9 months ago
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 message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const appStore = useAppStore()
const props = defineProps({
schema: {
type: Array
9 months ago
},
userOptions: {
type: Array
9 months ago
}
})
const formSchema = computed(() => {
return [
...props.schema,
{
component: 'Input',
label: '诉求',
field: 'requirement',
componentProps: {
type: 'textarea'
},
colProps: {
span: 24
}
},
{
component: 'Editor',
label: '备注',
field: 'remark',
colProps: {
span: 24
}
}
]
})
11 months ago
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formRef = ref() // 表单 Ref
9 months ago
const rules = {
name: { required: true, message: '线索名称不可为空', trigger: 'blur' },
phone: { required: true, message: '联系方式不可为空', trigger: 'blur' },
source: { required: true, message: '线索来源不可为空', trigger: 'change' },
intentionState: { required: true, message: '意向状态不可为空', trigger: 'change' }
}
11 months ago
const tabName = ref('info')
const followList = ref([])
9 months ago
const areaValue = ref('')
11 months ago
const areaList = ref([])
9 months ago
const address = ref('')
const defaultLatLng = ref({
lat: 31.86119,
lng: 117.283042
})
const info = ref({})
11 months ago
9 months ago
const diyFieldArr = ref([])
9 months ago
const open = async (type, id) => {
11 months ago
dialogVisible.value = true
9 months ago
tabName.value = 'info'
11 months ago
dialogTitle.value = type == 'create' ? '新增线索' : '修改线索'
formType.value = type
9 months ago
resetForm()
11 months ago
// 修改时,设置数据
9 months ago
if (id) {
11 months ago
formLoading.value = true
try {
9 months ago
const data = await ClueApi.getClue(id)
info.value = { ...data, ...data.diyParams }
11 months ago
nextTick(() => {
9 months ago
followList.value = data.followUser
address.value = data.address || ''
formRef.value.setValues(info.value)
11 months ago
})
} finally {
formLoading.value = false
}
9 months ago
} else {
followList.value = []
address.value = []
defaultLatLng.value = {
lat: 31.86119,
lng: 117.283042
}
11 months ago
}
9 months ago
if (appStore.getAppInfo?.instanceType == 1 && !dialogMap.value) {
11 months ago
nextTick(() => {
9 months ago
getSchoolPlace()
initMap(info.value)
remoteMethod(address.value)
11 months ago
})
}
}
9 months ago
11 months ago
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
9 months ago
function resetForm() {
info.value.address = undefined
info.value.lat = undefined
info.value.lng = undefined
info.value.followUsers = []
info.value.diyParams = {}
}
9 months ago
const placeList = ref([])
function getSchoolPlace() {
getPlaceList().then((data) => {
placeList.value = data.placeList
})
}
9 months ago
const emit = defineEmits(['success'])
9 months ago
async function handleSave() {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.getElFormRef().validate()
if (!valid) return
if (followList.value && followList.value.length && followList.value.some((it) => !it.userId)) {
message.info('请将跟进人填写完整!')
return
}
// 提交请求
formLoading.value = true
try {
let params = { ...formRef.value.formModel, address: address.value }
params.lat = defaultLatLng.value.lat
params.lng = defaultLatLng.value.lng
params.followUsers = [...followList.value]
params.diyParams = {}
9 months ago
diyFieldArr.value.map((it) => {
params.diyParams[it.field] = undefined
})
for (const key in params.diyParams) {
if (Object.hasOwnProperty.call(params, key)) {
params.diyParams[key] = params[key]
9 months ago
}
}
if (formType.value === 'create') {
await ClueApi.createClue(params)
message.success(t('common.createSuccess'))
} else {
await ClueApi.updateClue(params)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
11 months ago
}
function handleAppendFollow() {
followList.value.push({
9 months ago
userId: undefined,
followTime: formatDate(new Date()),
editable: true
11 months ago
})
}
function handleRemove(index) {
followList.value.splice(index, 1)
}
// 地图相关
const dialogMap = ref(null)
const aMap = ref(null)
let AutoComplete = ref(null)
let geoCoder = ref(null)
9 months ago
function initMap(data) {
11 months ago
AMapLoader.load({
key: '2ffb0e2ea90b1df0b8be48ed66e18fc8', //设置您的key
version: '2.0',
plugins: ['AMap.Geocoder', 'AMap.AutoComplete']
}).then((AMap) => {
aMap.value = AMap
9 months ago
if (data.lng || data.lat) {
defaultLatLng.value = {
lng: data.lng,
lat: data.lat
}
}
11 months ago
dialogMap.value = new AMap.Map('dialogMap', {
zoom: 12,
zooms: [2, 22],
9 months ago
center: [defaultLatLng.value.lng, defaultLatLng.value.lat]
11 months ago
})
9 months ago
if (data.lng || data.lat) {
addmark(data.lng, data.lat, AMap)
}
11 months ago
AutoComplete.value = new AMap.AutoComplete({
city: '全国'
})
geoCoder.value = new AMap.Geocoder()
dialogMap.value.on('click', (e) => {
9 months ago
defaultLatLng.value = {
lng: e.lnglat.getLng(),
lat: e.lnglat.getLat()
}
11 months ago
addmark(e.lnglat.getLng(), e.lnglat.getLat(), AMap)
9 months ago
regeoCode(e.lnglat.getLng(), e.lnglat.getLat())
11 months ago
})
})
}
9 months ago
function regeoCode(lng, lat) {
try {
geoCoder.value.getAddress([lng, lat], (status, result) => {
if (status === 'complete' && result.regeocode) {
address.value = result.regeocode.formattedAddress
} else {
message.error('根据经纬度查询地址失败')
}
})
} catch (error) {
console.log(error)
}
}
11 months ago
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 showSchool = ref(false)
const schoolMarkers = ref([])
function handleShowSchool() {
if (showSchool.value) {
9 months ago
const flagMap = {
red: FlagRed,
yellow: FlagYellow,
purple: FlagPurple,
green: FlagGreen,
blue: FlagBlue,
black: FlagBlack
}
schoolMarkers.value = []
for (let i = 0; i < placeList.value.length; i++) {
const place = placeList.value[i]
const marker = new aMap.value.Marker({
map: dialogMap.value,
position: [place.lng, place.lat],
label: {
content: place.name,
direction: 'left'
},
icon: flagMap[place.flagColor || 'red'],
extData: place,
clickable: true
})
schoolMarkers.value.push(marker)
}
11 months ago
} else {
dialogMap.value.remove(schoolMarkers.value)
}
}
function remoteMethod(searchValue) {
if (searchValue !== '') {
setTimeout(() => {
10 months ago
AutoComplete.value?.search(searchValue, (status, result) => {
11 months ago
if (result.tips?.length) {
areaList.value = result?.tips
}
})
}, 200)
}
}
function currentSelect(val) {
const area = areaList.value.find((it) => it.name == val)
if (area) {
addmark(area.location?.lng, area.location?.lat, aMap.value)
dialogMap.value.setCenter([area.location?.lng, area.location?.lat], '', 500)
9 months ago
regeoCode(area.location?.lng, area.location?.lat)
11 months ago
}
}
9 months ago
9 months ago
function destroyMap() {
dialogMap.value = null
aMap.value = null
}
function getDiyList() {
getDiyFieldList().then((data) => {
diyFieldArr.value = data
9 months ago
})
9 months ago
}
onMounted(() => {
getDiyList()
9 months ago
})
11 months ago
</script>
<style scoped>
:deep() .amap-logo {
display: none !important;
}
:deep() .amap-copyright {
display: none !important;
}
</style>