tianyun 3 miesięcy temu
rodzic
commit
b535b0d567
1 zmienionych plików z 320 dodań i 248 usunięć
  1. 320 248
      simple-demo/nav.html

+ 320 - 248
simple-demo/nav.html

@@ -3,10 +3,10 @@
 <head>
     <meta charset="UTF-8">
     <title>导航</title>
-    <!--解决资源跨域-->
+    <!-- 解决资源跨域 -->
     <meta name="referrer" content="no-referrer"/>
 
-    <!--解决缓存问题-->
+    <!-- 解决缓存问题 -->
     <meta http-equiv="pragma" content="no-cache"/>
     <meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
     <meta http-equiv="expires" content="0"/>
@@ -18,15 +18,42 @@
     <script src="elementUI/index.js"></script>
     <link rel="stylesheet" href="elementUI/index.css"/>
     <style>
+        /* 新增样式 */
+        .group-title {
+            /*padding: 20px 0 10px;*/
+            font-size: 20px;
+            font-weight: bold;
+            color: #409EFF;
+            border-bottom: 1px solid #c7daf5;
+            margin: 20px 0;
+            display: flex;
+            align-items: center;
+        }
+
+        .group-title i {
+            margin-right: 8px;
+        }
+
+        .tab-sort-bar {
+            /*background: #f5f7fa;*/
+            /*padding: 10px;*/
+            /*margin-bottom: 20px;*/
+            border-radius: 4px;
+            /*box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);*/
+        /*    固定在顶部*/
+            position: fixed;
+            top: 5px;
+        }
+
         .tab-item {
             min-width: 100px;
             max-width: 200px;
             /*    文字居中*/
             text-align: center;
             /*文字垂直居中*/
-            line-height: 2em;
-            background: #c5e7cd;
-            margin: 0 20px;
+            /*line-height: 2em;*/
+            background: #ecf5f0;
+            margin: 0 10px;
             /*    鼠标变成手*/
             cursor: pointer;
         }
@@ -48,7 +75,6 @@
 
         .content-item {
             margin: 10px;
-            /*padding: 15px;*/
             border: 1px solid #ebeef5;
             border-radius: 4px;
             transition: all .3s;
@@ -56,6 +82,7 @@
             display: inline-block;
             width: 380px;
             box-sizing: border-box;
+            margin-bottom: 15px;
         }
 
         .content-item:hover {
@@ -96,18 +123,6 @@
             text-overflow: ellipsis;
         }
 
-        .copy-btn {
-            color: #409EFF;
-        }
-
-        .copy-btn:hover {
-            color: #206BC4;
-        }
-
-        .content-item {
-            margin-bottom: 15px;
-        }
-
         @media (max-width: 768px) {
             .content-item {
                 width: calc(50% - 20px);
@@ -150,10 +165,6 @@
             padding: 20px;
         }
 
-        .dynamic-delete-button {
-            cursor: pointer;
-            color: #F56C6C;
-        }
     </style>
     <script src="js/Sortable.min.js"></script>
     <script src="js/vuedraggable.umd.min.js"></script>
@@ -161,137 +172,134 @@
 </head>
 <body>
 <div id="app" class="tab-container">
-    <el-tabs v-model="activeTab" @tab-click="handleTabClick" type="card" ref="navTabs">
-        <el-tab-pane
-                v-for="(content, tabName) in myObj"
-                :key="tabName"
-                :label="tabName"
-                :name="tabName">
-            <div class="tab-content">
-                <draggable
-                        animation="300"
-                        :disabled="readOnly"
-                        v-model="myObj[tabName]"
-                        tag="div"
-                        @change="onDragChange(tabName)"
-                        handle=".drag-handle"
-                        class="content-list">
-                    <div
-                            v-for="(item, index) in myObj[tabName]"
-                            :key="index"
-                            class="content-item">
-                        <el-card shadow="hover" class="item-card">
-                            <div slot="header" class="item-content">
-                                <el-image
-                                        :src="item.icon"
-                                        style="width: 20px; height: 20px; margin-right: 8px;"
-                                        fit="contain"
-                                ></el-image>
-                                <span>{{ item.title }}</span>
-                                <div class="action-buttons">
-                                    <div v-if="!readOnly">
-                                        <el-button type="text" class="drag-handle">
-                                            <i class="el-icon-rank"></i>
-                                        </el-button>
-                                        <el-button
-                                                type="text"
-                                                @click.stop="openEditDialog(tabName, index)">
-                                            <i class="el-icon-edit"></i>
-                                        </el-button>
-                                        <el-button
-                                                type="text"
-                                                @click.stop="deleteContentItem(tabName, index)">
-                                            <i class="el-icon-delete"></i>
-                                        </el-button>
-                                    </div>
-
+    <!-- 标签排序栏 -->
+        <div class="tab-sort-bar" style="display: flex">
+            <div style="">拖拽排序:</div>
+            <draggable
+                    :disabled="readOnly"
+                    v-model="groups"
+                    @change="onTabOrderChange"
+                    handle=".drag-handle"
+                    style="display: flex; flex-wrap: wrap;"
+                    animation="300">
+                <div
+                        v-for="item in groups"
+                        :key="item"
+                        class="tab-item drag-handle"
+                        @click="scrollToGroup(item.name)">
+                    <span>{{ item.name }}</span>
+                </div>
+            </draggable>
+        </div>
+    <!-- 分组内容展示 -->
+    <div v-for="(group,group_index) in groups"
+         :key="group.name"
+         :id="'group-'+group.name">
+        <div class="group-title">
+<!--            <i class="el-icon-folder-opened"></i>-->
+            <div style="margin-right: 20px;">{{ group.name }}</div>
+
+            <!-- 新增按钮 -->
+            <el-button type="text" @click.stop="addTabAfter(group.name)" v-if="!readOnly" >
+                <i class="el-icon-plus"></i>
+            </el-button>
+            <el-button type="text" @click.stop="editCurrentTabName(group.name)" v-if="!readOnly" >
+                <i class="el-icon-edit"></i>
+            </el-button>
+            <el-button type="text" @click.stop="deleteTab(group.name)" :disabled="Object.keys(groups).length <= 1" v-if="!readOnly" >
+                <i class="el-icon-delete"></i>
+            </el-button>
+            <div style="margin: 0 20px;color: #ccc;" v-if="!readOnly" >|</div>
+            <el-button type="text" @click.stop="addContentItemAfter(group.name, group.datas.length-1)" v-if="!readOnly">
+                新增卡片
+            </el-button>
+        </div>
+
+        <draggable
+                group="site"
+                animation="300"
+                :disabled="readOnly"
+                v-model="group.datas"
+                tag="div"
+                @change="onDragChange(group.name)"
+                handle=".drag-handle"
+                class="content-list">
+            <transition-group>
+                <div v-for="(item, index) in group.datas"
+                     :key="index"
+                     class="content-item">
+                    <el-card shadow="hover" class="item-card">
+                        <div slot="header" class="item-content drag-handle">
+                            <el-image
+                                    :src="item.icon"
+                                    style="width: 20px; height: 20px; margin-right: 8px;"
+                                    fit="contain"
+                            ></el-image>
+                            <span>{{ item.title }}</span>
+                            <div class="action-buttons">
+                                <div v-if="!readOnly">
+                                    <el-button type="text" class="">
+                                        <i class="el-icon-rank"></i>
+                                    </el-button>
+                                    <el-button
+                                            type="text"
+                                            @click.stop="openEditDialog(group,item)">
+                                        <i class="el-icon-edit"></i>
+                                    </el-button>
+                                    <el-button
+                                            type="text"
+                                            @click.stop="deleteContentItem(group.name, index)">
+                                        <i class="el-icon-delete"></i>
+                                    </el-button>
+                                    <el-button
+                                            type="text"
+                                            @click.stop="addContentItemAfter(group.name, index)">
+                                        <i class="el-icon-plus"></i>
+                                    </el-button>
                                 </div>
+
                             </div>
-                            <div class="item-content">
-                                <div style="width: 100%">
-                                    <div class="item-description">{{ item.description || item.title }}</div>
-                                    <el-tooltip>
-                                        <div class="link-section">
-                                            <el-link
-                                                    type="primary"
-                                                    :href="item.mainUrl"
-                                                    target="_blank">{{ item.mainUrl }}
-                                            </el-link>
-                                        </div>
-                                        <div slot="content" v-if="item.extraUrls && item.extraUrls.length" class="extra-links">
-                                            <el-tag
-                                                    v-for="(url, idx) in item.extraUrls"
-                                                    :key="idx"
-                                                    type="info"
-                                                    class="extra-link-tag">
-                                                <el-link :href="url" target="_blank">{{ url }}</el-link>
-                                            </el-tag>
-                                        </div>
-                                        <div slot="content" v-else>
-                                            没有更多链接
-                                        </div>
-                                    </el-tooltip>
-
-
-                                    <div v-if="item.screenshot" class="screenshot-section">
-                                        <el-image
-                                                :src="item.screenshot"
-                                                style="max-width: 100%; height: 220px;"
-                                                :preview-src-list="[item.screenshot]"
-                                                fit="cover"></el-image>
+                        </div>
+                        <div class="item-content">
+                            <div style="width: 100%">
+                                <div class="item-description">{{ item.description || item.title }}</div>
+                                <el-tooltip>
+                                    <div class="link-section">
+                                        <el-link
+                                                type="primary"
+                                                :href="item.mainUrl"
+                                                target="_blank">{{ item.mainUrl }}
+                                        </el-link>
+                                    </div>
+                                    <div slot="content" v-if="item.extraUrls && item.extraUrls.length" class="extra-links">
+                                        <el-tag
+                                                v-for="(url, idx) in item.extraUrls"
+                                                :key="idx"
+                                                type="info"
+                                                class="extra-link-tag">
+                                            <el-link :href="url" target="_blank">{{ url }}</el-link>
+                                        </el-tag>
+                                    </div>
+                                    <div slot="content" v-else>
+                                        没有更多链接
                                     </div>
+                                </el-tooltip>
+                                <div v-if="item.screenshot" class="screenshot-section">
+                                    <el-image
+                                            :src="item.screenshot"
+                                            style="max-width: 100%; height: 220px;"
+                                            :preview-src-list="[item.screenshot]"
+                                            fit="cover"></el-image>
                                 </div>
                             </div>
-                        </el-card>
-                    </div>
-                </draggable>
-
-                <!-- 添加新项部分 -->
-                <div v-if="!readOnly">
-                    <el-button type="primary" @click="openAddDialog">添加新项</el-button>
-                    <el-button @click="showTabManage = !showTabManage">
-                        {{ showTabManage ? '隐藏标签管理' : '显示标签管理' }}
-                    </el-button>
-
-                    <!-- 标签管理 -->
-                    <!-- 标签管理部分 -->
-                    <div v-if="showTabManage" style="margin-top: 20px;">
-                        <el-input
-                                v-model="newTabName"
-                                placeholder="新标签页名称"
-                                style="width: 200px; margin-right: 10px;"></el-input>
-                        <el-button
-                                type="primary"
-                                @click="addTab"
-                                :disabled="!newTabName || myObj[newTabName]">
-                            添加标签页
-                        </el-button>
-                        <el-button
-                                type="warning"
-                                @click="editCurrentTabName"
-                                :disabled="!activeTab">
-                            修改当前标签名
-                        </el-button>
-                        <el-button
-                                type="danger"
-                                @click="deleteTab(activeTab)"
-                                :disabled="Object.keys(myObj).length <= 1">
-                            删除当前标签
-                        </el-button>
-                    </div>
+                        </div>
+                    </el-card>
                 </div>
-            </div>
-        </el-tab-pane>
-    </el-tabs>
-    <br>
-    <div v-if="showTabManage" style="display: flex;line-height: 2em">
-        <div>拖拽排序:</div>
-        <draggable v-model="tabOrder" @change="onTabOrderChange" handle=".drag-handle" style="display: flex;" animation="300">
-            <div v-for="tabName in tabOrder" :key="tabName" class="tab-item drag-handle">
-                <span>{{ tabName }}</span>
-            </div>
+            </transition-group>
+
         </draggable>
     </div>
+    <br>
     <!-- 编辑对话框 -->
     <el-dialog
             :close-on-click-modal="false"
@@ -328,10 +336,10 @@
             <el-form-item label="所属标签">
                 <el-select v-model="targetTab" placeholder="请选择标签页">
                     <el-option
-                            v-for="tab in Object.keys(myObj)"
-                            :key="tab"
-                            :label="tab"
-                            :value="tab">
+                            v-for="tab in groups"
+                            :key="tab.name"
+                            :label="tab.name"
+                            :value="tab.name">
                     </el-option>
                 </el-select>
             </el-form-item>
@@ -355,22 +363,25 @@
     let _this = new Vue({
         el: '#app',
         data: {
-            tabOrder: [],
             all_data: {},
             activeTab: '常用工具',
-            myObj: {
-                "常用工具": [{
-                    title: "GitHub",
-                    icon: "https://github.githubassets.com/favicons/favicon.svg",
-                    mainUrl: "https://github.com",
-                    description: "代码托管平台",
-                    extraUrls: [
-                        "https://gist.github.com",
-                        "https://pages.github.com"
-                    ],
-                    screenshot: ""
-                }]
-            },
+            groups: [
+                {
+                    name: "常用工具",
+                    datas: [{
+                        title: "GitHub",
+                        icon: "https://github.githubassets.com/favicons/favicon.svg",
+                        mainUrl: "https://github.com",
+                        description: "代码托管平台",
+                        extraUrls: [
+                            "",
+                            "https://pages.github.com"
+                        ],
+                        screenshot: ""
+                    }]
+                }
+            ],
+
             newTabName: '',
             readOnly: false,
             showTabManage: false,
@@ -386,8 +397,9 @@
                 screenshot: ''
             },
             originalTab: '',
-            originalIndex: -1,
-            targetTab: ''
+            originalIndex: -1, // 编辑时,记录原始数据在当前标签页中的索引
+            targetTabIndex: -1, // 编辑时,记录目标标签页名称
+            targetTab:''
         },
         mounted() {
             pageManager.init().then(res => {
@@ -395,59 +407,25 @@
                 if (res && res.content) {
                     let showContent = res.content;
                     if (pageManager.isBackup) showContent = res.content_back;
-                    this.myObj = JSON.parse(showContent);
+                    this.groups = JSON.parse(showContent);
                 }
-                this.activeTab = Object.keys(this.myObj)[0] || '';
                 if (pageManager.isReadOnly()) this.readOnly = true;
 
-                this.mouseOver();
-                this.tabOrder = Object.keys(this.myObj);
             });
         },
         methods: {
+            scrollToGroup(tabName) {
+                const element = document.getElementById('group-' + tabName);
+                if (element) {
+                    element.scrollIntoView({
+                        behavior: 'smooth',
+                        block: 'start'
+                    });
+                }
+            },
             onTabOrderChange() {
-                const newMyObj = {};
-                this.tabOrder.forEach(tabName => {
-                    newMyObj[tabName] = this.myObj[tabName];
-                });
-                this.myObj = newMyObj;
                 this.updateData();
             },
-            // 鼠标滑过,切换tabs
-            mouseOver() {
-                this.$nextTick(() => {
-                    this.$refs.navTabs.$refs.nav.$nextTick(() => {
-                        // console.log('$refs.nav', this.$refs.navTabs.$refs.nav.$el)
-                        // 此时tab的nav才渲染dom 否则拿不到el-tabs__item
-                        const target = document.getElementsByClassName('el-tabs__item')
-                        for (let i = 0; i < target.length; i++) {
-                            target[i].addEventListener('mouseover', () => {
-                                target[i].click();
-                            })
-                        }
-                    })
-                });
-            },
-            editCurrentTabName() {
-                this.$prompt('请输入新标签名', '修改标签名称', {
-                    inputValue: this.activeTab,
-                    closeOnClickModal: false,
-                    inputValidator: (value) => {
-                        if (!value) return '标签名不能为空';
-                        if (this.myObj[value] && value !== this.activeTab) return '标签名已存在';
-                        return true;
-                    }
-                }).then(({value}) => {
-                    if (value !== this.activeTab) {
-                        // 创建新标签并转移数据
-                        this.$set(this.myObj, value, this.myObj[this.activeTab]);
-                        delete this.myObj[this.activeTab];
-                        this.activeTab = value;
-                        this.updateData();
-                    }
-                }).catch(() => {
-                });
-            },
             openAddDialog() {
                 this.isEditing = false;
                 this.currentItem = {
@@ -462,34 +440,77 @@
                 this.dialogVisible = true;
             },
 
-            openEditDialog(tabName, index) {
+            openEditDialog(group, item) {
+                // 根据 groupName 和 index 获取对应的数据
+
                 this.isEditing = true;
-                this.originalTab = tabName;
-                this.originalIndex = index;
-                this.currentItem = JSON.parse(JSON.stringify(this.myObj[tabName][index]));
-                this.targetTab = tabName;
                 this.dialogVisible = true;
+                this.currentItem = item;
+                this.targetTab = group.name;
+                this.originalTab = group.name;
+            },
+            getTabNames() {
+                return this.groups.map(g => g.name);
             },
+            // 根据标签名获取数据
+            getGroupByName(name) {
+                return this.groups.find(g => g.name === name);
+            },
+// 新增标签页
+            addTab() {
+                if (!this.newTabName) return;
+
+                this.groups.push({
+                    name: this.newTabName,
+                    datas: []
+                });
 
+                this.activeTab = this.newTabName;
+                this.newTabName = '';
+                this.updateData();
+            },
+
+            // 保存新项目
             saveItem() {
                 if (!this.currentItem.title || !this.currentItem.mainUrl) {
                     this.$message.warning('标题和主地址为必填项');
                     return;
                 }
-
-                // 删除原有项(编辑时)
+                const targetGroup = this.getGroupByName(this.targetTab);
+                if (!targetGroup) return;
                 if (this.isEditing) {
-                    this.myObj[this.originalTab].splice(this.originalIndex, 1);
+                    // 编辑模式
+                    const originalGroup = this.getGroupByName(this.originalTab);
+                    if (originalGroup) {
+                        originalGroup.datas.splice(this.originalIndex, 1);
+
+                        if (this.targetTab === this.originalTab) {
+                            // 同组内移动,插入到原位置
+                            targetGroup.datas.splice(this.originalIndex, 0, this.currentItem);
+                        } else {
+                            // 跨组移动,添加到末尾
+                            targetGroup.datas.push(this.currentItem);
+                        }
+                    }
+                } else {
+                    // 新增模式
+                    if (this.originalIndex !== undefined) {
+                        // 在指定位置后插入
+                        targetGroup.datas.splice(this.originalIndex + 1, 0, this.currentItem);
+                    } else {
+                        // 直接添加到末尾
+                        targetGroup.datas.push(this.currentItem);
+                    }
                 }
-
-                // 添加到目标标签
-                if (!this.myObj[this.targetTab]) this.myObj[this.targetTab] = [];
-                this.myObj[this.targetTab].push(this.currentItem);
-
                 this.updateData();
                 this.dialogVisible = false;
+
+                // 重置位置信息
+                this.originalIndex = undefined;
+                this.originalTab = '';
             },
 
+
             addExtraUrl() {
                 this.currentItem.extraUrls.push('');
             },
@@ -498,40 +519,52 @@
                 this.currentItem.extraUrls.splice(index, 1);
             },
 
-            addTab() {
-                if (!this.newTabName) return;
-                this.$set(this.myObj, this.newTabName, []);
-                this.activeTab = this.newTabName;
-                this.newTabName = '';
-                _this.tabOrder = Object.keys(this.myObj);
-                this.updateData();
-            },
 
-            deleteTab(tabName) {
-                this.$confirm('确定删除该标签页?', '警告', {
-                    confirmButtonText: '确定',
-                    cancelButtonText: '取消',
-                    type: 'warning'
-                }).then(() => {
-                    delete this.myObj[tabName];
-                    this.activeTab = Object.keys(this.myObj)[0] || '';
+            // 删除标签页
+            deleteTab(name) {
+                const idx = this.groups.findIndex(g => g.name === name);
+                if (idx > -1) {
+                    this.groups.splice(idx, 1);
+                    this.activeTab = this.groups[0]?.name;
                     this.updateData();
+                }
+            },
+// 修改标签名
+            editCurrentTabName(oldName) {
+                this.$prompt('请输入新标签名', '修改标签名称', {
+                    inputValue: oldName,
+                    closeOnClickModal: false,
+                    inputValidator: (value) => {
+                        if (!value) return '标签名不能为空';
+                        if (this.getGroupByName(value) && value !== oldName) {
+                            return '标签名已存在';
+                        }
+                        return true;
+                    }
+                }).then(({value}) => {
+                    if (value !== oldName) {
+                        const group = this.getGroupByName(oldName);
+                        if (group) {
+                            group.name = value;
+                            this.activeTab = value;
+                            this.updateData();
+                        }
+                    }
                 });
             },
 
-            deleteContentItem(tabName, index) {
-                this.$confirm('确定删除该项?', '警告', {
-                    confirmButtonText: '确定',
-                    cancelButtonText: '取消',
-                    type: 'warning'
-                }).then(() => {
-                    this.myObj[tabName].splice(index, 1);
+            // 删除内容项
+            deleteContentItem(groupName, index) {
+                const group = this.getGroupByName(groupName);
+                if (group) {
+                    group.datas.splice(index, 1);
                     this.updateData();
-                });
+                }
             },
 
+
             updateData() {
-                this.all_data.content = JSON.stringify(this.myObj);
+                this.all_data.content = JSON.stringify(this.groups);
                 pageManager.updateData(this.all_data);
             },
 
@@ -541,7 +574,46 @@
 
             handleTabClick(tab) {
                 this.activeTab = tab.name;
-            }
+            },
+            // 在当前标签后新增标签
+            addTabAfter(name) {
+                this.$prompt('请输入新标签名', '添加标签名称', {
+                    closeOnClickModal: false,
+                    inputValidator: (value) => {
+                        if (!value) return '标签名不能为空';
+                        if (this.getGroupByName(value)) return '标签名已存在';
+                        return true;
+                    }
+                }).then(({value}) => {
+                    const idx = this.groups.findIndex(g => g.name === name);
+                    this.groups.splice(idx + 1, 0, {
+                        name: value,
+                        datas: []
+                    });
+                    this.activeTab = value;
+                    this.updateData();
+                });
+            },
+
+            addContentItemAfter(groupName, index) {
+                this.isEditing = false;
+                // 重置当前项
+                this.currentItem = {
+                    title: '',
+                    icon: '',
+                    mainUrl: '',
+                    description: '',
+                    extraUrls: [],
+                    screenshot: ''
+                };
+
+                // 存储目标位置信息
+                this.targetTab = groupName;
+                this.originalTab = groupName;
+                this.originalIndex = index;
+
+                this.dialogVisible = true;
+            },
         }
     });
 </script>