gerson
2025-02-09 a7ac2d87ee0f8ae2e7a507729660dedd01036402
src/components/chat/chatComponents/summaryCom/components/recordSetTable/map/panelTool/ThemeControl.vue
@@ -1,6 +1,6 @@
<template>
   <div class="layer-control bg-white">
      <div class="w-[370px] bg-white p-3 rounded">
   <div class="layer-control bg-white p-3 w-[280px]">
      <div class="bg-white rounded">
         <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>
@@ -9,7 +9,7 @@
         </div>
      </div>
      <div class="content rounded-lg max-h-[700px] overflow-y-auto min-h-[600px]">
         <el-tree
         <!-- <el-tree
            class="w-full"
            :data="themeInfo"
            :props="defaultProps"
@@ -23,16 +23,63 @@
            <template #default="{ node, data }">
               <span>{{ node.label }}</span>
            </template>
         </el-tree>
         </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 { computed, ref, watch, watchEffect } from 'vue';
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 { ElTree } from 'element-plus';
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']);
@@ -45,27 +92,306 @@
   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;
   travelTree(themeInfo.value, (theme) => {
      console.log("🚀 ~ theme.type:", theme.type)
   // 取消分组中旧的选项
   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 (!checkedKeys.includes(id)) {
         if (!realCheckedKeys.includes(id)) {
            theme.isSet = false;
         } else {
            theme.isSet = true;
         }
      }
   });
   console.log('🚀 ~ human keys:', checkedKeys);
   checkTreeRef.value?.setCheckedKeys(realCheckedKeys);
};
const handleNodeClick = () => {};
const themeInfo = computed(() => (props.olMap as OLMap).themeInfo.value);
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 lineStyle = props.olMap?.getLineStyles({
               lsize: lSize,
               lcolor: lColor,
               lstyle: lStyle,
            });
            lineStyle && styles.push(...lineStyle);
            break;
         case 'Polygon':
            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.layerInfo.value.reduce((preVal, curVal) => {
      return preVal.concat(curVal.children.map((item) => item));
   }, []);
   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.layerInfo.value.reduce((preVal, curVal) => {
         return preVal.concat(curVal.children.map((item) => item.model));
      }, []);
      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,
@@ -84,7 +410,6 @@
         }
      });
      // const keys = layerInfo.value.filter((item) => item.isSet).map((item) => item.id);
      console.log('🚀 ~ watch keys:', keys);
      checkTreeRef.value?.setCheckedKeys(keys);
   }
);