Преглед на файлове

feat: 添加线段新功能

bill преди 3 месеца
родител
ревизия
3619923b2a

+ 0 - 3
src/core/components/line/index.ts

@@ -96,9 +96,6 @@ export const interactiveToData: InteractiveTo<"line"> = ({
 
 export const interactiveFixData: InteractiveFix<"line"> = ({ data, info }) => {
   const nv = [...info.consumed, info.cur!];
-  data.points = []
-  data.lines = []
-  data.polygon = []
 
   for (let i = 0; i < nv.length; i++) {
     if (inRevise(data.points[i], nv[i])) {

+ 0 - 1
src/core/components/line/line.vue

@@ -4,7 +4,6 @@
     @updateShape="emit('updateShape', { ...data })"
     :canEdit="true"
   />
-  
 </template>
 
 <script lang="ts" setup>

+ 54 - 20
src/core/components/line/single-line.vue

@@ -2,7 +2,7 @@
   <EditLine
     :ref="(d: any) => shape = d?.shape"
     :data="{ ...line, ...style }"
-    :opacity="addMode ? 0.3 : 1"
+    :opacity="isDrawIng ? 0.5 : 1"
     :points="points"
     :closed="false"
     :id="data.id"
@@ -19,27 +19,26 @@
     @add-point="addPoint"
   />
 
-  <v-group>
-    <SizeLine
-      v-if="config.showComponentSize || isDrawIng || dragIng"
-      :points="points"
-      :strokeWidth="style.strokeWidth"
-      :stroke="style.stroke"
-    />
-  </v-group>
+  <SizeLine
+    v-if="config.showComponentSize || isDrawIng || dragIng"
+    :points="points"
+    :strokeWidth="style.strokeWidth"
+    :stroke="style.stroke"
+  />
 
-  <template v-if="!mode.include(Mode.readonly) && canEdit">
+  <template v-if="(!mode.include(Mode.readonly) && canEdit) || isDrawIng">
     <EditPoint
       v-for="(point, ndx) in points"
       :key="point.id"
       :opacity="1"
-      :size="line.strokeWidth + 6"
+      :size="line.strokeWidth"
       :points="points"
+      :drawIng="ndx === 0 && isDrawIng"
       :ndx="ndx"
       :closed="false"
       :id="line.id"
       :disable="addMode"
-      :color="style.stroke"
+      :color="isDrawIng ? themeColor : style.stroke"
       @dragstart="dragstartHandler([point.id])"
       @update:position="(p) => emit('updatePoint', { ...point, ...p })"
       @dragend="dragendHandler"
@@ -54,9 +53,10 @@
     :name="shapeName"
     @change="
       () => {
-        emit('updateBefore');
+        isStartChange || emit('updateBefore');
         emit('updateLine', { ...line });
         emit('update');
+        isStartChange = false;
       }
     "
     @delete="delHandler"
@@ -69,7 +69,7 @@ import { computed, ref } from "vue";
 import { getMouseStyle, getSnapInfos, LineData, shapeName } from "./index.ts";
 import { onlyId } from "@/utils/shared.ts";
 import EditLine from "../share/edit-line.vue";
-import { Pos } from "@/utils/math.ts";
+import { getVectorLine, lineCenter, lineLen, lineVector, Pos } from "@/utils/math.ts";
 import EditPoint from "../share/edit-point.vue";
 import { Line } from "konva/lib/shapes/Line";
 import { DC } from "@/deconstruction.js";
@@ -111,6 +111,45 @@ const points = computed(() => [
 const lineData = computed(() => props.line);
 const describes = mergeDescribes(lineData, {}, ["stroke", "strokeWidth"]);
 (describes.strokeWidth.props as any).proportion = true;
+let isStartChange = false;
+describes.length = {
+  type: "inputNum",
+  label: "线段长度",
+  "layout-type": "row",
+  get value() {
+    return lineLen(points.value[0], points.value[1]);
+  },
+  set value(val) {
+    if (!isStartChange) {
+      emit("updateBefore");
+    }
+    isStartChange = true;
+    const aCount = props.data.lines.filter(
+      (line) => line.a === points.value[0].id || line.b === points.value[0].id
+    ).length;
+    const bCount = props.data.lines.filter(
+      (line) => line.a === points.value[1].id || line.b === points.value[1].id
+    ).length;
+
+    if (aCount === bCount || (aCount > 1 && bCount > 1)) {
+      // 两端伸展
+      const center = lineCenter(points.value);
+      const l1 = getVectorLine(lineVector([center, points.value[0]]), center, val / 2);
+      const l2 = getVectorLine(lineVector([center, points.value[1]]), center, val / 2);
+      emit("updatePoint", { ...points.value[0], ...l1[1] });
+      emit("updatePoint", { ...points.value[1], ...l2[1] });
+    } else {
+      // 单端伸展
+      const changeNdx = aCount > 1 ? 1 : 0;
+      const start = points.value[aCount > 1 ? 0 : 1];
+      const end = points.value[changeNdx];
+      const line = getVectorLine(lineVector([start, end]), start, val);
+      emit("updatePoint", { ...points.value[changeNdx], ...line[1] });
+    }
+  },
+  props: { proportion: true },
+};
+
 const delHandler = () => {
   emit("updateBefore");
   emit("delLine");
@@ -134,12 +173,7 @@ const isDrawIng = computed(
 );
 
 const style = computed(() =>
-  isDrawIng.value
-    ? {
-        ...mstyle.value,
-        stroke: themeColor,
-      }
-    : mstyle.value
+  isDrawIng.value ? { ...mstyle.value, stroke: themeColor } : mstyle.value
 );
 
 const addPoint = (pos: Pos) => {

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

@@ -110,8 +110,5 @@ const updateHandler = () => {
 };
 
 const shape = ref<DC<Group>>();
-useZIndex(
-  shape,
-  computed(() => props.data)
-);
+useZIndex(shape, data);
 </script>

+ 1 - 2
src/core/components/line/use-draw.ts

@@ -101,6 +101,7 @@ export const useDraw = () => {
         };
         drawItems[0] = {
           ...data,
+          createTime: initData.createTime + 1,
           points: [],
           lines: [],
           polygon: [],
@@ -210,7 +211,6 @@ export const useDraw = () => {
         };
       }
       cItem = normalLineData(cItem, ctx);
-      console.log(cItem);
       drawSnapInfos.forEach(customSnapInfos.add);
       if (drawItems[0] && store.getItemById(drawItems[0].id)) {
         store.setItem(type, { id: cItem.id, value: cItem });
@@ -369,7 +369,6 @@ export const normalLineData = (data: LineData, ctx: NLineDataCtx) => {
         repPointRef(data, p1.id, p2.id);
         data.points.splice(i, 1);
         i--;
-        console.log("p1 pre", p1, p2);
       }
     }
   }

+ 1 - 1
src/core/components/polygon/index.ts

@@ -18,7 +18,7 @@ export const defaultStyle = {
 
 export const getMouseStyle = (data: PolygonData) => {
   const fillStatus = data.fill && getMouseColors(data.fill);
-  const strokeStatus = data.stroke && getMouseColors(defaultStyle.stroke);
+  const strokeStatus = data.stroke && getMouseColors(data.stroke);
   const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth;
 
   return {

+ 69 - 12
src/core/components/serial/index.ts

@@ -16,7 +16,17 @@ export { default as GroupComponent } from "./serial-group.vue";
 export { default as Component } from "./serial.vue";
 export { default as TempComponent } from "./temp-serial.vue";
 
-export const shapeName = "序号";
+export const defaultTableStyle = {
+  right: 44,
+  top: 175,
+  fontSize: 16,
+  colHeight: 32,
+  padding: 8,
+  nameColWidth: 100,
+  valueColWidth: 180,
+  repColCount: 1,
+};
+export const shapeName = "图例";
 export const addMode = "dot";
 export const defaultStyle = {
   ...circleDefaultStyle,
@@ -56,10 +66,11 @@ export const interactiveToData: InteractiveTo<"serial"> = ({
       ...defaultStyle,
       fontSize: defaultStyle.fontSize,
       ...getBaseItem(),
-      ...preset,
       padding: 3,
       content: getCurrentNdx(store),
+      ...preset,
     } as unknown as SerialData;
+
     return interactiveFixData({ ...args, info, store, data: item });
   }
 };
@@ -83,17 +94,63 @@ export const delItem = (store: DrawStore, item: SerialData) => {
   const data = store.getTypeItems("serial");
   const ndx = data.indexOf(item);
 
-  for (let i = ndx + 1; i < data.length; i++) {
-    const c = (Number(data[i].content) - 1).toString();
-    table.content[i + 1][0].content = data[i].content = c.toString();
-  }
-  table.height -= table.content[ndx + 1][0].height;
-  table.content.splice(ndx + 1, 1);
+  const getPosition = (itemNdx: number) => {
+    const colCount = table.content[0].length / 2;
+    const rowNdx = 1 + Math.floor(itemNdx / colCount);
+    const colNdx = itemNdx % colCount;
+    return [rowNdx, colNdx * colCount, colCount] as const;
+  };
+
+  if (table && ~ndx) {
+    let i = ndx + 1;
+    let s = getPosition(i - 1);
+    let r;
+    let oldItem = { ...data[i - 1] };
+    for (; i < data.length; i++) {
+      r = s;
+      s = getPosition(i);
+      for (let j = 0; j < s[2]; j++) {
+        table.content[r[0]][r[1] + j] = table.content[s[0]][s[1] + j];
+        if (j === 0) {
+          table.content[r[0]][r[1] + j].content = oldItem.content!;
+        } else {
+          table.content[r[0]][r[1] + j].content =
+            table.content[s[0]][s[1] + j].content!;
+        }
+      }
+      let cur = { ...data[i] };
+      data[i].content = oldItem.content;
+      const radius = (getSerialFontW(data[i]) * Math.sqrt(2)) / 2;
+      data[i].radiusX = radius;
+      data[i].radiusY = radius;
+      matResponse({
+        data: data[i],
+        mat: new Transform(data[i].mat),
+        increment: false,
+      });
+      store.setItem("serial", { id: data[i].id, value: { ...data[i] } });
+      oldItem = cur;
+    }
+    table.content[s[0]][s[1]].content = "";
+    const cols = table.content.flatMap((row) => {
+      const cols = [];
+      for (let i = 0; i < row.length; i += 2) {
+        cols.push(row[i]);
+      }
+      return cols;
+    });
+    const delRowCount = Math.floor(
+      cols.filter((item) => item.content === "").length / 2
+    );
+    for (let i = 0; i < delRowCount; i++) {
+      table.height -= table.content.pop()![0].height;
+    }
 
-  if (data.length === 1) {
-    store.delItem("table", table.id);
-  } else {
-    store.setItem("table", { id: table.id, value: table });
+    if (data.length === 1) {
+      store.delItem("table", table.id);
+    } else {
+      store.setItem("table", { id: table.id, value: table });
+    }
   }
   store.delItem("serial", item.id);
 };

+ 76 - 32
src/core/components/serial/serial-group.vue

@@ -19,8 +19,12 @@ import { useStore, useStoreRenderProcessors } from "../../store";
 import { computed, watch } from "vue";
 import { useHistory } from "@/core/hook/use-history";
 import { ShapeType } from "..";
-import { TableData, interactiveToData as tableInteractiveToData } from "../table";
-import { delItem, getCurrentNdx, SerialData } from ".";
+import {
+  TableCollData,
+  TableData,
+  interactiveToData as tableInteractiveToData,
+} from "../table";
+import { defaultTableStyle, delItem, getCurrentNdx, SerialData } from ".";
 import {
   useGetViewBoxPositionPixel,
   useViewerInvertTransform,
@@ -38,10 +42,6 @@ const addHandler = (value: SerialData) => {
   store.addItem("serial", value);
 };
 
-const delHandler = (ndx: number) => {
-  delItem(store, data.value[ndx]);
-};
-
 const joinKey = "serial-table";
 const jTable = computed(() =>
   store.getTypeItems("table").find((item) => item.key === joinKey)
@@ -58,11 +58,16 @@ const margin = computed(() => {
 const invMat = useViewerInvertTransform();
 const getPosition = useGetViewBoxPositionPixel();
 const addTable = () => {
-  const w = 304;
-  const h = 32;
+  const w =
+    (defaultTableStyle.nameColWidth + defaultTableStyle.valueColWidth) *
+    defaultTableStyle.repColCount;
+  const h = defaultTableStyle.colHeight;
 
   let pos = getPosition(
-    { right: 44 + margin.value[1], top: 175 + margin.value[0] },
+    {
+      right: defaultTableStyle.right + margin.value[1],
+      top: defaultTableStyle.top + margin.value[0],
+    },
     { width: w, height: h }
   );
   pos = invMat.value.point(pos);
@@ -71,18 +76,40 @@ const addTable = () => {
     y: pos.y + h,
   };
 
+  const content: TableCollData[] = [];
+  for (let i = 0; i < defaultTableStyle.repColCount; i++) {
+    content.push(
+      {
+        content: "序号",
+        readonly: true,
+        notdel: true,
+        width: defaultTableStyle.nameColWidth,
+        height: defaultTableStyle.colHeight,
+        key: "serial-name",
+        fontSize: defaultTableStyle.fontSize,
+        padding: defaultTableStyle.padding,
+      },
+      {
+        content: "描述",
+        readonly: true,
+        notdel: true,
+        width: defaultTableStyle.valueColWidth,
+        height: defaultTableStyle.colHeight,
+        fontSize: defaultTableStyle.fontSize,
+        key: "serial-value",
+        padding: defaultTableStyle.padding,
+      }
+    );
+  }
+
   return tableInteractiveToData({
     info: { cur: [pos, end] },
     preset: {
       notaddRow: true,
       notaddCol: true,
       key: joinKey,
-      content: [
-        [
-          { content: "序号", readonly: true, notdel: true },
-          { content: "描述", readonly: true, notdel: true },
-        ],
-      ],
+      fontSize: defaultTableStyle.fontSize,
+      content: [content],
     },
     store,
     history,
@@ -92,28 +119,40 @@ const addTable = () => {
 
 const syncTable = (table: TableData) => {
   const items = data.value;
-  const cols = table.content.map((row) => row[0]);
+  const cols = table.content.flatMap((row) => {
+    const cols = [];
+    for (let i = 0; i < row.length; i += 2) {
+      cols.push(row[i]);
+    }
+    return cols;
+  });
   const tempRow = table.content[table.content.length - 1];
-  let isUpdate = false;
+  const colCount = tempRow.length / 2;
 
-  for (const item of items) {
+  let isUpdate = false;
+  for (let i = 0; i < items.length; i++) {
+    const item = items[i];
     if (cols.some((col) => col.content === item.content)) {
       continue;
     }
-    table.height += tempRow[0].height;
-    table.content.push([
-      { ...tempRow[0], content: item.content! },
-      { ...tempRow[1], content: "", readonly: false },
-    ]);
-    isUpdate = true;
-  }
-  for (let i = 1; i < cols.length; i++) {
-    const col = cols[i];
-    if (items.some((item) => col.content === item.content)) {
-      continue;
+    let rowNdx = Math.floor(i / colCount) + 1;
+    let colNdx = (i % colCount) * 2;
+    if (colNdx) {
+      table.content[rowNdx][colNdx].content = item.content!;
+    } else {
+      table.height += tempRow[0].height;
+      let cols = [
+        { ...tempRow[0], content: item.content! },
+        { ...tempRow[1], content: "", readonly: false },
+      ];
+      for (let i = 1; i < colCount; i++) {
+        cols.push(
+          { ...tempRow[2], content: "" },
+          { ...tempRow[3], content: "", readonly: false }
+        );
+      }
+      table.content.push(cols);
     }
-    table.height -= tempRow[0].height;
-    table.content.splice(i, 1);
     isUpdate = true;
   }
   return isUpdate;
@@ -127,7 +166,12 @@ const updateJoinTable = () => {
     table && history.preventTrack(() => store.delItem("table", table!.id));
     return;
   }
-  table || history.preventTrack(() => store.addItem("table", (table = addTable())));
+  if (!table) {
+    history.preventTrack(() => {
+      store.addItem("table", (table = addTable()));
+      console.log(table);
+    });
+  }
   if (syncTable(table!)) {
     history.preventTrack(() =>
       store.setItem("table", {

+ 3 - 1
src/core/components/share/edit-point.vue

@@ -21,6 +21,7 @@ import { useShapeIsHover } from "@/core/hook/use-mouse-status";
 import { useCursor } from "@/core/hook/use-global-vars";
 import { rangMod } from "@/utils/shared";
 import { Operate } from "../../html-mount/propertys/index.ts";
+import { collapseItemProps } from "element-plus";
 
 const props = defineProps<{
   points: Pos[];
@@ -29,6 +30,7 @@ const props = defineProps<{
   color?: string;
   size?: number;
   disable?: boolean;
+  drawIng?: boolean;
   opacity?: number;
   closed?: boolean;
   notDelete?: boolean;
@@ -48,7 +50,7 @@ const style = computed(() => {
   const size = props.size || 5;
   return {
     radius: size / 2,
-    fill: isHover.value || dragIng.value ? "#fff" : color.pub,
+    fill: props.drawIng || isHover.value || dragIng.value ? "#fff" : color.pub,
     stroke: color.pub,
     strokeWidth: size / 4,
     opacity: props.opacity !== undefined ? props.opacity : props.disable ? 0.5 : 1,

+ 9 - 4
src/core/components/table/index.ts

@@ -40,6 +40,7 @@ export type TableCollData = Partial<typeof defaultCollData> &
     padding: number;
     readonly?: boolean;
     notdel?: boolean;
+    key?: string
   };
 export type TableData = Partial<typeof defaultStyle> &
   BaseItem &
@@ -49,6 +50,10 @@ export type TableData = Partial<typeof defaultStyle> &
     notaddCol?: boolean;
     mat: number[];
     content: TableCollData[][];
+    tempSize?: {
+      colWidth: number[]
+      rowHeight: number[]
+    }
   };
 
 export const getMouseStyle = (data: TableData) => {
@@ -105,6 +110,7 @@ export const interactiveFixData: InteractiveFix<"table"> = ({
     data.width = Math.abs(area[0].x - area[1].x);
     data.height = Math.abs(area[0].y - area[1].y);
 
+
     if (!notdraw || !(data.content?.length && data.content[0].length)) {
       const colNum = Math.floor(data.width / autoCollWidth) || 1;
       const rawNum = Math.floor(data.height / autoCollHeight) || 1;
@@ -125,10 +131,9 @@ export const interactiveFixData: InteractiveFix<"table"> = ({
       const colWidth = data.width / data.content[0].length;
       data.content.forEach((row) => {
         row.forEach((col) => {
-          col.width = colWidth;
-          col.height = colHeight;
-          col.padding = 8;
-          console.log(col.content);
+          col.width = col.width || colWidth;
+          col.height = col.height || colHeight;
+          col.padding = col.padding || 8;
         });
       });
     }

+ 41 - 0
src/core/html-mount/propertys/components/input-num.vue

@@ -0,0 +1,41 @@
+<template>
+  <div>
+    <el-input-number
+      :controls="false"
+      :modelValue="props.proportion ? transform(value) : value"
+      @update:model-value="(val: any) => props.proportion ? changeHandler(invTransform(val)) : changeHandler(val)"
+      @change="$emit('change')"
+      style="width: 98px"
+      :precision="0"
+      :min="min"
+      :max="max"
+    >
+      <template #suffix v-if="props.proportion">
+        <span style="color: var(--el-input-text-color, var(--el-text-color-regular))">
+          {{ proportion.unit }}</span
+        >
+      </template>
+    </el-input-number>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useProportion } from "@/core/hook/use-proportion";
+import { ElInputNumber } from "element-plus";
+
+const props = defineProps<{
+  value: number;
+  min?: number;
+  max?: number;
+  proportion?: boolean;
+}>();
+const emit = defineEmits<{
+  (e: "update:value", val: number): void;
+  (e: "change"): void;
+}>();
+const { proportion, transform, invTransform } = useProportion();
+
+const changeHandler = (val: number) => {
+  emit("update:value", val);
+};
+</script>

+ 1 - 1
src/core/html-mount/propertys/describes.json

@@ -139,7 +139,7 @@
     "type": "num",
     "label": "文字大小",
     "props": {
-      "min": 12,
+      "min": 2,
       "step": 1,
       "max": 100
     },

+ 5 - 1
src/core/html-mount/propertys/index.ts

@@ -2,6 +2,8 @@ import Color from "./components/color.vue";
 import Select from "./components/select.vue";
 import Num from "./components/num.vue";
 import Checkbox from "./components/checkbox.vue";
+import InputNum from "./components/input-num.vue";
+
 import Proportion from "./components/proportion.vue";
 import Text from "./components/text.vue";
 import FixProportion from "./components/fix-proportion.vue";
@@ -15,6 +17,7 @@ export const checkType = "check";
 export const proportionType = "proportion";
 export const fixProportionType = "fixProportion"
 export const textType = 'text'
+export const inputNumType = 'inputNum'
 
 export const propertyComponents = {
   [colorType]: Color,
@@ -23,7 +26,8 @@ export const propertyComponents = {
   [checkType]: Checkbox,
   [proportionType]: Proportion,
   [fixProportionType]: FixProportion,
-  [textType]: Text
+  [textType]: Text,
+  [inputNumType]: InputNum
 };
 
 export type PropertyType = keyof typeof propertyComponents;

+ 1 - 1
src/example/components/slide/actions.ts

@@ -55,7 +55,7 @@ export const table: MenuItem = {
 
 export const serial: MenuItem = {
   icon: "order_no",
-  ...genDrawItem("serial", { content: "1" }),
+  ...genDrawItem("serial", {}),
 };
 
 export const imp: MenuItem = {

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

@@ -80,6 +80,7 @@ export const genTabulationData = async (paperKey: PaperKey, cover?: TabCover | n
         [{ ...nameColl, content: "绘图人" }, valueColl],
         [{ ...nameColl, content: "绘图时间" }, valueColl],
       ],
+      fontSize: getRealPixel(4, paperKey),
       key: tableTableKey,
       width: nameColl.width + valueColl.width,
       height: nameColl.height * 5,

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

@@ -22,6 +22,7 @@ import { getImage as getResourceImage } from "@/utils/resource.ts";
 import { nextTick } from "vue";
 import { tabulationData } from "../../store.ts";
 import { Mode } from "@/constant/mode.ts";
+import { tabulationId } from "../../router.ts";
 
 const draw = useDraw();
 
@@ -101,8 +102,8 @@ const actions = [
   ],
 ];
 
-const saveHandler = () => {
-  window.platform.saveTabulationData({
+const saveHandler = async () => {
+  tabulationId.value = await window.platform.saveTabulationData(tabulationId.value, {
     store: draw!.getData(),
     viewport: draw!.viewer.transform.m,
     paperKey: tabulationData.value.paperKey,

+ 9 - 1
src/example/fuse/views/tabulation/index.vue

@@ -30,7 +30,8 @@ import {
 } from "../../store";
 import { ImageData } from "@/core/components/image";
 import { TextData } from "@/core/components/text";
-import { getCoverPaperScale, setCoverPaperScale } from "./gen-tab";
+import { getCoverPaperScale, getRealPixel, setCoverPaperScale } from "./gen-tab";
+import { defaultTableStyle } from "@/core/components/serial";
 
 const uploadResourse = window.platform.uploadResourse;
 const full = ref(false);
@@ -56,6 +57,13 @@ const init = async (draw: Draw) => {
     },
   });
   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;
 };
 watch(draw, (draw) => draw && init(draw));