| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- <template>
- <div class="project-layer-wrapper height-100">
- <div class="scrollbar">
- <LayersTree :layer-data="mapLayers" class="project-layer-content" @select="selectLayer"
- v-model:selectedKeys="selectedKeys" v-model:checkedKeys="checkedKeys" v-model:expandedKeys="expandedKeys" />
- </div>
- <a-drawer title="地图元素" placement="right" :closable="true" v-model:visible="visible" :mask="true"
- getContainer="#g-container" :wrap-style="{ position: 'absolute' }" wrapClassName="drawer-element-wrapper"
- @close="closeDrawer" width="300">
- <div class="drawer-element-content">
- <div class="element-item">
- <span class="mr30">名称:</span>
- <a-input v-model:value="layerState.layerName" style="width:110px" placeholder="element name"
- @change="changeLayer" />
- </div>
- <div class="element-item" v-if="layerState.currentType === geoType.Point">
- <span class="mr30">经度:</span>
- {{ layerState.longitude || '--' }}
- </div>
- <div class="element-item" v-if="layerState.currentType === geoType.Point">
- <span class="mr30">纬度:</span>
- {{ layerState.latitude || '--' }}
- </div>
- <div class="element-item" v-if="layerState.element_from === 2">
- <span class="mr30">高度:</span>
- {{ layerState.height || '--' }}
- </div>
- <div class="element-item" v-if="layerState.currentType === geoType.Polygon">
- <span class="mr30">面积:</span>
- {{ layerState.area || '--' }}
- </div>
- <div class="color-content">
- <span class="mr30">颜色: </span>
- <div v-for="item in colors" :key="item.id" class="color-item" :style="'background:' + item.color"
- @click="changeColor(item)">
- <svg-icon v-if="item.color === layerState.color" :size="18" name="check"></svg-icon>
- </div>
- </div>
- <div class="element-item">
- <span class="mr30">用户:</span>
- {{ layerState.user_name || '--' }}
- </div>
- <div class="element-item">
- <span class="mr30">来源:</span>
- <Icon v-if="layerState.element_from === 2">
- <template #component>
- <svg width="1em" height="1em" fill="currentColor" viewBox="0 0 1024 1024">
- <path
- d="M1024 418.21v576H0v-576h1024z m-672 192c-53.02 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96z m320 0c-53.02 0-96 42.98-96 96s42.98 96 96 96 96-42.98 96-96-42.98-96-96-96zM236.59 29.79h125.55v320H236.59v-320z m425.28 0h125.55v320H661.87v-320z"
- p-id="4408" />
- </svg>
- </template>
- </Icon>
- <DesktopOutlined v-else />
- </div>
- </div>
- <div class="flex-row flex-justify-around flex-align-center mt20">
- <a-button type="primary" @click="deleteElement">删除</a-button>
- </div>
- </a-drawer>
- <PhotoDrawer :visible="state.photoDrawerVisible" :fileId="selectedKeys[0]" :closeDrawer="closeDrawer" />
- </div>
- </template>
- <script lang="ts" setup>
- import { onMounted, reactive, ref, watch } from 'vue'
- import Icon from '@ant-design/icons-vue';
- import { DesktopOutlined } from '@ant-design/icons-vue';
- import LayersTree from '/@/components/LayersTree.vue'
- import PhotoDrawer from './components/PhotoDrawer.vue'
- import { MapElementEnum } from '/@/constants/map'
- import { useGMapCover } from '/@/hooks/use-g-map-cover'
- import { GeojsonCoordinate, LayerResource } from '/@/types/map'
- import { Color, GeoType } from '/@/types/mapLayer'
- import {
- deleteElementReq,
- getElementGroupsReq,
- updateElementsReq
- } from '/@/api/layer'
- import { useMyStore } from '/@/store'
- import { gcj02towgs84, wgs84togcj02 } from '/@/vendors/coordtransform'
- import { getRoot } from '/@/root'
- const root = getRoot()
- interface Props {
- mapClickElement: {
- id: string,
- type: string,
- },
- };
- const props = withDefaults(defineProps<Props>(), {
- });
- const state = reactive({
- photoDrawerVisible: false,
- })
- watch(() => props.mapClickElement, (newValue, oldValue) => {
- if (newValue.id) {
- if (newValue.type === 'DEFAULT') {// 默认文件夹
- // 默认文件夹列表
- const defaultFileList: any = mapLayers.value.filter((item: any) => item.type === 2);
- if (defaultFileList.length === 0) {
- return;
- }
- const pid = defaultFileList[0].id;
- expandedKeys.value = [pid];
- selectedKeys.value = [`resource__${newValue.id}`];
- selectedLayer.value = getCurrentLayer(`resource__${newValue.id}`)
- setBaseInfo()
- visible.value = true;
- state.photoDrawerVisible = false
- } else if (newValue.type === 'PHOTO') {
- const expandedKeyList: string[] = [];
- // 图片标注列表
- const photoList: any = mapLayers.value.filter((item: any) => item.type === 3);
- if (photoList.length === 0) {
- return;
- }
- const list = photoList[0];
- expandedKeyList.push(list.id);
- list.elements.forEach((item: any) => {
- item.elements.forEach((element: any) => {
- if (element.id === newValue.id) {
- const coordinates = element.resource.content.geometry.coordinates;
- root.$map.setCenter(coordinates);
- expandedKeyList.push(element.pid)
- expandedKeys.value = expandedKeyList;
- selectedKeys.value = [`resource__${newValue.id}`];
- selectedLayer.value = getCurrentLayer(`resource__${newValue.id}`)
- state.photoDrawerVisible = true
- visible.value = false;
- }
- });
- })
- }
- }
- }, { deep: true });
- const store = useMyStore()
- let useGMapCoverHook = useGMapCover()
- const mapLayers = ref(store.state.Layers)
- const checkedKeys = ref<string[]>([])
- const expandedKeys = ref<string[]>([])
- const selectedKeys = ref<string[]>([])
- const selectedKey = ref<string>('')
- const selectedLayer = ref<any>(null)
- const visible = ref<boolean>(false)
- const geoType = GeoType
- const layerState = reactive({
- layerId: '',
- layerName: '',
- longitude: 0,
- latitude: 0,
- height: 0,
- area: '',// 面积
- currentType: '',// “LineString”,"Polygon","Point"
- color: '#212121',
- user_name: '',
- element_from: 1,// 1-web 2-遥控器
- })
- const colors = ref<Color[]>([
- { id: 1, name: 'BLUE', color: '#2D8CF0', selected: true },
- { id: 2, name: 'GREEN', color: '#19BE6B', selected: false },
- { id: 3, name: 'YELLOW', color: '#FFBB00', selected: false },
- { id: 4, name: 'ORANGE', color: '#B620E0', selected: false },
- { id: 5, name: 'RED', color: '#E23C39', selected: false },
- { id: 6, name: 'NAME_DEFAULT', color: '#212121', selected: false }
- ])
- const scorllHeight = ref()
- async function getAllElement() {
- getElementGroups('init')
- setTimeout(() => {
- useGMapCoverHook = useGMapCover()
- initMapCover()
- }, 1000)
- }
- function initMapCover() {
- mapLayers.value.forEach((item: any) => {
- if (item.elements) {
- setMapCoverByElement(item.elements)
- }
- })
- }
- watch(() => store.state.Layers, newData => {
- mapLayers.value = newData
- }, { deep: true })
- function setMapCoverByElement(elements: LayerResource[]) {
- elements.forEach((element: any) => {
- if (element.elements && element.elements.length) {// 图片标注
- element.elements.forEach((item: any) => {
- const name = item.name;
- const content: any = item.resource?.content;
- const pictureInfo = content.picture;
- const coordinates: any = content.geometry.coordinates;
- useGMapCoverHook.updatePhotoElement(pictureInfo.file_id, name, pictureInfo.thumbnail_url, coordinates);
- })
- } else {// 默认文件夹
- const name = element.name;
- const content: any = element.resource?.content;
- const color = content.properties.color;
- updateMapElement(element, name, color);
- }
- })
- }
- function updateMapElement(
- element: LayerResource,
- name: string,
- color: string | undefined
- ) {
- const geoType = element.resource?.content.geometry.type
- const id = element.id
- const type = element.resource?.type as number
- if (MapElementEnum.PIN === type) {
- const coordinates = element.resource?.content.geometry
- .coordinates as GeojsonCoordinate
- useGMapCoverHook.updatePinElement(id, name, coordinates, color)
- } else if (MapElementEnum.LINE === type && geoType === 'LineString') {
- const coordinates = element.resource?.content.geometry
- .coordinates as GeojsonCoordinate[]
- useGMapCoverHook.updatePolylineElement(id, name, coordinates, color)
- } else if (MapElementEnum.POLY === type && geoType === 'Polygon') {
- const coordinates = element.resource?.content.geometry
- .coordinates as GeojsonCoordinate[][]
- useGMapCoverHook.updatePolygonElement(id, name, coordinates, color)
- }
- }
- function selectLayer(keys: string[], e) {
- visible.value = false
- state.photoDrawerVisible = false
- if (e.selected) {
- selectedKey.value = e.node.eventKey
- selectedLayer.value = getCurrentLayer(selectedKey.value)
- const type = e.node.label;
- if (type === 'DEFAULT') {// 默认文件夹
- setBaseInfo()
- visible.value = true;
- } else if (type === 'PHOTO') {// 图片标注
- state.photoDrawerVisible = true;
- }
- }
- }
- function getCurrentLayer(id: string) {
- const Layers = store.state.Layers
- const key = id.replaceAll('resource__', '')
- let layer = null
- const findCan = function (V: any) {
- V.forEach((item: any) => {
- if (item.id === key) {
- layer = item
- }
- if (item.elements) {
- findCan(item.elements)
- }
- })
- }
- findCan(Layers)
- return layer
- }
- function setBaseInfo() {
- const layer = selectedLayer.value
- if (layer) {
- const content = layer.resource?.content;
- const coordinates = content.geometry.coordinates;
- const type = content.geometry.type;
- layerState.layerId = layer.id;
- layerState.layerName = layer.name;
- layerState.longitude = coordinates[0];
- layerState.latitude = coordinates[1];
- layerState.height = layer.height;
- layerState.currentType = type;
- layerState.color = content.properties.color;
- layerState.user_name = layer.resource?.user_name;
- layerState.element_from = layer.element_from;
- if (type === 'Polygon') {
- const area = useGMapCoverHook.getPolygonArea(coordinates);
- layerState.area = area + 'm²';
- }
- }
- }
- onMounted(() => {
- const element = document.getElementsByClassName('scrollbar').item(0) as HTMLDivElement
- const parent = element?.parentNode as HTMLDivElement
- scorllHeight.value = parent?.clientHeight - parent.firstElementChild!.clientHeight
- getAllElement()
- })
- function closeDrawer() {
- store.commit('SET_MAP_CLICK_ELEMENT', {
- id: '',
- type: '',
- });
- visible.value = false
- state.photoDrawerVisible = false
- selectedKeys.value = []
- }
- function changeColor(color: Color) {
- layerState.color = color.color
- updateElements()
- }
- function changeLayer(val: string) {
- updateElements()
- }
- async function deleteElement() {
- const elementid = selectedLayer.value.id
- await deleteElementReq(elementid, {}).then(async (res: any) => {
- if (res.code !== 0) {
- return
- }
- visible.value = false
- // 移除标点
- useGMapCoverHook.removeCoverFromMap(elementid)
- // 移除文本覆盖物
- useGMapCoverHook.removeCoverFromMap(elementid + '_other')
- getElementGroups()
- })
- }
- async function getElementGroups(type?: string) {
- const result = await getElementGroupsReq({
- groupId: '',
- isDistributed: true
- })
- mapLayers.value = result.data.map((item: any) => {
- const data = { ...item };
- if (item.elements) {// 默认文件夹
- data.elements = item.elements.map((o: any) => {
- return {
- ...o,
- id: o.id,
- height: o.resource.content.geometry.coordinates[2]
- }
- })
- } else {// 图片标注
- data.elements = item.child_groups.map((o: any) => {
- return {
- ...o,
- elements: o.elements.map((val: any) => {
- const values = { ...val };
- const coordinates = values.resource?.content.geometry.coordinates as GeojsonCoordinate;
- const transResult = wgs84togcj02(
- coordinates[0],
- coordinates[1]
- ) as GeojsonCoordinate
- values.id = val.resource.content.picture.file_id;
- values.pid = 'resource__' + o.id;
- values.resource.content.geometry.coordinates = transResult;
- return values;
- })
- }
- });
- delete data.child_groups;
- }
- return data;
- })
- mapLayers.value = updateWgs84togcj02()
- if (type && type === 'init') {
- store.dispatch('setLayerInfo', mapLayers.value)
- }
- store.commit('SET_LAYER_INFO', mapLayers.value)
- }
- async function updateElements() {
- let content = null
- const currentLayer = selectedLayer.value
- content = currentLayer.resource.content
- content.properties.color = layerState.color
- // 移除标点
- useGMapCoverHook.removeCoverFromMap(currentLayer.id)
- // 移除文本覆盖物
- useGMapCoverHook.removeCoverFromMap(currentLayer.id + '_other')
- updateMapElement(selectedLayer.value, layerState.layerName, layerState.color)
- const result = await updateElementsReq(layerState.layerId, {
- name: layerState.layerName,
- content: content
- })
- getElementGroups()
- }
- function updateWgs84togcj02() {
- const layers = mapLayers.value
- layers.forEach((item: any) => {
- if (item.elements) {
- item.elements.forEach((ele: any) => {
- updateCoordinates('wgs84-gcj02', ele)
- })
- }
- })
- return layers
- }
- function updateCoordinates(transformType: string, element: LayerResource) {
- const type = element.resource?.type as number
- if (element.resource) {
- if (MapElementEnum.PIN === type) {
- const coordinates = element.resource?.content.geometry
- .coordinates as GeojsonCoordinate
- if (transformType === 'wgs84-gcj02') {
- const transResult = wgs84togcj02(
- coordinates[0],
- coordinates[1]
- ) as GeojsonCoordinate
- element.resource.content.geometry.coordinates = transResult
- } else if (transformType === 'gcj02-wgs84') {
- const transResult = gcj02towgs84(
- coordinates[0],
- coordinates[1]
- ) as GeojsonCoordinate
- element.resource.content.geometry.coordinates = transResult
- }
- } else if (MapElementEnum.LINE === type) {
- const coordinates = element.resource?.content.geometry
- .coordinates as GeojsonCoordinate[]
- if (transformType === 'wgs84-gcj02') {
- coordinates.forEach((coordinate, i, arr) => {
- arr[i] = wgs84togcj02(
- coordinate[0],
- coordinate[1]
- ) as GeojsonCoordinate
- })
- } else if (transformType === 'gcj02-wgs84') {
- coordinates.forEach((coordinate, i, arr) => {
- arr[i] = gcj02towgs84(
- coordinate[0],
- coordinate[1]
- ) as GeojsonCoordinate
- })
- }
- element.resource.content.geometry.coordinates = coordinates
- } else if (MapElementEnum.POLY === type) {
- const coordinates = element.resource?.content.geometry
- .coordinates[0] as GeojsonCoordinate[]
- if (transformType === 'wgs84-gcj02') {
- coordinates.forEach((coordinate, i, arr) => {
- arr[i] = wgs84togcj02(
- coordinate[0],
- coordinate[1]
- ) as GeojsonCoordinate
- })
- } else if (transformType === 'gcj02-wgs84') {
- coordinates.forEach((coordinate, i, arr) => {
- arr[i] = gcj02towgs84(
- coordinate[0],
- coordinate[1]
- ) as GeojsonCoordinate
- })
- }
- element.resource.content.geometry.coordinates = [coordinates]
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- @import '/@/styles/index.scss';
- </style>
- <style lang="scss">
- .drawer-element-wrapper {
- .ant-drawer-content {
- background-color: $dark-highlight;
- color: $text-white-basic;
- .ant-drawer-header {
- background-color: $dark-highlight;
- .ant-drawer-title {
- color: $text-white-basic;
- }
- .ant-drawer-close {
- color: $text-white-basic;
- }
- }
- .ant-input {
- background-color: #101010;
- border-color: $dark-border;
- color: $text-white-basic;
- }
- }
- .color-content {
- display: flex;
- align-items: center;
- margin: 8px 0 10px;
- .color-item {
- cursor: pointer;
- width: 18px;
- height: 18px;
- line-height: 18px;
- display: flex;
- align-items: center;
- margin-right: 5px;
- }
- }
- .title {
- display: inline-flex;
- width: 80px;
- }
- .element-item {
- margin-bottom: 10px;
- }
- }
- .scrollbar {
- overflow: auto;
- }
- </style>
|