Forráskód Böngészése

调整页面样式

Veronique 1 éve
szülő
commit
fc7d6fabc5

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 6454 - 5282
pnpm-lock.yaml


+ 3 - 1
src/configs/canvas.ts

@@ -100,7 +100,9 @@ export const propertiesToInclude = [
     'originHeight',
     'globalCompositeOperation',
     'permissionsConfig',
-    'itemName'
+    'itemName',
+    'userSelectableColors',
+    'userPresetTexts'
 ]
 
 export const WorkSpaceDrawData = {

+ 2 - 1
src/mocks/templates.ts

@@ -54,7 +54,8 @@ export const Templates: Template[] = [
                 "skewX": 0,
                 "skewY": 0,
                 "permissionsConfig": [],
-                "itemName": "背景"
+                "itemName": "背景",
+                "userSelectableColors":[]
             }
         ],
         "workSpace": {

+ 4 - 0
src/types/canvas.ts

@@ -64,6 +64,7 @@ export interface WorkSpaceElement {
   gradientRotate?: number
   backgroundColor?: string
   permissionsConfig?: []
+  userSelectableColors?:[]
 }
 
 export interface BackgroundElement {
@@ -84,6 +85,7 @@ export interface BackgroundElement {
   gradientOffsetY?: number
   backgroundColor?: string
   permissionsConfig?: []
+  userSelectableColors?:[]
 }
 
 export interface TextboxElement extends Textbox, CommenElement {
@@ -92,6 +94,7 @@ export interface TextboxElement extends Textbox, CommenElement {
   fillRepeat: TPatternRepeat
   fillURL: string
   editable: boolean
+  userPresetTexts?:[]
 }
 
 export interface ITextElement extends IText, CommenElement {
@@ -100,6 +103,7 @@ export interface ITextElement extends IText, CommenElement {
   fillRepeat: TPatternRepeat
   fillURL: string
   editable: boolean
+  userPresetTexts?:[]
 }
 
 export interface PathElement extends Path, CommenElement {

+ 4 - 0
src/types/components.d.ts

@@ -16,6 +16,7 @@ declare module 'vue' {
     ElAffix: typeof import('element-plus/es')['ElAffix']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
+    ElCard: typeof import('element-plus/es')['ElCard']
     ElCarousel: typeof import('element-plus/es')['ElCarousel']
     ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@@ -24,6 +25,7 @@ declare module 'vue' {
     ElCol: typeof import('element-plus/es')['ElCol']
     ElCollapse: typeof import('element-plus/es')['ElCollapse']
     ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
+    ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
@@ -38,6 +40,7 @@ declare module 'vue' {
     ElInput: typeof import('element-plus/es')['ElInput']
     ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
     ElLabel: typeof import('element-plus/es')['ElLabel']
+    ElMenu: typeof import('element-plus/es')['ElMenu']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
     ElPopover: typeof import('element-plus/es')['ElPopover']
@@ -49,6 +52,7 @@ declare module 'vue' {
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSlider: typeof import('element-plus/es')['ElSlider']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTab: typeof import('element-plus/es')['ElTab']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']

+ 299 - 150
src/views/Editor/CanvasLeft/Menu/components/FrontEditorPool.vue

@@ -1,160 +1,247 @@
 <template>
   <div>
     <el-scrollbar>
-      <el-collapse v-model="activeNames">
-        <template v-for="(object,index) in objects">
-          <el-collapse-item v-if="object.permissionsConfig && (object.permissionsConfig.length > 0)"
-                            :title="object.itemName" :name="object.id">
-            <el-form
-                :key="object.id"
-                label-width="auto"
-                label-position="right"
-                size="small"
-            >
-              <template v-for="(perm) in object.permissionsConfig">
-                <template v-if="object.id == 'WorkSpaceDrawType'">
-                  <!--              背景-->
-                  <el-form-item v-if="perm == '0'" label="背景颜色">
-                    <el-popover trigger="click" placement="right" :width="265">
-                      <template #reference>
-                        <ColorButton style="width: 100px" :color="background.color || '#fff'"/>
-                      </template>
-                      <ColorPicker
-                          :modelValue="background.color"
-                          @update:modelValue="(color: string) => updateBackground({color: color, fill: color})"
-                      />
-                    </el-popover>
-                  </el-form-item>
-                  <el-form-item v-else-if="perm == '1'" label="背景尺寸">
+      <!--        背景-->
+      <el-card v-if="backGroundObject.permissionsConfig.length > 0"
+               :body-style="{background: '#f8f8f8'}"
+               style="max-width: 100%; border-radius: 8px">
+
+        <el-row style="margin-bottom: 10px">
+          <el-col :span="22">
+            <span class="eleTitle">编辑背景</span>
+          </el-col>
+        </el-row>
+
+        <template v-for="(perm) in backGroundObject.permissionsConfig">
+          <el-row class="permRow" v-if="perm == '0'">
+            <el-row>
+              <span class="eleLabel">选择颜色</span>
+            </el-row>
+            <el-row :gutter="10">
+              <el-col :span="4" v-for="color in backGroundObject.userSelectableColors">
+                <el-button class="colorButton" :color="color" @click="updateBackground({color: color, fill: color})"/>
+              </el-col>
+            </el-row>
+          </el-row>
+
+          <el-row class="permRow" v-if="perm == '1'">
+            <el-row>
+              <span class="eleLabel">背景尺寸</span>
+            </el-row>
+            <el-row :gutter="10">
+              <el-col :span="11">
+                <el-input
+                    v-model="canvasWidth"
+                    :value="pageSizeWidth"
+                    @change="changeTemplateWidth"
+                    oninput="value=value.replace(/[^\d.]/g,'')"
+                >
+                  <template #prepend>宽</template>
+                </el-input>
+              </el-col>
+              <el-col :span="2"/>
+              <el-col :span="11">
+                <el-input
+                    v-model="canvasHeight"
+                    :value="pageSizeHeight"
+                    @change="changeTemplateHeight"
+                    oninput="value=value.replace(/[^\d.]/g,'')"
+                >
+                  <template #prepend>高</template>
+                </el-input>
+              </el-col>
+            </el-row>
+          </el-row>
+        </template>
+      </el-card>
+
+      <!--      文本-->
+      <el-card v-if="textPermDisplay"
+               :body-style="{background: '#f8f8f8'}"
+               style="max-width: 100%; border-radius: 8px">
+
+        <el-row style="margin-bottom: 10px">
+          <el-col :span="24">
+            <span class="eleTitle">编辑文本</span>
+          </el-col>
+          <el-col :span="24">
+            <el-tabs v-model="activeTextTabIndex">
+              <el-tab-pane v-for="(text,index) in textObjects" :label="text.itemName" :name="index">
+                <template v-for="(perm) in text.permissionsConfig">
+                  <!--                  文本内容-->
+                  <el-row class="permRow" v-if="perm == '0'">
+                    <el-col :span="4">
+                      <span class="eleLabel">文本</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-select
+                          v-model="text.text"
+                          filterable
+                          allow-create
+                          default-first-option
+                          @change="eleTextChange(text.id, $event)">
+                        <el-option v-for="str in text.userPresetTexts"
+                                   :key="str.text"
+                                   :label="str.text"
+                                   :value="str.text"/>
+                      </el-select>
+                    </el-col>
+                  </el-row>
+
+                  <!--                  字号-->
+                  <el-row class="permRow" v-if="perm == '1'">
+                    <el-col :span="4">
+                      <span class="eleLabel">字号</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-button-group class="full-group">
+                        <el-tooltip placement="top" content="增大字号" :hide-after="0">
+                          <el-button class="font-size" @click="handleElementFontsize(text.id,'+')">
+                            <IconFontSize/>
+                            +
+                          </el-button>
+                        </el-tooltip>
+
+                        <el-tooltip placement="top" content="减小字号" :hide-after="0">
+                          <el-button @click="handleElementFontsize(text.id,'-')">
+                            <IconFontSize/>
+                            -
+                          </el-button>
+                        </el-tooltip>
+                      </el-button-group>
+                    </el-col>
+                  </el-row>
+
+                  <!--                  字体-->
+                  <el-row class="permRow" v-if="perm == '2'">
+                    <el-col :span="4">
+                      <span class="eleLabel">字体</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-select v-model="text.fontFamily" placement="left"
+                                 @change="handleElementFontFamily(text.id, $event)">
+                        <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
+                          <template v-if="group.type == 0">
+                            <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
+                                       :style="{fontFamily: item.value}"></el-option>
+                          </template>
+                          <template v-else>
+                            <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily"
+                                       :label="item.fontName">
+                              <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
+                            </el-option>
+                          </template>
+                        </el-option-group>
+                      </el-select>
+                    </el-col>
+                  </el-row>
+
+                  <!--                  颜色-->
+                  <el-row class="permRow" v-if="perm == '3'">
                     <el-row>
-                      <el-col :span="11">
-                        <el-input
-                            v-model="canvasWidth"
-                            :value="pageSizeWidth"
-                            @change="changeTemplateWidth"
-                            oninput="value=value.replace(/[^\d.]/g,'')"
-                        >
-                          <template #prepend>宽</template>
-                        </el-input>
-                      </el-col>
-                      <el-col :span="1"/>
-                      <el-col :span="11">
-                        <el-input
-                            v-model="canvasHeight"
-                            :value="pageSizeHeight"
-                            @change="changeTemplateHeight"
-                            oninput="value=value.replace(/[^\d.]/g,'')"
-                        >
-                          <template #prepend>高</template>
-                        </el-input>
+                      <span class="eleLabel">选择颜色</span>
+                    </el-row>
+                    <el-row :gutter="10">
+                      <el-col :span="4" v-for="color in text.userSelectableColors">
+                        <el-button class="colorButton" :color="color" @click="updateFontColor(text.id,color)"/>
                       </el-col>
                     </el-row>
-                  </el-form-item>
-                  <el-form-item v-else-if="perm == '2'" label="上传背景">
-                    <!--                暂不实现-->
-                  </el-form-item>
+                  </el-row>
+
+                  <!--                  字间距-->
+                  <el-row class="permRow" v-if="perm == '4'">
+                    <el-col :span="4">
+                      <span class="eleLabel">缩进</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-button-group class="full-group">
+                        <el-tooltip placement="top" content="减小缩进" :hide-after="0">
+                          <el-button @click="handleElementCharSpacing(text.id,'-')">
+                            <IconIndentLeft/>
+                          </el-button>
+                        </el-tooltip>
+                        <el-tooltip placement="top" content="增大缩进" :hide-after="0">
+                          <el-button @click="handleElementCharSpacing(text.id,'+')">
+                            <IconIndentRight/>
+                          </el-button>
+                        </el-tooltip>
+                      </el-button-group>
+                    </el-col>
+                  </el-row>
+
+                  <!--                  尺寸及位置-->
+                  <el-row class="permRow" v-if="perm == '5'">
+                    <el-col :span="4">
+                      <span class="eleLabel">尺寸</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-tag effect="dark" type="info">请拖拽右侧元素更改元素尺寸</el-tag>
+                    </el-col>
+                  </el-row>
+                  <el-row class="permRow" v-if="perm == '6'">
+                    <el-col :span="4">
+                      <span class="eleLabel">位置</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-tag effect="dark" type="info">请拖拽右侧元素更改元素位置</el-tag>
+                    </el-col>
+                  </el-row>
                 </template>
-
-                <template v-else-if="object.name == 'textbox'">
-                  <!--             文字-->
-                  <el-form-item v-if="perm == '0'" label="内容">
-                    <el-tag type="primary">请点击右侧元素更改文字内容</el-tag>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '1'" label="字号">
-                    <el-button-group class="full-group">
-                      <el-tooltip placement="top" content="增大字号" :hide-after="0">
-                        <el-button class="font-size" @click="handleElementFontsize(object.id,'+')">
-                          <IconFontSize/>
-                          +
-                        </el-button>
-                      </el-tooltip>
-
-                      <el-tooltip placement="top" content="减小字号" :hide-after="0">
-                        <el-button @click="handleElementFontsize(object.id,'-')">
-                          <IconFontSize/>
-                          -
-                        </el-button>
-                      </el-tooltip>
-                    </el-button-group>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '2'" label="字体">
-                    <el-select v-model="object.fontFamily" placement="left" style="width: 180px"
-                               @change="handleElementFontFamily(object.id, $event)">
-                      <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
-                        <template v-if="group.type == 0">
-                          <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
-                                     :style="{fontFamily: item.value}"></el-option>
-                        </template>
-                        <template v-else>
-                          <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily"
-                                     :label="item.fontName">
-                            <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
-                          </el-option>
-                        </template>
-                      </el-option-group>
-                    </el-select>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '3'" label="颜色">
-                    <el-tooltip placement="top" content="文字颜色" :hide-after="0">
-                      <div @click.stop>
-                        <el-popover trigger="click" placement="right" :width="265" @click.stop>
-                          <template #reference>
-                            <el-button class="font-color">
-                              <TextColorButton :color="object.color">
-                                <IconText/>
-                              </TextColorButton>
-                            </el-button>
-                          </template>
-                          <ColorPicker :modelValue="object.color"
-                                       @update:modelValue="(color: string) => updateFontColor(object.id,color)"/>
-                        </el-popover>
-                      </div>
-                    </el-tooltip>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '4'" label="字间距">
-                    <el-button-group class="full-group">
-                      <el-tooltip placement="top" content="减小缩进" :hide-after="0">
-                        <el-button @click="handleElementCharSpacing(object.id,'-')">
-                          <IconIndentLeft/>
+              </el-tab-pane>
+            </el-tabs>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!--      图片-->
+      <el-card v-if="textPermDisplay"
+               :body-style="{background: '#f8f8f8'}"
+               style="max-width: 100%; border-radius: 8px;">
+
+        <el-row style="margin-bottom: 10px">
+          <el-col :span="24">
+            <span class="eleTitle">编辑图片</span>
+          </el-col>
+          <el-col :span="24">
+            <el-tabs v-model="activeImgTabIndex">
+              <el-tab-pane v-for="(img,index) in imgObjects" :label="img.itemName" :name="index">
+                <template v-for="(perm) in img.permissionsConfig">
+                  <!--                  上传图片-->
+                  <el-row class="permRow" v-if="perm == '0'">
+                    <el-col :span="4">
+                      <span class="eleLabel">上传</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <FileInput class="full-width-btn" @change="(files: FileList) => replaceImage(img.id,files)">
+                        <el-button class="full-btn">
+                          <IconTransform class="btn-icon"/>
                         </el-button>
-                      </el-tooltip>
-                      <el-tooltip placement="top" content="增大缩进" :hide-after="0">
-                        <el-button @click="handleElementCharSpacing(object.id,'+')">
-                          <IconIndentRight/>
-                        </el-button>
-                      </el-tooltip>
-                    </el-button-group>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '5'" label="尺寸">
-                    <el-tag type="primary">请拖拽右侧元素更改元素尺寸</el-tag>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '6'" label="位置">
-                    <el-tag type="primary">请拖拽右侧元素更改元素位置</el-tag>
-                  </el-form-item>
-                </template>
-
-                <template v-else-if="object.name == 'image'">
-                  <!--             图片-->
-                  <el-form-item v-if="perm == '0'" label="上传图片">
-                    <FileInput class="full-width-btn" @change="(files: FileList) => replaceImage(object.id,files)">
-                      <el-button class="full-btn">
-                        <IconTransform class="btn-icon"/>
-                      </el-button>
-                    </FileInput>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '1'" label="尺寸">
-                    <el-tag type="primary">请拖拽右侧元素更改元素尺寸</el-tag>
-                  </el-form-item>
-                  <el-form-item v-if="perm == '2'" label="位置">
-                    <el-tag type="primary">请拖拽右侧元素更改元素位置</el-tag>
-                  </el-form-item>
+                      </FileInput>
+                    </el-col>
+                  </el-row>
+                  <!--                  尺寸及位置-->
+                  <el-row class="permRow" v-if="perm == '1'">
+                    <el-col :span="4">
+                      <span class="eleLabel">尺寸</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-tag effect="dark" type="info">请拖拽右侧元素更改元素尺寸</el-tag>
+                    </el-col>
+                  </el-row>
+                  <el-row class="permRow" v-if="perm == '2'">
+                    <el-col :span="4">
+                      <span class="eleLabel">位置</span>
+                    </el-col>
+                    <el-col :span="20">
+                      <el-tag effect="dark" type="info">请拖拽右侧元素更改元素位置</el-tag>
+                    </el-col>
+                  </el-row>
                 </template>
-
-              </template>
-            </el-form>
-          </el-collapse-item>
-        </template>
-      </el-collapse>
+              </el-tab-pane>
+            </el-tabs>
+          </el-col>
+        </el-row>
+      </el-card>
     </el-scrollbar>
   </div>
 </template>
@@ -187,8 +274,40 @@ const fabricStore = useFabricStore();
 const {clip, safe, zoom, opacity} = storeToRefs(fabricStore);
 const objects = canvas.getObjects().filter((object) => !["WorkSpaceMaskType", "WorkSpaceClipType", "WorkSpaceSafeType", "WorkSpaceClipType"].includes(object.id));
 
+const backGroundObject = computed(() => {
+  return canvas.getObjects().filter((item) => item.id === WorkSpaceDrawType)[0];
+})
+const textObjects = computed(() => {
+  return canvas.getObjects().filter((item) => item.name === 'textbox');
+})
+const imgObjects = computed(() => {
+  return canvas.getObjects().filter((item) => item.name === 'image');
+})
 
-const activeNames = ref([])
+const textPermDisplay = computed(() => {
+  let result = false;
+  textObjects.value.forEach(x => {
+    if (x.permissionsConfig.length > 0) {
+      result = true;
+      return;
+    }
+  })
+  return result;
+})
+
+const imgPermDisplay = computed(() => {
+  let result = false;
+  imgObjects.value.forEach(x => {
+    if (x.permissionsConfig.length > 0) {
+      result = true;
+      return;
+    }
+  })
+  return result;
+})
+
+const activeTextTabIndex = ref(0)
+const activeImgTabIndex = ref(0)
 
 const pageSizeWidth = computed(() => {
   return Math.round(templateWidth.value * 100) / 100
@@ -353,6 +472,18 @@ const findObject = (objId: string) => {
   return objects.find((item) => item.id === objId);
 }
 
+//修改文字信息
+const eleTextChange = (objId: string, text) => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  if (handleElement.isEditing) {
+    handleElement.setSelectionStyles({text})
+  } else {
+    templatesStore.modifedElement(handleElement, {text})
+  }
+  canvas.renderAll()
+}
+
 // 修改字体大小
 const handleElementFontsize = (objId: string, mode: string) => {
   const handleElement = findObject(objId) as Textbox | ArcText;
@@ -410,7 +541,6 @@ const replaceImage = (objId: string, files: FileList) => {
 
 onMounted(() => {
   console.log(objects)
-  activeNames.value = objects.map(x => x.id)
 })
 
 </script>
@@ -465,4 +595,23 @@ onMounted(() => {
   }
 }
 
+.eleTitle {
+  font-size: 1.0rem;
+  font-weight: bold;
+}
+
+.eleLabel {
+  font-size: 0.8rem;
+  font-weight: bold;
+  margin-bottom: 10px;
+}
+
+.colorButton {
+  margin-bottom: 5px;
+}
+
+.permRow {
+  margin-top: 10px;
+}
+
 </style>

+ 496 - 0
src/views/Editor/CanvasLeft/Menu/components/FrontEditorPool_Backup.vue

@@ -0,0 +1,496 @@
+<template>
+  <div>
+    <el-scrollbar>
+      <el-collapse v-model="activeNames">
+        <template v-for="(object,index) in objects">
+          <el-collapse-item v-if="object.permissionsConfig && (object.permissionsConfig.length > 0)"
+                            :title="object.itemName" :name="object.id">
+            <el-form
+                :key="object.id"
+                label-width="auto"
+                label-position="right"
+                size="small"
+            >
+              <template v-for="(perm) in object.permissionsConfig">
+                <template v-if="object.id == 'WorkSpaceDrawType'">
+                  <!--              背景-->
+                  <el-form-item v-if="perm == '0'" label="背景颜色">
+                    <el-popover trigger="click" placement="right" :width="265">
+                      <template #reference>
+                        <ColorButton style="width: 100px" :color="background.color || '#fff'"/>
+                      </template>
+                      <ColorPicker
+                          :modelValue="background.color"
+                          @update:modelValue="(color: string) => updateBackground({color: color, fill: color})"
+                      />
+                    </el-popover>
+                  </el-form-item>
+                  <el-form-item v-else-if="perm == '1'" label="背景尺寸">
+                    <el-row>
+                      <el-col :span="11">
+                        <el-input
+                            v-model="canvasWidth"
+                            :value="pageSizeWidth"
+                            @change="changeTemplateWidth"
+                            oninput="value=value.replace(/[^\d.]/g,'')"
+                        >
+                          <template #prepend>宽</template>
+                        </el-input>
+                      </el-col>
+                      <el-col :span="1"/>
+                      <el-col :span="11">
+                        <el-input
+                            v-model="canvasHeight"
+                            :value="pageSizeHeight"
+                            @change="changeTemplateHeight"
+                            oninput="value=value.replace(/[^\d.]/g,'')"
+                        >
+                          <template #prepend>高</template>
+                        </el-input>
+                      </el-col>
+                    </el-row>
+
+                  </el-form-item>
+                  <el-form-item v-else-if="perm == '2'" label="上传背景">
+                    <!--                暂不实现-->
+                  </el-form-item>
+                </template>
+
+                <template v-else-if="object.name == 'textbox'">
+                  <!--             文字-->
+                  <el-form-item v-if="perm == '0'" label="内容">
+                    <!--                    <el-tag type="primary">请点击右侧元素更改文字内容</el-tag>-->
+                    <el-row>
+                      <el-col :span="22">
+                        <!--                        <el-input @change="eleTextChange(object.id, $event)"/>-->
+                        <el-select
+                            v-model="object.text"
+                            filterable
+                            allow-create
+                            default-first-option
+                            @change="eleTextChange(object.id, $event)">
+                          <el-option
+                              label="aaaa"
+                              value="aaaa"/>
+                        </el-select>
+                      </el-col>
+                    </el-row>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '1'" label="字号">
+                    <el-button-group class="full-group">
+                      <el-tooltip placement="top" content="增大字号" :hide-after="0">
+                        <el-button class="font-size" @click="handleElementFontsize(object.id,'+')">
+                          <IconFontSize/>
+                          +
+                        </el-button>
+                      </el-tooltip>
+
+                      <el-tooltip placement="top" content="减小字号" :hide-after="0">
+                        <el-button @click="handleElementFontsize(object.id,'-')">
+                          <IconFontSize/>
+                          -
+                        </el-button>
+                      </el-tooltip>
+                    </el-button-group>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '2'" label="字体">
+                    <el-select v-model="object.fontFamily" placement="left" style="width: 180px"
+                               @change="handleElementFontFamily(object.id, $event)">
+                      <el-option-group v-for="group in fontOptionGroups" :key="group.label" :label="group.label">
+                        <template v-if="group.type == 0">
+                          <el-option v-for="item in group.options" :key="item" :value="item.value" :label="item.label"
+                                     :style="{fontFamily: item.value}"></el-option>
+                        </template>
+                        <template v-else>
+                          <el-option v-for="item in group.options" :key="item.id" :value="item.fontFamily"
+                                     :label="item.fontName">
+                            <el-image style="height: 18px; width: 90px" :src="item.fontThumbUrl"></el-image>
+                          </el-option>
+                        </template>
+                      </el-option-group>
+                    </el-select>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '3'" label="颜色">
+                    <el-tooltip placement="top" content="文字颜色" :hide-after="0">
+                      <div @click.stop>
+                        <el-popover trigger="click" placement="right" :width="265" @click.stop>
+                          <template #reference>
+                            <el-button class="font-color">
+                              <TextColorButton :color="object.color">
+                                <IconText/>
+                              </TextColorButton>
+                            </el-button>
+                          </template>
+                          <ColorPicker :modelValue="object.color"
+                                       @update:modelValue="(color: string) => updateFontColor(object.id,color)"/>
+                        </el-popover>
+                      </div>
+                    </el-tooltip>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '4'" label="字间距">
+                    <el-button-group class="full-group">
+                      <el-tooltip placement="top" content="减小缩进" :hide-after="0">
+                        <el-button @click="handleElementCharSpacing(object.id,'-')">
+                          <IconIndentLeft/>
+                        </el-button>
+                      </el-tooltip>
+                      <el-tooltip placement="top" content="增大缩进" :hide-after="0">
+                        <el-button @click="handleElementCharSpacing(object.id,'+')">
+                          <IconIndentRight/>
+                        </el-button>
+                      </el-tooltip>
+                    </el-button-group>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '5'" label="尺寸">
+                    <el-tag type="primary">请拖拽右侧元素更改元素尺寸</el-tag>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '6'" label="位置">
+                    <el-tag type="primary">请拖拽右侧元素更改元素位置</el-tag>
+                  </el-form-item>
+                </template>
+
+                <template v-else-if="object.name == 'image'">
+                  <!--             图片-->
+                  <el-form-item v-if="perm == '0'" label="上传图片">
+                    <FileInput class="full-width-btn" @change="(files: FileList) => replaceImage(object.id,files)">
+                      <el-button class="full-btn">
+                        <IconTransform class="btn-icon"/>
+                      </el-button>
+                    </FileInput>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '1'" label="尺寸">
+                    <el-tag type="primary">请拖拽右侧元素更改元素尺寸</el-tag>
+                  </el-form-item>
+                  <el-form-item v-if="perm == '2'" label="位置">
+                    <el-tag type="primary">请拖拽右侧元素更改元素位置</el-tag>
+                  </el-form-item>
+                </template>
+
+              </template>
+            </el-form>
+          </el-collapse-item>
+        </template>
+      </el-collapse>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import useCanvas from "@/views/Canvas/useCanvas";
+import {computed, ref} from "vue";
+import {MaxSize, MinSize, TransparentFill} from "@/configs/background";
+import {WorkSpaceElement} from "@/types/canvas";
+import {storeToRefs} from "pinia";
+import {useFabricStore, useMainStore, useTemplatesStore} from "@/store";
+import {propertiesToInclude, WorkSpaceDrawType} from "@/configs/canvas";
+import {Gradient, Pattern, Image, util, Textbox} from "fabric";
+import useHandleBackground from "@/hooks/useHandleBackground";
+import {mm2px, px2mm} from "@/utils/image";
+import {ElMessage} from "element-plus";
+import useI18n from "@/hooks/useI18n";
+import {ArcText} from "@/extension/object/ArcText";
+import {FontGroupOption} from "@/types/elements";
+
+const {t} = useI18n();
+const mainStore = useMainStore();
+const {sizeMode, unitMode} = storeToRefs(mainStore);
+const {canvasObject, systemFonts, onlineFonts} = storeToRefs(mainStore)
+const [canvas] = useCanvas();
+const templatesStore = useTemplatesStore();
+const {setBackgroudImage} = useHandleBackground();
+const {currentTemplate} = storeToRefs(templatesStore);
+const fabricStore = useFabricStore();
+const {clip, safe, zoom, opacity} = storeToRefs(fabricStore);
+const objects = canvas.getObjects().filter((object) => !["WorkSpaceMaskType", "WorkSpaceClipType", "WorkSpaceSafeType", "WorkSpaceClipType"].includes(object.id));
+
+
+const activeNames = ref([])
+
+const pageSizeWidth = computed(() => {
+  return Math.round(templateWidth.value * 100) / 100
+})
+const pageSizeHeight = computed(() => {
+  return Math.round(templateHeight.value * 100) / 100
+})
+const templateWidth = computed(() => {
+  const workWidth = currentTemplate.value.width / currentTemplate.value.zoom;
+  return unitMode.value === 0 ? px2mm(workWidth) : workWidth;
+});
+
+const templateHeight = computed(() => {
+  const workHeight = currentTemplate.value.height / currentTemplate.value.zoom;
+  return unitMode.value === 0 ? px2mm(workHeight) : workHeight;
+});
+
+const canvasWidth = ref<number>(templateWidth.value);
+const canvasHeight = ref<number>(templateHeight.value);
+
+const background = computed(() => {
+  if (!currentTemplate.value) {
+    return {
+      fillType: 0,
+      fill: TransparentFill,
+      backgroundColor: "#fff",
+    } as WorkSpaceElement;
+  }
+  if (!currentTemplate.value.workSpace) {
+    return {
+      fillType: 0,
+      fill: TransparentFill,
+      backgroundColor: "#fff",
+    } as WorkSpaceElement;
+  }
+  return currentTemplate.value.workSpace;
+});
+
+const fontOptionGroups = ref<FontGroupOption[]>([
+  {
+    type: 0,
+    label: '系统字体',
+    options: systemFonts.value
+  },
+  {
+    type: 1,
+    label: '在线字体',
+    options: onlineFonts.value
+  }
+])
+
+
+// 固定宽高
+const isFixed = ref(false);
+// 设置背景
+const updateBackground = (props: Partial<WorkSpaceElement>) => {
+  const [canvas] = useCanvas();
+  const workSpaceDraw = canvas.getObjects().filter((item) => item.id === WorkSpaceDrawType)[0];
+  if (!workSpaceDraw) return;
+  workSpaceDraw.set({...props});
+  if (props.fill instanceof Pattern) {
+    props.fill = props.fill.toObject() as Pattern
+  }
+  templatesStore.updateWorkSpace({workSpace: {...background.value, ...props}});
+  const workProps = workSpaceDraw.toObject(propertiesToInclude as any[]);
+  templatesStore.updateElement({id: workSpaceDraw.id, props: {...workProps, ...props}});
+  canvas.renderAll();
+};
+
+// 修改上传背景
+const changeBackgroundImage = async (imageURL: string) => {
+  if (background.value.imageSize === "repeat") {
+    const backgroundImage = await util.loadImage(imageURL);
+    const workSpacePattern = new Pattern({
+      source: backgroundImage,
+      repeat: "repeat",
+    });
+    updateBackground({fill: workSpacePattern, imageURL});
+  } else {
+    setBackgroudImage(imageURL);
+    updateBackground({fill: TransparentFill, imageURL});
+  }
+};
+
+// 获取画布尺寸
+const getCanvasSize = () => {
+  let width =
+      unitMode.value === 0 ? mm2px(canvasWidth.value) : canvasWidth.value;
+  let height =
+      unitMode.value === 0 ? mm2px(canvasHeight.value) : canvasHeight.value;
+  width = width * zoom.value;
+  height = height * zoom.value;
+  return {width, height};
+};
+
+// 修改画布宽度
+const changeTemplateWidth = () => {
+  const [canvas] = useCanvas();
+  const workSpaceDraw = canvas
+      .getObjects()
+      .filter((item) => item.id === WorkSpaceDrawType)[0];
+  if (!workSpaceDraw) return;
+  const ratio = currentTemplate.value.height / currentTemplate.value.width;
+  let {width, height} = getCanvasSize();
+  if (width / zoom.value < mm2px(MinSize)) {
+    ElMessage({
+      message: t("style.minimumSizeLimit") + MinSize,
+      type: "warning",
+    });
+    width = mm2px(MinSize) * zoom.value;
+  }
+  if (width / zoom.value > mm2px(MaxSize)) {
+    ElMessage({
+      message: t("style.maximumSizeLimit") + MaxSize,
+      type: "warning",
+    });
+    width = mm2px(MaxSize) * zoom.value;
+  }
+  height = isFixed.value ? width * ratio : height;
+  workSpaceDraw.set({width: width / zoom.value, height: height / zoom.value});
+  templatesStore.setSize(width, height, zoom.value);
+  sizeMode.value = 2;
+  canvas.renderAll();
+  // resetCanvas()
+  // addHistorySnapshot();
+};
+
+// 修改画布高度
+const changeTemplateHeight = () => {
+  const [canvas] = useCanvas();
+  const workSpaceDraw = canvas
+      .getObjects()
+      .filter((item) => item.id === WorkSpaceDrawType)[0];
+  if (!workSpaceDraw) return;
+  const ratio = currentTemplate.value.height / currentTemplate.value.width;
+  let {width, height} = getCanvasSize();
+  if (height / zoom.value < mm2px(MinSize)) {
+    ElMessage({
+      message: t("style.minimumSizeLimit") + MinSize,
+      type: "warning",
+    });
+    height = mm2px(MinSize) * zoom.value;
+  }
+  if (height / zoom.value > mm2px(MaxSize)) {
+    ElMessage({
+      message: t("style.maximumSizeLimit") + MaxSize,
+      type: "warning",
+    });
+    height = mm2px(MaxSize) * zoom.value;
+  }
+  width = isFixed.value ? height / ratio : width;
+  workSpaceDraw.set({width: width / zoom.value, height: height / zoom.value});
+  templatesStore.setSize(width, height, zoom.value);
+  sizeMode.value = 2;
+  canvas.renderAll();
+  // resetCanvas()
+  // addHistorySnapshot();
+};
+
+//查找目标元素
+const findObject = (objId: string) => {
+  return objects.find((item) => item.id === objId);
+}
+
+//修改文字信息
+const eleTextChange = (objId: string, text) => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  if (handleElement.isEditing) {
+    handleElement.setSelectionStyles({text})
+  } else {
+    templatesStore.modifedElement(handleElement, {text})
+  }
+  canvas.renderAll()
+}
+
+// 修改字体大小
+const handleElementFontsize = (objId: string, mode: string) => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  if (handleElement.fontSize <= 6) return
+  const fontSize = mode === '+' ? handleElement.fontSize + 1 : handleElement.fontSize - 1
+  if (handleElement.isEditing) {
+    handleElement.setSelectionStyles({fontSize})
+  } else {
+    templatesStore.modifedElement(handleElement, {fontSize})
+  }
+  canvas.renderAll()
+}
+
+// 修改字体族
+const handleElementFontFamily = (objId: string, fontFamily: string) => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  if (handleElement.isEditing) {
+    handleElement.setSelectionStyles({fontFamily})
+  } else {
+    templatesStore.modifedElement(handleElement, {fontFamily})
+  }
+  canvas.renderAll()
+}
+
+// 修改字体颜色
+const updateFontColor = (objId: string, fill: string) => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  if (handleElement.isEditing) {
+    handleElement.setSelectionStyles({fill})
+  } else {
+    templatesStore.modifedElement(handleElement, {fill, color: fill})
+  }
+}
+
+// 修改缩进
+const handleElementCharSpacing = (objId: string, mode: '+' | '-') => {
+  const handleElement = findObject(objId) as Textbox | ArcText;
+
+  const handleCharSpacing = handleElement.charSpacing
+  const charSpacing = mode === '+' ? handleCharSpacing + 10 : handleCharSpacing - 10
+  templatesStore.modifedElement(handleElement, {charSpacing})
+}
+
+// 替换图片(保持当前的样式)
+const replaceImage = (objId: string, files: FileList) => {
+  const handleElement = findObject(objId) as Image;
+
+  const props = {src: files.fileLink};
+  handleElement.setSrc(files.fileLink); //红就红吧type硬赋值了
+  templatesStore.updateElement({id: handleElement.id, props});
+};
+
+onMounted(() => {
+  console.log(objects)
+  activeNames.value = objects.map(x => x.id)
+})
+
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-tabs__item) {
+  padding: 0;
+}
+
+.layout-search {
+  margin: 0 auto;
+  width: 80%;
+  padding: 20px 10px 10px;
+}
+
+.layout-upload {
+  justify-content: center;
+}
+
+.layout-tabs {
+  width: 90%;
+  margin: 0 auto;
+}
+
+.layout-templates {
+  display: flex;
+  flex-wrap: wrap;
+  padding: 2px;
+
+  .thumbnail {
+    display: flex;
+    width: 124px;
+    margin: 2px;
+  }
+
+  .thumbnail img {
+    outline: 1px solid $borderColor;
+    margin: 0 5px;
+    cursor: pointer;
+
+    &:hover {
+      outline-color: $themeColor;
+    }
+  }
+}
+
+.col-img {
+  height: 100px;
+
+  img {
+    max-height: 100%;
+  }
+}
+
+</style>

+ 2 - 2
src/views/Editor/CanvasLeft/Menu/index.vue

@@ -67,12 +67,12 @@ const leftToggle = () => {
   border-left: 1px solid $borderColor;
   border-right: 1px solid $borderColor;
   border-bottom: 1px solid $borderColor;
-  transition: left 0.5s linear, right 0.5s linear;
+  transition: left 0.3s linear, right 0.3s linear;
 }
 
 .menu-pool {
   width: 300px;
-  height: 100vh;
+  height: 95vh;
   transition: left .3s linear;
   border-bottom: 1px solid $borderColor;
 }

+ 63 - 11
src/views/Editor/CanvasRight/CanvasStylePanel/index.vue

@@ -5,10 +5,27 @@
     </div>
     <div class="mb-10">
       <el-checkbox-group v-model="backgroundPerms" size="small">
-        <el-checkbox v-for="perm in selectablePerms" :key="perm.code" :value="perm.code"
-                     @change="backgroundPermChange">
-          {{ perm.name }}
-        </el-checkbox>
+        <el-row v-for="perm in selectablePerms">
+          <el-col :span="10">
+            <el-checkbox :key="perm.code" :value="perm.code"
+                         @change="backgroundPermChange">
+              {{ perm.name }}
+            </el-checkbox>
+          </el-col>
+          <el-col v-if="perm.code == 0 && backgroundPerms.includes('0')" :span="13">
+            <el-tag
+                v-for="item in userSelectableColors"
+                :key="item"
+                :color="item"
+                closable
+                size="small"
+                effect="dark"
+                @close="onDeleteUserColor(item)"
+                class="colorTag"
+            />
+            <el-color-picker v-model="initColor" size="small" @change="addUserSelectableColor"/>
+          </el-col>
+        </el-row>
       </el-checkbox-group>
     </div>
 
@@ -270,6 +287,30 @@ const selectablePerms = [
   // }
 ]
 
+const initColor = ref("#ffffff")
+
+const userSelectableColors = ref([])
+
+const addUserSelectableColor = (color: string) => {
+  userSelectableColors.value.push(color);
+
+  const [canvas] = useCanvas();
+  canvas.getObjects()
+      .filter((item) => item.id === WorkSpaceDrawType).forEach(workSpaceDraw => {
+    workSpaceDraw.set({userSelectableColors: userSelectableColors.value})
+  })
+}
+
+const onDeleteUserColor = (tag: string) => {
+  userSelectableColors.value.splice(userSelectableColors.value.indexOf(tag), 1)
+
+  const [canvas] = useCanvas();
+  canvas.getObjects()
+      .filter((item) => item.id === WorkSpaceDrawType).forEach(workSpaceDraw => {
+    workSpaceDraw.set({userSelectableColors: userSelectableColors.value})
+  })
+}
+
 // 固定宽高
 const isFixed = ref(false);
 
@@ -433,13 +474,19 @@ onMounted(() => {
   const recentGridCache = localStorage.getItem(RECENT_GRIDS);
   if (recentGridCache) gridColorRecent.value = JSON.parse(recentGridCache);
 
-  const [canvas] = useCanvas();
-  const workSpaceDraw = canvas
-      .getObjects()
-      .filter((item) => item.id === WorkSpaceDrawType)[0];
-  if (!workSpaceDraw) return;
-  if (workSpaceDraw.get('permissionsConfig'))
-    backgroundPerms.value = workSpaceDraw.get('permissionsConfig')
+  setTimeout(() => {
+    const [canvas] = useCanvas();
+    const workSpaceDraw = canvas
+        .getObjects()
+        .filter((item) => item.id === WorkSpaceDrawType)[0];
+    if (!workSpaceDraw) return;
+    if (workSpaceDraw.get('permissionsConfig')) {
+      backgroundPerms.value = workSpaceDraw.get('permissionsConfig')
+    }
+    if (workSpaceDraw.get('userSelectableColors')) {
+      userSelectableColors.value = workSpaceDraw.get('userSelectableColors')
+    }
+  }, 150)
 });
 
 // 保存缓存最近添加的网格
@@ -649,4 +696,9 @@ const changeMaskOpacity = () => {
 :deep(.size-row .el-input-group__prepend) {
   min-width: 24px;
 }
+
+.colorTag {
+  margin-bottom: 13px;
+}
+
 </style>

+ 141 - 13
src/views/Editor/CanvasRight/Components/ElementPermission.vue

@@ -5,24 +5,80 @@
     </div>
     <div class="mb-2">
       <el-checkbox-group v-if="eleType == 'text'" v-model="handleElement.permissionsConfig" size="small">
-        <el-checkbox v-for="perm in textPerms" :key="perm.code" :value="perm.code"
-                     @change="eleChange">
-          {{ perm.name }}
-        </el-checkbox>
+        <el-row v-for="perm in textPerms">
+          <el-col :span="10">
+            <el-checkbox :key="perm.code" :value="perm.code"
+                         @change="eleChange">
+              {{ perm.name }}
+            </el-checkbox>
+          </el-col>
+          <el-col v-if="perm.code == '3' &&  handleElement.permissionsConfig?.includes('3')" :span="13">
+            <el-tag
+                v-for="item in handleElement.userSelectableColors"
+                :key="item"
+                :color="item"
+                closable
+                size="small"
+                effect="dark"
+                @close="onDeleteUserColor(item)"
+                class="colorTag"
+            />
+            <el-color-picker v-model="initColor" size="small" @change="addUserSelectableColor"/>
+          </el-col>
+        </el-row>
       </el-checkbox-group>
+
       <el-checkbox-group v-if="eleType == 'image'" v-model="handleElement.permissionsConfig" size="small">
-        <el-checkbox v-for="perm in imagePerms" :key="perm.code" :value="perm.code"
-                     @change="eleChange">
-          {{ perm.name }}
-        </el-checkbox>
+        <el-row v-for="perm in imagePerms">
+          <el-col :span="10">
+            <el-checkbox :key="perm.code" :value="perm.code"
+                         @change="eleChange">
+              {{ perm.name }}
+            </el-checkbox>
+          </el-col>
+        </el-row>
       </el-checkbox-group>
     </div>
     <el-divider style="margin: 12px 0"/>
 
+    <template v-if="eleType == 'text' && handleElement.permissionsConfig?.includes('0')">
+      <el-row>
+        <el-col :span="7" class="slider-name">预设文案:
+        </el-col>
+        <el-col :span="1"></el-col>
+        <el-col :span="14">
+          <el-select v-model="value" @change="eleTextChange">
+            <el-option v-for="(item, index) in handleElement.userPresetTexts" :key="index" :label="item.text" :value="item.text">
+              <span style="float: left">{{ item.text }}</span>
+              <el-button size="small" style="float: right" @click.stop="delUserPresetText(item)" :icon="Delete"/>
+            </el-option>
+            <template #footer>
+              <el-button v-if="!isAddingText" text bg size="small" @click="isAddingText = true">
+                添加文案
+              </el-button>
+              <template v-else>
+                <el-input
+                    v-model="toAddText"
+                    class="option-input"
+                    placeholder="输入文案"
+                    size="small"
+                />
+                <el-button type="primary" size="small" @click="addUserPresetText">
+                  确认
+                </el-button>
+                <el-button size="small" @click="cancelAddText">取消</el-button>
+              </template>
+            </template>
+          </el-select>
+        </el-col>
+      </el-row>
+      <el-divider style="margin: 12px 0"/>
+    </template>
+
     <el-row>
       <el-col :span="7" class="slider-name">元素名称:
       </el-col>
-      <el-col :span="3"></el-col>
+      <el-col :span="1"></el-col>
       <el-col :span="14">
         <el-input v-model="handleElement.itemName" @change="itemNameChange">
         </el-input>
@@ -35,11 +91,11 @@
 <script lang="ts" setup>
 import {computed, ref, watch} from "vue";
 import {storeToRefs} from "pinia";
-import {useMainStore} from "@/store";
-import * as fabric from "fabric";
+import {useMainStore, useTemplatesStore} from "@/store";
 import useCanvas from "@/views/Canvas/useCanvas";
 import {CanvasElement} from "@/types/canvas";
-import {ElMessage} from "element-plus";
+import {CheckboxValueType, ElMessage} from "element-plus";
+import {Delete} from "@icon-park/vue-next";
 
 const props = defineProps({
   eleType: {
@@ -48,7 +104,6 @@ const props = defineProps({
   },
 });
 
-const eleName = ref("")
 const textPerms = [
   {
     "name": "文字内容",
@@ -95,6 +150,16 @@ const imagePerms = [
   }
 ]
 
+const isAddingText = ref(false)
+const toAddText = ref("")
+const value = ref("")
+
+const initColor = ref("#ffffff")
+
+// const userSelectableColors = ref([])
+const templatesStore = useTemplatesStore();
+
+
 const [canvas] = useCanvas();
 const {canvasObject} = storeToRefs(useMainStore());
 
@@ -105,6 +170,16 @@ const eleChange = () => {
   canvas.renderAll();
 }
 
+//修改文字信息
+const eleTextChange = (text) => {
+  if (handleElement.value.isEditing) {
+    handleElement.value.setSelectionStyles({text})
+  } else {
+    templatesStore.modifedElement(handleElement.value, {text})
+  }
+  canvas.renderAll()
+}
+
 const itemNameChange = () => {
   if (!handleElement.value) return;
   const objects = canvas.getObjects().filter((object) => !["WorkSpaceMaskType", "WorkSpaceClipType", "WorkSpaceSafeType", "WorkSpaceClipType"].includes(object.id));
@@ -114,6 +189,50 @@ const itemNameChange = () => {
   }
 }
 
+const addUserPresetText = () => {
+  if (!handleElement.value) return;
+  if (!toAddText.value) return;
+
+  if (!handleElement.value.userPresetTexts) handleElement.value.userPresetTexts = []
+  handleElement.value.userPresetTexts.push({
+    text: toAddText.value
+  });
+  cancelAddText();
+  canvas.renderAll();
+}
+
+const delUserPresetText = (item) => {
+  if (!handleElement.value) return;
+  handleElement.value.userPresetTexts.splice(handleElement.value.userPresetTexts.indexOf(item), 1)
+}
+
+const cancelAddText = () => {
+  toAddText.value = ''
+  isAddingText.value = false
+}
+
+const addUserSelectableColor = (color: string) => {
+
+  if (!handleElement.value) return;
+
+  handleElement.value.userSelectableColors.push(color);
+  // handleElement.value.set({userSelectableColors: userSelectableColors.value})
+  canvas.renderAll();
+}
+
+const onDeleteUserColor = (tag: string) => {
+  if (!handleElement.value) return;
+  handleElement.value.userSelectableColors.splice(handleElement.value.userSelectableColors.indexOf(tag), 1)
+  // handleElement.value.set({userSelectableColors: userSelectableColors.value})
+  canvas.renderAll();
+}
+
+onMounted(() => {
+  if (!handleElement.value.userSelectableColors)
+    handleElement.value.userSelectableColors = []
+  // userSelectableColors.value = handleElement.value.userSelectableColors;
+})
+
 </script>
 
 <style lang="scss" scoped>
@@ -142,4 +261,13 @@ const itemNameChange = () => {
   align-items: center;
   justify-content: center;
 }
+
+.colorTag {
+  margin-bottom: 13px;
+}
+
+.option-input {
+  width: 100%;
+  margin-bottom: 8px;
+}
 </style>