Merge branch 'build' into release

This commit is contained in:
aixianling
2024-08-19 16:20:41 +08:00
12 changed files with 1361 additions and 29 deletions

View File

@@ -269,10 +269,9 @@ export default {
return list
},
filterNode(value, data) {
filterNode(value, data = {}) {
if (!value) return true
return (data.girdName && data.girdName.indexOf(value) !== -1) || (data.name && data.name.indexOf(value) !== -1) || (data.name && data.phone.indexOf(value) !== -1)
return ["girdName", "name", "phone"].some(e => data[e]?.includes(value))
},
onTreeChange(e) {

View File

@@ -59,13 +59,21 @@
<div>{{editInfo.managerWxid}}</div>
</el-form-item>
<el-form-item label="人员关联" prop="managerTpUserName" style="width: 100%;" :rules="[{ required: true, message: '请选择人员' }]">
<el-input disabled size="small" v-model="editInfo.managerTpUserName" clearable placeholder="请选择人员">
<!-- <el-input disabled size="small" v-model="editInfo.managerTpUserName" clearable placeholder="请选择人员">
<template slot="append">
<ai-user-selecter refs="addTags" :isMultiple="false" :instance="instance" v-model="editInfo.user" @change="onChooseUser">
<el-button size="small">选择人员</el-button>
</ai-user-selecter>
</template>
</el-input>
</el-input> -->
<ai-person-select :instance="instance" :customClicker="true" :chooseUserList="chooseUserList"
url="/app/appgirdmemberinfo/list" headerTitle="网格员列表"
:isMultiple="false" dialogTitle="选择" @selectPerson="selectPerson" class="aipersonselect">
<template name="option" v-slot:option="{ item }">
<span class="iconfont iconProlife">{{ item.name }}</span>
<ai-id mode="show" :show-eyes="false" :value="item.idNumber"/>
</template>
</ai-person-select>
</el-form-item>
</el-form>
</ai-dialog>
@@ -109,7 +117,8 @@ export default {
dialog: false,
editInfo: {},
showArea: false,
areaInfo: {}
areaInfo: {},
chooseUserList: [],
}
},
created() {
@@ -190,7 +199,7 @@ export default {
edit(row) {
this.dialog = true
this.editInfo = {...row}
this.editInfo.user = [{id: row.managerTpwxid, name: row.managerTpUserName}]
this.chooseUserList = [{id: row.managerTpwxid, name: row.managerTpUserName}]
},
onChooseUser(v) {
this.editInfo.wxUserId = v[0].id
@@ -235,6 +244,16 @@ export default {
}
});
},
selectPerson(val) {
if (val) {
this.editInfo.managerTpUserName = val.name
this.editInfo.wxUserId = val.wxUserId
} else {
this.editInfo.managerTpUserName = ''
this.editInfo.wxUserId = ''
}
},
},
}
</script>

View File

@@ -14,6 +14,9 @@
<div class="detail-content__wrapper">
<div class="detail-content__wrapper--left">
<ai-card title="基础信息">
<template #right>
<el-button type="primary" @click="editClick()" v-if="detail.eventStatus != 2 && detail.eventStatus != 3">编辑</el-button>
</template>
<template #content>
<ai-wrapper>
<ai-info-item label="上报人员" :value="detail.name"></ai-info-item>
@@ -27,7 +30,8 @@
<ai-info-item label="现场照片" isLine>
<ai-uploader :instance="instance" disabled v-model="detail.files"></ai-uploader>
</ai-info-item>
<ai-info-item label="所属网格">{{ detail.girdName }}</ai-info-item>
<ai-info-item label="所属网格" v-if="detail.eventSource == 0 || detail.eventSource == 1">{{ detail.girdName }}</ai-info-item>
<ai-info-item label="所属群聊" v-else>{{ detail.wxGroupName }}</ai-info-item>
<ai-info-item label="事件位置" isLine>
<div id="map" style="width: 500px; height: 280px;"></div>
</ai-info-item>
@@ -153,6 +157,112 @@
</el-tree>
</div>
</ai-dialog>
<ai-dialog
:visible.sync="isShowEdit"
width="800px"
@close="onCloseEdit"
title="编辑事件"
@onConfirm="onConfirmEdit">
<el-form class="ai-form" label-width="120px" :model="editInfo" ref="editInfo">
<el-form-item label="事件类型" prop="groupName" :rules="[{ required: true, message: '请选择事件类型' }]" style="width: 100%;">
<ai-select v-model="editInfo.groupName" :selectList="typeList" placeholder="请选择" />
</el-form-item>
<el-form-item label="事件描述" prop="content" style="width: 100%;" :rules="[{ required: true, message: '请输入事件描述' }]">
<el-input type="textarea" :rows="8" :maxlength="500" v-model="editInfo.content" clearable placeholder="请输入事件描述" show-word-limit></el-input>
</el-form-item>
<el-form-item label="上报位置" prop="address" style="width: 100%;" :rules="[{ required: true, message: '请选择上报位置' }]">
<el-input disabled size="small" v-model="editInfo.address" style="width: calc(100% - 100px);"></el-input>
<el-button @click="showMap = true" style="margin-left:16px;">地图标绘</el-button>
</el-form-item>
<el-form-item label="所属网格" prop="girdName" style="width: 100%;" :rules="[{ required: true, message: '请选择所属网格' }]">
<!-- <el-input disabled size="small" v-model="editInfo.girdName" clearable>
<template slot="append">
<ai-picker :ops="{label: 'girdName'}" :instance="instance" v-model="editInfo.girdId" @pick="e => onUserChange(e)" dialogTitle="选择所属网格" action="/app/appgirdmemberinfo/queryMyGirdList">
<div class="time-select">
<span class="dept-name" style="color:#999;">选择所属网格</span>
<i class="el-icon-arrow-down"></i>
</div>
</ai-picker>
</template>
</el-input> -->
<el-input disabled size="small" v-model="editInfo.girdName" clearable placeholder="请选择所属网格">
<template slot="append">
<el-button @click="getEditGirdList().then(()=>isEditShowUser=true )">选择</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="图片" prop="files" style="width: 100%;">
<ai-uploader
:instance="instance"
v-model="editInfo.files"
isShowTip
:limit="9">
</ai-uploader>
</el-form-item>
<el-form-item label="姓名" prop="name" style="width: 50%;" :rules="[{ required: true, message: '请输入姓名' }]">
<el-input
size="small"
v-model="editInfo.name"
placeholder="请输入..."
clearabel
:maxLength="11"
></el-input>
</el-form-item>
<el-form-item label="联系方式" prop="phone" style="width: 50%;" :rules="[{ required: true, message: '请输入联系方式' }]">
<el-input
size="small"
v-model="editInfo.phone"
placeholder="请输入..."
clearabel
:maxLength="11"
></el-input>
</el-form-item>
</el-form>
</ai-dialog>
<ai-dialog title="地图" :visible.sync="showMap" @opened="initMapSelect" width="800px" class="mapDialog" @onConfirm="selectMap">
<div id="mapDialog" style="height:400px;"></div>
<el-form label-width="80px" style="padding: 10px 20px 0 0;">
<el-row type="flex" justify="space-between">
<el-form-item label="经度">
<el-input disabled size="small" v-model="placeDetail.lng" style="width:100px;"></el-input>
</el-form-item>
<el-form-item label="纬度">
<el-input disabled size="small" v-model="placeDetail.lat" style="width:100px;"></el-input>
</el-form-item>
<el-form-item label="地址">
<el-input disabled size="small" v-model="placeDetail.address" style="width:300px;"></el-input>
</el-form-item>
</el-row>
</el-form>
<el-input id="searchPlaceInput" size="medium" class="searchPlaceInput" clearable v-model="searchPlace" autocomplete="on" @change="placeSearch.search(searchPlace)" placeholder="请输入关键字">
<el-button type="primary" slot="append" @click="placeSearch.search(searchPlace)">搜索</el-button>
</el-input>
<div id="searchPlaceOutput" />
</ai-dialog>
<ai-dialog
:visible.sync="isEditShowUser"
width="800px"
title="选择所属网格"
@onConfirm="onEditConfirm">
<div class="grid-wrapper">
<el-tree
:filter-node-method="filterNodeEdit"
ref="tree"
:props="defaultProps"
node-key="id"
:data="treeEdit"
highlight-current
@current-change="onTreeEditChange">
<div class="tree-container" slot-scope="{ data }">
<div class="tree-container__user">
<div class="tree-user__item">
<span>{{ data.girdName }}</span>
</div>
</div>
</div>
</el-tree>
</div>
</ai-dialog>
</template>
</ai-detail>
</template>
@@ -197,7 +307,20 @@ export default {
content: [],
eventStatus: '1'
},
evaluation: {}
evaluation: {},
isShowEdit: false, //编辑工单信息
editInfo: {},
placeDetail: {
lng: '',
lat: '',
address: ''
},
showMap: false,
searchPlace: '',
map: null,
typeList: [],
isEditShowUser: false,
treeEdit: []
}
},
@@ -231,6 +354,7 @@ export default {
this.initMap()
})
this.isLoading = false
this.getTypeList()
}
}).catch(() => {
this.isLoading = false
@@ -284,6 +408,11 @@ export default {
return (data.girdName && data.girdName.indexOf(value) !== -1) || (data.name && data.name.indexOf(value) !== -1) || (data.name && data.phone.indexOf(value) !== -1)
},
filterNodeEdit(value, data) {
if (!value) return true
return (data.girdName && data.girdName.indexOf(value) !== -1)
},
onTreeChange(e) {
this.gridInfo = e
@@ -395,7 +524,129 @@ export default {
})
}
})
}
},
editClick() {
this.isShowEdit = true
this.editInfo = {...this.detail}
this.editInfo.girdIds = []
if(this.editInfo.girdId) {
this.editInfo.girdIds.push(this.editInfo.girdId)
}
},
selectMap() {
this.editInfo.lng = this.placeDetail.lng
this.editInfo.lat = this.placeDetail.lat
this.editInfo.address = this.placeDetail.address
this.showMap = false
},
initMapSelect() {
AMapLoader.load({
key: 'b553334ba34f7ac3cd09df9bc8b539dc',
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Geocoder'],
}).then((AMap2) => {
this.map = new AMap2.Map('mapDialog', {
resizeEnable: true,
zooms: [6, 20],
zoom: 11,
center:[107.11059, 31.56618],
})
this.placeSearch = new AMap2.PlaceSearch({ map: this.map })
new AMap2.AutoComplete({
input: 'searchPlaceInput',
output: 'searchPlaceOutput',
}).on('select', (e) => {
if (e?.poi) {
this.placeSearch.setCity(e.poi.adcode)
this.movePosition(e.poi.location)
}
})
this.map.on('click', (e) => {
new AMap2.Geocoder().getAddress(e.lnglat, (sta, res) => {
if (res?.regeocode) {
this.placeDetail = {
lng: e.lnglat?.lng,
lat: e.lnglat?.lat,
address: res.regeocode.formattedAddress,
}
}
})
this.movePosition(e.lnglat)
})
})
},
movePosition(center) {
if (this.map) {
this.map.clearMap()
this.map.panTo(center)
this.map.add([
new AMap.Marker({
position: center,
clickable: true,
}),
])
this.map.setFitView()
}
},
getEditGirdList() {
return this.instance.post(`/app/appgirdmemberinfo/queryMyGirdList`).then(res => {
if (res?.data) {
this.treeEdit = res.data
}
})
},
onTreeEditChange(e) {
console.log(e)
this.editInfo.girdId = e.id
this.editInfo.girdName = e.girdName
},
onEditConfirm() {
if(!this.editInfo.girdId) {
return this.$message.error('请选择网格!')
}
this.isEditShowUser = false
},
onCloseEdit() {
this.isShowEdit = false
this.editInfo = {}
},
onConfirmEdit() {
this.$refs.editInfo.validate(v => {
if (v) {
this.instance.post('/app/apppatrolreportinfov2/update', {
...this.editInfo,
id: this.$route.query.id
}).then(res => {
if (res?.code == 0) {
this.isShowEdit = false
this.getDetail()
this.$message.success('编辑成功!')
}
})
}
})
},
getTypeList() {
this.typeList = []
this.instance.post(`/app/apppatrolreportinfov2/eventTypeList?eventSource=${this.detail.eventSource}`).then(res => {
if (res.code == 0) {
res.data.map((item) => {
var i = {dictName: item, dictValue: item}
this.typeList.push(i)
})
}
})
},
}
}
</script>

View File

@@ -70,7 +70,7 @@
class="search-input"
size="small"
v-throttle="() => {search.current = 1, getList()}"
:placeholder="currIndex == 1 ? '敏感词、涉及对象、姓名、手机号' : '请输入内容描述/上报人员/联系方式'"
:placeholder="placeholderTextList[currIndex]"
clearable
@change="getList"
@clear="search.current = 1, search.content = '', getList()"
@@ -139,6 +139,7 @@ export default {
],
currIndex: 0,
rightBtnTextList: ['工单特征库', '敏感关键词', '事件类型', '事件类型'],
placeholderTextList: ['请输入内容描述/昵称', '请输入内容描述/姓名', '请输入内容描述/上报人员/联系方式', '请输入内容描述/上报人员/联系方式'],
colConfigs: []
}
},
@@ -259,7 +260,7 @@ export default {
},
handleDelete(ids) {
this.$confirm("是否要进行删除?").then(() => {
this.instance.post("/app/appsessionarchivereportinfo/delete", null, {
this.instance.post("/app/apppatrolreportinfov2/delete", null, {
params: {ids}
}).then(res => {
if (res?.code == 0) {

View File

@@ -0,0 +1 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}

773
public/cdn/highlight/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,11 +8,14 @@
<link rel="stylesheet" href="<%= BASE_URL %>cdn/viewerjs/1.11.6/viewer.css"/>
<link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/2.10.18/index.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/jsoneditor/10.0.2/jsoneditor.min.css">
<link rel="stylesheet" href="<%= BASE_URL %>cdn/highlight/atom-one-dark.min.css">
<script src="<%= BASE_URL %>cdn/jsoneditor/10.0.2/jsoneditor.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/viewerjs/1.11.6/viewer.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/avue/2.10.18/avue.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/echarts/echarts@5.5.0.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/echarts/echarts-gl@2.0.9.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/markdown-it/markdown-it.min.js" charset="utf-8"></script>
<script src="<%= BASE_URL %>cdn/highlight/highlight.min.js" charset="utf-8"></script>
<title>web端应用库-展示页面</title>
<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js" referrerpolicy="origin"></script>
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js" referrerpolicy="origin"></script>

View File

@@ -2,6 +2,8 @@
import ChatContent from "./components/chatContent.vue";
import ThinkingBar from "./components/thinkingBar.vue";
import {mapState} from "vuex";
import AiDrag from "../basic/AiDrag.vue";
import AiLocateDialog from "../tools/AiLocateDialog.vue";
export default {
name: "AiCopilot",
@@ -20,26 +22,34 @@ export default {
filter: "",
conversations: [],
currentConversation: null,
app: {}
app: {},
locate: false,
latlng: ""
}
},
computed: {
...mapState(["user"]),
profile: v => v.user.info || {},
expandBtn: v => v.expand ? "收起" : "展开",
btns: () => [
btns: v => [
{
label: "文件", icon: "https://cdn.sinoecare.com/i/2024/07/04/668663436e46e.png", click: () => {
}
},
{
label: "位置", icon: "https://cdn.sinoecare.com/i/2024/08/19/66c2f907bd444.png", click: () => {
v.locate = true
}
}
],
rowBtns: v => [
{icon: "https://cdn.sinoecare.com/i/2024/07/04/66866edc2910a.png", label: "置顶", click: row => 0},
{icon: "https://cdn.sinoecare.com/i/2024/07/04/66866ed734540.png", label: "编辑", click: row => 0},
{icon: "https://cdn.sinoecare.com/i/2024/07/04/66866eda99e4d.png", label: "删除", click: row => v.handleDeleteConversation(row.conversationId)},
]
],
dialogWidth: v => v.expand ? 960 : 468
},
components: {ThinkingBar, ChatContent},
components: {AiLocateDialog, AiDrag, ThinkingBar, ChatContent},
methods: {
getHistory(params) {
this.http.post("/app/appaicopilotinfo/list", null, {params}).then(res => {
@@ -80,8 +90,8 @@ export default {
if (++i < content.length) setTimeout(() => concatenateStr(content, i, target), 50)
}
this.$debounce(() => {
const {currentConversation: conversationId, app, prompt: content} = this.$data
const message = {appType: "2", userType: 0, content, conversationId, ...app}
const {currentConversation: conversationId, app, prompt: content, latlng} = this.$data
const message = {appType: "2", userType: 0, content, conversationId, ...app, supplementContent: latlng}
this.history.push(message)
this.loading = true
this.prompt = ""
@@ -121,6 +131,11 @@ export default {
return {
backgroundImage: `url(${icon})`
}
},
handleLocate(v) {
const {lat, lng} = v.location
this.latlng = [lat, lng].join(",")
this.locate = false
}
},
watch: {
@@ -140,7 +155,7 @@ export default {
<template>
<section class="AiCopilot">
<div class="copilot" v-if="show">
<ai-drag class="copilot" v-if="show" :w="dialogWidth" :h="600" :minHeight="600" :minWidth="dialogWidth" :x="-dialogWidth-12" :y="-542">
<div class="flex header">
<b class="fill" v-text="title"/>
<div class="expandBtn pointer" v-text="expandBtn" @click="expand=!expand"/>
@@ -181,8 +196,9 @@ export default {
</div>
</div>
</div>
</div>
<img class="icon" src="https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png" @click="show=true"/>
</ai-drag>
<ai-locate-dialog v-model="locate" :ins="http" @confirm="v=>handleLocate(v)" :modal="false"/>
<img class="icon" src="https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png" @click="show=!show"/>
</section>
</template>
@@ -197,10 +213,23 @@ export default {
z-index: 1888;
.copilot {
border-radius: 0 0 8px 8px;
height: 600px;
background: #FFFFFF;
box-shadow: 0 0 20px 1px #0a255c1a;
:deep(.vdr) {
border-radius: 0 0 8px 8px;
height: 600px;
background: #FFFFFF;
box-shadow: 0 0 20px 1px #0a255c1a;
border: none;
.handle {
opacity: 0;
}
&:hover {
.handle {
opacity: 1;
}
}
}
.header {
height: 48px;
@@ -238,14 +267,15 @@ export default {
.content {
height: calc(100% - 50px);
float: right;
.left {
width: 0;
height: 100%;
transition: width 1s;
padding: 14px 0;
overflow-x: hidden;
overflow-y: auto;
flex-shrink: 0;
& > * {
margin: 0 14px;
@@ -256,7 +286,7 @@ export default {
& + .right {
border-left-color: #ddd;
width: 660px;
min-width: 660px;
}
}
@@ -372,7 +402,8 @@ export default {
}
.right {
width: 468px;
width: 100%;
min-width: 468px;
height: 100%;
padding: 14px 0 14px 14px;
align-items: stretch;
@@ -407,6 +438,7 @@ export default {
border-bottom: 1px solid #E0E0E0;
margin-bottom: 10px;
align-items: flex-start;
gap: 8px;
.btn {
font-size: 12px;

View File

@@ -3,7 +3,7 @@
<div class="chat-wrapper" v-for="item in list" :key="item.id">
<div class="chat-text" :class="{right:item.userType == '0',system:item.userType == '2'}">
<img v-if="item.userType!=2" class="avatar" :src="avatar(item)" alt=""/>
<div class="content" v-text="item.content"/>
<div class="content" v-html="markdown(item.content)"/>
</div>
</div>
</el-scrollbar>
@@ -19,6 +19,11 @@ export default {
props: {
list: {default: () => []}
},
data() {
return {
md: null
}
},
computed: {
lastMessage: v => v.list.at(-1)?.content
},
@@ -37,9 +42,24 @@ export default {
avatar(item) {
return item.avatar || (item.userType == '0' ? 'https://cdn.sinoecare.com/i/2024/06/17/666fdb275be82.png' :
'https://cdn.sinoecare.com/i/2024/06/04/665ec6f5ef213.png')
},
markdown(v) {
return this.md?.render(v) || v
}
},
mounted() {
const {hljs, markdownit} = window
this.md = markdownit({
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, {language: lang}).value;
} catch (__) {
}
}
return ''; // use external default escaping
}
})
this.scrollBottom()
}
}
@@ -49,6 +69,21 @@ export default {
.chatContent {
:deep(.el-scrollbar__wrap) {
overflow-x: hidden;
.content {
pre {
background-color: #282c34;
color: #abb2bf;
font-size: 12px;
padding: 4px;
line-height: 16px;
}
code {
white-space: pre-wrap;
width: 100%;
}
}
}
.chat-text {

View File

@@ -0,0 +1,35 @@
<template>
<section class="AiDrag">
<vue-draggable-resizable v-bind="$attrs">
<slot/>
</vue-draggable-resizable>
</section>
</template>
<script>
import 'vue-draggable-resizable/dist/VueDraggableResizable.css'
import VueDraggableResizable from 'vue-draggable-resizable'
export default {
name: "AiDrag",
components: {VueDraggableResizable},
props: {
type: {default: "show"} //show:只拖拽
}
}
</script>
<style lang="scss" scoped>
.AiDrag {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
pointer-events: none;
:deep(.vdr) {
pointer-events: auto;
}
}
</style>

View File

@@ -0,0 +1,181 @@
<template>
<section class="AiLocateDialog">
<ai-dialog :visible.sync="dialog" title="标绘" @closed="$emit('visible',false),selected={}"
@opened="$nextTick(()=>initMap())"
@onConfirm="handleConfirm" v-bind="$attrs">
<div id="amap" v-if="dialog"/>
<div class="poi">
<el-input ref="poiInput" v-model="search" size="small" clearable @change="handleSearch" placeholder="请输入地点"/>
</div>
<el-form class="selected" v-if="!!selected.location" id="result" size="mini" label-suffix=""
label-position="left">
<div class="header">
<i class="iconfont iconLocation"/>
<span v-html="[selected.location.lng, selected.location.lat].join(',')"/>
</div>
<el-form-item label="地点">{{ selected.name || "未知地名" }}</el-form-item>
<el-form-item label="类型" v-if="!!selected.type">{{ selected.type }}</el-form-item>
<el-form-item label="地址" v-if="!!selected.address">{{ selected.address }}</el-form-item>
</el-form>
</ai-dialog>
</section>
</template>
<script>
import AMapLoader from '@amap/amap-jsapi-loader'
export default {
name: "AiLocateDialog",
model: {
prop: "visible",
event: "visible",
},
props: ['latlng', 'visible'],
data() {
return {
dialog: false,
search: "",
poi: null,
map: null,
AMap: null,
selected: {},
geo: null
}
},
watch: {
visible(v) {
this.dialog = v
}
},
methods: {
initMap() {
AMapLoader.load({
key: "b553334ba34f7ac3cd09df9bc8b539dc",
version: '2.0',
plugins: ['AMap.PlaceSearch', 'AMap.Marker', 'AMap.Geolocation'],
}).then(AMap => {
this.AMap = AMap
this.map = new AMap.Map('amap', {
zoom: 14,
center: this.latlng ? [this.latlng.lng, this.latlng.lat] : ''
}).on('click', res => {
this.map.clearMap()
this.selected = {location: res.lnglat}
this.poi?.searchNearBy('', res.lnglat, 100)
});
if (this.latlng) {
let marker = new AMap.Marker({
position: [this.latlng.lng, this.latlng.lat]
})
this.map.add(marker)
}
this.poi = new AMap.PlaceSearch().on('complete', ({poiList}) => {
this.map.clearMap()
if (poiList?.length > 0) {
poiList?.pois?.map(e => {
let marker = new AMap.Marker({
position: e.location,
}).on('click', () => this.selected = e)
this.map.add(marker)
})
} else {
let marker = new AMap.Marker({
position: this.selected.location,
})
this.map.add(marker)
}
})
this.geo = new AMap.Geolocation({
enableHighAccuracy: true,//是否使用高精度定位
zoomToAccuracy: true//定位成功后是否自动调整地图视野到定位点
})
this.map.addControl(this.geo)
})
},
handleSearch(v) {
if (v) {
this.poi.searchNearBy(v, this.map.getCenter(), 50000)
}
},
handleConfirm() {
if (this.selected?.location) {
this.$emit('confirm', this.selected)
} else {
this.$message.error('请先选择坐标位置')
}
}
},
created() {
this.dialog = this.visible
}
}
</script>
<style lang="scss" scoped>
.AiLocateDialog {
:deep( .el-dialog__body ){
padding: 0;
height: 480px;
position: relative;
.ai-dialog__content--wrapper {
padding: 0 !important;
}
#amap {
width: 100%;
height: 480px;
.amap-logo, .amap-copyright {
display: none !important;
}
.amap-marker-label {
border-color: transparent;
box-shadow: 1px 1px 0 0 rgba(#999, .2);
}
}
.poi {
position: absolute;
left: 10px;
top: 10px;
display: flex;
height: 32px;
flex-direction: column;
div {
flex-shrink: 0;
}
}
.selected {
position: absolute;
right: 16px;
top: 16px;
background: #fff;
min-width: 200px;
box-sizing: border-box;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.header {
color: #fff;
background: #26f;
text-align: center;
display: flex;
align-items: center;
height: 32px;
font-size: 14px;
gap: 4px;
padding: 0 8px;
}
.el-form-item {
padding: 0 8px;
margin: 0;
}
}
}
}
</style>