Browse Source

新增页面、卡片、右键操作

qianduan 6 months ago
parent
commit
4481cd15c8

+ 83 - 0
package-lock.json

@@ -11,6 +11,8 @@
         "@element-plus/icons-vue": "^2.3.1",
         "color": "^4.2.3",
         "element-plus": "^2.7.5",
+        "pinia": "^2.2.0",
+        "pinia-plugin-persistedstate": "^3.2.1",
         "vue": "^3.4.21",
         "vue-drag-resize": "^1.5.4",
         "vue-router": "^4.3.3",
@@ -1377,6 +1379,64 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/pinia": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.0.tgz",
+      "integrity": "sha512-iPrIh26GMqfpUlMOGyxuDowGmYousTecbTHFwT0xZ1zJvh23oQ+Cj99ZoPQA1TnUPhU6AuRPv6/drkTCJ0VHQA==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.3.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia-plugin-persistedstate": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz",
+      "integrity": "sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==",
+      "peerDependencies": {
+        "pinia": "^2.0.0"
+      }
+    },
+    "node_modules/pinia/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/postcss": {
       "version": "8.4.38",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",
@@ -2598,6 +2658,29 @@
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true
     },
+    "pinia": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.0.tgz",
+      "integrity": "sha512-iPrIh26GMqfpUlMOGyxuDowGmYousTecbTHFwT0xZ1zJvh23oQ+Cj99ZoPQA1TnUPhU6AuRPv6/drkTCJ0VHQA==",
+      "requires": {
+        "@vue/devtools-api": "^6.6.3",
+        "vue-demi": "^0.14.8"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.14.10",
+          "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+          "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+          "requires": {}
+        }
+      }
+    },
+    "pinia-plugin-persistedstate": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz",
+      "integrity": "sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==",
+      "requires": {}
+    },
     "postcss": {
       "version": "8.4.38",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.38.tgz",

+ 2 - 0
package.json

@@ -12,6 +12,8 @@
     "@element-plus/icons-vue": "^2.3.1",
     "color": "^4.2.3",
     "element-plus": "^2.7.5",
+    "pinia": "^2.2.0",
+    "pinia-plugin-persistedstate": "^3.2.1",
     "vue": "^3.4.21",
     "vue-drag-resize": "^1.5.4",
     "vue-router": "^4.3.3",

+ 118 - 26
src/assets/icons/iconfont/demo_index.html

@@ -55,6 +55,36 @@
           <ul class="icon_lists dib-box">
           
             <li class="dib">
+              <span class="icon iconfont">&#xe600;</span>
+                <div class="name">首页</div>
+                <div class="code-name">&amp;#xe600;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe669;</span>
+                <div class="name">组件</div>
+                <div class="code-name">&amp;#xe669;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe623;</span>
+                <div class="name">锁</div>
+                <div class="code-name">&amp;#xe623;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe63b;</span>
+                <div class="name">组件-组件数</div>
+                <div class="code-name">&amp;#xe63b;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6b7;</span>
+                <div class="name">卡片</div>
+                <div class="code-name">&amp;#xe6b7;</div>
+              </li>
+          
+            <li class="dib">
               <span class="icon iconfont">&#xe689;</span>
                 <div class="name">09左对齐</div>
                 <div class="code-name">&amp;#xe689;</div>
@@ -180,12 +210,6 @@
                 <div class="code-name">&amp;#xe6e1;</div>
               </li>
           
-            <li class="dib">
-              <span class="icon iconfont">&#xe616;</span>
-                <div class="name">锁定-F</div>
-                <div class="code-name">&amp;#xe616;</div>
-              </li>
-          
           </ul>
           <div class="article markdown">
           <h2 id="unicode-">Unicode 引用</h2>
@@ -204,9 +228,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1720751173867') format('woff2'),
-       url('iconfont.woff?t=1720751173867') format('woff'),
-       url('iconfont.ttf?t=1720751173867') format('truetype');
+  src: url('iconfont.woff2?t=1722304783786') format('woff2'),
+       url('iconfont.woff?t=1722304783786') format('woff'),
+       url('iconfont.ttf?t=1722304783786') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -233,6 +257,51 @@
         <ul class="icon_lists dib-box">
           
           <li class="dib">
+            <span class="icon iconfont icon-shouye"></span>
+            <div class="name">
+              首页
+            </div>
+            <div class="code-name">.icon-shouye
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-zujian"></span>
+            <div class="name">
+              组件
+            </div>
+            <div class="code-name">.icon-zujian
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-suo"></span>
+            <div class="name">
+              锁
+            </div>
+            <div class="code-name">.icon-suo
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-page"></span>
+            <div class="name">
+              组件-组件数
+            </div>
+            <div class="code-name">.icon-page
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-kapian"></span>
+            <div class="name">
+              卡片
+            </div>
+            <div class="code-name">.icon-kapian
+            </div>
+          </li>
+          
+          <li class="dib">
             <span class="icon iconfont icon-textLeft"></span>
             <div class="name">
               09左对齐
@@ -421,15 +490,6 @@
             </div>
           </li>
           
-          <li class="dib">
-            <span class="icon iconfont icon-suoding"></span>
-            <div class="name">
-              锁定-F
-            </div>
-            <div class="code-name">.icon-suoding
-            </div>
-          </li>
-          
         </ul>
         <div class="article markdown">
         <h2 id="font-class-">font-class 引用</h2>
@@ -459,6 +519,46 @@
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-shouye"></use>
+                </svg>
+                <div class="name">首页</div>
+                <div class="code-name">#icon-shouye</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-zujian"></use>
+                </svg>
+                <div class="name">组件</div>
+                <div class="code-name">#icon-zujian</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-suo"></use>
+                </svg>
+                <div class="name">锁</div>
+                <div class="code-name">#icon-suo</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-page"></use>
+                </svg>
+                <div class="name">组件-组件数</div>
+                <div class="code-name">#icon-page</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-kapian"></use>
+                </svg>
+                <div class="name">卡片</div>
+                <div class="code-name">#icon-kapian</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-textLeft"></use>
                 </svg>
                 <div class="name">09左对齐</div>
@@ -625,14 +725,6 @@
                 <div class="code-name">#icon-chexiao1</div>
             </li>
           
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#icon-suoding"></use>
-                </svg>
-                <div class="name">锁定-F</div>
-                <div class="code-name">#icon-suoding</div>
-            </li>
-          
           </ul>
           <div class="article markdown">
           <h2 id="symbol-">Symbol 引用</h2>

+ 23 - 7
src/assets/icons/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 4583886 */
-  src: url('iconfont.woff2?t=1720751173867') format('woff2'),
-       url('iconfont.woff?t=1720751173867') format('woff'),
-       url('iconfont.ttf?t=1720751173867') format('truetype');
+  src: url('iconfont.woff2?t=1722304783786') format('woff2'),
+       url('iconfont.woff?t=1722304783786') format('woff'),
+       url('iconfont.ttf?t=1722304783786') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,26 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-shouye:before {
+  content: "\e600";
+}
+
+.icon-zujian:before {
+  content: "\e669";
+}
+
+.icon-suo:before {
+  content: "\e623";
+}
+
+.icon-page:before {
+  content: "\e63b";
+}
+
+.icon-kapian:before {
+  content: "\e6b7";
+}
+
 .icon-textLeft:before {
   content: "\e689";
 }
@@ -97,7 +117,3 @@
   content: "\e6e1";
 }
 
-.icon-suoding:before {
-  content: "\e616";
-}
-

File diff suppressed because it is too large
+ 0 - 0
src/assets/icons/iconfont/iconfont.js


+ 35 - 7
src/assets/icons/iconfont/iconfont.json

@@ -6,6 +6,41 @@
   "description": "",
   "glyphs": [
     {
+      "icon_id": "39033012",
+      "name": "首页",
+      "font_class": "shouye",
+      "unicode": "e600",
+      "unicode_decimal": 58880
+    },
+    {
+      "icon_id": "3591708",
+      "name": "组件",
+      "font_class": "zujian",
+      "unicode": "e669",
+      "unicode_decimal": 58985
+    },
+    {
+      "icon_id": "14445364",
+      "name": "锁",
+      "font_class": "suo",
+      "unicode": "e623",
+      "unicode_decimal": 58915
+    },
+    {
+      "icon_id": "32700671",
+      "name": "组件-组件数",
+      "font_class": "page",
+      "unicode": "e63b",
+      "unicode_decimal": 58939
+    },
+    {
+      "icon_id": "35995924",
+      "name": "卡片",
+      "font_class": "kapian",
+      "unicode": "e6b7",
+      "unicode_decimal": 59063
+    },
+    {
       "icon_id": "19296202",
       "name": "09左对齐",
       "font_class": "textLeft",
@@ -151,13 +186,6 @@
       "font_class": "chexiao1",
       "unicode": "e6e1",
       "unicode_decimal": 59105
-    },
-    {
-      "icon_id": "37507632",
-      "name": "锁定-F",
-      "font_class": "suoding",
-      "unicode": "e616",
-      "unicode_decimal": 58902
     }
   ]
 }

BIN
src/assets/icons/iconfont/iconfont.ttf


BIN
src/assets/icons/iconfont/iconfont.woff


BIN
src/assets/icons/iconfont/iconfont.woff2


+ 1 - 1
src/components/drag/resizable.vue

@@ -7,7 +7,7 @@
         @deactivated="deactivated" @click.native="clickHandler">
         <component :is="getWidget(currentItem.type)" :config="currentItem"></component>
         <div class="card_lock center_in" v-if="active && lock">
-            <span class="iconfont lock_icon_img icon-suoding"></span>
+            <span class="iconfont lock_icon_img icon-suo"></span>
         </div>
     </Vue3DraggableResizable>
 </template>

+ 8 - 0
src/main.ts

@@ -8,8 +8,16 @@ import './assets/icons/iconfont/iconfont.css'
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import zhCn from "element-plus/es/locale/lang/zh-cn";//国际化
 
+import { createPinia } from 'pinia'
+// 导入插件
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
 const app = createApp(App);
 
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
+app.use(pinia)
+
 app.use(ElementPlus, { locale: zhCn }).use(router).mount('#app')
 
 //全局注册图标组件

+ 33 - 0
src/stores/index.ts

@@ -0,0 +1,33 @@
+import { defineStore } from 'pinia'
+
+export const useUserStore = defineStore('user', {
+    state: () => ({
+        id: 134, // 年龄
+        name: 'cesc', // 用户名
+        viewList: [{
+            id: 1,
+            flag: true,
+            visible: false,
+            inputFlag: false,
+            name: '卡片',
+            icon: 'icon-kapian',
+            viewData: [],
+        }, {
+            id: 2,
+            flag: false,
+            visible: false,
+            inputFlag: false,
+            name: '首页',
+            icon: 'icon-shouye',
+            viewData: [],
+        }] // 公斤
+    }),
+    getters: {},
+    actions: {},
+    // 持久化插件配置
+    persist: {
+        key: 'store-user', // 本地存储key名称
+        // storage: sessionStorage // 不设置默认存储localStorage
+        paths: ['id', 'name', 'viewList'] // 指定持久化的值
+    }
+})

+ 34 - 12
src/views/HomeView.vue

@@ -54,20 +54,17 @@
         <div class="left_tabs_switch">
           <div class="card_tabs_view center_in" :class="tabsType == 'module' ? 'tabs_active' : ''"
             @click="getTabs('module')">
-            <el-icon size="30" :color="tabsType == 'module' ? '#409EFF' : '#606266'">
-              <Platform />
-            </el-icon>
+            <span class="iconfont tabs_icon_img icon-zujian" :color="tabsType == 'module' ? '#409EFF' : '#606266'"></span>
             <span>组件</span>
           </div>
           <div class="card_tabs_view center_in" :class="tabsType == 'page' ? 'tabs_active' : ''" @click="getTabs('page')">
-            <el-icon size="30" :color="tabsType == 'page' ? '#409EFF' : '#606266'">
-              <Platform />
-            </el-icon>
+            <span class="iconfont tabs_icon_img icon-page" :color="tabsType == 'page' ? '#409EFF' : '#606266'"></span>
             <span>页面</span>
           </div>
         </div>
-        <div class="left_paneLeft">
-          <div class="pane_tabs">
+        <div class="left_paneLeft"
+          :style="{ boxShadow: (tabsType == 'module' ? 'rgba(0, 0, 0, 0.05) 3px 0px 5px' : '') }">
+          <div class="pane_tabs" v-if="tabsType == 'module'">
             <el-tabs v-model="activeName" stretch class="demo-tabs" @tab-click="handleClick">
               <el-tab-pane label="常用推荐" name="stock">
                 <div>214</div>
@@ -77,8 +74,11 @@
               </el-tab-pane>
             </el-tabs>
           </div>
+          <div v-else>
+            <additionPage @switchPage="switchPage"></additionPage>
+          </div>
         </div>
-        <div class="left_paneRight">
+        <div class="left_paneRight" v-if="tabsType == 'module'">
           <el-scrollbar style="width: 100%; height: 100%" class="component-list scrollbar-wrapper">
             <draggable class="component_layout" :list="componentLibrary" itemKey="id" animation="300" @start="onStart"
               @end="onEnd" :clone="cloneComponent" :group="{ name: 'itxst', pull: 'clone', put: false }"
@@ -138,7 +138,7 @@
               :class="[activate1 ? 'active_icon' : 'icon_item_gray', activateLock ? 'active_lock' : '']"
               @click="dragLock">
               <el-tooltip class="box-item" :content="activateLock ? '取消锁定' : '锁定'" placement="left" effect="light">
-                <span class="iconfont icon_alignment icon-suoding"></span>
+                <span class="iconfont icon_alignment icon-suo"></span>
               </el-tooltip>
             </div>
             <div class="line_divider"></div>
@@ -176,10 +176,17 @@ import getName from '../components/widgets/getWidget';
 import VueDrag from '../components/drag/resizable.vue'
 import iconLibrary from '../components/iconLibrary.vue'
 import maskedbox from './maskedbox.vue'
+import additionPage from './additionPage.vue'
 import { baseComponent, attributeValue, layout } from '../module/index';
 import { ref, computed } from "vue";
 import { useRouter } from 'vue-router'
 import { ElMessageBox } from 'element-plus'
+// 存储
+import { useUserStore } from '../stores/index'
+import { storeToRefs } from 'pinia';
+const storeUser = useUserStore()
+const { viewList } = storeToRefs(storeUser)
+
 //module、page、切换组件页面
 const tabsType = ref('module')
 // 图标弹窗
@@ -195,6 +202,18 @@ const componentLibrary: any = ref([])
 componentLibrary.value = JSON.parse(JSON.stringify(extraImgs.value))
 // 视图库
 const extraImgs1: any = ref([]);
+viewList.value.forEach(item => {
+  if (item.flag) {
+    extraImgs1.value = item.viewData
+  }
+})
+function switchPage() {
+  viewList.value.forEach(item => {
+    if (item.flag) {
+      extraImgs1.value = item.viewData
+    }
+  })
+}
 // 撤销操作的栈
 const undoStack: any = ref([]);
 // 恢复操作的栈
@@ -670,12 +689,16 @@ function getIconSelection(params: any) {
   cursor: pointer;
 
   span {
-    font-size: 14px;
+    font-size: 13px;
     margin-top: 5px;
     color: #606266;
   }
 }
 
+.tabs_icon_img {
+  font-size: 20px !important;
+}
+
 .tabs_active {
   background-color: #fff;
 
@@ -692,7 +715,6 @@ function getIconSelection(params: any) {
   height: 100%;
   width: 180px;
   background-color: rgb(255, 255, 255);
-  box-shadow: rgba(0, 0, 0, 0.05) 3px 0px 5px;
   padding: 0px 0px 18px 0px;
   overflow-y: overlay;
 }

+ 308 - 0
src/views/additionPage.vue

@@ -0,0 +1,308 @@
+<template>
+    <div style="padding: 22px 18px;">
+        <div class="space_between_in card_add_page_title" @click="addPage">
+            <span>页面</span>
+            <el-icon color="#337ecc">
+                <Plus />
+            </el-icon>
+        </div>
+        <div style="width: 100%;" v-for="(item, index) in listTabs" :key="index">
+            <el-popover :ref="setPopoverRef" :visible="item.visible" placement="right" :width="160"
+                @visible-change="handleVisibleChange">
+                <div class="card_right_click">
+                    <div class="title_right" @click.stop="editTitle(item)">
+                        <div class="icon_right_click">
+                            <el-icon>
+                                <EditPen />
+                            </el-icon>
+                        </div>
+                        <span>编辑标题</span>
+                    </div>
+                    <div class="title_right" @click.stop="deletePage(item)" v-if="item.name != '首页' && item.name != '卡片'">
+                        <div class="icon_right_click">
+                            <el-icon>
+                                <Delete />
+                            </el-icon>
+                        </div>
+                        <span>删除</span>
+                    </div>
+                </div>
+                <template #reference>
+                    <div :class="[item.flag ? 'active_card_page' : '', !item.inputFlag ? 'item_tabs_page' : 'edit_card_page']"
+                        @click="getTabs(item)" @contextmenu.stop="onContextMenu($event, item)">
+                        <div style="display: flex;align-items: center;" v-if="!item.inputFlag"
+                            @dblclick="handleDoubleClick(item)">
+                            <span class="iconfont page_icon_img" :style="{ color: (item.flag ? '#409EFF' : '') }"
+                                :class="item.icon" v-if="item.icon"></span>
+                            <span>{{ item.name }}</span>
+                        </div>
+                        <input v-if="editIndex === index && item.inputFlag" :ref="setInputRef" class="title_input_edit"
+                            v-model="item.name" type="text" @input="handleInput" @blur="handleBlur">
+                    </div>
+                </template>
+            </el-popover>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ElPopover, ElMessageBox } from 'element-plus';
+import { ref, onMounted, onBeforeUnmount } from "vue";
+
+import { useUserStore } from '../stores/index'
+import { storeToRefs } from 'pinia';
+const storeUser = useUserStore()
+const { viewList } = storeToRefs(storeUser)
+
+const emit = defineEmits(['switchPage']);
+const listTabs = ref(viewList)
+function getTabs(params: any) {
+    listTabs.value.forEach(item => {
+        if (params.id == item.id) {
+            item.flag = true
+        } else {
+            item.flag = false
+        }
+    })
+    emit('switchPage');
+}
+// 右键弹窗编辑、删除
+const onContextMenu = (event, value) => {
+    listTabs.value.forEach(item => {
+        if (value.id == item.id) {
+            item.visible = true
+        } else {
+            item.visible = false
+        }
+    })
+    event.preventDefault();
+}
+const editIndex: any = ref(null);
+const inputRef: any = ref(null);
+const setInputRef = (el) => {
+    inputRef.value = el;
+};
+// 双击输入框获取焦点
+function handleDoubleClick(params: any) {
+    listTabs.value.forEach((item, index) => {
+        if (params.id == item.id) {
+            item.inputFlag = true
+            editIndex.value = index;
+            setTimeout(() => {
+                if (inputRef.value) {
+                    inputRef.value.focus();
+                }
+            });
+        } else {
+            item.inputFlag = false
+        }
+    })
+}
+// 右键编辑标题
+function editTitle(params: any) {
+    listTabs.value.forEach((item, index) => {
+        if (params.id == item.id) {
+            item.visible = false
+            item.inputFlag = true
+            editIndex.value = index;
+            setTimeout(() => {
+                if (inputRef.value) {
+                    inputRef.value.focus();
+                }
+            });
+        } else {
+            item.inputFlag = false
+        }
+    })
+}
+// 右键删除页面
+function deletePage(params: any) {
+    const index = listTabs.value.findIndex(item => item.id === params.id);
+    if (index !== -1) {
+        listTabs.value[index].visible = false;
+        listTabs.value[index].inputFlag = false;
+    }
+    ElMessageBox.confirm(
+        '删除后不可恢复,请谨慎操作',
+        '确定要删除该页面吗?',
+        {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+        }
+    ).then(() => {
+        listTabs.value = listTabs.value.filter(item => item.id !== params.id);
+    }).catch((err) => {
+        console.log(err);
+    })
+}
+// 修改编辑输入框内容变化
+function handleInput(params: any) {
+    console.log(params, 3);
+
+}
+// 修改编辑失去焦点
+function handleBlur() {
+    listTabs.value.forEach(item => {
+        item.inputFlag = false
+    })
+}
+// 添加页面
+function addPage() {
+    listTabs.value.forEach(item => {
+        item.flag = false
+    })
+    let arrList: any = {
+        id: listTabs.value.length + 1,
+        flag: true,
+        visible: false,
+        inputFlag: false,
+        name: '未命名 ' + (listTabs.value.length + 1),
+    }
+    listTabs.value.push(arrList)
+}
+
+
+const popoverRefs = new Set<any>();
+const setPopoverRef = (el: any) => {
+    popoverRefs.add(el);
+};
+const handleVisibleChange = (visible: boolean) => {
+    if (!visible) {
+        popoverRefs.forEach((popover) => {
+            popover.visible = false;
+        });
+    }
+};
+onMounted(() => {
+    document.addEventListener('click', handleClickOutside);
+});
+onBeforeUnmount(() => {
+    document.removeEventListener('click', handleClickOutside);
+});
+const handleClickOutside = (event: MouseEvent) => {
+    for (const item of listTabs.value) {
+        if (item.visible && event.target instanceof Node) {
+            const target = event.target as Element;
+            let node = target;
+            while (node) {
+                if (node === document.querySelector(`[data-item-id="${item.id}"]`)) {
+                    return;
+                }
+                node = node.parentNode as Element;
+            }
+            item.visible = false;
+            break;
+        }
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.card_add_page_title {
+    cursor: pointer;
+    padding: 0px 5px;
+    margin-bottom: 22px;
+
+    span {
+        font-size: 14px;
+        color: rgb(51, 51, 51);
+    }
+}
+
+.card_right_click {
+    display: flex;
+    flex-direction: column;
+}
+
+.title_right {
+    display: flex;
+    flex-direction: row;
+    -webkit-box-align: center;
+    align-items: center;
+    cursor: pointer;
+    position: relative;
+    padding-left: 23px;
+    height: 32px;
+    transition: 0.2s;
+    overflow: hidden;
+    font-size: 12px;
+    border-radius: 4px;
+}
+
+.title_right:hover {
+    background-color: rgba(15, 115, 230, 0.08);
+
+    .icon_right_click {
+        color: #409EFF;
+    }
+
+    span {
+        color: #409EFF;
+    }
+}
+
+.icon_right_click {
+    position: absolute;
+    left: 5px;
+}
+
+.item_tabs_page {
+    display: flex;
+    flex-direction: row;
+    -webkit-box-align: center;
+    align-items: center;
+    position: relative;
+    cursor: pointer;
+    padding-left: 23px;
+    height: 32px;
+    overflow: hidden;
+    font-size: 12px;
+    border-radius: 5px;
+}
+
+.item_tabs_page:hover {
+    color: #409EFF;
+    background: rgba(15, 115, 230, 0.08);
+
+    span {
+        color: #409EFF;
+    }
+}
+
+.active_card_page {
+    color: #409EFF;
+    background: rgba(15, 115, 230, 0.08);
+}
+
+.edit_card_page {
+    width: 100%;
+}
+
+
+.page_icon_img {
+    position: absolute;
+    left: 5px;
+    font-size: 13px;
+    color: #606266;
+}
+
+.title_input_edit {
+    width: 100%;
+    border: 1px solid #409EFF;
+    height: 32px;
+    padding: 0;
+    /* 添加自定义样式 */
+    background-color: #fff;
+    font-size: 14px;
+    padding: 8px;
+    border-radius: 3px;
+}
+
+/* 当input获得焦点时的样式 */
+.title_input_edit:focus {
+    outline: none;
+    border-color: #409EFF;
+    box-shadow: 0 0 0 2px #c6e2ff;
+}
+</style>

Some files were not shown because too many files changed in this diff