|
@@ -3,10 +3,10 @@
|
|
<head>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta charset="UTF-8">
|
|
<title>导航</title>
|
|
<title>导航</title>
|
|
- <!--解决资源跨域-->
|
|
|
|
|
|
+ <!-- 解决资源跨域 -->
|
|
<meta name="referrer" content="no-referrer"/>
|
|
<meta name="referrer" content="no-referrer"/>
|
|
|
|
|
|
- <!--解决缓存问题-->
|
|
|
|
|
|
+ <!-- 解决缓存问题 -->
|
|
<meta http-equiv="pragma" content="no-cache"/>
|
|
<meta http-equiv="pragma" content="no-cache"/>
|
|
<meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
|
|
<meta http-equiv="Cache-Control" content="no-cache, must-revalidate"/>
|
|
<meta http-equiv="expires" content="0"/>
|
|
<meta http-equiv="expires" content="0"/>
|
|
@@ -18,15 +18,42 @@
|
|
<script src="elementUI/index.js"></script>
|
|
<script src="elementUI/index.js"></script>
|
|
<link rel="stylesheet" href="elementUI/index.css"/>
|
|
<link rel="stylesheet" href="elementUI/index.css"/>
|
|
<style>
|
|
<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 {
|
|
.tab-item {
|
|
min-width: 100px;
|
|
min-width: 100px;
|
|
max-width: 200px;
|
|
max-width: 200px;
|
|
/* 文字居中*/
|
|
/* 文字居中*/
|
|
text-align: center;
|
|
text-align: center;
|
|
/*文字垂直居中*/
|
|
/*文字垂直居中*/
|
|
- line-height: 2em;
|
|
|
|
- background: #c5e7cd;
|
|
|
|
- margin: 0 20px;
|
|
|
|
|
|
+ /*line-height: 2em;*/
|
|
|
|
+ background: #ecf5f0;
|
|
|
|
+ margin: 0 10px;
|
|
/* 鼠标变成手*/
|
|
/* 鼠标变成手*/
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
@@ -48,7 +75,6 @@
|
|
|
|
|
|
.content-item {
|
|
.content-item {
|
|
margin: 10px;
|
|
margin: 10px;
|
|
- /*padding: 15px;*/
|
|
|
|
border: 1px solid #ebeef5;
|
|
border: 1px solid #ebeef5;
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
transition: all .3s;
|
|
transition: all .3s;
|
|
@@ -56,6 +82,7 @@
|
|
display: inline-block;
|
|
display: inline-block;
|
|
width: 380px;
|
|
width: 380px;
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
|
+ margin-bottom: 15px;
|
|
}
|
|
}
|
|
|
|
|
|
.content-item:hover {
|
|
.content-item:hover {
|
|
@@ -96,18 +123,6 @@
|
|
text-overflow: ellipsis;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
|
|
- .copy-btn {
|
|
|
|
- color: #409EFF;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- .copy-btn:hover {
|
|
|
|
- color: #206BC4;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- .content-item {
|
|
|
|
- margin-bottom: 15px;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
@media (max-width: 768px) {
|
|
@media (max-width: 768px) {
|
|
.content-item {
|
|
.content-item {
|
|
width: calc(50% - 20px);
|
|
width: calc(50% - 20px);
|
|
@@ -150,10 +165,6 @@
|
|
padding: 20px;
|
|
padding: 20px;
|
|
}
|
|
}
|
|
|
|
|
|
- .dynamic-delete-button {
|
|
|
|
- cursor: pointer;
|
|
|
|
- color: #F56C6C;
|
|
|
|
- }
|
|
|
|
</style>
|
|
</style>
|
|
<script src="js/Sortable.min.js"></script>
|
|
<script src="js/Sortable.min.js"></script>
|
|
<script src="js/vuedraggable.umd.min.js"></script>
|
|
<script src="js/vuedraggable.umd.min.js"></script>
|
|
@@ -161,137 +172,134 @@
|
|
</head>
|
|
</head>
|
|
<body>
|
|
<body>
|
|
<div id="app" class="tab-container">
|
|
<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>
|
|
</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>
|
|
</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>
|
|
</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>
|
|
- </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>
|
|
</draggable>
|
|
</div>
|
|
</div>
|
|
|
|
+ <br>
|
|
<!-- 编辑对话框 -->
|
|
<!-- 编辑对话框 -->
|
|
<el-dialog
|
|
<el-dialog
|
|
:close-on-click-modal="false"
|
|
:close-on-click-modal="false"
|
|
@@ -328,10 +336,10 @@
|
|
<el-form-item label="所属标签">
|
|
<el-form-item label="所属标签">
|
|
<el-select v-model="targetTab" placeholder="请选择标签页">
|
|
<el-select v-model="targetTab" placeholder="请选择标签页">
|
|
<el-option
|
|
<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-option>
|
|
</el-select>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form-item>
|
|
@@ -355,22 +363,25 @@
|
|
let _this = new Vue({
|
|
let _this = new Vue({
|
|
el: '#app',
|
|
el: '#app',
|
|
data: {
|
|
data: {
|
|
- tabOrder: [],
|
|
|
|
all_data: {},
|
|
all_data: {},
|
|
activeTab: '常用工具',
|
|
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: '',
|
|
newTabName: '',
|
|
readOnly: false,
|
|
readOnly: false,
|
|
showTabManage: false,
|
|
showTabManage: false,
|
|
@@ -386,8 +397,9 @@
|
|
screenshot: ''
|
|
screenshot: ''
|
|
},
|
|
},
|
|
originalTab: '',
|
|
originalTab: '',
|
|
- originalIndex: -1,
|
|
|
|
- targetTab: ''
|
|
|
|
|
|
+ originalIndex: -1, // 编辑时,记录原始数据在当前标签页中的索引
|
|
|
|
+ targetTabIndex: -1, // 编辑时,记录目标标签页名称
|
|
|
|
+ targetTab:''
|
|
},
|
|
},
|
|
mounted() {
|
|
mounted() {
|
|
pageManager.init().then(res => {
|
|
pageManager.init().then(res => {
|
|
@@ -395,59 +407,25 @@
|
|
if (res && res.content) {
|
|
if (res && res.content) {
|
|
let showContent = res.content;
|
|
let showContent = res.content;
|
|
if (pageManager.isBackup) showContent = res.content_back;
|
|
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;
|
|
if (pageManager.isReadOnly()) this.readOnly = true;
|
|
|
|
|
|
- this.mouseOver();
|
|
|
|
- this.tabOrder = Object.keys(this.myObj);
|
|
|
|
});
|
|
});
|
|
},
|
|
},
|
|
methods: {
|
|
methods: {
|
|
|
|
+ scrollToGroup(tabName) {
|
|
|
|
+ const element = document.getElementById('group-' + tabName);
|
|
|
|
+ if (element) {
|
|
|
|
+ element.scrollIntoView({
|
|
|
|
+ behavior: 'smooth',
|
|
|
|
+ block: 'start'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ },
|
|
onTabOrderChange() {
|
|
onTabOrderChange() {
|
|
- const newMyObj = {};
|
|
|
|
- this.tabOrder.forEach(tabName => {
|
|
|
|
- newMyObj[tabName] = this.myObj[tabName];
|
|
|
|
- });
|
|
|
|
- this.myObj = newMyObj;
|
|
|
|
this.updateData();
|
|
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() {
|
|
openAddDialog() {
|
|
this.isEditing = false;
|
|
this.isEditing = false;
|
|
this.currentItem = {
|
|
this.currentItem = {
|
|
@@ -462,34 +440,77 @@
|
|
this.dialogVisible = true;
|
|
this.dialogVisible = true;
|
|
},
|
|
},
|
|
|
|
|
|
- openEditDialog(tabName, index) {
|
|
|
|
|
|
+ openEditDialog(group, item) {
|
|
|
|
+ // 根据 groupName 和 index 获取对应的数据
|
|
|
|
+
|
|
this.isEditing = true;
|
|
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.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() {
|
|
saveItem() {
|
|
if (!this.currentItem.title || !this.currentItem.mainUrl) {
|
|
if (!this.currentItem.title || !this.currentItem.mainUrl) {
|
|
this.$message.warning('标题和主地址为必填项');
|
|
this.$message.warning('标题和主地址为必填项');
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 删除原有项(编辑时)
|
|
|
|
|
|
+ const targetGroup = this.getGroupByName(this.targetTab);
|
|
|
|
+ if (!targetGroup) return;
|
|
if (this.isEditing) {
|
|
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.updateData();
|
|
this.dialogVisible = false;
|
|
this.dialogVisible = false;
|
|
|
|
+
|
|
|
|
+ // 重置位置信息
|
|
|
|
+ this.originalIndex = undefined;
|
|
|
|
+ this.originalTab = '';
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+
|
|
addExtraUrl() {
|
|
addExtraUrl() {
|
|
this.currentItem.extraUrls.push('');
|
|
this.currentItem.extraUrls.push('');
|
|
},
|
|
},
|
|
@@ -498,40 +519,52 @@
|
|
this.currentItem.extraUrls.splice(index, 1);
|
|
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();
|
|
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();
|
|
this.updateData();
|
|
- });
|
|
|
|
|
|
+ }
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+
|
|
updateData() {
|
|
updateData() {
|
|
- this.all_data.content = JSON.stringify(this.myObj);
|
|
|
|
|
|
+ this.all_data.content = JSON.stringify(this.groups);
|
|
pageManager.updateData(this.all_data);
|
|
pageManager.updateData(this.all_data);
|
|
},
|
|
},
|
|
|
|
|
|
@@ -541,7 +574,46 @@
|
|
|
|
|
|
handleTabClick(tab) {
|
|
handleTabClick(tab) {
|
|
this.activeTab = tab.name;
|
|
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>
|
|
</script>
|