Alsmile 2 年之前
父節點
當前提交
43fd730151

+ 1 - 1
index.html

@@ -23,7 +23,7 @@
         overflow: hidden;
       }
     </style>
-    <script src="//at.alicdn.com/t/c/font_4042197_zjhjxu3egl.js"></script>
+    <script src="//at.alicdn.com/t/c/font_4042197_mmzk2f7bbb.js"></script>
   </head>
   <body>
     <div id="app"></div>

文件差異過大導致無法顯示
+ 200 - 178
pnpm-lock.yaml


+ 8 - 5
src/services/selections.ts

@@ -19,18 +19,21 @@ const selections = reactive<{
 });
 
 export const useSelection = () => {
-  const select = (data: any) => {
-    if (!data) {
+  const select = (pens?: Pen[]) => {
+    if (!pens || !pens.length) {
       selections.mode = SelectionMode.File;
       selections.pen = undefined;
       selections.pens = undefined;
-    } else if (Array.isArray(data)) {
+      return;
+    }
+
+    if (pens.length > 1) {
       selections.mode = SelectionMode.Pens;
       selections.pen = undefined;
-      selections.pens = data;
+      selections.pens = pens;
     } else {
       selections.mode = SelectionMode.Pen;
-      selections.pen = data;
+      selections.pen = pens[0];
       selections.pens = undefined;
     }
   };

+ 49 - 1
src/styles/app.css

@@ -11,7 +11,7 @@ body {
   padding: 0;
   font-size: var(--font-size);
   font-family: var(--font-family);
-  overflow: overlay;
+  overflow: hidden;
   background: var(--color-background);
   color: var(--color);
 }
@@ -195,6 +195,9 @@ a.hover:hover {
 
 .flex {
   display: flex;
+  &.column {
+    flex-direction: column;
+  }
   &.middle {
     align-items: center;
   }
@@ -223,6 +226,10 @@ a.hover:hover {
   margin-right: 4px;
 }
 
+.mr-8 {
+  margin-right: 8px;
+}
+
 .ml-8 {
   margin-left: 8px;
 }
@@ -231,6 +238,10 @@ a.hover:hover {
   margin-left: 16px;
 }
 
+.mt-4 {
+  margin-top: 4px;
+}
+
 .mt-8 {
   margin-top: 8px;
 }
@@ -243,6 +254,43 @@ a.hover:hover {
   margin-top: 16px;
 }
 
+.px-12 {
+  padding-left: 12px;
+  padding-right: 12px;
+}
+
+.px-16 {
+  padding-left: 16px;
+  padding-right: 16px;
+}
+
+.py-16 {
+  padding-top: 16px;
+  padding-bottom: 16px;
+}
+
+.transparent {
+  background-color: transparent;
+  background-image: linear-gradient(
+      45deg,
+      #c5c5c5 25%,
+      transparent 0,
+      transparent 75%,
+      #c5c5c5 0,
+      #c5c5c5
+    ),
+    linear-gradient(
+      45deg,
+      #c5c5c5 25%,
+      transparent 0,
+      transparent 75%,
+      #c5c5c5 0,
+      #c5c5c5
+    );
+  background-size: 6px 6px;
+  background-position: 0 0, 3px 3px;
+}
+
 /*定义滚动条轨道 内阴影+圆角*/
 ::-webkit-scrollbar {
   width: 5px;

+ 21 - 7
src/styles/props.css

@@ -13,7 +13,7 @@
     display: flex;
 
     label {
-      width: 72px;
+      width: 76px;
       font-size: 12px;
       line-height: 30px;
       flex-shrink: 0;
@@ -21,7 +21,7 @@
     }
 
     .t-input-number {
-      width: 100px;
+      width: 100%;
       line-height: 30px;
       height: 30px;
 
@@ -49,6 +49,10 @@
 
     .t-input {
       border-color: transparent;
+
+      .t-input__inner {
+        text-overflow: unset;
+      }
       &:hover,
       &.t-is-focused {
         border-color: var(--color-border-input);
@@ -63,14 +67,14 @@
 
   .t-collapse.t--border-less {
     .t-collapse-panel__header {
-      font-size: 14px;
-      font-weight: 400;
-      border-top: 1px solid var(--color-border-input);
+      font-size: 13px;
+      font-weight: 700;
+      border-top: 1px solid var(--td-border-level-1-color);
       color: var(--color-title);
     }
 
     .t-collapse-panel__wrapper .t-collapse-panel__content {
-      padding: 8px 16px 16px 16px;
+      padding: 0 16px 16px 16px;
     }
   }
 
@@ -204,7 +208,17 @@
   .t-slider {
     .t-slider__rail,
     .t-slider__track {
-      height: 2px;
+      height: 3px;
+    }
+
+    .t-slider__button {
+      width: 8px;
+      height: 8px;
+      border: none;
+    }
+
+    .t-slider__button--dragging {
+      box-shadow: none;
     }
   }
 

+ 55 - 10
src/styles/tdesign.css

@@ -328,27 +328,35 @@
   }
 
   &.simple {
-    width: 30px;
+    width: 16px;
+    height: 16px;
 
-    .t-input--auto-width {
-      min-width: 20px;
-      width: 20px;
+    * .t-input--auto-width {
+      min-width: 14px;
+      width: 14px;
     }
 
     .t-input {
-      height: 30px;
+      height: 16px;
       border: none;
       padding: 0;
       background: none;
 
       & > .t-input__prefix {
-        width: 20px;
-        height: 20px;
-        border: 1px solid var(--color-border-input);
-        border-radius: 2px;
+        margin: 0;
+        width: 14px;
+        height: 14px;
+        border: 1px solid var(--color-desc);
+        border-radius: 3px;
+
+        &:hover {
+          border-color: var(--color-primary);
+        }
 
         span {
-          height: 18px;
+          border: none;
+          height: 12.4px;
+          cursor: pointer;
         }
       }
 
@@ -359,6 +367,21 @@
   }
 }
 
+.t-radio-group {
+  .t-radio-button {
+    border-color: var(--color-border-input);
+    &:last-child {
+      border-right-color: var(--color-border-input);
+    }
+
+    &.t-is-checked {
+      border-color: var(--color-border-input);
+      background-color: var(--color-border-input);
+      color: var(--td-text-color-primary) !important;
+    }
+  }
+}
+
 .rectangle.t-radio-group.t-radio-group--filled {
   background: none;
 
@@ -421,3 +444,25 @@
     color: #ffffff !important;
   }
 }
+
+.t-color-picker__trigger--default__color {
+  background-color: transparent;
+  background-image: linear-gradient(
+      45deg,
+      #c5c5c5 25%,
+      transparent 0,
+      transparent 75%,
+      #c5c5c5 0,
+      #c5c5c5
+    ),
+    linear-gradient(
+      45deg,
+      #c5c5c5 25%,
+      transparent 0,
+      transparent 75%,
+      #c5c5c5 0,
+      #c5c5c5
+    );
+  background-size: 6px 6px;
+  background-position: 0 0, 3px 3px;
+}

+ 2 - 1
src/styles/var.css

@@ -57,6 +57,7 @@
   --td-font-family: var(--font-family);
   --td-font-body-medium: var(--font-size) / var(--td-line-height-body-medium)
     var(--td-font-family);
-  --td-border-level-1-color: var(--color-background-popup-hover);
+  --td-border-level-1-color: var(--color-background-input);
   --td-text-color-placeholder: var(--color-desc);
+  --td-brand-color: var(--color-primary);
 }

+ 4 - 4
src/views/components/FileProps.vue

@@ -2,7 +2,7 @@
   <div class="props">
     <t-tabs v-model="data.tab">
       <t-tab-panel :value="1" label="画布">
-        <t-space direction="vertical" class="panel">
+        <t-space direction="vertical" size="small" class="panel">
           <div class="form-item">
             <label>画布尺寸</label>
             <t-input-number
@@ -77,14 +77,14 @@
             />
           </div>
         </t-space>
-        <t-space direction="vertical" class="mt-8">
+        <t-space direction="vertical" size="small" class="mt-8">
           <t-collapse
             :defaultValue="['1']"
             expandIconPlacement="right"
             :borderless="true"
           >
             <t-collapse-panel value="1" header="预览设置">
-              <t-space direction="vertical">
+              <t-space direction="vertical" size="small">
                 <div class="form-item">
                   <label>缩放方式</label>
                   <t-radio-group class="ml-8" default-value="1">
@@ -120,7 +120,7 @@
               </t-space>
             </t-collapse-panel>
             <t-collapse-panel value="2" header="进阶设置">
-              <t-space direction="vertical">
+              <t-space direction="vertical" size="small">
                 <div class="form-item">
                   <label>初始化动作</label>
                   <t-button

+ 47 - 41
src/views/components/Graphics.vue

@@ -47,51 +47,51 @@
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, reactive, ref } from "vue";
-import { shapeLib, chartLib, formLib } from "@/services/defaults";
-import { getPngFolders } from "@/services/png";
+import { onMounted, onUnmounted, reactive, ref } from 'vue';
+import { shapeLib, chartLib, formLib } from '@/services/defaults';
+import { getPngFolders } from '@/services/png';
 
-const activeGroup = ref("图形");
+const activeGroup = ref('图形');
 const groups = reactive([
   {
-    icon: "desktop",
-    name: "场景",
-    key: "",
+    icon: 'desktop',
+    name: '场景',
+    key: '',
   },
   {
-    icon: "root-list",
-    name: "模板",
-    key: "",
+    icon: 'root-list',
+    name: '模板',
+    key: '',
   },
   {
-    icon: "chart",
-    name: "图表",
-    key: "chart",
+    icon: 'chart',
+    name: '图表',
+    key: 'chart',
   },
   {
-    icon: "control-platform",
-    name: "控件",
-    key: "",
+    icon: 'control-platform',
+    name: '控件',
+    key: '',
   },
   {
-    icon: "image",
-    name: "素材",
-    key: "",
+    icon: 'image',
+    name: '素材',
+    key: '',
   },
   {
-    icon: "file-icon",
-    name: "图标",
-    key: "",
+    icon: 'file-icon',
+    name: '图标',
+    key: '',
   },
   {
-    icon: "chart-bubble",
-    name: "图形",
-    key: "shape",
+    icon: 'chart-bubble',
+    name: '图形',
+    key: 'shape',
   },
   {
-    icon: "app",
-    name: "我的",
-    key: "",
+    icon: 'app',
+    name: '我的',
+    key: '',
   },
 ]);
 
@@ -101,7 +101,7 @@ const getCommponentsLib = () => {};
 
 const getMaterialLib = async () => {
   const [pngs] = await Promise.all([getPngFolders()]);
-  console.log("png", pngs);
+  console.log('png', pngs);
   return pngs;
 };
 let materialLib: any[] = [];
@@ -109,16 +109,16 @@ let materialLib: any[] = [];
 const groupChange = async (name: string) => {
   activeGroup.value = name;
   switch (name) {
-    case "图形":
+    case '图形':
       showList.value = shapeLib;
       break;
-    case "图表":
+    case '图表':
       showList.value = chartLib;
       break;
-    case "控件":
+    case '控件':
       showList.value = formLib;
       break;
-    case "素材":
+    case '素材':
       if (materialLib.length === 0) {
         materialLib = await getMaterialLib();
       }
@@ -131,7 +131,7 @@ const groupChange = async (name: string) => {
 const showList = ref<any[]>([]);
 
 const handlePanelChange = (e) => {
-  console.log("change", e);
+  console.log('change', e);
 };
 
 const dragStart = (event: DragEvent, item: any) => {
@@ -140,7 +140,7 @@ const dragStart = (event: DragEvent, item: any) => {
   }
 
   event.dataTransfer.setData(
-    "Meta2d",
+    'Meta2d',
     JSON.stringify(item.componentData || item.data)
   );
   event.stopPropagation();
@@ -159,14 +159,14 @@ const dragend = (event: any) => {
 };
 
 onMounted(() => {
-  groupChange("图形");
-  document.removeEventListener("dragstart", dragstart);
-  document.removeEventListener("dragend", dragend);
+  groupChange('图形');
+  document.removeEventListener('dragstart', dragstart);
+  document.removeEventListener('dragend', dragend);
 });
 
 onUnmounted(() => {
-  document.addEventListener("dragstart", dragstart, false);
-  document.addEventListener("dragend", dragend, false);
+  document.addEventListener('dragstart', dragstart, false);
+  document.addEventListener('dragend', dragend, false);
 });
 </script>
 <style lang="postcss" scoped>
@@ -198,7 +198,7 @@ onUnmounted(() => {
 
         .t-icon {
           font-size: 20px;
-          margin-bottom: 5px;
+          margin-bottom: 8px;
         }
 
         &:hover {
@@ -216,6 +216,12 @@ onUnmounted(() => {
     .list {
       overflow-y: auto;
       max-height: calc(100vh - 100px);
+      background-color: var(--color-background-active);
+
+      * {
+        background-color: var(--color-background-active);
+      }
+
       :deep(.t-collapse) {
         border: 0px;
       }

+ 381 - 57
src/views/components/PenProps.vue

@@ -2,33 +2,34 @@
   <div class="props">
     <t-tabs v-model="data.tab">
       <t-tab-panel :value="1" label="外观">
-        <t-space direction="vertical" class="panel">
+        <t-space direction="vertical" class="py-16 w-full">
           <div class="form-item">
-            <t-input-number
+            <t-input
+              class="ml-4"
               label="X"
-              v-model="data.rect.x"
-              placeholder="x"
-              theme="normal"
+              placeholder="x坐标"
+              v-model.number="data.rect.x"
               style="width: 80px"
-              @change="changeRect"
+              :format="decimalPlaces"
+              @change="changeValue('x')"
             />
             <t-icon name="link" class="hidden ml-4" />
-            <t-input-number
-              class="ml-8"
+            <t-input
+              class="ml-4"
               label="Y"
-              placeholder="y"
-              theme="normal"
-              v-model="data.rect.y"
+              placeholder="y坐标"
+              v-model.number="data.rect.y"
               style="width: 80px"
-              @change="changeRect"
+              :format="decimalPlaces"
+              @change="changeValue('y')"
             />
-
             <t-input
-              class="ml-8"
-              v-model="data.pen.rotate"
+              class="ml-16"
+              v-model.number="data.pen.rotate"
               placeholder="旋转"
-              style="width: 72px"
-              @change="changeValue"
+              style="width: 80px"
+              :format="decimalRound"
+              @change="changeValue('rotate')"
             >
               <template #prefix-icon>
                 <svg class="l-icon" aria-hidden="true">
@@ -38,14 +39,15 @@
             </t-input>
           </div>
           <div class="form-item hover-icons" style="margin-top: -12px">
-            <t-input-number
+            <t-input
+              class="ml-4"
               label="W"
-              v-model="data.rect.width"
+              v-model.number="data.rect.width"
               placeholder="宽"
-              theme="normal"
               min="1"
               style="width: 80px"
-              @change="changeRect(1)"
+              :format="decimalPlaces"
+              @change="changeValue('width')"
             />
             <t-tooltip v-if="data.pen.ratio" content="固定比例" placement="top">
               <t-icon
@@ -62,23 +64,23 @@
               />
             </t-tooltip>
 
-            <t-input-number
-              class="ml-8"
+            <t-input
+              class="ml-4"
               label="H"
               placeholder="高"
-              theme="normal"
+              v-model.number="data.rect.height"
               min="1"
-              v-model="data.rect.height"
               style="width: 80px"
-              @change="changeRect(2)"
+              :format="decimalPlaces"
+              @change="changeValue('height')"
             />
 
             <t-input
-              class="ml-8"
+              class="ml-16"
               v-model.number="data.pen.borderRadius"
               placeholder="圆角"
-              style="width: 72px"
-              @change="changeValue"
+              style="width: 80px"
+              @change="changeValue('borderRadius')"
             >
               <template #prefix-icon>
                 <svg class="l-icon" aria-hidden="true">
@@ -87,33 +89,320 @@
               </template>
             </t-input>
           </div>
+
           <t-divider style="margin: -8px 0" />
-          <div class="form-item" style="margin-top: -12px">
+          <div class="form-item px-16" style="margin-top: -12px">
             <label>不透明度</label>
             <t-slider
               v-model="data.pen.globalAlpha"
               :min="0"
               :max="1"
               :step="0.01"
-              @change="changeValue"
+              @change="changeValue('globalAlpha')"
             />
             <span class="ml-16" style="width: 50px; line-height: 30px">
               {{ data.pen.globalAlpha }}
             </span>
           </div>
-          <div class="form-item" style="margin-top: -12px">
-            <label>不透明度</label>
-            <t-slider
-              v-model="data.pen.globalAlpha"
-              :min="0"
-              :max="1"
-              :step="0.01"
-              @change="changeValue"
-            />
-            <span class="ml-16" style="width: 50px; line-height: 30px">
-              {{ data.pen.globalAlpha }}
-            </span>
+          <t-divider style="margin: -8px 0" />
+          <div class="form-item px-16" style="margin-top: -12px">
+            <t-checkbox
+              v-model="data.pen.flipX"
+              @change="changeValue('flipX')"
+              style="width: 100px"
+            >
+              水平翻转
+            </t-checkbox>
+            <t-checkbox
+              v-model="data.pen.flipY"
+              @change="changeValue('flipY')"
+              style="width: 100px"
+            >
+              垂直翻转
+            </t-checkbox>
           </div>
+          <t-collapse
+            :defaultValue="['1']"
+            expandIconPlacement="right"
+            :borderless="true"
+          >
+            <t-collapse-panel
+              v-if="data.pen.props.color !== false"
+              value="1"
+              header="外观"
+            >
+              <t-space direction="vertical" size="small" class="w-full">
+                <div class="form-item">
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :enable-alpha="true"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    :clearable="true"
+                    v-model="data.pen.color"
+                    @change="changeValue('color')"
+                  />
+                  <label style="width: 44px">前景</label>
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    v-model="data.pen.background"
+                    @change="changeValue('background')"
+                  />
+                  <label style="width: 44px">背景</label>
+
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    v-model="data.pen.hoverColor"
+                    @change="changeValue('hoverColor')"
+                  />
+                  <label style="width: 44px">悬停</label>
+
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    v-model="data.pen.activeColor"
+                    @change="changeValue('activeColor')"
+                  />
+                  <label style="width: 44px">选中</label>
+                </div>
+
+                <div class="form-item">
+                  <label style="width: 32px">线条 </label>
+                  <t-select
+                    v-model="data.pen.dash"
+                    size="small"
+                    placeholder="线条样式"
+                    @change="changeValue('dash')"
+                    style="width: 80px"
+                  >
+                    <template #valueDisplay="{ value }">
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        version="1.1"
+                        style="width: 100%; height: 20px"
+                      >
+                        <g fill="none" stroke="var(--color)" stroke-width="1">
+                          <path v-if="value === 0" d="M0 9 l85 0" />
+                          <path
+                            v-else-if="value === 1"
+                            stroke-dasharray="5 5"
+                            d="M0 9 l85 0"
+                          />
+                          <path
+                            v-else-if="value === 2"
+                            stroke-dasharray="10 10"
+                            d="M0 9 l85 0"
+                          />
+                          <path
+                            v-else-if="value === 3"
+                            stroke-dasharray="10 10 2 10"
+                            d="M0 9 l85 0"
+                          />
+                        </g>
+                      </svg>
+                    </template>
+                    <t-option :key="0" :value="0">
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        version="1.1"
+                        style="width: 80px; height: 14px"
+                      >
+                        <g fill="none" stroke="var(--color)" stroke-width="1">
+                          <path d="M0 9 l85 0" />
+                        </g>
+                      </svg>
+                    </t-option>
+                    <t-option :key="1" :value="1">
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        version="1.1"
+                        style="width: 80px; height: 14px"
+                      >
+                        <g fill="none" stroke="var(--color)" stroke-width="1">
+                          <path stroke-dasharray="5 5" d="M0 9 l85 0" />
+                        </g>
+                      </svg>
+                    </t-option>
+                    <t-option :key="2" :value="2">
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        version="1.1"
+                        style="width: 80px; height: 14px"
+                      >
+                        <g fill="none" stroke="var(--color)" stroke-width="1">
+                          <path stroke-dasharray="10 10" d="M0 9 l85 0" />
+                        </g>
+                      </svg>
+                    </t-option>
+                    <t-option :key="3" :value="3">
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        version="1.1"
+                        style="width: 80px; height: 14px"
+                      >
+                        <g fill="none" stroke="var(--color)" stroke-width="1">
+                          <path stroke-dasharray="10 10 2 10" d="M0 9 l85 0" />
+                        </g>
+                      </svg>
+                    </t-option>
+                  </t-select>
+                  <t-input-number
+                    theme="normal"
+                    placeholder="线条宽度"
+                    v-model="data.pen.lineWidth"
+                    :min="1"
+                    :decimalPlaces="0"
+                    @change="changeValue('lineWidth')"
+                    class="ml-4"
+                    style="width: 40px"
+                  />
+                  <t-tooltip content="线条渐变" placement="top">
+                    <div class="flex middle ml-8">
+                      <t-checkbox
+                        v-model="data.pen.strokeType"
+                        @change="changeValue('strokeType')"
+                        style="width: 22px"
+                      />
+
+                      <t-color-picker
+                        v-if="data.pen.strokeType"
+                        class="simple mr-4"
+                        format="CSS"
+                        :color-modes="['linear-gradient']"
+                        :show-primary-color-preview="false"
+                        :clearable="true"
+                        :enableAlpha="true"
+                        v-model="data.pen.lineGradientColors"
+                        @change="changeValue('lineGradientColors')"
+                        placeholder="无"
+                      />
+                    </div>
+                  </t-tooltip>
+                </div>
+                <div class="flex" style="margin-left: 40px">
+                  <div class="flex column middle">
+                    <t-radio-group
+                      size="small"
+                      v-model="data.pen.lineCap"
+                      default-value="butt"
+                      @change="changeValue('lineCap')"
+                    >
+                      <t-radio-button value="butt">
+                        <t-tooltip content="默认" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-duandian1"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                      <t-radio-button value="round">
+                        <t-tooltip content="圆形" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-duandian2"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                      <t-radio-button value="square">
+                        <t-tooltip content="方形" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-duandian3"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                    </t-radio-group>
+                    <div class="mt-4" style="font-size: 12px">末端样式</div>
+                  </div>
+                  <div class="flex column middle ml-16">
+                    <t-radio-group
+                      size="small"
+                      v-model="data.pen.lineJoin"
+                      default-value="miter"
+                      @change="changeValue('lineJoin')"
+                    >
+                      <t-radio-button value="miter">
+                        <t-tooltip content="默认" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-jiedian1"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                      <t-radio-button value="round">
+                        <t-tooltip content="圆形" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-jiedian2"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                      <t-radio-button value="bevel">
+                        <t-tooltip content="斜角" placement="top">
+                          <svg class="l-icon" aria-hidden="true">
+                            <use xlink:href="#l-jiedian3"></use>
+                          </svg>
+                        </t-tooltip>
+                      </t-radio-button>
+                    </t-radio-group>
+                    <div class="mt-4" style="font-size: 12px">连接样式</div>
+                  </div>
+                </div>
+                <div class="form-item">
+                  <label style="width: 32px">阴影 </label>
+                  <div class="flex middle ml-8">
+                    <t-checkbox
+                      v-model="data.pen.shadow"
+                      @change="changeValue('shadow')"
+                      style="width: 22px"
+                    />
+                    <t-color-picker
+                      v-if="data.pen.shadow"
+                      class="simple"
+                      format="CSS"
+                      :color-modes="['monochrome']"
+                      :show-primary-color-preview="false"
+                      v-model="data.pen.shadowColor"
+                      @change="changeValue('shadowColor')"
+                    />
+                  </div>
+                </div>
+                <div class="form-item" v-if="data.pen.shadow">
+                  <label style="width: 28px"></label>
+                  <div class="flex" style="margin-top: -8px">
+                    <t-input
+                      class="ml-4"
+                      label="X"
+                      placeholder="x偏移"
+                      v-model.number="data.pen.shadowOffsetX"
+                      style="width: 60px"
+                      @change="changeValue('x')"
+                    />
+                    <t-input
+                      class="ml-4"
+                      label="Y"
+                      placeholder="y偏移"
+                      v-model.number="data.pen.shadowOffsetY"
+                      style="width: 60px"
+                      @change="changeValue('shadowOffsetY')"
+                    />
+                    <t-input
+                      class="ml-4"
+                      label="模糊"
+                      placeholder="模糊"
+                      v-model.number="data.pen.shadowBlur"
+                      style="width: 64px"
+                      @change="changeValue('shadowBlur')"
+                    />
+                  </div>
+                </div>
+              </t-space>
+            </t-collapse-panel>
+          </t-collapse>
         </t-space>
       </t-tab-panel>
       <t-tab-panel :value="2" label="事件"> </t-tab-panel>
@@ -143,6 +432,16 @@ const { selections } = useSelection();
 
 onBeforeMount(() => {
   data.pen = selections.pen;
+  if (!data.pen.props) {
+    data.pen.props = {};
+  }
+  if (!data.pen.globalAlpha) {
+    data.pen.globalAlpha = 1;
+  }
+  if (!data.pen.dash) {
+    data.pen.dash = 0;
+  }
+  data.pen.shadow = !!data.pen.shadowColor;
   getRect();
   meta2d.on('translatePens', getRect);
   meta2d.on('resizePens', getRect);
@@ -153,25 +452,38 @@ const getRect = () => {
   data.rect = meta2d.getPenRect(data.pen);
 };
 
-const changeRect = (mode?: number) => {
-  //宽高比锁定
-  if (data.pen.ratio) {
-    if (mode === 1) {
-      data.rect.height = (data.rect.width / data.pen.width) * data.pen.height;
-    } else if (mode === 2) {
-      data.rect.width = (data.rect.height / data.pen.height) * data.pen.width;
-    }
+const decimalPlaces = (val: number) => {
+  if (!val) {
+    return 0;
   }
+  return Math.round(+val * 100) / 100;
+};
 
-  meta2d.setPenRect(data.pen, data.rect, true);
+const decimalRound = (val: number) => {
+  return Math.round(+val || 0);
 };
 
-const changeValue = (mode?: number) => {
-  switch (mode) {
-    case 1:
-      break;
+const changeValue = (prop: string) => {
+  const v: any = { id: data.pen.id };
+  v[prop] = data.pen[prop];
+  if (prop === 'x') {
+    v.x = data.rect.x;
+  } else if (prop === 'y') {
+    v.y = data.rect.y;
+  } else if (prop === 'width') {
+    v.height = (data.rect.width / data.pen.width) * data.pen.height;
+  } else if (prop === 'height') {
+    v.width = (data.rect.height / data.pen.height) * data.pen.width;
+  } else if (prop === 'shadow') {
+    if (v[prop]) {
+      !v.shadowOffsetX && (v.shadowOffsetX = 0);
+      !v.shadowOffsetY && (v.shadowOffsetY = 0);
+      !v.shadowBlur && (v.shadowBlur = 0);
+    } else {
+      v.shadowColor = '';
+    }
   }
-  meta2d.render();
+  meta2d.setValue(v);
 };
 
 onUnmounted(() => {
@@ -182,5 +494,17 @@ onUnmounted(() => {
 </script>
 <style lang="postcss" scoped>
 .props {
+  :deep(.t-color-picker__trigger) {
+    &.small {
+      width: 90px;
+
+      .t-input {
+        width: 90px;
+        .t-input__inner {
+          text-overflow: ellipsis;
+        }
+      }
+    }
+  }
 }
 </style>

+ 50 - 80
src/views/components/View.vue

@@ -180,36 +180,36 @@
 </template>
 
 <script lang="ts" setup>
-import { Meta2d, Options, Pen, deepClone, LockState } from "@meta2d/core";
-import { onMounted, onUnmounted, watch, ref, reactive } from "vue";
-import { registerBasicDiagram } from "@/services/register";
-import { useRouter, useRoute } from "vue-router";
-import { useUser } from "@/services/user";
-import { getLe5le2d } from "@/services/api";
-import { useDot } from "@/services/common";
+import { Meta2d, Options, Pen, deepClone, LockState } from '@meta2d/core';
+import { onMounted, onUnmounted, watch, ref, reactive } from 'vue';
+import { registerBasicDiagram } from '@/services/register';
+import { useRouter, useRoute } from 'vue-router';
+import { useUser } from '@/services/user';
+import { getLe5le2d } from '@/services/api';
+import { useDot } from '@/services/common';
 import {
   save,
   newFile,
   SaveType,
   onScaleView,
   onScaleWindow,
-} from "./Header.vue";
-import { useSelection, SelectionMode } from "@/services/selections";
-import { defaultFormat } from "@/services/defaults";
-import { MessagePlugin } from "tdesign-vue-next";
-import { localMeta2dDataName } from "@/services/utils";
-import localforage from "localforage";
-import { checkData } from "@/services/utils";
+} from './Header.vue';
+import { useSelection, SelectionMode } from '@/services/selections';
+import { defaultFormat } from '@/services/defaults';
+import { MessagePlugin } from 'tdesign-vue-next';
+import { localMeta2dDataName } from '@/services/utils';
+import localforage from 'localforage';
+import { checkData } from '@/services/utils';
 const router = useRouter();
 const route = useRoute();
 const { user, message, getUser, getMessage, signout } = useUser();
 const { dot, setDot, getDot } = useDot();
-const { selections } = useSelection();
+const { select } = useSelection();
 
 const meta2dOptions: Options = {
-  cdn: "https://assets.le5lecdn.com",
+  cdn: 'https://assets.le5lecdn.com',
   rule: true,
-  background: "#1e2430",
+  background: '#1e2430',
   x: 32,
   y: 32,
   width: 1920,
@@ -217,17 +217,17 @@ const meta2dOptions: Options = {
   defaultFormat: { ...defaultFormat },
 };
 onMounted(() => {
-  meta2d = new Meta2d("meta2d", meta2dOptions);
+  meta2d = new Meta2d('meta2d', meta2dOptions);
   registerBasicDiagram();
   open();
   // @ts-ignore
-  meta2d.on("active", active);
+  meta2d.on('active', active);
   // @ts-ignore
-  meta2d.on("inactive", inactive);
+  meta2d.on('inactive', inactive);
   // @ts-ignore
-  meta2d.on("scale", scaleListener);
+  meta2d.on('scale', scaleListener);
   // @ts-ignore
-  meta2d.on("add", lineAdd);
+  meta2d.on('add', lineAdd);
 });
 
 const watcher = watch(
@@ -239,12 +239,14 @@ const watcher = watch(
 
 const open = async () => {
   if (route.query.id) {
-    const ret: any = getLe5le2d(route.query.id + "");
+    const ret: any = getLe5le2d(route.query.id + '');
     ret && meta2d.open(ret);
   } else {
     meta2d.open();
   }
+  // @ts-ignore
   meta2d.store.data.x = meta2d.store.options.x;
+  // @ts-ignore
   meta2d.store.data.y = meta2d.store.options.y;
 };
 
@@ -252,39 +254,23 @@ onUnmounted(() => {
   watcher();
   if (meta2d) {
     // @ts-ignore
-    meta2d.off("active", active);
+    meta2d.off('active', active);
     // @ts-ignore
-    meta2d.off("inactive", inactive);
+    meta2d.off('inactive', inactive);
     // @ts-ignore
-    meta2d.off("scale", scaleListener);
+    meta2d.off('scale', scaleListener);
     // @ts-ignore
-    meta2d.off("add", lineAdd);
+    meta2d.off('add', lineAdd);
     meta2d.destroy();
   }
 });
 
-function getPenRectPens(oldPens: Pen[]): Pen[] {
-  return oldPens.map((pen) => {
-    const rect = meta2d.getPenRect(pen);
-    return {
-      ...pen,
-      ...rect,
-    };
-  });
-}
-
-const inactive = (pens: Pen[]) => {
-  setTimeout(() => {
-    active(meta2d.store.active as Pen[]);
-  });
+const inactive = () => {
+  select();
 };
 
-const active = (oldPens: Pen[]) => {
-  setTimeout(() => {
-    const pens = getPenRectPens(oldPens);
-    checkPropType(pens);
-    selections.pens = oldPens;
-  }, 10);
+const active = (pens: Pen[]) => {
+  select(pens);
 
   //格式刷处理
   if (one.value || always.value) {
@@ -293,23 +279,6 @@ const active = (oldPens: Pen[]) => {
   }
 };
 
-/**
- * 根据当前传入的 pens 判断属性面板的类型
- */
-const checkPropType = (pens: Pen[]) => {
-  if (pens.length === 1) {
-    selections.mode = SelectionMode.Pen;
-    if (Array.isArray(pens[0].frames)) {
-      (pens[0] as any).showDuration = meta2d.calcAnimateDuration(pens[0]);
-    }
-    selections.pen = pens[0];
-  } else if (pens.length > 1) {
-    selections.mode = SelectionMode.Pens;
-  } else {
-    selections.mode = SelectionMode.File;
-  }
-};
-
 const one = ref(false);
 const always = ref(false);
 
@@ -346,12 +315,12 @@ const connectShow = () => {
   connectVisible.value = true;
 };
 
-const currentLineType = ref("curve");
+const currentLineType = ref('curve');
 const lineTypes = reactive([
-  { name: "曲线", icon: "t-icon t-curve2", value: "curve" },
-  { name: "线段", icon: "t-icon t-polyline", value: "polyline" },
-  { name: "直线", icon: "t-icon t-line", value: "line" },
-  { name: "脑图曲线", icon: "t-icon t-mind", value: "mind" },
+  { name: '曲线', icon: 't-icon t-curve2', value: 'curve' },
+  { name: '线段', icon: 't-icon t-polyline', value: 'polyline' },
+  { name: '直线', icon: 't-icon t-line', value: 'line' },
+  { name: '脑图曲线', icon: 't-icon t-mind', value: 'mind' },
 ]);
 
 const changeLineType = (value: string) => {
@@ -386,7 +355,7 @@ const alwaysDraw = () => {
 };
 
 const lineAdd = (pens: Pen[]) => {
-  if (pens.length === 1 && pens[0].name === "line") {
+  if (pens.length === 1 && pens[0].name === 'line') {
     //连线类型
     if (oneD.value && !alwaysD.value) {
       if (meta2d.canvas.drawingLineName) {
@@ -402,28 +371,28 @@ const lineAdd = (pens: Pen[]) => {
 
 const onAddShape = (event: DragEvent, name: string) => {
   let data: any;
-  if (name === "text") {
+  if (name === 'text') {
     data = {
-      text: "le5le Meta2d",
+      text: 'le5le Meta2d',
       width: 100,
       height: 20,
-      name: "text",
+      name: 'text',
     };
-  } else if (name === "line") {
+  } else if (name === 'line') {
     data = {
       anchors: [
-        { id: "0", x: 0, y: 0.5 },
-        { id: "1", x: 1, y: 0.5 },
+        { id: '0', x: 0, y: 0.5 },
+        { id: '1', x: 1, y: 0.5 },
       ],
       width: 100,
       height: 1,
-      name: "line",
+      name: 'line',
     };
   }
   if (!event.dataTransfer) {
     meta2d.canvas.addCaches = deepClone([data]);
   } else {
-    event.dataTransfer.setData("Meta2d", JSON.stringify(data));
+    event.dataTransfer.setData('Meta2d', JSON.stringify(data));
   }
   event.stopPropagation();
 };
@@ -442,6 +411,7 @@ function onLock() {
 
 const preview = async () => {
   meta2d.stopAnimate();
+  // @ts-ignore
   const data: Meta2dBackData = meta2d.data();
   checkData(data);
   if (dot && user && data._id) {
@@ -452,9 +422,9 @@ const preview = async () => {
     await localforage.setItem(localMeta2dDataName, JSON.stringify(data));
   }
   router.push({
-    path: "/preview",
+    path: '/preview',
     query: {
-      r: Date.now() + "",
+      r: Date.now() + '',
       id: data._id,
     },
   });

部分文件因文件數量過多而無法顯示