<template>
|
<div class="layer-control bg-white flex-col" style="display: flex">
|
<div class="bg-white rounded flex-0">
|
<div class="header flex-items-center pb-1.5" style="border-bottom: 1px solid black">
|
<div class="flex-items-center">
|
<span class="ywifont ywicon-guanbi cursor-pointer mr-1.5" @click="closeClick"></span>
|
<span class="text-lg font-bold">主题</span>
|
</div>
|
</div>
|
</div>
|
<div class="flex-auto content rounded-lg overflow-y-auto">
|
<!-- <el-tree
|
class="w-full"
|
:data="themeInfo"
|
:props="defaultProps"
|
show-checkbox
|
node-key="id"
|
default-expand-all
|
ref="checkTreeRef"
|
@check="handleCheck"
|
@node-click="handleNodeClick"
|
>
|
<template #default="{ node, data }">
|
<span>{{ node.label }}</span>
|
</template>
|
</el-tree> -->
|
<div>
|
<div class="theme-item" v-for="themeGroup in themeInfo" :key="themeGroup.id">
|
<div class="theme-item-title h-fit flex-items-center gap-2 mb-1">
|
<div class="w-1 h-4 bg-[#1677ff]"></div>
|
<span class="font-bold">{{ themeGroup.title }}</span>
|
</div>
|
<div>
|
<el-radio-group v-model="themeGroup.activeTheme" class="w-full" @change="(val) => handleThemeChange(val, themeGroup)">
|
<div class="flex w-full" v-for="(children, index) in themeGroup.viewChildren" :key="index">
|
<el-radio v-for="item in children" :key="item.id" :label="item.id" class="flex-1">{{ item.title }}</el-radio>
|
</div>
|
</el-radio-group>
|
</div>
|
</div>
|
</div>
|
<!-- <el-checkbox-group v-model="checkList" :max="1">
|
<el-checkbox label="Option A" value="Value A" />
|
<el-checkbox label="Option B" value="Value B" />
|
<el-checkbox label="Option C" value="Value C" />
|
<el-checkbox label="disabled" value="Value disabled" disabled />
|
<el-checkbox label="selected and disabled" value="Value selected and disabled" disabled />
|
</el-checkbox-group> -->
|
<div class="theme-item" v-for="legend in legendList" :key="legend.title">
|
<div class="theme-item-title h-fit flex-items-center gap-2 mb-2">
|
<div class="w-1 h-4 bg-[#1677ff]"></div>
|
<span class="font-bold">{{ legend.title }}</span>
|
</div>
|
<div class="theme-item-content">
|
<el-row :gutter="0">
|
<el-col v-for="item in legend.legend" :key="item.style" :span="12" class="mb20">
|
<div class="flex-items-center gap-2">
|
<div class="w-4 h-2 bg-[#1677ff]" :style="{ backgroundColor: `#${item.style}` }"></div>
|
<span>{{ item.label }}</span>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup lang="ts" name="ThemeControl">
|
import type { ElTree } from 'element-plus';
|
import { chunk } from 'lodash-es';
|
import Circle from 'ol/style/Circle';
|
import Style from 'ol/style/Style';
|
import { computed, ref, watch } from 'vue';
|
import { switchMapTheme } from '/@/api/map';
|
import type { OLMap } from '/@/model/map/OLMap';
|
import { formatDate } from '/@/utils/formatTime';
|
import { travelTree } from '/@/utils/util';
|
import Fill from 'ol/style/Fill';
|
import Stroke from 'ol/style/Stroke';
|
import { Text } from 'ol/style';
|
import { ElLoadingService } from 'element-plus';
|
|
const props = defineProps(['olMap']);
|
const emit = defineEmits(['close']);
|
|
const defaultProps = {
|
children: 'children',
|
label: 'title',
|
};
|
const closeClick = () => {
|
emit('close');
|
};
|
const checkTreeRef = ref<InstanceType<typeof ElTree>>();
|
const parseLegends = (legends) => {
|
const result = legends.map((item) => {
|
const isEqual = item.operate === '=';
|
const result = {
|
...item,
|
legend: item.legend.map((legendItem, index, array) => {
|
const preItem = array[index - 1];
|
let label = '';
|
if (isEqual) {
|
label = legendItem.value;
|
} else if (preItem) {
|
label = `>${preItem.value}且${item.operate}${legendItem.value}`;
|
} else {
|
label = `${item.operate}${legendItem.value}`;
|
}
|
return {
|
...legendItem,
|
label: label,
|
};
|
}),
|
};
|
|
result.legend.push({
|
style: item.default,
|
value: '',
|
label: result.legend.length == 0 ? '默认' : `>${item.legend[item.legend.length - 1].value}`,
|
});
|
return result;
|
});
|
return result;
|
};
|
let isHumanCheckTrigger = false;
|
const handleCheck = (data: any, node: any) => {
|
const checkedKeys = node.checkedKeys;
|
isHumanCheckTrigger = true;
|
|
// 取消分组中旧的选项
|
const excludeKeys = [];
|
for (const themeGroup of themeInfo.value) {
|
// 原来有多少个设置的
|
const originSet = themeGroup.children.filter((item) => item.isSet);
|
// 现在有多少个设置的
|
const newSet = themeGroup.children.filter((item) => checkedKeys.includes(item.id));
|
if (originSet.length === 1 && newSet.length === 2) {
|
originSet[0].isSet = false;
|
excludeKeys.push(originSet[0].id);
|
}
|
}
|
|
const realCheckedKeys = checkedKeys.filter((key) => !excludeKeys.includes(key));
|
|
travelTree(themeInfo.value, (theme, index, array, parent) => {
|
if (theme.type === 'theme') {
|
const id = theme.id;
|
if (!realCheckedKeys.includes(id)) {
|
theme.isSet = false;
|
} else {
|
theme.isSet = true;
|
}
|
}
|
});
|
checkTreeRef.value?.setCheckedKeys(realCheckedKeys);
|
};
|
|
const handleNodeClick = () => {};
|
const themeInfo = computed(() => {
|
const info = (props.olMap as OLMap).themeInfo.value;
|
// info.push({
|
// id: 'group-测试',
|
// title: '测试',
|
// type: 'theme-group',
|
// children: [
|
// {
|
// id: 'junction_1',
|
// title: '测试1',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_2',
|
// title: '测试2',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_3',
|
// title: '测试3',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_4',
|
// title: '测试4',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_5',
|
// title: '测试5',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_6',
|
// title: '测试6',
|
// type: 'theme',
|
// },
|
// ],
|
// });
|
|
const result = info.map((item, index) => {
|
if (item.children) {
|
// index == 0 &&
|
// item.children.push(
|
// ...[
|
// {
|
// id: 'junction_1',
|
// title: '测试1',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_2',
|
// title: '测试2',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_3',
|
// title: '测试3',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_4',
|
// title: '测试4',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_5',
|
// title: '测试5',
|
// type: 'theme',
|
// },
|
// {
|
// id: 'junction_6',
|
// title: '测试6',
|
// type: 'theme',
|
// },
|
// ]
|
// );
|
item.viewChildren = chunk(item.children, 2);
|
}
|
return item;
|
});
|
|
return result;
|
});
|
|
const styleMap: Record<string, Record<string, Style>> = {
|
junction: {},
|
pipe: {},
|
polygon: {},
|
};
|
const maxResolution = 1;
|
|
const getText = function (feature, resolution) {
|
const oname = feature.get('oname');
|
let text = `first-${oname[0]}`;
|
if (resolution > maxResolution) {
|
text = '';
|
}
|
|
// else if (type == 'hide') {
|
// text = '';
|
// } else if (type == 'shorten') {
|
// text = text.trunc(12);
|
// } else if (type == 'wrap' && (!dom.placement || dom.placement.value != 'line')) {
|
// text = stringDivider(text, 16, '\n');
|
// }
|
|
return text;
|
};
|
const createTextStyle = (feature, resolution) => {
|
return new Style({
|
text: new Text({
|
text: getText(feature, resolution),
|
font: '14px Arial',
|
fill: new Fill({ color: '#000' }),
|
stroke: new Stroke({ color: '#fff', width: 2 }),
|
offsetX: 10, // 文字相对于几何中心的水平偏移
|
offsetY: 20, // 文字相对于几何中心的垂直偏移
|
}),
|
});
|
};
|
|
const legendList = ref([]);
|
const changeTheme = async (themeId: string) => {
|
const loadingInstance = ElLoadingService({
|
text: '加载主题中...',
|
target: '.layout-parent',
|
});
|
const res = await switchMapTheme({
|
theme_id: themeId,
|
time: formatDate(new Date()),
|
}).finally(() => {
|
loadingInstance.close();
|
});
|
|
// 加入主题之后的样式函数
|
const themeLayerStyleFunc = (feature, resolution) => {
|
const otype = feature.get('otype');
|
const oname = feature.get('oname');
|
// oname 映射样式
|
const onameStyleMap = res?.[`O_${otype}`] ?? {};
|
const themeStyles = res?.styles ?? [];
|
const styleIndex = onameStyleMap[oname]?.style_id;
|
const themeStyle = styleIndex == null ? null : themeStyles[styleIndex];
|
// if(themeStyle){
|
// }
|
const shape = feature.getGeometry().getType();
|
const styles = [];
|
switch (shape) {
|
case 'Point':
|
const pSize = themeStyle?.PSIZE ?? feature.get('psize');
|
|
const pcolor = themeStyle?.PCOLOR ?? feature.get('pcolor');
|
|
const pStyle = themeStyle?.PSTYLE ?? feature.get('pstyle');
|
|
const pointStyle = props.olMap?.getPointStyles({
|
size: pSize,
|
color: pcolor,
|
style: pStyle,
|
});
|
pointStyle && styles.push(pointStyle);
|
break;
|
case 'LineString':
|
const lSize = themeStyle?.LSIZE ?? feature.get('lsize');
|
const lColor = themeStyle?.LCOLOR ?? feature.get('lcolor');
|
const lStyle = themeStyle?.LSTYLE ?? feature.get('lstyle');
|
|
const pSize1 = themeStyle?.PSIZE ?? feature.get('psize');
|
const pcolor1 = themeStyle?.PCOLOR ?? feature.get('pcolor');
|
const pStyle1 = themeStyle?.PSTYLE ?? feature.get('pstyle');
|
|
const lineStyle = props.olMap?.getLineStyles({
|
lsize: lSize,
|
lcolor: lColor,
|
lstyle: lStyle,
|
psize: pSize1,
|
pcolor: pcolor1,
|
pstyle: pStyle1,
|
});
|
lineStyle && styles.push(...lineStyle);
|
|
break;
|
case 'Polygon':
|
const pColor = feature.get('pcolor');
|
const lSize1 = feature.get('lsize');
|
const lColor1 = feature.get('lcolor');
|
const lstyle1 = feature.get('lstyle');
|
const polygonStyle = props.olMap?.getPolygonStyles({
|
pcolor: pColor,
|
lsize: lSize1,
|
lcolor: lColor1,
|
lstyle: lstyle1,
|
});
|
polygonStyle && styles.push(...polygonStyle);
|
|
break;
|
default:
|
break;
|
}
|
|
// switch (otype) {
|
// case 'WDM_JUNCTIONS':
|
// const textStyle = createTextStyle(feature, resolution);
|
// styles.push(textStyle);
|
|
// case 'WDM_PIPES':
|
// break;
|
// }
|
//#region ====================== 添加标注 ======================
|
const tString = themeStyle?.TSTRING ?? feature.get('tstring');
|
const tStringStyle = props.olMap?.getLabelStyles({
|
textContent: tString,
|
resolution,
|
});
|
tStringStyle && styles.push(tStringStyle);
|
//#endregion
|
|
return styles;
|
};
|
const allLayers = props.olMap.getAllLayers();
|
for (const item of allLayers) {
|
if (props.olMap?.unsupportedLayers.includes(item.id)) {
|
continue;
|
}
|
item.model.setStyle(themeLayerStyleFunc);
|
}
|
return res?.legends ?? [];
|
};
|
|
const handleThemeChange = async (val, themeGroup) => {
|
const allIds = [];
|
for (const item of themeInfo.value) {
|
if (item.activeTheme) {
|
allIds.push(item.activeTheme);
|
}
|
}
|
const ids = allIds.join(',');
|
|
if (!ids) {
|
const allLayers = props.olMap.getAllLayerModels();
|
for (const item of allLayers) {
|
const originStyle = item.get('originStyle');
|
originStyle && item.setStyle(originStyle);
|
}
|
return;
|
}
|
const legends = await changeTheme(ids);
|
legendList.value = parseLegends(legends);
|
};
|
|
watch(
|
() => themeInfo.value,
|
(val) => {
|
if (isHumanCheckTrigger) {
|
isHumanCheckTrigger = false;
|
return;
|
}
|
|
const keys = [];
|
travelTree(themeInfo.value, (item) => {
|
if (item.type === 'theme') {
|
if (item.isSet) {
|
keys.push(item.id);
|
}
|
}
|
});
|
// const keys = layerInfo.value.filter((item) => item.isSet).map((item) => item.id);
|
checkTreeRef.value?.setCheckedKeys(keys);
|
}
|
);
|
</script>
|
<style scoped lang="scss"></style>
|