Parcourir la source

feat: 对接地图功能

bill il y a 2 mois
Parent
commit
93b78709c3

+ 5 - 2
src/core/components/icon/temp-icon.vue

@@ -33,10 +33,12 @@ watch(
   async (url) => {
     svg.value = null;
     const svgContent = await getSvgContent(url);
-    svg.value = parseSvgContent(svgContent);
-    if (svg.value.paths.length === 0) {
+    const content = parseSvgContent(svgContent);
+    if (content.paths.length === 0) {
       svg.value = null;
       console.error(props.data, "路径数据不正确不是svg");
+    } else {
+      svg.value = content;
     }
   },
   { immediate: true }
@@ -44,6 +46,7 @@ watch(
 
 const scale = computed(() => {
   if (!svg.value) return null;
+
   let w = data.value.width;
   let h = data.value.height;
   w = w || svg.value.width || 0;

+ 4 - 1
src/core/components/line/single-line.vue

@@ -121,7 +121,10 @@ const points = computed(() => [
 const lineData = computed(() => props.line);
 const describes = mergeDescribes(lineData, {}, ["stroke", "strokeWidth"]);
 const d = describes as any;
-d.strokeWidth.props.proportion = true;
+d.strokeWidth.props = {
+  ...d.strokeWidth.props,
+  proportion: true,
+};
 d.strokeWidth.label = "粗细";
 d.stroke.label = "颜色";
 

+ 2 - 0
src/core/components/serial/index.ts

@@ -179,5 +179,7 @@ export const matResponse = (
 export const getPredefine = (key: keyof CircleData) => {
   if (key === "fill") {
     return { canun: true };
+  } else if (key === 'strokeWidth') {
+    return { proportion: true }
   }
 };

+ 3 - 3
src/core/components/serial/serial.vue

@@ -45,9 +45,9 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus<
   props,
   getMouseStyle,
   defaultStyle,
-  alignment: (data, mat) => {
-    return matResponse({ mat: mat.multiply(new Transform(data.mat)), data });
-  },
+  // alignment: (data, mat) => {
+  //   return matResponse({ mat: mat.multiply(new Transform(data.mat)), data });
+  // },
   transformType: "custom",
   customTransform(callback, shape, data) {
     let initRadius: Pos;

+ 2 - 2
src/core/components/table/table.vue

@@ -247,10 +247,10 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus<
     "strokeWidth",
     "fontSize",
     // "ref",
-    "opacity",
+    // "opacity",
     //  "zIndex"
     "align",
-    "fontStyle",
+    // "fontStyle",
   ],
 });
 

+ 3 - 1
src/core/html-mount/propertys/mount.vue

@@ -6,7 +6,9 @@
           v-if="name || data?.itemName || (type && components[type].shapeName)"
           class="title"
         >
-          <h4>设置{{ name || data?.itemName || (type && components[type].shapeName) }}</h4>
+          <h4>
+            设置{{ name || data?.itemName || (type && components[type].shapeName) }}
+          </h4>
           <icon
             name="close"
             size="20px"

+ 12 - 4
src/example/fuse/enter.ts

@@ -101,7 +101,7 @@ const login = (isBack = true) => {
       delete query["redirect"];
       link = urlUpdateQuery(url.toString(), query, true);
     }
-    location.replace(link);
+    // location.replace(link);
   }
 };
 
@@ -186,7 +186,11 @@ const getTabulationId = async (id: string) => {
 const getTabulationData = genLoading(async (id: string) => {
   if (!id) {
     return {
-      store: {},
+      store: {
+        layers: {
+          default: {},
+        },
+      },
       cover: null,
       isAutoGen: true,
       viewport: null,
@@ -196,11 +200,15 @@ const getTabulationData = genLoading(async (id: string) => {
   const data = await get(`fusion/caseTabulation/info`, { tabulationId: id });
   return {
     ...data,
-    store: JSON.parse(data.store),
+    store: JSON.parse(data.store) || {
+      layers: {
+        default: {},
+      },
+    },
     viewport: JSON.parse(data.viewport),
     cover: JSON.parse(data.cover),
     isAutoGen: Number(data.isAutoGen),
-    paperKey: data.paperKey,
+    paperKey: data.paperKey || 'a4',
   };
 });
 

+ 3 - 3
src/example/fuse/store.ts

@@ -24,10 +24,7 @@ export type TabCover = {
 };
 
 export const overviewData = ref() as Ref<{
-  high?: number,
-  width?: number,
   title?: string
-  mapUrl: string | null,
   cover?: string
   store: StoreData;
   viewport: number[] | null;
@@ -45,6 +42,9 @@ export const tabulationData = ref() as Ref<{
   paperKey: PaperKey;
   isAutoGen: boolean
   viewport: number[] | null;
+  mapUrl: string | null,
+  high?: number,
+  width?: number,
 }>
 export const refreshTabulationData = () => {
   return window.platform.getTabulationData(tabulationId.value).then((data: any) => tabulationData.value = data)

+ 2 - 0
src/example/fuse/views/overview/header.vue

@@ -198,4 +198,6 @@ const gotoTabulation = async () => {
   await saveHandler();
   router.push({ ...router.currentRoute.value, name: "tabulation" } as any);
 };
+
+defineExpose({ gotoTabulation, saveHandler })
 </script>

+ 8 - 32
src/example/fuse/views/overview/index.vue

@@ -5,7 +5,11 @@
     :ref="(d: any) => (draw = d?.draw)"
   >
     <template #header>
-      <Header @selectVR="(scene) => (vrScene = scene)" :title="title" />
+      <Header
+        @selectVR="(scene) => (vrScene = scene)"
+        :title="title"
+        :ref="(r) => (header = r)"
+      />
     </template>
     <template #slide>
       <Slide />
@@ -27,50 +31,22 @@ import { computed, ref, watch, watchEffect } from "vue";
 import { Draw } from "../../../components/container/use-draw";
 import { Scene } from "../../../platform/platform-resource";
 import Dialog from "../../../dialog/dialog.vue";
-import { refreshOverviewData, overviewData, mapImageKey } from "../../store";
-import { getMapImageItem } from "@/example/components/slide/actions";
-import { defaultLayer } from "@/constant";
+import { refreshOverviewData, overviewData } from "../../store";
 
 const uploadResourse = window.platform.uploadResourse;
 const full = ref(false);
 const draw = ref<Draw>();
 const vrScene = ref<Scene>();
-
-const setMap = () => {
-  if (
-    !(overviewData.value.mapUrl && overviewData.value.high && overviewData.value.width)
-  ) {
-    return;
-  }
-
-  const mapItem = getMapImageItem(overviewData.value.mapUrl, {
-    width: overviewData.value.width,
-    height: overviewData.value.high,
-  });
-  const images = overviewData.value.store.layers[defaultLayer].image;
-  if (!images) {
-    overviewData.value.store.layers[defaultLayer].image = [mapItem];
-  } else {
-    const oldMapItem = images.find((item) => item.key === mapImageKey);
-    if (oldMapItem) {
-      oldMapItem.url = mapItem.url;
-      oldMapItem.width = mapItem.width;
-      oldMapItem.height = mapItem.height;
-    } else {
-      images.push(mapItem);
-    }
-  }
-  overviewData.value.mapUrl = null;
-};
+const header = ref<any>();
 
 const init = async (draw: Draw) => {
   await refreshOverviewData();
-  setMap();
   draw.config.showCompass = false;
   draw.config.showLabelLine = true;
   draw.config.showComponentSize = false;
   draw.config.back = { color: "#f0f2f5", opacity: 1 };
   draw.store.setConfig({ proportion: { scale: 10, unit: "mm" } });
+  // setMap(draw);
   draw.store.setStore(overviewData.value.store);
   overviewData.value.viewport && draw.viewer.setViewMat(overviewData.value.viewport);
 };

+ 43 - 0
src/example/fuse/views/tabulation/defStyle.ts

@@ -0,0 +1,43 @@
+import { defaultStyle as iconDefStyle } from '@/core/components/icon'
+import { defaultStyle as rectDefStyle } from '@/core/components/rectangle'
+import { defaultStyle as circleDefStyle } from '@/core/components/circle'
+import { defaultStyle as triangleDefStyle } from '@/core/components/triangle'
+import { defaultStyle as arrowDefStyle } from '@/core/components/arrow'
+import { PaperKey } from '@/example/components/slide/actions'
+import { mergeFuns } from '@/utils/shared'
+import { getRealPixel } from './gen-tab'
+import { defaultTableStyle } from '@/core/components/serial'
+
+const setDefStyle = <T extends {}>(sys: T, custom: Partial<T>) => {
+  const backs: (() => void)[] = []
+  for (const key in custom) {
+    const oldVal = sys[key]
+    sys[key] = custom[key]!
+    backs.push(() => sys[key] = oldVal)
+  }
+
+  return mergeFuns(backs)
+}
+
+export const tabCustomStyle = (p: PaperKey) => {
+  const backs = [
+    setDefStyle(iconDefStyle, {
+      width: getRealPixel(2, p),
+      height: getRealPixel(2, p),
+    }),
+    setDefStyle(defaultTableStyle, {
+      nameColWidth: defaultTableStyle.valueColWidth = getRealPixel(20, p),
+      fontSize: getRealPixel(4, p),
+      padding: getRealPixel(2, p),
+      colHeight: getRealPixel(8, p),
+      repColCount: 2,
+    }),
+    setDefStyle(rectDefStyle, {strokeWidth: getRealPixel(1, p)}),
+    setDefStyle(circleDefStyle, {strokeWidth: getRealPixel(1, p)}),
+    setDefStyle(triangleDefStyle, {strokeWidth: getRealPixel(1, p)}),
+    setDefStyle(arrowDefStyle, {strokeWidth: getRealPixel(1, p)}),
+  ]
+  console.log(iconDefStyle)
+
+  return mergeFuns(backs)
+}

+ 0 - 1
src/example/fuse/views/tabulation/gen-tab.ts

@@ -52,7 +52,6 @@ export const setCoverPaperScale = (cover: ImageData, paperKey: PaperKey, scale:
 }
 
 export const genTabulationData = async (paperKey: PaperKey, compass?: number, cover?: TabCover | null) => {
-  console.error(cover)
   const { margin, size } = getPaperConfig(
     paperConfigs[paperKey].size,
     paperConfigs[paperKey].scale

+ 2 - 1
src/example/fuse/views/tabulation/header.vue

@@ -3,6 +3,7 @@
     :action-groups="actions"
     :title="title"
     :draw="draw"
+    :noBack="!overviewId"
     @back="router.replace({ name: 'overview', query: router.currentRoute.value.query })"
   >
     <template #saves>
@@ -27,7 +28,7 @@ import { getImage as getResourceImage } from "@/utils/resource.ts";
 import { nextTick, onUnmounted } from "vue";
 import { tabulationData } from "../../store.ts";
 import { Mode } from "@/constant/mode.ts";
-import { tabulationId } from "@/example/env.ts";
+import { overviewId, tabulationId } from "@/example/env.ts";
 import { listener } from "@/utils/event.ts";
 import { router } from "../../router.ts";
 

+ 56 - 11
src/example/fuse/views/tabulation/index.vue

@@ -31,19 +31,51 @@ import {
 } from "../../store";
 import { ImageData } from "@/core/components/image";
 import { TextData } from "@/core/components/text";
-import { getCoverPaperScale, getRealPixel, setCoverPaperScale } from "./gen-tab";
-import { defaultTableStyle } from "@/core/components/serial";
+import {
+  genTabulationData,
+  getCoverPaperScale,
+  getRealPixel,
+  repTabulationStore,
+  setCoverPaperScale,
+} from "./gen-tab";
 import { IconData } from "@/core/components/icon";
 import { Transform } from "konva/lib/Util";
 import { MathUtils } from "three";
 import { components } from "@/core/components";
 import { ShapeType } from "@/index";
 import { round } from "@/utils/shared";
+import { PaperKey } from "@/example/components/slide/actions";
+import { StoreData } from "@/core/store/store";
+import { getImageSize } from "@/utils/shape";
+import { tabCustomStyle } from "./defStyle";
+import { defaultLayer } from "@/constant";
 
 const uploadResourse = window.platform.uploadResourse;
 const full = ref(false);
 const draw = ref<Draw>();
 
+const setMap = async (paperKey: PaperKey, compass: number, store: StoreData) => {
+  const data = tabulationData.value;
+
+  if (data.mapUrl && data.high && data.width) {
+    const size = await getImageSize(data.mapUrl);
+    const cover = {
+      url: data.mapUrl,
+      ...size,
+      proportion: {
+        scale: (data.width / size.width) * 1000,
+        unit: "mm",
+      },
+    };
+    if (!data.store.config) {
+      const layer = await genTabulationData(paperKey, compass, cover);
+      data.store.layers[defaultLayer] = layer;
+    } else {
+      await repTabulationStore(paperKey, compass, cover, store);
+    }
+  }
+};
+
 const inited = ref(false);
 const init = async (draw: Draw) => {
   await refreshTabulationData();
@@ -51,7 +83,10 @@ const init = async (draw: Draw) => {
   draw.config.showGrid = false;
   draw.config.showLabelLine = false;
   draw.config.showComponentSize = false;
+
   const config: any = tabulationData.value.store.config || {};
+  const p = tabulationData.value.paperKey;
+  await setMap(p, 0, tabulationData.value.store);
   draw.store.setStore({
     ...tabulationData.value.store,
     config: {
@@ -61,25 +96,35 @@ const init = async (draw: Draw) => {
         ...(config.compass || {}),
         url: "./icons/compass.svg",
       },
+      proportion: {
+        scale: 1 / getRealPixel(1, p),
+        unit: "mm",
+      },
     },
   });
   inited.value = true;
-
-  const p = tabulationData.value.paperKey;
-  defaultTableStyle.nameColWidth = defaultTableStyle.valueColWidth = getRealPixel(20, p);
-  defaultTableStyle.fontSize = getRealPixel(4, p);
-  defaultTableStyle.padding = getRealPixel(2, p);
-  defaultTableStyle.colHeight = getRealPixel(8, p);
-  defaultTableStyle.repColCount = 2;
+  return tabCustomStyle(p);
 };
-watch(draw, (draw) => {
+watch(draw, (draw, _, onCleanup) => {
   if (draw) {
-    init(draw);
     for (const type in components) {
       draw.menusFilter.setMenusFilter(type as ShapeType, (items) => {
         return items.filter((item) => item.label !== "隐藏");
       });
     }
+    let des = false;
+    let unInit: () => void;
+    init(draw).then((_unInit) => {
+      if (!des) {
+        unInit = _unInit;
+      } else {
+        _unInit();
+      }
+    });
+    onCleanup(() => {
+      unInit && unInit();
+      des = true;
+    });
   }
 });
 

+ 136 - 0
src/example/fuse/views/tabulation/slide-icons.vue

@@ -0,0 +1,136 @@
+<template>
+  <ElCollapse class="icon-layout" v-model="activeGroups">
+    <ElCollapseItem
+      v-for="group in searchGroups"
+      :name="group.name"
+      v-if="searchGroups.length"
+    >
+      <template #title>
+        <h2>{{ group.name }}</h2>
+      </template>
+
+      <div class="type-children" v-for="typeChildren in group.children">
+        <h3 v-if="typeChildren.name">{{ typeChildren.name }}</h3>
+        <div class="icon-items">
+          <div
+            v-for="item in typeChildren.children"
+            @click="drawIcon(`./icons/${item.icon}.svg`, item.name, item)"
+          >
+            <Icon :name="item.icon" size="32px" :color="item.color" />
+            <span>{{ item.name }}</span>
+          </div>
+        </div>
+      </div>
+    </ElCollapseItem>
+    <el-empty description="暂无数据" v-else />
+  </ElCollapse>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from "vue";
+import { ElCollapse, ElCollapseItem, ElEmpty } from "element-plus";
+import { getSvgContent, parseSvgContent } from "@/utils/resource";
+import { Draw } from "../../../components/container/use-draw.ts";
+import { iconGroups } from "../../../constant";
+
+const groups = [iconGroups[iconGroups.length - 1]];
+const props = defineProps<{ draw: Draw }>();
+const emit = defineEmits<{ (e: "exit"): void }>();
+
+const drawIcon = async (url: string, name: string, item: any) => {
+  const svgContent = parseSvgContent(await getSvgContent(url));
+  console.log(item);
+
+  const maxSize = 40;
+  const addHeight = (maxSize / svgContent.width) * svgContent.height;
+  const size = {
+    width: maxSize,
+    height: addHeight,
+  };
+  if (size.height > maxSize) {
+    size.height = maxSize;
+    size.width = (maxSize / svgContent.height) * svgContent.width;
+  }
+
+  props.draw.enterDrawShape(
+    "icon",
+    {
+      url,
+      ...size,
+      name,
+      fill: "#000000",
+      ...(item.parse || {}),
+    },
+    true
+  );
+  emit("exit");
+};
+
+const activeGroups = ref(groups.map((item) => item.name));
+const keyword = ref("");
+const searchGroups = computed(() => {
+  return groups
+    .map((typeChildren) => {
+      const filterTypeChildren = typeChildren.children
+        .map((type) => {
+          const children = type.children.filter((item) =>
+            item.name.includes(keyword.value)
+          );
+          return {
+            ...type,
+            children,
+          };
+        })
+        .filter((type) => type.children.length > 0);
+      return {
+        ...typeChildren,
+        children: filterTypeChildren,
+      };
+    })
+    .filter((typeChildren) => typeChildren.children.length > 0);
+});
+</script>
+
+<style lang="scss" scoped>
+.icon-layout {
+  width: 320px;
+  height: 100%;
+  background-color: var(--el-menu-bg-color);
+  border-right: solid 1px var(--el-menu-border-color);
+  overflow-y: auto;
+  padding: 0 20px;
+  font-size: 14px;
+  color: #333;
+
+  h2,
+  h3 {
+    font-size: inherit;
+    color: inherit;
+  }
+
+  // .type-children:not(:first-child) {
+  //   margin-top: 20px;
+  // }
+  h3 {
+    margin-bottom: 20px;
+  }
+}
+
+.icon-items {
+  display: flex;
+  flex-wrap: wrap;
+
+  > div {
+    width: 25%;
+    text-align: center;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 20px;
+    span {
+      margin-top: 10px;
+    }
+  }
+}
+</style>

+ 16 - 1
src/example/fuse/views/tabulation/slide.vue

@@ -11,12 +11,16 @@ import {
   table,
   test,
   PaperKey,
+  draw as drawMenuRaw,
 } from "../../../components/slide/actions.ts";
 import { useDraw } from "../../../components/container/use-draw.ts";
-import { computed, nextTick, reactive, watch } from "vue";
+import { computed, markRaw, nextTick, reactive, watch } from "vue";
 import { tabulationData } from "../../store";
 import { genTabulationData } from "./gen-tab.ts";
 import { defaultLayer } from "@/constant/index.ts";
+import { copy } from "@/utils/shared.ts";
+import { v4 as uuid } from "uuid";
+import Icons from "./slide-icons.vue";
 
 const draw = useDraw();
 const paper = reactive({
@@ -27,8 +31,19 @@ const paper = reactive({
     handler: () => (tabulationData.value.paperKey = item.key as PaperKey),
   })),
 });
+
+const drawMenu = copy(drawMenuRaw);
+drawMenu.children!.shift();
+
 const menus = reactive([
   paper,
+  drawMenu,
+  {
+    icon: "legend",
+    name: "图例",
+    value: uuid(),
+    mount: markRaw(Icons),
+  },
   {
     ...text,
     payload: { ...text.payload, preset: { ...text.payload.preset, fontSize: 16 } },