Files
jwl-manage-web/src/views/zs/clue/components/ClueFormDialog.vue
2023-08-29 10:34:48 +08:00

643 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div style="margin-bottom: 50px">
<el-dialog title="学员信息导入" :close-on-click-modal="false" append-to-body :visible.sync="visible" width="400px" @close="closeDialog">
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="form.createTime" value-format="yyyy-MM-dd HH:mm" format="yyyy-MM-dd HH:mm" type="datetime" :disabled="admin != 'true'" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="线索来源" prop="source">
<el-select v-model="form.source" placeholder="请选择" clearable>
<el-option v-for="dict in options.sourceOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="跟进人员" prop="followUser">
<el-select v-model="form.followUser" multiple clearable :disabled="admin != 'true' && form.clueId != undefined">
<el-option v-for="dict in options.userOptions" :key="dict.id" :label="dict.name" :value="dict.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="phone">
<el-input v-model="form.phone" placeholder="请输入联系方式" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="意向状态" prop="intentionState">
<el-select v-model="form.intentionState" placeholder="请选择" clearable>
<el-option v-for="dict in options.intentionOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue">
<i class="el-icon-star-on" :style="dict.cssClass" />
<span style="float: right; color: #8492a6; font-size: 13px">
{{ dict.dictValue }}
</span>
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="下次跟进时间" prop="followTime">
<el-date-picker v-model="form.followTime" value-format="yyyy-MM-dd HH:mm" format="yyyy-MM-dd HH:mm" type="datetime" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否近期看场地" prop="recentLook">
<el-radio v-model="form.recentLook" label="是"></el-radio>
<el-radio v-model="form.recentLook" label="否"></el-radio>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="显示场地">
<el-radio-group v-model="mapPlaceType" @change="createMarkersInMap">
<el-radio :label="0">自营场地</el-radio>
<el-radio :label="1">全部场地</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="备注" prop="clueMemo">
<el-input v-model="form.clueMemo" type="textarea" :rows="2" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20 plr20" style="position: relative">
<div id="map" class="amap-cavans" />
<el-collapse class="box-card">
<el-collapse-item title="附近驾校">
<div style="padding: 10px">
<div slot="header">附近驾校</div>
<div v-if="nearbySchoolSearching">正在搜索中...</div>
<template v-else>
<div v-for="p in nearbySchoolList" :key="p.index">
<div class="hover-pointer" style="font-size: 14px; color: blue" @click="getClassType(p)">
<i v-if="p.recommend" class="el-icon-star-off" />
驾校: {{ p.deptName }}-{{ p.name }}
</div>
<div class="mt5">地址{{ p.address }}</div>
<div class="mt5">
直线距离: {{ p.distance }} 公里;
<span class="ml0">步行距离{{ p.walkdistance }}</span>
</div>
<el-divider />
</div>
</template>
</div>
</el-collapse-item>
</el-collapse>
<div class="address">
<el-form-item label="位置" prop="address" label-width="80px">
<el-input v-model="form.address" placeholder="请输入位置" disabled>
<el-button slot="append" class="p10" icon="el-icon-location-information" @click="handleMapEdit" />
<el-button slot="append" class="p10" icon="el-icon-delete-solid" @click="handleRemovePosition" />
</el-input>
</el-form-item>
</div>
</el-col>
</el-row>
<el-row style="margin-bottom: 20px">
<el-col :span="24">
<el-form-item label="跟进情况">
<el-timeline v-if="folowInfos != undefined && folowInfos.length > 0" style="max-height: 200px; overflow-y: auto">
<el-timeline-item v-for="item in folowInfos" :key="item.record" :timestamp="item.operateTime" placement="top" style="padding: 5px !important">
<el-card>
<div style="font-weight: bold">用户 {{ item.operateUserName }}</div>
<div style="padding-left: 10px" v-html="item.centent" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-dialog>
<el-dialog width="800px" title="地图编辑" :visible.sync="mapDialogShow" append-to-body @opened="initDialogMap()">
<div id="dialogMap" class="dialog-map" />
<!-- <el-input id="search" v-model="searchBody" class="search-body" placeholder="请输入..." /> -->
<el-autocomplete v-model="searchBody" popper-class="my-autocomplete" class="search-body" placeholder="请输入..." :trigger-on-focus="false" :fetch-suggestions="querySearch" @select="handleSelect">
<template slot-scope="{ item }">
<span class="name">{{ item.name }}</span>
<span class="addr">{{ item.district }}</span>
</template>
</el-autocomplete>
<span slot="footer" class="dialog-footer">
<el-button @click="mapDialogShow = false"> </el-button>
<el-button type="primary" @click="handleMapSave"> </el-button>
</span>
</el-dialog>
<el-dialog width="40%" :title="innerTitle" :visible.sync="innerVisible" append-to-body>
<div v-if="classType == undefined || classType.length == 0">无班型数据</div>
<el-table v-else :data="classType" style="height: 300px; overflow-y: auto">
<el-table-column label="班型">
<template slot-scope="scope">
<span>{{ scope.row.licenseType }}-{{ scope.row.dictLabel }}</span>
</template>
</el-table-column>
<el-table-column property="currentPrice" label="报价" />
<el-table-column property="minPrice" label="底价" />
<el-table-column property="description" label="描述" />
</el-table>
</el-dialog>
</div>
</template>
<!-- eslint-disable no-undef -->
<script>
import { getFollowRecord } from '@/api/zs/clue';
import { getClassTypes } from '@/api/tool/common';
import { inputtips, regeo, walking } from '@/api/tool/map';
// import AMap from 'AMap';
export default {
model: {
prop: 'info',
event: 'update'
},
props: {
info: {
type: Object,
default: () => { }
},
options: {
type: Object,
default: () => ({
userOptions: [],
sourceOptions: [],
intentionOptions: [],
placeInfo: []
})
}
},
data() {
return {
admin: localStorage.getItem('admin'),
userId: localStorage.getItem('userId'),
form: JSON.parse(JSON.stringify(this.info)),
mapDialogShow: false,
map: null,
locationMarker: null,
dialogMap: null, // 弹窗地图(打点用)
currentPoint: {}, // 地图弹窗经纬度信息
marker: null, // 地图打点
placeSearch: null,
geocoder: null,
searchBody: '',
nearbySchoolList: [],
folowInfos: [],
nearbySchoolSearching: false, // 搜索附近场地
rules: {
name: {
required: true,
message: '姓名不为空',
trigger: 'blur'
},
phone: {
required: true,
message: '联系方式不为空',
trigger: 'blur'
},
createTime: {
required: true,
message: '创建时间不为空',
trigger: 'blur,change'
},
consultTime: {
required: true,
message: '咨询时间不为空',
trigger: 'blur,change'
},
source: {
required: true,
message: '线索来源不为空',
trigger: 'blur,change'
},
address: {
required: true,
message: '位置不为空',
trigger: 'blur'
},
intentionState: {
required: true,
message: '意向状态不为空',
trigger: 'blur,change'
}
},
innerTitle: '',
classType: [],
innerVisible: false,
consultRecord: [],
mapPlaceType: 0 // 地图上展示的场地类型 0自营 1 全部
};
},
watch: {
form: {
handler(val) {
this.$emit('update', val);
},
deep: true,
immediate: true
}
},
created() {
if (this.form.clueId) {
this.handleUpdate();
}
},
mounted() {
this.initMap();
},
beforeDestroy() {
this.geocoder = null;
this.locationMarker = null;
this.marker = null;
this.placeSearch = null;
this.map.clearMap();
this.map && this.map.destroy();
this.map = null;
this.dialogMap && this.dialogMap.destroy();
this.dialogMap = null;
},
methods: {
// 查询该场地下的班型报价
getClassType(item) {
this.classType = [];
this.innerTitle = item.deptName + '-' + item.name + '-班型报价';
getClassTypes({
schoolId: item.deptId,
placeId: item.placeId,
status: '0'
}).then((resp) => {
if (resp && resp.code === 200 && resp.data) {
this.classType = resp.data;
this.innerVisible = true;
}
});
},
// 编辑
handleUpdate() {
// 加载跟进记录
getFollowRecord({ clueId: this.form.clueId }).then((resp) => {
this.folowInfos = resp.data;
});
},
initMap() {
if (!this.map) {
this.map = new AMap.Map('map', {
zoom: 12,
center: [117.226095, 31.814372],
resizeEnable: true
});
}
// 地图坐标点定位
if (this.form.lat && this.form.lng) {
this.locationMarker = new AMap.Marker({
position: [this.form.lng, this.form.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.map.add(this.locationMarker);
this.map.setCenter([this.form.lng, this.form.lat]);
this.map.setZoom(14);
}
this.getNearbySchool();
this.createMarkersInMap();
},
// 生成markers
createMarkersInMap() {
let arr = this.options.placeInfo;
if (this.mapPlaceType === 0) {
arr = arr.filter((item) => item.recommend);
}
this.map && this.map.clearMap();
this.locationMarker && this.map.add(this.locationMarker);
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
const tmpMarker = new AMap.Marker({
map: this.map,
position: [element.lng, element.lat],
label: {
content: element.name,
direction: 'left'
},
icon: require(`@/assets/images/position_${element.flagColor}.png`),
extData: element,
clickable: true
});
tmpMarker.on('click', (ev) => this.getClassType(ev.target.getExtData()));
}
},
handleMapEdit() {
this.searchBody = '';
this.mapDialogShow = true;
if (this.form.lat && this.form.lng) {
this.currentPoint.lat = this.form.lat;
this.currentPoint.lng = this.form.lng;
}
this.dialogMap && this.dialogMap.clearMap();
},
handleRemovePosition() {
this.form.lng = undefined;
this.form.lat = undefined;
this.form.address = undefined;
},
initDialogMap() {
if (!this.dialogMap) {
this.dialogMap = new AMap.Map('dialogMap', {
zoom: 12,
resizeEnable: true,
center: [117.283042, 31.86119]
});
this.dialogMap.on('click', (ev) => {
this.currentPoint.lat = ev.lnglat.lat;
this.currentPoint.lng = ev.lnglat.lng;
this.regeoCode();
this.marker && this.dialogMap.remove(this.marker);
this.marker = new AMap.Marker({
position: [this.currentPoint.lng, this.currentPoint.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.dialogMap.add(this.marker);
});
this.dialogMap.addControl(new AMap.Scale());
// const auto = new AMap.Autocomplete({
// input: 'search', // 前端搜索框
// })
// this.placeSearch = new AMap.PlaceSearch({
// map: this.dialogMap,
// pageSize: 1, // 单页显示结果条数
// pageIndex: 1, // 页码
// autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
// })
// AMap.event.addListener(auto, 'select', this.select)
this.geocoder = new AMap.Geocoder();
}
// 初始化编辑地图的中心点
if (this.form.lat && this.form.lng) {
this.searchBody = this.form.address;
this.marker = new AMap.Marker({
map: this.dialogMap,
position: [this.form.lng, this.form.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.dialogMap.setCenter([this.form.lng, this.form.lat]);
this.dialogMap.setZoom(14);
}
},
handleMapSave() {
if (this.currentPoint.lat && this.currentPoint.lng) {
this.form.lng = this.currentPoint.lng;
this.form.lat = this.currentPoint.lat;
this.$set(this.form, 'address', this.currentPoint.address);
this.locationMarker && this.map.remove(this.locationMarker);
this.locationMarker = new AMap.Marker({
map: this.map,
position: [this.currentPoint.lng, this.currentPoint.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.map.setCenter([this.currentPoint.lng, this.currentPoint.lat]);
this.map.setZoom(14);
this.getNearbySchool();
this.mapDialogShow = false;
} else {
this.$message.error('请在地图上选择位置后保存!');
}
},
// 选择查询结果
select(e) {
this.placeSearch.setCity(e.poi.adcode);
this.placeSearch.search(e.poi.name, (status, result) => {
// 搜索成功时result即是对应的匹配数据
if (result && result.info && result.info === 'OK' && result.poiList && result.poiList.pois && result.poiList.pois.length > 0) {
this.currentPoint.lat = result.poiList.pois[0].location.lat;
this.currentPoint.lng = result.poiList.pois[0].location.lng;
this.currentPoint.address = e.poi.name;
this.dialogMap.clearMap();
this.marker && this.dialogMap.remove(this.marker);
this.marker = new AMap.Marker({
map: this.dialogMap,
position: [this.currentPoint.lng, this.currentPoint.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.dialogMap.setZoom(14);
this.dialogMap.setCenter([this.currentPoint.lng, this.currentPoint.lat]);
}
});
},
// 经纬度 -> 地址
regeoCode() {
// this.geocoder.getAddress(
// [this.currentPoint.lng, this.currentPoint.lat],
// (status, result) => {
// if (status === 'complete' && result.regeocode) {
// this.currentPoint.address = result.regeocode.formattedAddress;
// this.searchBody = result.regeocode.formattedAddress;
// }
// }
// );
if (this.currentPoint.lng && this.currentPoint.lat) {
regeo({
// key: 'f2f35d6adc4a16bb879d303cead56237',
key: '0e62be0896c6b8d27d453445f0fb8bc4',
location: this.currentPoint.lng + ',' + this.currentPoint.lat
}).then((resp) => {
if (resp.status === '1') {
this.currentPoint.address = resp.regeocode.formatted_address;
this.searchBody = resp.regeocode.formatted_address;
}
});
}
},
// 计算各个场地到目标点的距离·
getNearbySchool() {
if (this.form.lng && this.form.lat) {
this.nearbySchoolList = [];
this.nearbySchoolSearching = true;
// 推荐的场地
let places1 = [];
// 普通的场地
let places2 = [];
const p2 = [this.form.lng, this.form.lat];
for (let i = 0; i < this.options.placeInfo.length; i++) {
const element = this.options.placeInfo[i];
const p1 = [element.lng, element.lat];
// 计算直线距离
element.distance = (window.AMap.GeometryUtil.distance(p1, p2) / 1000).toFixed(2);
element.recommend ? places1.push(element) : places2.push(element);
}
// 按直线距离排序
// 排序
if (places1.length > 1) {
places1 = places1.sort((a, b) => a.distance - b.distance);
}
// 排序
if (places2.length > 1) {
places2 = places2.sort((a, b) => a.distance - b.distance);
}
// 取普通场地和推荐场地,组合, 取四个
this.nearbySchoolList = [];
for (let i = 0; i < 4; i++) {
places1.length > i && this.nearbySchoolList.push(places1[i]);
places2.length > i && this.nearbySchoolList.push(places2[i]);
if (this.nearbySchoolList.length === 4) {
break;
}
}
// 计算步行距离
this.nearbySchoolList.map(async (item) => {
const p1 = [item.lng, item.lat];
const resp = await this.getWalkingDistance(p1, p2);
item.walkdistance = resp;
this.$forceUpdate();
});
this.nearbySchoolSearching = false;
}
},
// 获取两点之间的步行距离
async getWalkingDistance(start, end) {
if (start && end) {
const resp = await walking({
// key: 'f2f35d6adc4a16bb879d303cead56237',
key: '0e62be0896c6b8d27d453445f0fb8bc4',
origin: start[0] + ',' + start[1],
destination: end[0] + ',' + end[1]
});
if (resp.status === '1') {
const num = resp.route.paths[0].distance;
return num > 1000 ? `${(num / 1000).toFixed(2)} 公里` : `${num}`;
} else {
return '步行数据无法确定';
}
}
// return new Promise((resolve, reject) => {
// })
// return new Promise((resolve, reject) => {
// window.AMap.plugin('AMap.Walking', () => {
// const walking = new AMap.Walking();
// let num = 0;
// walking.search(start, end, (status, result) => {
// debugger
// if (status === 'complete') {
// result.routes.forEach((item) => {
// num += item.distance;
// });
// resolve(
// num > 1000 ? `${(num / 1000).toFixed(2)} 公里` : `${num} 米`
// );
// } else {
// resolve('步行数据无法确定');
// }
// });
// });
// });
},
validateForm() {
return this.$refs.form.validate();
},
async querySearch(queryString, cb) {
if (queryString) {
const resp = await inputtips({
// key: 'f2f35d6adc4a16bb879d303cead56237',
key: '0e62be0896c6b8d27d453445f0fb8bc4',
keywords: queryString
});
cb(resp.tips);
}
},
handleSelect(item) {
this.currentPoint.lat = item.location.split(',')[1];
this.currentPoint.lng = item.location.split(',')[0];
this.currentPoint.address = item.name;
this.searchBody = item.district + item.name;
this.dialogMap.clearMap();
this.marker && this.dialogMap.remove(this.marker);
this.marker = new AMap.Marker({
map: this.dialogMap,
position: [this.currentPoint.lng, this.currentPoint.lat],
icon: require(`@/assets/images/place/flag_red.png`)
});
this.dialogMap.setZoom(14);
this.dialogMap.setCenter([this.currentPoint.lng, this.currentPoint.lat]);
}
}
};
</script>
<style lang="scss" scoped>
.amap-cavans {
width: 100%;
height: 600px;
}
.dialog-map {
width: 100%;
height: 400px;
}
.address {
position: absolute;
left: 30px;
top: 10px;
width: 400px;
background: #fff;
}
.box-card {
position: absolute;
right: 30px;
top: 10px;
width: 400px;
}
.search-body {
position: absolute;
top: 90px;
left: 25px;
width: 300px;
}
.el-divider--horizontal {
margin: 6px 0;
}
li {
padding: 6px;
.name {
font-size: 12px;
line-height: 16px;
text-overflow: ellipsis;
overflow: hidden;
}
.addr {
line-height: 16px;
font-size: 10px;
color: #b4b4b4;
}
.highlighted .addr {
color: #ddd;
}
}</style>