浏览代码

pens props

Alsmile 2 年之前
父节点
当前提交
15725f9c59

+ 1 - 1
index.html

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

+ 30 - 0
src/services/common.ts

@@ -458,3 +458,33 @@ const calcElem = (node: any) => {
 
   return elem;
 };
+
+export const setChildrenVisible = (node: any, v: boolean) => {
+  const children = node.getChildren();
+  if (children && children.length > 0) {
+    for (const child of children) {
+      child.data.visible = v;
+      setChildrenVisible(child, v);
+    }
+  }
+};
+
+export const fonts = [
+  '新宋体',
+  '微软雅黑',
+  '黑体',
+  '楷体',
+  '-apple-system',
+  'BlinkMacSystemFont',
+  'PingFang SC',
+  'Hiragino Sans GB',
+  'Microsoft YaHei UI',
+  'Microsoft YaHei',
+  'fangsong',
+  'Source Han Sans CN',
+  'sans-serif',
+  'serif',
+  'Apple Color Emoji',
+  'Segoe UI Emoji',
+  'Segoe UI Symbol',
+];

+ 6 - 0
src/styles/tdesign.css

@@ -510,3 +510,9 @@
 .t-checkbox__label {
   font-size: 12px;
 }
+
+.t-is-active {
+  .t-tree__label {
+    background: none;
+  }
+}

+ 1 - 0
src/views/components/Actions.vue

@@ -81,6 +81,7 @@
               @clear="a.params = undefined"
               @focus="a.popupVisible = true"
               @blur="a.popupVisible = undefined"
+              placeholder="缺省第一个动画"
             >
               <template #panel>
                 <ul style="padding: 8px 12px">

+ 7 - 13
src/views/components/ElementTree.vue

@@ -46,7 +46,9 @@
             content="可编辑"
             placement="top"
           >
-            <t-icon name="lock-off" @click="lock(node, 1)" />
+            <svg class="l-icon" aria-hidden="true" @click="lock(node, 1)">
+              <use xlink:href="#l-unlock"></use>
+            </svg>
           </t-tooltip>
           <t-tooltip
             class="mr-4"
@@ -54,7 +56,9 @@
             content="禁止编辑"
             placement="top"
           >
-            <t-icon name="lock-on" @click="lock(node, 2)" />
+            <svg class="l-icon" aria-hidden="true" @click="lock(node, 2)">
+              <use xlink:href="#l-lock"></use>
+            </svg>
           </t-tooltip>
           <t-tooltip
             class="mr-4"
@@ -138,7 +142,7 @@ import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue';
 import { MessagePlugin } from 'tdesign-vue-next';
 
 import { LockState, Pen } from '@meta2d/core';
-import { getPenTree } from '@/services/common';
+import { getPenTree, setChildrenVisible } from '@/services/common';
 
 const tree = ref<any>(null);
 const data = reactive<any>({
@@ -225,16 +229,6 @@ const visible = (node: any, v: boolean) => {
   pen && meta2d.setVisible(pen, v);
 };
 
-const setChildrenVisible = (node: any, v: boolean) => {
-  const children = node.getChildren();
-  if (children && children.length > 0) {
-    for (const child of children) {
-      child.data.visible = v;
-      setChildrenVisible(child, v);
-    }
-  }
-};
-
 const onDescription = (node: any) => {
   node.data.edited = false;
   node.setData({ label: node.data.label });

+ 4 - 23
src/views/components/PenProps.vue

@@ -191,7 +191,6 @@
                   <label style="width: 32px">线条 </label>
                   <t-select
                     v-model="data.pen.dash"
-                    size="small"
                     placeholder="线条样式"
                     @change="changeValue('dash')"
                     style="width: 80px"
@@ -981,13 +980,15 @@
 
 <script lang="ts" setup>
 import { onBeforeMount, onUnmounted, reactive, ref } from 'vue';
-import { getCookie } from '@/services/cookie';
-import { useSelection } from '@/services/selections';
 
 import CodeEditor from '@/views/components/common/CodeEditor.vue';
 import PenAnimates from './PenAnimates.vue';
 import PenDatas from './PenDatas.vue';
 import PenEvents from './PenEvents.vue';
+
+import { getCookie } from '@/services/cookie';
+import { useSelection } from '@/services/selections';
+import { fonts } from '@/services/common';
 import { updatePen } from './pen';
 
 const headers = {
@@ -1003,26 +1004,6 @@ const data = reactive<any>({
   rect: {},
 });
 
-const fonts = [
-  '新宋体',
-  '微软雅黑',
-  '黑体',
-  '楷体',
-  '-apple-system',
-  'BlinkMacSystemFont',
-  'PingFang SC',
-  'Hiragino Sans GB',
-  'Microsoft YaHei UI',
-  'Microsoft YaHei',
-  'fangsong',
-  'Source Han Sans CN',
-  'sans-serif',
-  'serif',
-  'Apple Color Emoji',
-  'Segoe UI Emoji',
-  'Segoe UI Symbol',
-];
-
 const { selections } = useSelection();
 
 const tooltipDialog = reactive<any>({

+ 902 - 2
src/views/components/PensProps.vue

@@ -1,9 +1,909 @@
 <template>
-  <div class="props"></div>
+  <div class="props">
+    <t-tabs v-model="data.tab">
+      <t-tab-panel :value="1" label="外观">
+        <t-space direction="vertical" class="py-16 w-full">
+          <div class="px-16 flex between">
+            <label>选中了{{ selections.pens.length }}个图元</label>
+            <div class="icons">
+              <t-tooltip
+                class="mr-4"
+                v-if="!data.locked"
+                content="可编辑"
+                placement="top"
+              >
+                <svg class="l-icon" aria-hidden="true" @click="lock(1)">
+                  <use xlink:href="#l-unlock"></use>
+                </svg>
+              </t-tooltip>
+              <t-tooltip
+                class="mr-4"
+                v-else-if="data.locked == 1"
+                content="禁止编辑"
+                placement="top"
+              >
+                <svg class="l-icon" aria-hidden="true" @click="lock(2)">
+                  <use xlink:href="#l-lock"></use>
+                </svg>
+              </t-tooltip>
+              <t-tooltip
+                class="mr-4"
+                v-else-if="data.locked == 2"
+                content="禁止编辑和移动"
+                placement="top"
+              >
+                <svg class="l-icon" aria-hidden="true" @click="lock(10)">
+                  <use xlink:href="#l-wufayidong"></use>
+                </svg>
+              </t-tooltip>
+              <t-tooltip
+                class="mr-4"
+                v-else-if="data.locked == 10"
+                content="禁止所有事件"
+                placement="top"
+              >
+                <svg class="l-icon" aria-hidden="true" @click="lock(node, 0)">
+                  <use xlink:href="#l-jinyong"></use>
+                </svg>
+              </t-tooltip>
+              <t-icon
+                v-if="data.visible !== false"
+                name="browse"
+                @click="visible(false)"
+                class="ml-8"
+              />
+              <t-icon
+                v-else
+                name="browse-off"
+                @click="visible(true)"
+                class="ml-8"
+              />
+            </div>
+          </div>
+          <t-collapse
+            :defaultValue="['1', '2', '3', '4']"
+            expandIconPlacement="right"
+            :borderless="true"
+          >
+            <t-collapse-panel value="1" header="对齐">
+              <t-space direction="vertical" size="small" class="w-full">
+                <div style="color: var(--color); margin-bottom: 2px">
+                  区域对齐
+                </div>
+                <div class="icons">
+                  <t-tooltip
+                    v-for="item in aligns"
+                    :content="item.label"
+                    placement="top"
+                  >
+                    <svg
+                      class="l-icon btn"
+                      aria-hidden="true"
+                      @click="align(item.value)"
+                    >
+                      <use :xlink:href="item.icon"></use>
+                    </svg>
+                  </t-tooltip>
+                </div>
+              </t-space>
+              <t-divider
+                style="margin: 16px -16px 12px -16px; width: calc(100% + 32px)"
+              />
+              <t-space direction="vertical" size="small" class="w-full">
+                <div style="color: var(--color); margin-bottom: 2px">
+                  以最后选中图元对齐
+                </div>
+                <div class="icons">
+                  <t-tooltip
+                    v-for="item in aligns2"
+                    :content="item.label"
+                    placement="top"
+                  >
+                    <svg
+                      class="l-icon btn"
+                      aria-hidden="true"
+                      @click="align2(item.value)"
+                    >
+                      <use :xlink:href="item.icon"></use>
+                    </svg>
+                  </t-tooltip>
+                </div>
+              </t-space>
+            </t-collapse-panel>
+            <t-collapse-panel value="2" 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.color"
+                    @change="changeValue('color')"
+                  />
+                  <label style="width: 64px">前景颜色</label>
+
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    v-model="data.hoverColor"
+                    @change="changeValue('hoverColor')"
+                  />
+                  <label style="width: 64px">悬停颜色</label>
+
+                  <t-color-picker
+                    class="simple mt-8 mr-4"
+                    format="CSS"
+                    :color-modes="['monochrome']"
+                    :show-primary-color-preview="false"
+                    v-model="data.activeColor"
+                    @change="changeValue('activeColor')"
+                  />
+                  <label style="width: 64px">选中颜色</label>
+                </div>
+
+                <div class="form-item">
+                  <label style="width: 32px">线条 </label>
+                  <t-select
+                    v-model="data.dash"
+                    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.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.strokeType"
+                        @change="changeValue('strokeType')"
+                        style="width: 22px"
+                      />
+
+                      <t-color-picker
+                        v-if="data.strokeType"
+                        class="simple mr-4"
+                        format="CSS"
+                        :color-modes="['linear-gradient']"
+                        :show-primary-color-preview="false"
+                        :clearable="true"
+                        :enableAlpha="true"
+                        v-model="data.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.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.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="ml-8">
+                    <t-radio-group
+                      size="small"
+                      v-model="data.bkType"
+                      :default-value="0"
+                      @change="changeValue('bkType')"
+                    >
+                      <t-radio-button :value="0"> 纯色 </t-radio-button>
+                      <t-radio-button :value="1"> 线性渐变 </t-radio-button>
+                      <t-radio-button :value="2"> 径向渐变 </t-radio-button>
+                    </t-radio-group>
+                    <div v-if="data.bkType === 0" class="mt-8 -ml-8">
+                      <t-color-picker
+                        class="w-full"
+                        format="CSS"
+                        :color-modes="['monochrome']"
+                        :show-primary-color-preview="false"
+                        v-model="data.background"
+                        @change="changeValue('background')"
+                      />
+                    </div>
+                    <div
+                      v-else-if="data.bkType === 1"
+                      class="mt-8 -ml-8"
+                      style="width: 200px"
+                    >
+                      <t-color-picker
+                        class="w-full"
+                        format="CSS"
+                        :color-modes="['linear-gradient']"
+                        :show-primary-color-preview="false"
+                        v-model="data.gradientColors"
+                        @change="changeValue('gradientColors')"
+                      />
+                    </div>
+                    <div v-else-if="data.bkType === 2" class="mt-8 flex middle">
+                      <t-color-picker
+                        class="simple"
+                        format="CSS"
+                        :color-modes="['linear-gradient']"
+                        :show-primary-color-preview="false"
+                        v-model="data.gradientColors"
+                        @change="changeValue('gradientColors')"
+                      />
+
+                      <t-input-number
+                        theme="column"
+                        placeholder="渐变半径"
+                        v-model="data.gradientRadius"
+                        :min="0"
+                        :max="1"
+                        :step="0.1"
+                        @change="changeValue('gradientRadius')"
+                        class="ml-8"
+                        style="width: 100px"
+                      />
+                    </div>
+                  </div>
+                </div>
+                <div class="form-item">
+                  <label style="width: 32px">阴影 </label>
+                  <div class="flex middle ml-8">
+                    <t-checkbox
+                      v-model="data.shadow"
+                      @change="changeValue('shadow')"
+                      style="width: 22px"
+                    />
+                    <t-color-picker
+                      v-if="data.shadow"
+                      class="simple"
+                      format="CSS"
+                      :color-modes="['monochrome']"
+                      :show-primary-color-preview="false"
+                      v-model="data.shadowColor"
+                      @change="changeValue('shadowColor')"
+                    />
+                  </div>
+                </div>
+                <div class="form-item" v-if="data.shadow">
+                  <label style="width: 28px"></label>
+                  <div class="flex" style="margin-top: -8px">
+                    <t-input
+                      class="ml-4"
+                      label="X"
+                      placeholder="0"
+                      v-model.number="data.shadowOffsetX"
+                      style="width: 60px"
+                      @change="changeValue('x')"
+                      title="X偏移"
+                    />
+                    <t-input
+                      class="ml-4"
+                      label="Y"
+                      placeholder="0"
+                      v-model.number="data.shadowOffsetY"
+                      style="width: 60px"
+                      @change="changeValue('shadowOffsetY')"
+                      title="Y偏移"
+                    />
+                    <t-input
+                      class="ml-4"
+                      label="模糊"
+                      placeholder="0"
+                      v-model.number="data.shadowBlur"
+                      style="width: 64px"
+                      @change="changeValue('shadowBlur')"
+                      title="模糊大小"
+                    />
+                  </div>
+                </div>
+              </t-space>
+            </t-collapse-panel>
+            <t-collapse-panel value="3" header="文字">
+              <t-space direction="vertical" size="small" class="w-full">
+                <div class="form-item">
+                  <div class="flex middle" style="margin-left: -10px">
+                    <t-select-input
+                      :value="data.fontFamily"
+                      :popup-visible="data.fontFamilyPopupVisible"
+                      placeholder="字体名"
+                      allow-input
+                      style="width: 170px"
+                      @change="changeValue('fontFamily')"
+                      @enter="changeValue('fontFamily')"
+                      @blur="changeValue('fontFamily')"
+                      @popup-visible-change="onFontPopupVisible"
+                      :popup-props="{
+                        overlayInnerStyle: { width: 'auto' },
+                      }"
+                    >
+                      <template #panel>
+                        <ul style="padding: 12px">
+                          <li
+                            v-for="item in fonts"
+                            :key="item"
+                            @click="onFontFamily(item)"
+                          >
+                            {{ item }}
+                          </li>
+                        </ul>
+                      </template>
+                      <template #suffixIcon>
+                        <t-icon name="chevron-down" />
+                      </template>
+                    </t-select-input>
+
+                    <t-input
+                      class="ml-8"
+                      placeholder="字体大小"
+                      v-model.number="data.fontSize"
+                      style="width: 80px"
+                      :format="decimalRound"
+                      @change="changeValue('fontSize')"
+                    />
+                  </div>
+                </div>
+                <div class="flex middle">
+                  <t-radio-group
+                    size="small"
+                    v-model="data.textAlign"
+                    default-value="center"
+                    @change="changeValue('textAlign')"
+                  >
+                    <t-radio-button value="left">
+                      <t-tooltip content="居左" placement="top">
+                        <t-icon name="format-vertical-align-left" />
+                      </t-tooltip>
+                    </t-radio-button>
+                    <t-radio-button value="center">
+                      <t-tooltip content="居中" placement="top">
+                        <t-icon name="format-vertical-align-center" />
+                      </t-tooltip>
+                    </t-radio-button>
+                    <t-radio-button value="right">
+                      <t-tooltip content="居右" placement="top">
+                        <t-icon name="format-vertical-align-right" />
+                      </t-tooltip>
+                    </t-radio-button>
+                  </t-radio-group>
+                  <t-radio-group
+                    class="ml-8"
+                    size="small"
+                    v-model="data.textBaseline"
+                    default-value="top"
+                    @change="changeValue('textBaseline')"
+                  >
+                    <t-radio-button value="top">
+                      <t-tooltip content="顶部对齐" placement="top">
+                        <t-icon name="format-horizontal-align-top" />
+                      </t-tooltip>
+                    </t-radio-button>
+                    <t-radio-button value="middle">
+                      <t-tooltip content="垂直居中" placement="middle">
+                        <t-icon name="format-horizontal-align-center" />
+                      </t-tooltip>
+                    </t-radio-button>
+                    <t-radio-button value="bottom">
+                      <t-tooltip content="底部对齐" placement="top">
+                        <t-icon name="format-horizontal-align-bottom" />
+                      </t-tooltip>
+                    </t-radio-button>
+                  </t-radio-group>
+
+                  <t-button
+                    :class="{ active: data.fontWeight === 'bold' }"
+                    class="ml-8 icon"
+                    shape="rectangle"
+                    variant="text"
+                    @click="
+                      data.fontWeight === 'bold'
+                        ? (data.fontWeight = 'normal')
+                        : (data.fontWeight = 'bold');
+                      changeValue('fontWeight');
+                    "
+                  >
+                    B
+                  </t-button>
+
+                  <t-button
+                    :class="{ active: data.fontStyle === 'italic' }"
+                    class="ml-4 icon"
+                    shape="rectangle"
+                    variant="text"
+                    @click="
+                      data.fontStyle === 'italic'
+                        ? (data.fontStyle = 'normal')
+                        : (data.fontStyle = 'italic');
+                      changeValue('fontStyle');
+                    "
+                    style="font-style: italic; font-family: serif"
+                    >I</t-button
+                  >
+                </div>
+                <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.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.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.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.activeColor"
+                    @change="changeValue('activeColor')"
+                  />
+                  <label style="width: 44px">选中</label>
+                </div>
+                <div class="form-item">
+                  <t-checkbox
+                    v-model="data.whiteSpace"
+                    @change="changeValue('whiteSpace')"
+                    style="width: 64px"
+                  >
+                    换行
+                  </t-checkbox>
+                  <t-checkbox
+                    v-model="data.ellipsis"
+                    @change="changeValue('ellipsis')"
+                    style="width: 68px"
+                  >
+                    省略号
+                  </t-checkbox>
+                  <t-tooltip content="行高">
+                    <t-input
+                      placeholder="行高"
+                      v-model.number="data.lineHeight"
+                      style="width: 40px"
+                      @change="changeValue('lineHeight')"
+                    />
+                  </t-tooltip>
+                  <t-tooltip content="显示时保留小数位数">
+                    <t-input
+                      class="ml-4"
+                      placeholder="小数"
+                      v-model.number="data.keepDecimal"
+                      style="width: 60px"
+                      @change="changeValue('keepDecimal')"
+                    />
+                  </t-tooltip>
+                </div>
+                <div class="form-item" style="margin-top: -4px">
+                  <t-tooltip content="水平偏移">
+                    <t-input
+                      placeholder="X"
+                      v-model.number="data.textLeft"
+                      style="width: 60px; margin-left: -8px"
+                      @change="changeValue('textLeft')"
+                    />
+                  </t-tooltip>
+                  <t-tooltip content="垂直偏移">
+                    <t-input
+                      class="ml-4"
+                      placeholder="Y"
+                      v-model.number="data.textTop"
+                      style="width: 60px"
+                      @change="changeValue('textTop')"
+                    />
+                  </t-tooltip>
+                  <t-tooltip content="宽">
+                    <t-input
+                      class="ml-4"
+                      placeholder="宽"
+                      v-model.number="data.textWidth"
+                      style="width: 60px"
+                      @change="changeValue('textWidth')"
+                    />
+                  </t-tooltip>
+                  <t-tooltip content="高">
+                    <t-input
+                      class="ml-4"
+                      placeholder="高"
+                      v-model.number="data.textHeight"
+                      style="width: 60px"
+                      @change="changeValue('textHeight')"
+                    />
+                  </t-tooltip>
+                </div>
+                <div class="flex middle">
+                  <t-checkbox
+                    v-model="data.disableInput"
+                    @change="changeValue('disableInput')"
+                    style="width: 64px"
+                  >
+                    只读
+                  </t-checkbox>
+
+                  <t-checkbox
+                    v-model="data.hiddenText"
+                    @change="changeValue('hiddenText')"
+                    style="width: 90px"
+                  >
+                    隐藏文字
+                  </t-checkbox>
+                </div>
+              </t-space>
+            </t-collapse-panel>
+          </t-collapse>
+        </t-space>
+        <t-divider style="margin-top: -8px" />
+        <div class="form-item p-16">
+          <t-checkbox
+            v-model="data.flipX"
+            @change="changeValue('flipX')"
+            style="width: 90px"
+          >
+            水平翻转
+          </t-checkbox>
+          <t-checkbox
+            v-model="data.flipY"
+            @change="changeValue('flipY')"
+            style="width: 90px"
+          >
+            垂直翻转
+          </t-checkbox>
+
+          <label style="width: 50px">锚点半径</label>
+          <input
+            class="ml-4"
+            v-model.number="data.anchorRadius"
+            style="width: 20px"
+            @change="changeValue('anchorRadius')"
+            placeholder="4"
+          />
+        </div>
+        <t-divider />
+        <div class="form-item p-16" style="margin-bottom: 20px">
+          <t-checkbox
+            v-model="data.disableRotate"
+            @change="changeValue('disableRotate')"
+            style="width: 90px"
+          >
+            禁止旋转
+          </t-checkbox>
+          <t-checkbox
+            v-model="data.disableSize"
+            @change="changeValue('disableSize')"
+            style="width: 90px"
+          >
+            禁止缩放
+          </t-checkbox>
+          <t-checkbox
+            v-model="data.disableAnchor"
+            @change="changeValue('disableAnchor')"
+            style="width: 90px"
+          >
+            禁用锚点
+          </t-checkbox>
+        </div>
+      </t-tab-panel>
+    </t-tabs>
+  </div>
 </template>
 
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+import { onBeforeMount, onUnmounted, reactive, ref } from 'vue';
+
+import { LockState, Pen } from '@meta2d/core';
+
+import { updatePen } from './pen';
+
+import { useSelection } from '@/services/selections';
+import { fonts, setChildrenVisible } from '@/services/common';
+
+const { selections } = useSelection();
+
+const data = reactive<any>({
+  tab: 1,
+  locked: 0,
+  lineWidth: 1,
+});
+
+const aligns = [
+  {
+    value: 'left',
+    label: '左对齐',
+    icon: '#l-align-left',
+  },
+  {
+    value: 'right',
+    label: '水平居中对齐',
+    icon: '#l-align-center',
+  },
+  {
+    value: 'right',
+    label: '右对齐',
+    icon: '#l-align-right',
+  },
+  {
+    value: 'top',
+    label: '顶部对齐',
+    icon: '#l-align-top',
+  },
+  {
+    value: 'right',
+    label: '垂直居中对齐',
+    icon: '#l-align-middle',
+  },
+  {
+    value: 'bottom',
+    label: '底部对齐',
+    icon: '#l-align-bottom',
+  },
+  {
+    value: 'h-distribute',
+    label: '水平等距',
+    icon: '#l-horizontal-between',
+  },
+  {
+    value: 'v-distribute',
+    label: '垂直等距',
+    icon: '#l-vertical-between',
+  },
+];
+
+const aligns2 = [
+  {
+    value: 'left',
+    label: '左对齐',
+    icon: '#l-align-left',
+  },
+  {
+    value: 'right',
+    label: '水平居中对齐',
+    icon: '#l-align-center',
+  },
+  {
+    value: 'right',
+    label: '右对齐',
+    icon: '#l-align-right',
+  },
+  {
+    value: 'top',
+    label: '顶部对齐',
+    icon: '#l-align-top',
+  },
+  {
+    value: 'right',
+    label: '垂直居中对齐',
+    icon: '#l-align-middle',
+  },
+  {
+    value: 'bottom',
+    label: '底部对齐',
+    icon: '#l-align-bottom',
+  },
+  {
+    value: 'same-size',
+    label: '相同大小',
+    icon: '#l-same-size',
+  },
+];
+
+onBeforeMount(() => {});
+
+const lock = (v: LockState) => {
+  data.locked = v;
+  for (const item of selections.pens) {
+    meta2d.setValue({
+      id: item.id,
+      locked: v,
+    });
+  }
+};
+
+const visible = (v: boolean) => {
+  data.visible = v;
+  for (const item of selections.pens) {
+    meta2d.setVisible(item, v);
+  }
+};
+
+const align = (align: string) => {
+  meta2d.alignNodes(align, meta2d.store.active);
+};
+
+const align2 = (align: string) => {
+  meta2d.alignNodes(align, meta2d.store.active);
+};
+
+const changeValue = (prop: string) => {
+  for (const item of selections.pens) {
+    data.id = item.id;
+    updatePen(data, prop, false);
+  }
+  meta2d.render();
+};
+
+const onFontFamily = (fontFamily: string) => {
+  data.fontFamily = fontFamily;
+  data.fontFamilyPopupVisible = false;
+  changeValue('fontFamily');
+};
+
+const onFontPopupVisible = (val: boolean) => {
+  data.fontFamilyPopupVisible = val;
+};
+</script>
 <style lang="postcss" scoped>
 .props {
+  .icons {
+    display: flex;
+
+    svg:hover {
+      cursor: pointer;
+      color: var(--color-primary);
+    }
+
+    .btn {
+      font-size: 16px;
+      margin-right: 16px;
+      color: var(--color);
+    }
+  }
 }
 </style>

+ 2 - 2
src/views/components/pen.ts

@@ -1,4 +1,4 @@
-export const updatePen = (pen: any, prop: string) => {
+export const updatePen = (pen: any, prop: string, render = true) => {
   const v: any = { id: pen.id };
   const rect: any = meta2d.getPenRect(pen);
 
@@ -32,5 +32,5 @@ export const updatePen = (pen: any, prop: string) => {
   } else if (prop === 'titleFnJs') {
     v.titleFn = null;
   }
-  meta2d.setValue(v);
+  meta2d.setValue(v, { render });
 };