Browse Source

feat: 增加图层勾选

feature-v1.0.0
chenhaojie 1 month ago
parent
commit
8dd33a23f2
  1. 27
      src/api/map/index.ts
  2. 1
      src/assets/map/changeScene.svg
  3. BIN
      src/assets/map/icon-folder.png
  4. BIN
      src/assets/map/icon-layer.png
  5. 1
      src/assets/map/layer.svg
  6. 3
      src/components.d.ts
  7. 84
      src/components/Tree/index.vue
  8. 5
      src/store/modules/project.ts
  9. 134
      src/theme/dialog.scss
  10. 8
      src/theme/index.scss
  11. 2
      src/theme/var.css
  12. 245
      src/utils/mapUtils.ts
  13. 65
      src/views/Main/Map/components/LayerSwitch.vue
  14. 138
      src/views/Main/Map/components/LayerTree.vue
  15. 9
      src/views/Main/Map/components/Legend.vue
  16. 29
      src/views/Main/Map/index.vue

27
src/api/map/index.ts

@ -0,0 +1,27 @@
import { request } from '../axios';
import axios from 'axios';
// 获取场景列表
export const getSceneListData = async (data: any) => {
return request({
url: `/map/scene/sceneList`,
method: 'post',
data: {
pageNum: 1,
pageSize: 10,
data: {
id: '',
name: data.name
},
params: {
order: 'asc',
orderBy: 'pub_date'
}
}
});
};
export const getLayerLegend = async (url: string) => {
return axios.get(url);
};

1
src/assets/map/changeScene.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
src/assets/map/icon-folder.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
src/assets/map/icon-layer.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

1
src/assets/map/layer.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="20" height="20" viewBox="0 0 20 20"><g><g><path d="M9.999699296875,9.385516708984376L17.591379296874997,6.222386708984375C18.104379296875,6.008626708984375,18.104379296875,5.250506708984375,17.591379296874997,5.036746708984375L9.999699296875,1.873626708984375L2.408007296875,5.036746708984375C1.894969796875,5.250506708984375,1.894970896875,6.008626708984375,2.408007296875,6.222386708984375L9.999699296875,9.385516708984376ZM9.999689296875,8.011006708984375L4.284089296875,5.629566708984375L9.999699296875,3.2481267089843753L15.715279296875,5.629566708984375L9.999689296875,8.011006708984375ZM1.873779296875,8.783306708984375L1.873779296875,10.317236708984375L10.000019296875,13.889926708984374L18.123779296875,10.318326708984374L18.123779296875,8.784396708984374L10.000019296875,12.356026708984375L1.873779296875,8.783306708984375ZM1.873779296875,13.024326708984376L1.873779296875,14.558226708984375L10.000139296875,18.130926708984376L18.123779296875,14.559426708984375L18.123779296875,13.025526708984374L10.000139296875,16.597026708984373L1.873779296875,13.024326708984376Z" fill-rule="evenodd" fill="#36B29E" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

3
src/components.d.ts

@ -10,6 +10,9 @@ declare module 'vue' {
Card: typeof import('./components/Card/index.vue')['default']
Dialog: typeof import('./components/Dialog/index.vue')['default']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElInput: typeof import('element-plus/es')['ElInput']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
Form: typeof import('./components/Form/index.vue')['default']
InputNumber: typeof import('./components/Input/input-number.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']

84
src/components/Tree/index.vue

@ -35,6 +35,8 @@
@node-click="handleNodeClick"
@node-expand="handleExpand"
@node-collapse="handleCollapse"
@check-change="handleCheckChange"
@check="handleCheck"
ref="tree"
>
<template v-slot="{ node, data }">
@ -77,87 +79,87 @@
</template>
<script lang="ts">
import { ref, nextTick, watch, defineComponent } from 'vue';
import type { PropType } from 'vue';
import type { TreeKey, LoadFunction } from 'element-plus/es/components/tree/src/tree.type';
import { ref, nextTick, watch, defineComponent } from "vue";
import type { PropType } from "vue";
import type { TreeKey, LoadFunction } from "element-plus/es/components/tree/src/tree.type";
export default defineComponent({
name: 'sy-tree',
name: "sy-tree",
props: {
data: {
type: Array,
default: () => []
default: () => [],
},
emptyText: {
type: String,
default() {
return '暂无数据';
}
return "暂无数据";
},
},
renderAfterExpand: {
type: Boolean,
default: false
default: false,
},
nodeKey: {
type: String,
default: 'key'
default: "key",
},
checkStrictly: {
type: Boolean,
default: false
default: false,
},
defaultExpandAll: {
type: Boolean,
default: false
default: false,
},
expandOnClickNode: {
type: Boolean,
default: true
default: true,
},
checkOnClickNode: {
type: Boolean,
default: false
default: false,
},
checkDescendants: {
type: Boolean,
default: false
default: false,
},
autoExpandParent: {
type: Boolean,
default: true
default: true,
},
defaultCheckedKeys: {
type: Array as PropType<TreeKey[]>,
default: () => []
default: () => [],
},
defaultExpandedKeys: {
type: Array as PropType<TreeKey[]>,
default: () => []
default: () => [],
},
currentNodeKey: [String, Number],
renderContent: Function,
showCheckbox: {
type: Boolean,
default: false
default: false,
},
draggable: {
type: Boolean,
default: false
default: false,
},
allowDrag: Function,
allowDrop: Function,
defaultProps: {
default() {
return {
children: 'children',
label: 'label',
isLeaf: 'isLeaf'
children: "children",
label: "label",
isLeaf: "isLeaf",
};
}
},
},
lazy: {
type: Boolean,
default: false
default: false,
},
highlightCurrent: Boolean,
load: Function as PropType<LoadFunction>,
@ -165,22 +167,22 @@ export default defineComponent({
accordion: Boolean,
indent: {
type: Number,
default: 18
default: 18,
},
iconClass: String,
noCheckbox: {
type: Boolean,
default: true
default: true,
},
showNodeLabelTips: Boolean,
showInput: {
type: Boolean,
default: true
}
default: true,
},
},
setup(props: any, { emit }) {
const tree = ref();
const filterText = ref('');
const filterText = ref("");
const moreMenuData = ref();
const isMoreMenuShow = ref(false);
const moreMenuTriggered = ref(false);
@ -195,21 +197,27 @@ export default defineComponent({
return data[props.defaultProps.label].indexOf(value) !== -1;
};
const handleNodeClick = (data: any, node: any, self: any) => {
emit('node-click', data, node, self);
emit("node-click", data, node, self);
};
const handleCheckChange = (...args: any[]) => {
emit("check-change", ...args);
};
const handleCheck = (...args: any[]) => {
emit("check", ...args);
};
const handleExpand = (data: any) => {
emit('node-expand', data);
emit("node-expand", data);
};
const handleCollapse = (data: any) => {
emit('node-collapse', data);
emit("node-collapse", data);
};
const onMoreHandle = (event: any, data: any) => {
moreMenuData.value = data;
isMoreMenuShow.value = true;
moreMenuTriggered.value = true;
nextTick(() => {
moreMenuBox.value.style.top = event.clientY + 20 + 'px';
moreMenuBox.value.style.left = event.clientX - moreMenuBox.value.offsetWidth + 15 + 'px';
moreMenuBox.value.style.top = event.clientY + 20 + "px";
moreMenuBox.value.style.left = event.clientX - moreMenuBox.value.offsetWidth + 15 + "px";
});
};
const showMoreMenu = () => {
@ -286,7 +294,7 @@ export default defineComponent({
() => filterText.value,
(val) => {
tree.value.filter(val);
}
},
);
return {
tree,
@ -296,6 +304,8 @@ export default defineComponent({
moreMenuBox,
filterNode,
handleNodeClick,
handleCheckChange,
handleCheck,
handleExpand,
handleCollapse,
onMoreHandle,
@ -309,9 +319,9 @@ export default defineComponent({
setChecked,
moreMenuLeave,
showTip,
onMouseoverTip
onMouseoverTip,
};
}
},
});
</script>

5
src/store/modules/project.ts

@ -8,7 +8,7 @@ import { ElMessage } from "element-plus";
import { getFirstScene, getLayerData, getSceneConfig } from "@/api/project";
export interface ProjectState {
projectName: string;
defaultViewPoint: { cameraPosture: any; layerId: string[] };
defaultViewPoint: { cameraPosture: any; layerId: string[]; };
layers: any;
baseLayers: any[];
terrainLayers: any[];
@ -189,7 +189,8 @@ export const useProjectStore = defineStore("project", {
const rData = await getFirstScene();
if (rData.code === 200) {
if (rData.records && rData.records.length > 0) {
this.defaultSceneId = rData.records[0].id;
this.defaultSceneId = "1897595541221593090";
// rData.records[0].id;
}
} else {
// 警告提示

134
src/theme/dialog.scss

@ -133,45 +133,32 @@
}
}
}
.layer-tree-dialog, .layer-legend-dialog {
right: 560px;
top: 705px;
.layer-tree-dialog {
right: 510px;
top: 70px;
width: 320px;
height: 336px;
z-index: 2000;
background: rgba(0, 57, 114, 0.5);
box-sizing: border-box;
border: 1px dashed rgba(10, 204, 204, 0.32);
backdrop-filter: none;
background: rgba(255, 255, 255, 0.8);
border-radius: 4px;
box-shadow: none;
.dialog-header {
height: 48px;
padding: 8px 24px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background: linear-gradient(270deg, rgba(76, 135, 255, 0) 5%, rgba(10, 204, 204, 0.25) 99%);
box-sizing: border-box;
border-image: linear-gradient(270deg, rgba(10, 204, 204, 0) 0%, rgba(24, 232, 232, 0.5) 99%) 1;
.title {
font-size: 28px;
font-weight: 500;
line-height: 32px;
letter-spacing: 0px;
color: var(--sy-main-font-color);
display: flex;
justify-content: flex-start;
align-items: center;
i {
width: 32px;
height: 25.02px;
background: url(@/assets/img/layer.png) no-repeat;
}
}
border: none;
.dialog-header,
.dialog-footer {
display: none;
}
.dialog-body {
width: 100%;
height: 100%;
padding: 0px;
.sy-tree,
.layer-tree {
height: 100%;
.item-name {
display: flex;
}
}
}
}
.layer-legend-dialog {
@ -368,7 +355,8 @@
}
}
}
.seepage-pressure-dialog,.deformation-dialog {
.seepage-pressure-dialog,
.deformation-dialog {
top: 282px;
left: 830px;
width: 544px;
@ -415,7 +403,7 @@
left: 830px;
width: 710px;
min-height: 450px;
.dialog-body{
.dialog-body {
padding: 16px 24px 4px;
.content {
height: auto;
@ -437,7 +425,7 @@
// max-height: 380px;
// height: 564px;
// overflow-x: hidden;
.title{
.title {
font-size: 20px;
font-weight: 500;
line-height: 28px;
@ -447,12 +435,12 @@
// margin-top: -42px;
position: relative;
&::before {
content: '';
content: "";
left: 0px;
top: 6px;
width: 2px;
height: 16px;
background: #00E8D2;
background: #00e8d2;
position: absolute;
margin-left: -8px;
}
@ -476,7 +464,7 @@
.label {
color: var(--sy-tips-font-color);
}
.value{
.value {
color: var(--sy-primary-font-color);
overflow: hidden;
text-overflow: ellipsis;
@ -514,9 +502,9 @@
display: block;
float: none;
overflow: auto;
text-overflow: initial;
white-space: normal;
.value{
text-overflow: initial;
white-space: normal;
.value {
display: block;
width: 100%;
max-height: 250px;
@ -559,7 +547,6 @@
color: var(--sy-primary-font-color);
font-size: 20px;
}
}
.upload-image-wrapper {
margin-right: 8px;
@ -577,11 +564,11 @@
.sy-icon {
margin: 22px auto;
text-align: center;
&:hover{
color: #0ACCCC;
&:hover {
color: #0acccc;
}
}
.img-card-text {
width: 72px;
height: 72px;
@ -602,11 +589,11 @@
// background: var(--sy-webkit-scrollbar-background);
height: 4px;
width: 4px;
}
::-webkit-scrollbar-thumb {
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
// background: var(--sy-webkit-scrollbar-thumb-background);
}
}
}
}
.button-content {
@ -626,8 +613,8 @@
border-radius: 0px;
border: 2px solid rgba(255, 255, 255, 0.4);
&:hover {
border: 2px solid #0ACCCC;
color: #0ACCCC;
border: 2px solid #0acccc;
color: #0acccc;
}
}
}
@ -670,34 +657,33 @@
padding: 12px 0px;
}
}
}
.info-dialog-tab {
padding: 0px 0px 24px;
height: 40px;
.tab-item {
padding: 8px 16px;
gap: 8px;
background: rgba(0, 37, 88, 0.6);
box-sizing: border-box;
border: 0.96px solid rgba(10, 204, 204, 0.5);
display: flex;
justify-content: flex-start;
align-items: center;
float: left;
font-size: 20px;
font-weight: normal;
line-height: 23.4px;
letter-spacing: 0px;
color: #FFFFFF;
&.active {
color: #0ACCCC;
background: none;
}
padding: 0px 0px 24px;
height: 40px;
.tab-item {
padding: 8px 16px;
gap: 8px;
background: rgba(0, 37, 88, 0.6);
box-sizing: border-box;
border: 0.96px solid rgba(10, 204, 204, 0.5);
display: flex;
justify-content: flex-start;
align-items: center;
float: left;
font-size: 20px;
font-weight: normal;
line-height: 23.4px;
letter-spacing: 0px;
color: #ffffff;
&.active {
color: #0acccc;
background: none;
}
}
}
.info-dialog-select{
.info-dialog-select {
display: flex;
justify-content: space-between;
align-items: center;
@ -711,7 +697,7 @@
font-weight: normal;
line-height: 44px;
letter-spacing: 0px;
color: #FFFFFF;
color: #ffffff;
margin-right: 12px;
}
.sy-select {
@ -725,7 +711,7 @@
font-weight: normal;
line-height: normal;
letter-spacing: 0em;
font-feature-settings: 'kern' on;
font-feature-settings: "kern" on;
color: #00e8d2;
display: flex;
cursor: pointer;

8
src/theme/index.scss

@ -30,7 +30,7 @@ div {
left: 0;
top: 0;
width: 100%;
height: 100%;
// height: 100%;
}
a {
@ -53,11 +53,7 @@ code {
align-items: center;
padding: 3px 0px 3px 16px;
border-left: 4px solid #59b295;
background: linear-gradient(
90deg,
rgba(134, 191, 105, 0.8) 0%,
rgba(57, 147, 191, 0.1) 100%
);
background: linear-gradient(90deg, rgba(134, 191, 105, 0.8) 0%, rgba(57, 147, 191, 0.1) 100%);
font-family: PingFang SC;
font-size: 16px;

2
src/theme/var.css

@ -77,7 +77,7 @@
--sy-border-color: rgba(10, 204, 204, 0.32);
/*滚动条*/
--sy-webkit-scrollbar-background: transparent;
--sy-webkit-scrollbar-thumb-background: rgba(0, 0, 0, 0.4);
--sy-webkit-scrollbar-thumb-background: rgba(180, 212, 212, 1);
/*边框*/
--sy-base-border-color: rgba(0, 0, 0, 0.2);
}

245
src/utils/mapUtils.ts

@ -0,0 +1,245 @@
import { getLayerLegend } from "@/api/map";
import axios from "axios";
const identifyLayerActions: any = {
"021102": async (params: any) => {
return new Promise(async (resolve, reject) => {
const { url, input_geometry, geometry_type, layers = "all", returnGeometry = false, tolerance = 1 } = params;
const result = await axios.get(`${url}.json`);
if (result.status === 200 && result.data) {
const { xmin, ymin, xmax, ymax } = result.data.fullExtent;
const mapExtent = `${xmin},${ymin},${xmax},${ymax}`;
const data = await axios.get(
`${url}/identify?geometry=${input_geometry}&geometryType=${geometry_type}&layers=${layers}&returnGeometry=${returnGeometry}&mapExtent=${mapExtent}&imageDisplay=600,550,96&tolerance=${tolerance}&out_fields=*&f=json`,
);
if (data.status === 200 && data.data) {
resolve(data.data);
} else {
resolve(null);
}
}
});
},
"030300": async (params: any) => {
console.log(params);
},
"081100": async (params: any) => {
return new Promise(async (resolve, reject) => {
let { queryMode, bufferDistance, geometry, url, layerTable, layerName, k } = params;
// const queryUrl = url.split('maps/')[0].replace('map-', 'data-');
let queryUrl = "";
if (layerName.split(";").length == 1 && layerName.includes(":")) {
queryUrl = url.split("maps/")[0].replace("map-", "data-");
} else if (layerName.split(";").length == 2 && layerName.includes(":")) {
let dataServiceLayerInfo = layerName.split(";");
queryUrl = dataServiceLayerInfo[0] + "/";
layerName = dataServiceLayerInfo[1];
}
let requestUrl = `${queryUrl}data/featureResults.rjson?returnContent=true`;
if (k) {
requestUrl += `&k=${k}`;
}
const result = await axios.post(
requestUrl,
JSON.stringify({
datasetNames: [layerTable || layerName],
getFeatureMode: queryMode,
bufferDistance,
geometry,
}),
{
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
},
);
resolve(result);
});
},
};
export const queryLayersByPos = (position: any, layerList = [], cb: any) => {
const { lng, lat } = position;
const layers = layerList;
layers.forEach(async (layer: any) => {
const action = identifyLayerActions[layer.layerType];
switch (layer.layerType) {
case "021102": {
const input_geometry = `${lng},${lat}`;
const geometry_type = "esriGeometryPoint";
const result = await action({
input_geometry,
geometry_type,
url: layer.url,
});
let res;
if (result?.results?.length > 0) {
res = result.results[0];
}
if (cb) cb(res);
break;
}
case "030300":
break;
case "081100": {
const queryMode = "BUFFER";
const bufferDistance = 0.005; // 缓冲距离大概是50米
// const bufferDistance = 0.5; // 缓冲距离大概是??米
const spatialQueryMode = "INTERSECT";
const geometry = {
id: 0,
style: null,
parts: [1],
points: [
{
x: lng,
y: lat,
},
],
type: "POINT",
};
let type = "";
let isSite = false;
let layerTable = "";
if (layer.extendData) {
const extendData = JSON.parse(layer.extendData);
}
const result = await action({
queryMode,
spatialQueryMode,
bufferDistance,
geometry,
layerName: layer.text,
layerTable,
url: layer.url,
k: layer.serviceToken,
});
let resData;
if ([200, 201].includes(result.status) && result?.data?.datasetInfos?.length > 0) {
console.log("处理业务数据 >>>>>> ", result?.data);
const fieldInfos = result.data.datasetInfos?.[0]?.fieldInfos;
console.log("fieldInfos >>>>> ", fieldInfos);
const currentFeature = result.data.features?.[0];
console.log("currentFeature >>>>> ", currentFeature);
const nameIndex = fieldInfos.findIndex((f: any) => ["res_name", "dike_name", "waga_name"].includes(f.name));
const codeIndex = fieldInfos.findIndex((f: any) => ["res_code", "dike_code", "waga_code"].includes(f.name));
// if (layer?.fields?.length > 0) {
// const fieldIndex = fieldInfos.findIndex((f) => f.name === layer?.fields[0]?.field);
// code = currentFeature?.fieldValues[fieldIndex];
// }
resData = {
attributes: {
showCode: "" + currentFeature?.fieldValues[codeIndex],
showName: "" + currentFeature?.fieldValues[nameIndex],
},
};
console.log("resData >>>>> ", resData);
}
if (cb) cb(resData);
break;
}
default:
break;
}
});
};
// 获取图层图例
export const getSelectedLayersLegend = (selectedLayersList: any) => {
let legendList: any = [];
selectedLayersList.forEach(async (layer: any) => {
switch (layer.layerType) {
case "021102": {
const res = await getLayerLegend(layer.url);
let mapName = "";
if (res.status === 200) {
mapName = res.data.mapName;
}
let queryUrl = `${layer.url}/legend.json`;
if (layer.serviceToken) {
queryUrl = `${layer.url}/legend.json?k=${layer.serviceToken}`;
}
const result = await getLayerLegend(queryUrl);
// 超图的arcgisrest服务 - 021102
let currentLayer = [];
if (mapName) {
currentLayer = result.data.layers.filter((item: any) => item.layerName === mapName);
}
const legends: any = [];
currentLayer.forEach((item: any) => {
legends.push(item);
});
legendList.push({
layerName: mapName,
legends,
});
break;
}
case "081100": {
let queryUrl = `${layer.url}.json`;
if (layer.serviceToken) {
queryUrl = `${layer.url}.json?k=${layer.serviceToken}`;
}
const res = await getLayerLegend(queryUrl);
// let mapName = '';
// if (res?.status === 200) {
// mapName = res.data.name;
// }
let mapName = layer.nameCn;
// 用逗号分隔的图层范围
let bbox =
res?.data?.viewBounds?.left +
"," +
res?.data?.viewBounds?.bottom +
"," +
res?.data?.viewBounds?.right +
"," +
res?.data?.viewBounds?.top;
let result = null;
if (layer.serviceToken) {
result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}&k=${layer.serviceToken}`);
} else {
result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}`);
}
console.log(result);
if (result.status === 200 && result.data) {
let currentLayer = [];
// if (mapName) {
// currentLayer = result.data.layerLegends.filter((item) => item.layerCaption === mapName);
// }
// if (currentLayer.length > 0) {
// currentLayer = currentLayer[0];
// legendList.push({
// ...currentLayer,
// layerName: mapName
// });
// }
if (result.data.layerLegends && result.data.layerLegends.length > 0) {
let layerLegends = result.data.layerLegends[0];
if (layerLegends.subLayerLegends && layerLegends.subLayerLegends.length > 0) {
let subLayerLegends = layerLegends.subLayerLegends;
let legends = [];
for (let item of subLayerLegends) {
legends.push(...item.legends);
}
legendList.push({
legends,
layerName: mapName,
});
} else {
currentLayer = result.data.layerLegends[0];
legendList.push({
...currentLayer,
layerName: mapName,
});
}
}
}
break;
}
}
});
return legendList;
};

65
src/views/Main/Map/components/LayerSwitch.vue

@ -0,0 +1,65 @@
<template>
<div class="layer-switch">
<div class="btn">
<img class="icon" src="@/assets/map/layer.svg" alt="" @click="layerTreeChange" />
</div>
<div class="slider-line"></div>
<div class="btn">
<el-tooltip effect="dark" :content="is3DScene ? '三维模式' : '二维模式'" placement="right">
<img class="icon" src="@/assets/map/changeScene.svg" alt="" @click="handleChangeMode" />
</el-tooltip>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import * as SyCim from "sy-cesium-sdk";
defineOptions({
name: "MapLayerSwitch",
});
let is3DScene = ref(true);
let showTreeBox = ref(false);
const emits = defineEmits(["layerTreeChange"]);
function handleChangeMode() {
is3DScene.value = !is3DScene.value;
const mapSceneType = !is3DScene.value ? 2 : 3;
viewer?.changeSceneMode(mapSceneType);
let position = new SyCim.Position(113.27, 23.13, 600000, 0, -90, 0);
viewer?.flyToPosition(
position,
() => {
console.log("移动结束 >>>>> ");
},
1,
);
}
function layerTreeChange() {
showTreeBox.value = !showTreeBox.value;
emits("showChange", showTreeBox.value);
}
</script>
<style lang="scss" scoped>
.layer-switch {
display: flex;
width: 100%;
height: 37px;
align-items: center;
justify-content: center;
.btn {
padding: 8px 12px;
.icon {
width: 17px;
height: 17px;
}
}
.slider-line {
width: 1px;
height: 50%;
background-color: rgba(0, 0, 0, 0.2);
}
}
</style>

138
src/views/Main/Map/components/LayerTree.vue

@ -0,0 +1,138 @@
<template>
<SyDialog dialog-class="layer-tree-dialog" :dialog-show="showDialog" :style="style">
<template #body>
<Tree
ref="layerTreeRef"
class="layer-tree"
node-key="id"
showNodeLabelTips
:data="layerData"
:defaultProps="defaultProps"
:default-expanded-keys="expandedLayerKeys"
:default-checked-keys="selectedLayerKeys"
:default-expand-all="true"
:show-input="false"
:show-checkbox="true"
@check="handleCheck"
>
<template v-slot:default="{ data, onMouseoverTip }">
<div class="item-name">
<div class="icon">
<img v-if="data && !data.iconCls" src="@/assets/map/icon-layer.png" alt="" />
<img v-else src="@/assets/map/icon-folder.png" alt="" />
</div>
<span class="name" @mouseover="onMouseoverTip($event)">
{{ data.nameCn || data.text }}
</span>
</div>
</template>
<template v-slot:right="{ node, data }">
<span class="btn-item" v-if="data && !data.iconCls" @click.stop="flyTo(data)">
<iconpark-icon size="16" name="location"></iconpark-icon>
</span>
</template>
</Tree>
</template>
</SyDialog>
</template>
<script setup lang="ts">
import { ref, watch, computed, nextTick } from "vue";
import SyDialog from "@/components/Dialog/index.vue";
import { useProjectStore } from "@/store/modules/project";
import { useLayer } from "@/hooks/web/useProject";
import { flattenTree } from "@/utils/index";
import Tree from "@/components/Tree/index.vue";
const { addLayer, removeLayer, zoomToLayer } = useLayer();
const projectStore = useProjectStore();
defineOptions({
name: "MapLayerTree",
});
defineProps({
showDialog: {
type: Boolean,
default: false,
},
type: {
type: String,
default: "normal",
},
style: {
type: String,
default: "",
},
});
const defaultProps: any = {
children: "children",
label: "text",
};
const layerTreeRef = ref();
const layerData = computed(() => projectStore.layerData);
const expandedLayerKeys = computed(() => projectStore.expandedLayerKeys);
const selectedLayerKeys = computed(() => projectStore.selectedLayerKeys);
const sceneConfig = computed(() => projectStore.sceneConfig);
const flyTo = (data: any) => {
zoomToLayer(data);
};
const handleCheck = (data: any, checked: boolean) => {
if (data.pid === "root" || data.layers) return;
if (checked) {
// const list = layerTreeRef.value?.getCheckedNodes();
addLayer(data);
// this.legendList = getSelectedLayersLegend(list);
// this.layerList.push(data);
} else {
// this.checkList = this.checkList.filter((v) => v != data.id);
// this.layerList = this.layerList.filter((v) => v.id != data.id);
removeLayer(data);
}
};
const toggleShow = (data: any, checked: boolean) => {
if (data.pid === "root" || data.layers) return;
layerTreeRef.value?.setChecked(data.id, checked, true);
onCheck(data);
};
const handleCheckChange = (data: any, checkedData: any) => {
const { id } = data;
const { checkedKeys } = checkedData;
const nodes = [data];
if (data.children?.length > 0) {
flattenTree(data.children, nodes);
}
if (checkedKeys.includes(id)) {
//
projectStore.setLayerSelectedNodes(nodes);
} else {
//
projectStore.removeLayerSelectedNodes(nodes);
}
};
const onCheck = (data: any) => {
//
const checkedKeys = layerTreeRef.value?.getCheckedKeys();
handleCheckChange(data, { checkedKeys });
};
watch(
() => sceneConfig.value,
(val) => {
if (val?.layerId) {
const layerIds = JSON.parse(val.layerId);
layerTreeRef.value?.setCheckedKeys(layerIds, true);
nextTick(() => {
setTimeout(() => {
const checkedNodes = layerTreeRef.value?.getCheckedNodes(true, false);
checkedNodes.forEach((data: any) => {
handleCheckChange(data, { checkedKeys: layerIds });
});
}, 500);
});
}
},
);
</script>

9
src/views/Main/Map/components/Legend.vue

@ -23,6 +23,9 @@ const props = defineProps({
default: () => [],
},
});
defineOptions({
name: "MapLegend",
});
let legendList: any = ref([]);
const defaultList = [
{
@ -73,7 +76,7 @@ onMounted(() => {
max-height: 150px;
overflow-y: auto;
flex-direction: column;
gap: 12px;
gap: 8px;
.row {
display: flex;
align-items: center;
@ -81,8 +84,8 @@ onMounted(() => {
color: rgba(0, 0, 0, 0.9);
}
.icon {
width: 24px;
height: 24px;
width: 20px;
height: 20px;
margin-right: 4px;
}
}

29
src/views/Main/Map/index.vue

@ -1,7 +1,9 @@
<script setup lang="ts">
import { onMounted, onBeforeUnmount } from "vue";
import { onMounted, ref, onBeforeUnmount } from "vue";
import { messageCallback } from "@/message/index";
import Legend from "./components/Legend.vue";
import MapLegend from "./components/Legend.vue";
import MapLayerSwitch from "./components/LayerSwitch.vue";
import LayerTree from "./components/LayerTree.vue";
import { useMap } from "@/hooks/web/useMap";
const { initMap } = useMap();
const resize = () => {
@ -11,6 +13,10 @@ const resize = () => {
const zoom = clientHeight / 1080;
(app.style as any).zoom = zoom * 100 + "%";
};
const showLayerTree = ref(false);
function layerTreeChange(val: boolean) {
showLayerTree.value = val;
}
onMounted(async () => {
initMap();
@ -27,19 +33,30 @@ onBeforeUnmount(() => {
<template>
<div class="legend-box">
<Legend></Legend>
<MapLegend></MapLegend>
</div>
<div class="layer-switch-box">
<MapLayerSwitch @showChange="layerTreeChange"></MapLayerSwitch>
</div>
<LayerTree :showDialog="showLayerTree" />
</template>
<style lang="scss" scoped>
.legend-box {
position: absolute;
position: fixed;
bottom: 24px;
right: 550px;
right: 510px;
padding: 12px 0 12px 12px;
overflow-y: auto;
border-radius: 4px;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
}
.layer-switch-box {
position: fixed;
top: 24px;
right: 510px;
border-radius: 4px;
background-color: #fff;
}
</style>

Loading…
Cancel
Save