bill 8 maanden geleden
bovenliggende
commit
6dbd3f5a9a

+ 11 - 1
src/core/components/arrow/arrow.vue

@@ -29,7 +29,7 @@ const { shape, tData, operateMenus, describes } = useComponentStatus<Arrow, Arro
   props,
   getMouseStyle,
   defaultStyle,
-  transformType: 'line',
+  transformType: "line",
   getRepShape(): Line {
     return new Line({
       fill: "rgb(0, 255, 0)",
@@ -42,5 +42,15 @@ const { shape, tData, operateMenus, describes } = useComponentStatus<Arrow, Arro
     data.points = data.points.map((v) => tf.point(v));
     return data;
   },
+  propertys: [
+    "fill",
+    "pointerPosition",
+    "strokeWidth",
+    "pointerLength",
+    "dash",
+    "ref",
+    "opacity",
+    "zIndex",
+  ],
 });
 </script>

+ 18 - 10
src/core/components/arrow/index.ts

@@ -9,28 +9,36 @@ import { getMouseColors } from "@/utils/colors.ts";
 export { default as Component } from "./arrow.vue";
 export { default as TempComponent } from "./temp-arrow.vue";
 
+export enum PointerPosition {
+  start = 'start',
+  end = 'end',
+  all = 'all',
+}
 export const shapeName = "箭头";
 export const defaultStyle = {
-  stroke: themeMouseColors.theme,
-  strokeWidth: 2,
   fill: themeMouseColors.theme,
+  pointerPosition: PointerPosition.end,
+  strokeWidth: 2,
+  pointerLength: 10,
 };
 
 export const addMode = "area";
 
 export const getMouseStyle = (data: ArrowData) => {
-  const fillStatus = getMouseColors(data.fill || defaultStyle.fill);
-  const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke);
+  const strokeStatus = getMouseColors(data.fill || defaultStyle.fill);
   const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth;
 
   return {
-    default: { fill: fillStatus.pub, stroke: strokeStatus.pub, strokeWidth },
-    hover: { fill: fillStatus.hover },
-    press: { fill: fillStatus.press },
+    default: {
+      stroke: strokeStatus.pub,
+      fill: strokeStatus.pub,
+      strokeWidth,
+    },
+    hover: { stroke: strokeStatus.hover, fill: strokeStatus.hover },
+    press: { stroke: strokeStatus.press, fill: strokeStatus.press },
   };
 };
 
-
 export type ArrowData = Partial<typeof defaultStyle> &
   BaseItem & {
     points: Pos[];
@@ -45,8 +53,8 @@ export const dataToConfig = (data: ArrowData): ArrowConfig => ({
 });
 
 export const getSnapInfos = (data: ArrowData) => {
-  return generateSnapInfos(data.points, true, false)
-}
+  return generateSnapInfos(data.points, true, false);
+};
 
 export const interactiveToData = (
   info: InteractiveMessage,

+ 22 - 6
src/core/components/arrow/temp-arrow.vue

@@ -3,26 +3,42 @@
     :config="{
       ...data,
       zIndex: undefined,
+      pointerLength: data.pointerLength,
+      pointerWidth: data.pointerLength,
       hitStrokeWidth: 20,
-      pointerLength: 10,
-      pointerWidth: 10,
       closed: true,
       points: flatPositions(data.points),
-      opacity: addMode ? 0.3 : 1,
+      ...eConfig,
+      opacity: addMode ? 0.3 : data.opacity,
     }"
     ref="shape"
   />
 </template>
 
 <script lang="ts" setup>
-import { ArrowData } from "./index.ts";
+import { ArrowData, defaultStyle, PointerPosition } from "./index.ts";
 import { DC } from "@/deconstruction.js";
-import { ref } from "vue";
+import { computed, ref, watchEffect } from "vue";
 import { flatPositions } from "@/utils/shared.ts";
 import { Arrow } from "konva/lib/shapes/Arrow";
-defineProps<{ data: ArrowData; addMode?: boolean }>();
 
+const props = defineProps<{ data: ArrowData; addMode?: boolean }>();
 const shape = ref<DC<Arrow>>();
+
+const eConfig = computed(() => {
+  const position =
+    "pointerPosition" in props.data
+      ? props.data.pointerPosition!
+      : defaultStyle.pointerPosition;
+  const eStart = [PointerPosition.all, PointerPosition.start].includes(position);
+  const eEnd = [PointerPosition.all, PointerPosition.end].includes(position);
+
+  return {
+    pointerAtBeginning: eStart,
+    pointerAtEnding: eEnd,
+  };
+});
+
 defineExpose({
   get shape() {
     return shape.value;

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

@@ -82,5 +82,6 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus<
       y: data.y + decTf.y,
     };
   },
+  propertys: ["fill", "stroke", "strokeWidth", "dash", "ref", "opacity", "zIndex"],
 });
 </script>

+ 7 - 7
src/core/components/circle/index.ts

@@ -14,22 +14,21 @@ export { default as TempComponent } from "./temp-circle.vue";
 
 export const shapeName = "圆形";
 export const defaultStyle = {
+  dash: [1, 0],
   stroke: themeMouseColors.theme,
-  fill: "#fff",
   strokeWidth: 1,
 };
 
 export const addMode = "area";
 
 export const getMouseStyle = (data: CircleData) => {
-  const fillStatus = getMouseColors(data.fill || defaultStyle.fill);
+  const fillStatus = data.fill && getMouseColors(data.fill);
   const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke);
   const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth;
-
   return {
-    default: { fill: fillStatus.pub, stroke: strokeStatus.pub, strokeWidth },
-    hover: { fill: fillStatus.hover },
-    press: { fill: fillStatus.press },
+    default: { fill: fillStatus && fillStatus.pub, stroke: strokeStatus.pub, strokeWidth },
+    hover: { fill: fillStatus && fillStatus.hover, stroke: strokeStatus.hover },
+    press: { fill: fillStatus && fillStatus.press, stroke: strokeStatus.press },
   };
 };
 
@@ -39,12 +38,13 @@ export const getSnapInfos = (data: CircleData) => {
     x: v.x + data.x,
     y: v.y + data.y,
   }));
-  console.log(points)
   return generateSnapInfos(points, true, false);
 };
 
 export type CircleData = Partial<typeof defaultStyle> &
   BaseItem & {
+    opacity?: number,
+    fill?: string
     x: number;
     y: number;
     radius: number;

+ 1 - 1
src/core/components/circle/temp-circle.vue

@@ -3,7 +3,7 @@
     :config="{
       ...data,
       zIndex: undefined,
-      opacity: addMode ? 0.3 : 1,
+      opacity: addMode ? 0.3 : data.opacity,
     }"
     ref="shape"
   >

+ 17 - 1
src/core/components/icon/icon.vue

@@ -27,11 +27,27 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
   emit,
   props,
   getMouseStyle,
-  transformType: 'mat',
+  transformType: "mat",
   defaultStyle,
   copyHandler(tf, data) {
     data.mat = tf.multiply(new Transform(data.mat)).m;
     return data;
   },
+  propertys: [
+    "fill",
+    "stroke",
+    "strokeWidth",
+    "strokeScaleEnabled",
+    "dash",
+    "opacity",
+
+    "coverFill",
+    // "coverStroke",
+    // "coverStrokeWidth",
+    "coverOpcatiy",
+
+    "ref",
+    "zIndex",
+  ],
 });
 </script>

+ 9 - 20
src/core/components/icon/index.ts

@@ -11,7 +11,7 @@ export const shapeName = "图例";
 export const defaultStyle = {
   coverFill: themeMouseColors.theme,
   coverOpcatiy: 0,
-  // strokeScaleEnabled: true,
+  strokeScaleEnabled: false,
   width: 80,
   height: 80,
 };
@@ -32,32 +32,20 @@ export const getSnapInfos = (data: IconData) => {
 
 export const getMouseStyle = (data: IconData) => {
   const fillStatus = getMouseColors(data.coverFill || defaultStyle.coverFill);
+  const hCoverOpcaoty = data.coverOpcatiy ? data.coverOpcatiy : 0.3
 
   return {
-    default: { coverFill: fillStatus.pub, coverOpcatiy: 0 },
-    hover: { coverFill: fillStatus.hover, coverOpcatiy: 0.3 },
-    press: { coverFill: fillStatus.press, coverOpcatiy: 0.3 },
+    default: { coverFill: fillStatus.pub, coverOpcatiy: data.coverOpcatiy ||0 },
+    hover: { coverFill: fillStatus.hover, coverOpcatiy: hCoverOpcaoty },
+    press: { coverFill: fillStatus.press, coverOpcatiy: hCoverOpcaoty },
   };
 };
 
-export const style = {
-  default: defaultStyle,
-  focus: {
-    coverOpcatiy: 0.3,
-    coverFill: themeMouseColors.hover,
-  },
-  hover: {
-    coverOpcatiy: 0.3,
-    coverFill: themeMouseColors.theme,
-  },
-  press: {
-    coverOpcatiy: 0.3,
-    coverFill: themeMouseColors.press,
-  },
-};
-
 export type IconData = Partial<typeof defaultStyle> &
   BaseItem & {
+    fill?: string,
+    stroke?: string,
+    strokeWidth?: number,
     coverFill?: string;
     coverStroke?: string;
     coverStrokeWidth?: number;
@@ -67,6 +55,7 @@ export type IconData = Partial<typeof defaultStyle> &
     url: string;
   };
 
+
 export const dataToConfig = (data: IconData) => {
   return {
     ...defaultStyle,

+ 2 - 1
src/core/components/image/image.vue

@@ -27,11 +27,12 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
   emit,
   props,
   getMouseStyle,
-  transformType: 'mat',
+  transformType: "mat",
   defaultStyle,
   copyHandler(tf, data) {
     data.mat = tf.multiply(new Transform(data.mat)).m;
     return data;
   },
+  propertys: ["stroke", "strokeWidth", "opacity", "ref", "zIndex"],
 });
 </script>

+ 3 - 3
src/core/components/image/index.ts

@@ -19,9 +19,9 @@ export const getMouseStyle = (data: ImageData) => {
   const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke);
 
   return {
-    default: { stroke: strokeStatus.pub, strokeWidth: 0 },
-    hover: { stroke: strokeStatus.hover, strokeWidth: 4 },
-    press: { stroke: strokeStatus.press, strokeWidth: 4 },
+    default: { stroke: strokeStatus.pub, },
+    hover: { stroke: strokeStatus.hover, },
+    press: { stroke: strokeStatus.press, },
   };
 };
 

+ 9 - 3
src/core/components/image/temp-image.vue

@@ -1,6 +1,13 @@
 <template>
   <v-group :config="groupConfig" v-if="groupConfig" ref="shape">
-    <v-image :config="{ ...data, ...config, zIndex: undefined }" v-if="image" />
+    <v-image
+      :config="{
+        ...data,
+        ...config,
+        zIndex: undefined,
+      }"
+      v-if="image"
+    />
   </v-group>
 </template>
 
@@ -49,8 +56,7 @@ const config = computed(() => {
 
   return {
     image: image.value,
-    opacity: props.addMode ? 0.3 : 1,
-    stroke: "red",
+    opacity: props.addMode ? 0.3 : props.data.opacity,
     width: w,
     height: h,
     offset: {

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

@@ -1,10 +1,8 @@
 import { Pos } from "@/utils/math.ts";
-import { flatPositions } from "@/utils/shared.ts";
 import {
   InteractiveAction,
   InteractiveMessage,
 } from "../../hook/use-interactive.ts";
-import { LineConfig } from "konva/lib/shapes/Line";
 import { themeMouseColors } from "@/constant/help-style.ts";
 import { BaseItem, generateSnapInfos, getBaseItem } from "../util.ts";
 import { getMouseColors } from "@/utils/colors.ts";

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

@@ -26,11 +26,12 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
   emit,
   props,
   getMouseStyle,
-  transformType: 'line',
+  transformType: "line",
   defaultStyle,
   copyHandler(tf, data) {
     data.points = data.points.map((v) => tf.point(v));
     return data;
   },
+  propertys: ["stroke", "strokeWidth", "dash", "opacity", "ref", "zIndex"],
 });
 </script>

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

@@ -4,7 +4,7 @@
       ...data,
       zIndex: undefined,
       points: flatPositions(data.points),
-      opacity: addMode ? 0.3 : 1,
+      opacity: addMode ? 0.3 : data.opacity,
       hitFunc,
     }"
     ref="shape"

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

@@ -14,18 +14,17 @@ export const shapeName = "多边形";
 export const defaultStyle = {
   stroke: themeMouseColors.theme,
   strokeWidth: 5,
-  fill: "#fff",
 };
 
 export const getMouseStyle = (data: PolygonData) => {
-  const fillStatus = getMouseColors(data.fill || defaultStyle.fill);
+  const fillStatus = data.fill && getMouseColors(data.fill);
   const strokeStatus = getMouseColors(data.stroke || defaultStyle.stroke);
-  const strokeWidth = data.strokeWidth || defaultStyle.strokeWidth;
+  const strokeWidth = data.strokeWidth ;
 
   return {
-    default: { fill: fillStatus.pub, stroke: strokeStatus.pub, strokeWidth },
-    hover: { fill: fillStatus.hover },
-    press: { fill: fillStatus.press },
+    default: { fill: fillStatus && fillStatus.pub, stroke: strokeStatus.pub, strokeWidth },
+    hover: { fill: fillStatus && fillStatus.hover, stroke: strokeStatus.hover },
+    press: { fill: fillStatus && fillStatus.press, stroke: strokeStatus.press },
   };
 };
 
@@ -34,6 +33,7 @@ export const addMode = "dots";
 export const getSnapInfos = (data: PolygonData) => generateSnapInfos(data.points, true, false)
 
 export type PolygonData = Partial<typeof defaultStyle> & BaseItem & {
+  fill?: string
   points: Pos[];
   attitude: number[];
 };

+ 2 - 1
src/core/components/polygon/polygon.vue

@@ -26,11 +26,12 @@ const { shape, tData, data, operateMenus, describes } = useComponentStatus({
   emit,
   props,
   getMouseStyle,
-  transformType: 'line',
+  transformType: "line",
   defaultStyle,
   copyHandler(tf, data) {
     data.points = data.points.map((v) => tf.point(v));
     return data;
   },
+  propertys: ["fill", "stroke", "strokeWidth", "dash", "opacity", "ref", "zIndex"],
 });
 </script>

+ 1 - 1
src/core/components/polygon/temp-polygon.vue

@@ -5,7 +5,7 @@
       closed: true,
       zIndex: undefined,
       points: flatPositions(data.points),
-      opacity: addMode ? 0.3 : 1,
+      opacity: addMode ? 0.3 : data.opacity,
     }"
     ref="shape"
   />

+ 4 - 0
src/core/components/util.ts

@@ -7,12 +7,16 @@ export type BaseItem = {
   id: string;
   createTime: number;
   zIndex: number;
+  opacity: number
+  ref: boolean
 };
 
 export const getBaseItem = (): BaseItem => ({
   id: onlyId(),
   createTime: Date.now(),
   zIndex: 0,
+  opacity: 1,
+  ref: false
 });
 
 export const getRectSnapPoints = (

+ 2 - 1
src/core/helper/active-boxs.vue

@@ -4,6 +4,7 @@
       :ref="(rect: any) => rects[i] = rect"
       :config="boxs.get(shape)"
       :strokeWidth="4"
+      :opacity="0.3"
       :stroke="themeColor"
       :dash="[10, 10]"
       :listening="false"
@@ -25,7 +26,7 @@ import { useDashAnimation } from "../hook/use-animation";
 
 const status = useMouseShapesStatus();
 const boxs = reactive(new WeakMap<EntityShape, IRect>());
-const padding = 1;
+const padding = 8;
 
 const updateBox = ($shape: EntityShape) => {
   const rect = $shape.getClientRect();

+ 25 - 50
src/core/hook/use-component.ts

@@ -4,17 +4,14 @@ import { useAutomaticData } from "./use-automatic-data";
 import { useMouseMigrateTempLayer, useZIndex } from "./use-layer";
 import { useAnimationMouseStyle } from "./use-mouse-status";
 import { DrawItem } from "../components";
-import {
-  useMatCompTransformer,
-  useLineTransformer,
-} from "./use-transformer";
+import { useMatCompTransformer, useLineTransformer } from "./use-transformer";
 import { useGetShapeCopyTransform } from "./use-copy";
 import { Delete, DocumentCopy } from "@element-plus/icons-vue";
 import { onlyId } from "@/utils/shared";
 import { Shape } from "konva/lib/Shape";
 import { Transform } from "konva/lib/Util";
-import { generateDescribes } from "../propertys/util";
-import { PropertyDescribes } from "../propertys";
+import { mergeDescribes, PropertyKeys } from "../propertys";
+
 
 type Emit<T> = EmitFn<{
   updateShape: (value: T) => void;
@@ -22,46 +19,25 @@ type Emit<T> = EmitFn<{
   delShape: () => void;
 }>;
 
-export type UseComponentStatusProps<T extends DrawItem, S extends EntityShape> = {
+export type UseComponentStatusProps<
+  T extends DrawItem,
+  S extends EntityShape
+> = {
   emit: Emit<T>;
   props: { data: T };
   getMouseStyle: any;
   defaultStyle: any;
-  transformType?: 'line' | 'mat' | 'custom'
-  customTransform?: (callback: () => void, shape: Ref<DC<S> | undefined>, data: Ref<T>) => void,
+  propertys: PropertyKeys,
+  transformType?: "line" | "mat" | "custom";
+  customTransform?: (
+    callback: () => void,
+    shape: Ref<DC<S> | undefined>,
+    data: Ref<T>
+  ) => void;
   getRepShape?: () => Shape;
   copyHandler: (transform: Transform, data: T) => T;
 };
 
-const getPropertyDescribes = (data: Ref<any>): PropertyDescribes => ({
-  stroke: { type: "color", label: "边框" },
-  fill: { type: "color", label: "填充" },
-  strokeWidth: { type: "num", label: "粗细", props: { min: 0.5, max: 10 } },
-  zIndex: {
-    type: "num",
-    label: "层级",
-    props: { min: -1000, max: 1000, step: 1 },
-  },
-  dash: {
-    type: "num",
-    label: "虚线比例",
-    props: { min: 0, max: 30 },
-    get value() {
-      if (!data.value.dash) {
-        return 30;
-      }
-      if (!data.value.dash[1]) {
-        return 30;
-      } else {
-        return data.value.dash[0];
-      }
-    },
-    set value(val) {
-      data.value.dash = [val, 30 - val];
-    },
-  },
-});
-
 export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
   args: UseComponentStatusProps<T, S>
 ) => {
@@ -73,6 +49,7 @@ export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
     defaultStyle,
     customTransform,
     getRepShape,
+    propertys,
     copyHandler,
   } = args;
 
@@ -84,22 +61,20 @@ export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
     getMouseStyle,
   }) as any;
 
-  if (transformType === 'line') {
+  if (transformType === "line") {
     useLineTransformer(
       shape as any,
       data as any,
       (newData) => emit("updateShape", newData as T),
       getRepShape as any
     );
-  } else if (transformType === 'mat') {
-    useMatCompTransformer(
-      shape,
-      data as any,
-      (nData) => emit("updateShape", nData as any),
+  } else if (transformType === "mat") {
+    useMatCompTransformer(shape, data as any, (nData) =>
+      emit("updateShape", nData as any)
     );
-  } else if (transformType === 'custom' && customTransform) {
-    console.log('????')
-    customTransform(() => emit("updateShape", data.value as any), shape, data)
+  } else if (transformType === "custom" && customTransform) {
+    console.log("????");
+    customTransform(() => emit("updateShape", data.value as any), shape, data);
   }
 
   useZIndex(shape, data);
@@ -128,11 +103,11 @@ export const useComponentStatus = <S extends EntityShape, T extends DrawItem>(
       },
     },
   ];
-
-  const describes = generateDescribes(
+  
+  const describes = mergeDescribes(
     data,
     defaultStyle,
-    getPropertyDescribes(data)
+    propertys || []
   );
 
   return {

+ 0 - 2
src/core/hook/use-transformer.ts

@@ -396,7 +396,6 @@ export const transformerRepShapeHandler = <T extends EntityShape>(
 ) => {
   if (import.meta.env.DEV) {
     repShape.visible(true);
-    shape.opacity(0.9);
     repShape.opacity(0.1);
   }
   shape.parent!.add(repShape);
@@ -408,7 +407,6 @@ export const transformerRepShapeHandler = <T extends EntityShape>(
     repShape,
     () => {
       repShape.remove();
-      shape.opacity(1);
     },
   ] as const;
 };

+ 18 - 0
src/core/propertys/components/checkbox.vue

@@ -0,0 +1,18 @@
+<template>
+  <el-checkbox
+    @update:model-value="(val: any) => $emit('update:value', val)"
+    :model-value="value"
+    :label="label"
+    style="height: auto"
+  />
+</template>
+
+<script lang="ts" setup>
+import { ElCheckbox } from "element-plus";
+
+defineProps<{
+  value?: boolean;
+  label?: string;
+}>();
+defineEmits<{ (e: "update:value", val: boolean): void }>();
+</script>

+ 8 - 3
src/core/propertys/color.vue

@@ -1,7 +1,8 @@
 <template>
   <el-color-picker
     :modelValue="value"
-    @active-change="(color) => $emit('update:value', color)"
+    @active-change="changeHandler"
+    @update:model-value="changeHandler"
     popper-class="com-color-pick"
     size="small"
     :predefine="predefineColors"
@@ -12,9 +13,13 @@
 import { ref } from "vue";
 import { ElColorPicker } from "element-plus";
 
-defineProps<{ value: string }>();
-defineEmits<{ (e: "update:value", val: string | null): string }>();
+defineProps<{ value?: string }>();
 
+const emit = defineEmits<{ (e: "update:value", val: string | null): string }>();
+const changeHandler = (color: string | null) => {
+  console.log(color);
+  emit("update:value", color);
+};
 const predefineColors = ref([
   "#ff4500",
   "#ff8c00",

+ 6 - 3
src/core/propertys/num.vue

@@ -2,7 +2,7 @@
   <el-slider
     class="property-num-slider"
     :modelValue="value"
-    @update:model-value="(val: any) => $emit('update:value', val)"
+    @update:model-value="(val: any) => changeHandler(val)"
     size="small"
     height="200px"
     width="50px"
@@ -15,7 +15,7 @@
 </template>
 
 <script lang="ts" setup>
-import { ElSlider, ElPopover, ElInput } from "element-plus";
+import { ElSlider } from "element-plus";
 
 defineProps<{
   value: number;
@@ -23,7 +23,10 @@ defineProps<{
   max?: number;
   step?: number;
 }>();
-defineEmits<{ (e: "update:value", val: number): void; (e: "click"): void }>();
+const emit = defineEmits<{ (e: "update:value", val: number): void }>();
+const changeHandler = (val: number) => {
+  emit("update:value", val);
+};
 </script>
 
 <style lang="scss">

+ 62 - 0
src/core/propertys/components/proportion.vue

@@ -0,0 +1,62 @@
+<template>
+  <el-slider
+    class="property-proportion-slider"
+    :modelValue="showValue"
+    @update:model-value="(val: any) => changeHandler(val)"
+    size="small"
+    height="200px"
+    width="50px"
+    style="cursor: pointer"
+    :step="step || 0.01"
+    :min="0"
+    :max="scale"
+  />
+</template>
+
+<script lang="ts" setup>
+import { ElSlider } from "element-plus";
+import { computed } from "vue";
+
+const props = withDefaults(
+  defineProps<{
+    value?: number[];
+    step?: number;
+    scale?: number;
+  }>(),
+  { scale: 1 }
+);
+const emit = defineEmits<{ (e: "update:value", val: number[]): void }>();
+
+const showValue = computed(() => {
+  if (!props.value) {
+    return 1 * props.scale;
+  }
+  if (!props.value[1]) {
+    return 1 * props.scale;
+  } else {
+    return props.value[1];
+  }
+});
+
+const changeHandler = (val: number) => {
+  emit("update:value", [val, props.scale - val]);
+};
+</script>
+
+<style lang="scss">
+.property-proportion-slider {
+  .el-input__wrapper {
+    padding: 0 !important;
+  }
+  .el-slider__input {
+    width: 50px;
+  }
+  .el-input-number__increase,
+  .el-input-number__decrease {
+    display: none;
+  }
+  .el-slider__runway.show-input {
+    margin-right: 10px;
+  }
+}
+</style>

+ 29 - 0
src/core/propertys/components/select.vue

@@ -0,0 +1,29 @@
+<template>
+  <el-select
+    :model-value="value"
+    @update:model-value="(value) => $emit('update:value', value)"
+    placeholder="选择"
+    size="small"
+    style="width: 100px"
+  >
+    <el-option
+      v-for="item in options"
+      :key="item.value"
+      :label="item.label"
+      :value="item.value"
+    />
+  </el-select>
+</template>
+
+<script lang="ts" setup>
+import { ElSelect, ElOption } from "element-plus";
+
+defineProps<{
+  value: any;
+  options: { label: string; value: any }[];
+  min?: number;
+  max?: number;
+  step?: number;
+}>();
+defineEmits<{ (e: "update:value", val: number): void; (e: "click"): void }>();
+</script>

+ 115 - 0
src/core/propertys/describes.json

@@ -0,0 +1,115 @@
+{
+  "stroke": {
+    "type": "color",
+    "label": "边框色"
+  },
+  "coverStroke": {
+    "type": "color",
+    "label": "背景边框颜色"
+  },
+  "fill": {
+    "type": "color",
+    "label": "填充色"
+  },
+  "coverFill": {
+    "type": "color",
+    "label": "背景颜色"
+  },
+  "ref": {
+    "type": "check",
+    "label": "参考物",
+    "default": false
+  },
+  "strokeScaleEnabled": {
+    "type": "check",
+    "label": "固定边框粗细",
+    "default": false
+  },
+  "strokeWidth": {
+    "type": "num",
+    "label": "边框粗细",
+    "default": 1,
+    "props": {
+      "min": 0,
+      "max": 10
+    }
+  },
+  "coverStrokeWidth": {
+    "type": "num",
+    "label": "背景边框粗细",
+    "default": 0,
+    "props": {
+      "min": 0.5,
+      "max": 10
+    }
+  },
+  "opacity": {
+    "type": "num",
+    "label": "透明度",
+    "props": {
+      "min": 0,
+      "max": 1,
+      "step": 0.01
+    },
+    "default": 1
+  },
+  "coverOpcatiy": {
+    "type": "num",
+    "label": "背景透明度",
+    "props": {
+      "min": 0,
+      "max": 1,
+      "step": 0.01
+    },
+    "default": 1
+  },
+  "zIndex": {
+    "type": "num",
+    "label": "层叠层级",
+    "props": {
+      "min": -1000,
+      "max": 1000,
+      "step": 1
+    },
+    "default": 0
+  },
+  "pointerLength": {
+    "type": "num",
+    "label": "箭头大小",
+    "props": {
+      "min": 0.5,
+      "max": 10
+    }
+  },
+  "dash": {
+    "type": "proportion",
+    "label": "虚线比例",
+    "props": {
+      "scale": 30
+    },
+    "default": [
+      0,
+      30
+    ]
+  },
+  "pointerPosition": {
+    "type": "select",
+    "label": "箭头方向",
+    "props": {
+      "options": [
+        {
+          "label": "起点",
+          "value": "start"
+        },
+        {
+          "label": "终点",
+          "value": "end"
+        },
+        {
+          "label": "全部",
+          "value": "all"
+        }
+      ]
+    }
+  }
+}

+ 46 - 4
src/core/propertys/index.ts

@@ -1,12 +1,23 @@
-import Color from "./color.vue";
-import Num from "./num.vue";
+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 Proportion from "./components/proportion.vue";
+import originDescribes from "./describes.json";
+import { Ref } from "vue";
 
 export const colorType = "color";
+export const selectType = "select";
 export const numType = "num";
+export const checkType = "check";
+export const proportionType = "proportion";
 
 export const propertyComponents = {
   [colorType]: Color,
   [numType]: Num,
+  [selectType]: Select,
+  [checkType]: Checkbox,
+  [proportionType]: Proportion,
 };
 
 export type PropertyType = keyof typeof propertyComponents;
@@ -23,13 +34,44 @@ export type PropertyDescribes = Record<
   {
     type: PropertyType;
     label: string;
+    default?: PropertyValue<PropertyType>;
     props?: Partial<PropertyProps<PropertyType>>;
-    value?: PropertyValue<PropertyType>
+    value?: PropertyValue<PropertyType>;
   }
 >;
 export type PropertysData<T extends PropertyDescribes = PropertyDescribes> = {
   [K in keyof T]: PropertyValue<T[K]["type"]>;
 };
 
-export { default as PropertyUpdate } from "./mount-property.vue";
+export type PropertyKey = keyof typeof originDescribes;
+export type PropertyKeys = PropertyKey[];
+
+export const mergeDescribes = (data: Ref<any>, defData: any, keys: PropertyKeys) => {
+  const describes: PropertyDescribes = {};
+  for (const key of keys) {
+    if (!originDescribes[key]) {
+      continue;
+    }
+    ;(describes as any)[key] = { ...originDescribes[key] };
+
+    if (!("value" in describes[key])) {
+      Object.defineProperty(describes[key], "value", {
+        get() {
+          return key in data.value
+            ? data.value[key]
+            : key in defData
+            ? defData[key]
+            : describes[key].default;
+        },
+        set(val) {
+          data.value[key] = val;
+          return true;
+        },
+      });
+    }
+  }
+  return describes;
+};
+
+export { default as PropertyUpdate } from "./mount.vue";
 export { default as Operate } from "./hover-operate.vue";

+ 30 - 29
src/core/propertys/mount-property.vue

@@ -3,20 +3,19 @@
     <transition name="mount-fade">
       <div class="mount-layout" v-if="!hidden">
         <div :size="8" class="mount-controller">
-          <template v-for="(val, key) in describes" :key="key">
-            <span>{{ val.label }}</span>
-            <div>
-              <component
-                v-bind="describes[key].props"
-                :value="
-                  'value' in describes[key] ? describes[key].value : data && data[key]
-                "
-                @update:value="(val: any) => updateValue(key, val)"
-                :is="propertyComponents[val.type]"
-                :key="key"
-              />
-            </div>
-          </template>
+          <div v-for="(val, key) in describes" :key="key" class="mount-item">
+            <span class="label">{{ val.label }}</span>
+            <component
+              v-bind="describes[key].props"
+              :value="
+                'value' in describes[key] ? describes[key].value : data && data[key]
+              "
+              @update:value="(val: any) => updateValue(key, val)"
+              @change="emit('change')"
+              :is="propertyComponents[val.type]"
+              :key="key"
+            />
+          </div>
         </div>
       </div>
     </transition>
@@ -24,7 +23,7 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from "vue";
+import { computed } from "vue";
 import { useStage, useTransformIngShapes } from "../hook/use-global-vars.ts";
 import { PropertyDescribes, propertyComponents } from "./index.ts";
 import { DC, EntityShape } from "@/deconstruction.js";
@@ -60,13 +59,6 @@ const updateValue = (key: string, val: any) => {
   }
   isUpdate = true;
 };
-
-watch(hidden, (nHidden, oHidden) => {
-  if (nHidden && nHidden !== oHidden && isUpdate) {
-    isUpdate = false;
-    emit("change");
-  }
-});
 </script>
 
 <style lang="scss" scoped>
@@ -84,15 +76,24 @@ watch(hidden, (nHidden, oHidden) => {
   width: 240px;
   font-size: 12px;
   box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
+  overflow: hidden;
 
   .mount-controller {
-    display: grid;
-    grid-template-columns: auto 1fr;
-    gap: 10px 0;
-    align-items: center;
-    overflow: hidden;
-    span {
-      margin-right: 4px;
+    // display: grid;
+    // grid-template-columns: auto 1fr;
+    // gap: 10px 0;
+    // align-items: center;
+
+    .mount-item {
+      margin-bottom: 15px;
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+    }
+    .label {
+      display: block;
+      margin-right: 10px;
+      color: #303133;
       &::after {
         content: ":";
       }

+ 0 - 30
src/core/propertys/util.ts

@@ -1,30 +0,0 @@
-import { Ref } from "vue";
-import { PropertyDescribes } from ".";
-
-export const generateDescribes = (
-  data: Ref<any>,
-  defaultData: any,
-  describesRaw: PropertyDescribes
-) => {
-  const describes = { ...describesRaw };
-  for (const key in describesRaw) {
-    const exisDef = defaultData && key in defaultData;
-    if (!(key in data.value) && !exisDef ) {
-      delete describes[key];
-      continue;
-    }
-
-    if (!("value" in describes[key])) {
-      Object.defineProperty(describes[key], "value", {
-        get() {
-          return key in data.value ? data.value[key] : defaultData[key];
-        },
-        set(val) {
-          data.value[key] = val;
-          return true;
-        },
-      });
-    }
-  }
-  return describes;
-};

+ 1 - 1
src/core/store/init.ts

@@ -187,7 +187,7 @@ export const initData = {
       height: 300,
       stroke: "red",
       strokeWidth: 1,
-      strokeScaleEnabled: false,
+      strokeScaleEnabled: true,
       mat: [1, 0, 0, 1, 954.53515625, 519.8671875],
     },
   ],