列表地图模式
概述
用于配置在列表页同时展示列表和地图, 需要下面的地图字段名不为空
功能介绍
- 会在列表页同样展示一个地图区域, 可配置为 仅地图、混合模式(混合模式是既有列表也有地图,并且可以动态切换)
- 地图区域会将列表的每个坐标点数据渲绘制一个一个的marker
- 互动: 选中marker会高亮列表行, 选中列表行会给对应marker添加弹跳动画
- 选中marker会出现窗口,带有坐标点基本信息和编辑详情按钮, 按钮功能和列表功能一致, 并且按钮权限跟列表一致
效果预览
- 列表展示
- 选中marker
地图字段名
-
列表每条数据的坐标点字段名称,为空则不启用地图模式
-
列表的坐标点数据需要遵循以下格式
JSON
格式的值fd_lng
为经度,fd_lat
为纬度fd_text
为地点名称
{
"fd_lng": 116.397451,
"fd_lat": 39.909187,
"fd_text": "北京市东城区东华门街道天安门"
}
展示模式
列默认表展示模式,可切换,选择仅地图后不可切换, 详见上面图片的切换按钮
列表布局
列表和地图的位置, 建议是列表在上, 地图在下. 可配置, 目前只支持上下布局
地图组件路径
自定义列表中地图组件的文件路径,为空时则为默认值
自定义地图组件示例可见项目的src\views\modules\online\cgform\auto\AutoMapCustom.vue
文件
自定义地图组件需要遵循以下规则
- props接收: hoverId(当前鼠标悬停列表的数据ID), mapField(地图数据字段), buttonSwitch(详情编辑按钮)
- 自定义初始化地图逻辑
- renderData方法, 参数是列表的数据,可以不使用这个数据, 主要用来做地图marker渲染
- loadData方法, 参数是列表的查询项参数, 可以用来通过自定义接口获取数据
- detail和edit 事件, 触发这些事件会打开对应表单弹窗
- highlight事件, 用来通知列表是否高亮列表某一行
自定义地图组件示例
<template>
<div id="auto-list-map-container"></div>
</template>
<script>
// 地图标记
import marker_icon from '@/assets/marker.png'
import { getAction } from '@/api/manage'
import { message } from 'ant-design-vue'
export default {
props: {
// 接收,当前鼠标悬停列表的数据ID
hoverId: {
type: String,
default: '',
},
// 地图字段
mapField: {
type: String,
default: '',
},
// 是否展示编辑,详情按钮
buttonSwitch: {
type: Object,
default: () => ({}),
},
},
data() {
return {
// 列表数据,可以是从父组件传递过来的,也可以自己根据查询条件查询的
dataList: [],
}
},
mounted() {
this.initialVariables()
},
watch: {
hoverId(val) {
if (val) {
return this.highlightMapMarker(val)
}
this.unHighlightMapMarker()
},
},
methods: {
// 初始化变量
initialVariables() {
this.map = null
this.infoWindow = null
this.markers = []
this.animateMarker = null
this.markerNormal = new AMap.Icon({
image: marker_icon,
size: new AMap.Size(74, 84),
imageSize: new AMap.Size(37, 42),
})
},
// 初始化地图
async initMap() {
if (this.map) return
const mapInitParams = {
resizeEnable: true, //是否监控地图容器尺寸变化
zoom: 12, //初始地图级别
}
console.log('地图组件初始化地图')
this.map = new AMap.Map('auto-list-map-container', mapInitParams)
this.infoWindow = new AMap.InfoWindow({ offset: new AMap.Pixel(0, -35) })
return true
},
// 渲染数据 -- 根据数据渲染地图标记
renderData(list = []) {
// 使用列表的数据
// console.log('地图组件渲染数据:', [...list])
// this.dataList = [...list]
// return this.renderMarker(list)
// 使用查询条件自定义查询数据
return this.renderMarker(this.dataList)
},
// 获取数据 -- 查询项参数变化,如果使用列表数据,函数体为空即可
async loadData(searchParams = {}) {
console.log('地图组件开始获取数据:', { ...searchParams })
const { pageNo, pageSize, url, ...rest } = searchParams
return getAction(url, {
...rest,
pageNo: 1,
pageSize: 10,
}).then((res = {}) => {
if (!res.success) {
return message.error(res.message)
}
let list = res.result?.records ?? []
console.log('list:', list)
this.dataList = list
})
},
// 渲染marker
renderMarker(list = []) {
if (!this.map) return
const mapFieldName = this.mapField
this.markers = list
.filter((v) => v && v[mapFieldName])
.map((v) => {
let mapFieldValue
try {
mapFieldValue = JSON.parse(v[mapFieldName])
} catch (error) {
console.error('解析地图坐点错误', v[mapFieldName])
}
if (!mapFieldValue) return
const { fd_lng, fd_lat, fd_text } = mapFieldValue
if (!fd_lng || !fd_lat) return null
console.log('fd_lng,fd_lng', { fd_lng, fd_lat, fd_text })
let normal_icon = this.markerNormal
const marker = new AMap.Marker({
position: [fd_lng, fd_lat],
cursor: 'pointer',
extData: v.id,
icon: normal_icon,
})
marker.on('click', () => {
this.onClickMarker({
dataId: v.id,
fd_lng,
fd_lat,
fd_text,
})
})
return marker
})
.filter((v) => !!v)
this.map.clearMap()
if (this.markers.length) {
this.map.add(this.markers)
const [firstMarker] = this.markers
this.map.setCenter(firstMarker.getPosition())
}
},
// 渲染infoWindow
renderInfoWindow(values) {
const { dataId, fd_text, fd_lng, fd_lat } = values
// 点击编辑或者详情
window.onAutoListMapClick = (type) => {
const record = this.dataList.find((v) => v.id === dataId)
if (!record) {
record = { id: dataId }
}
if (type === 'detail') {
return this.$emit('detail', record)
}
return this.$emit('edit', record)
}
let actionDOM = ''
actionDOM += '<div class="actions">'
if (this.buttonSwitch.detail) {
actionDOM += `<span class="item " onclick="onAutoListMapClick('detail')" >详情</span>`
}
if (this.buttonSwitch.update) {
actionDOM += `<span class="item " onclick="onAutoListMapClick('edit')" >编辑</span>`
}
actionDOM += '</div>'
const content = `<div class="auto-list-info-window">
<p style="margin:0;">${fd_text}</p>
<p style="margin:0;">经度:${fd_lng}</p>
<p style="margin:0;">纬度:${fd_lat}</p>
${actionDOM}
</div>`
this.infoWindow.setContent(content)
this.infoWindow.open(this.map, [fd_lng, fd_lat])
this.infoWindow.on('close', this.onInfoWindowClose)
return content
},
// 点击点标记
onClickMarker(values) {
this.$emit('highlight', values.dataId)
this.renderInfoWindow(values)
},
// 关闭信息窗体
onInfoWindowClose() {
this.$emit('highlight', null, true)
},
// 停止高亮标记
unHighlightMapMarker() {
if (!this.animateMarker) return
this.animateMarker.dom.classList.remove('marker-animate')
this.animateMarker = null
},
// 标记高亮
highlightMapMarker(dataId) {
if (!this.markers) return
const marker = this.markers.find((v) => v?.getExtData() === dataId)
if (!marker) return
marker.dom.classList.add('marker-animate')
this.animateMarker = marker
},
},
}
</script>
<style lang="less">
#auto-list-map-container {
height: 100%;
.amap-marker {
&.marker-animate {
animation: marker-animate 1s infinite;
}
@keyframes marker-animate {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50px);
}
}
}
}
</style>
<style lang="less">
// 地图样式
.auto-list-info-window {
padding: 10px 15px;
.actions {
display: flex;
align-items: center;
justify-content: flex-end;
padding-top: 8px;
margin-top: 4px;
border-top: solid 1px #ccc;
.item {
margin-right: 8px;
padding: 3px 10px;
color: #fff;
background: #1890ff;
border-radius: 3px;
cursor: pointer;
}
}
}
</style>
<style lang="less">
.auto-list-container {
.ant-table-row.highlight {
background-color: aquamarine;
}
}
</style>
感谢您的反馈。如果您有关于如何使用 KubeSphere 的具体问题,请在 Slack 上提问。如果您想报告问题或提出改进建议,请在 GitHub 存储库中打开问题。
页面内容