fix:分类改成书签分组。

This commit is contained in:
tianyongbao
2025-12-13 09:38:43 +08:00
parent fc6bbf099e
commit 667b06da37
2 changed files with 231 additions and 231 deletions

View File

@@ -1,7 +1,7 @@
<template>
<page class="manage-page">
<div class="manage-header">
<h1>管理数据</h1>
<h1>书签管理</h1>
<button class="back-btn" @click="goBack">返回工作台</button>
</div>
@@ -9,8 +9,8 @@
<!-- 分类管理 -->
<div class="manage-section">
<div class="section-header">
<h2>分类管理</h2>
<button class="add-btn" @click="showAddCategoryDialog">+ 添加分类</button>
<h2>书签分组管理</h2>
<button class="add-btn" @click="showAddCategoryDialog">+ 添加书签分组</button>
</div>
<div class="category-list">
<div v-for="category in categories" :key="category.id" class="category-item">
@@ -68,14 +68,14 @@
<!-- 分类编辑对话框 -->
<div v-if="showCategoryDialog" class="dialog-overlay" @click="closeCategoryDialog">
<div class="dialog" @click.stop>
<h3>{{ categoryForm.id ? '编辑分类' : '添加分类' }}</h3>
<h3>{{ categoryForm.id ? '编辑书签分组' : '添加书签分组' }}</h3>
<div class="form-group">
<label><span class="required">*</span>分类标识</label>
<label><span class="required">*</span>书签分组标识</label>
<input v-model="categoryForm.key" placeholder="例如doc" :disabled="!!categoryForm.id" />
</div>
<div class="form-group">
<label><span class="required">*</span>分类名称</label>
<input v-model="categoryForm.title" placeholder="例如:智聪科技" />
<label><span class="required">*</span>书签分组名称</label>
<input v-model="categoryForm.title" placeholder="例如:书签1" />
</div>
<div class="form-group">
<label>排序序号</label>
@@ -94,21 +94,21 @@
<h3>{{ toolForm.id ? '编辑网址' : '添加网址' }}</h3>
<div class="form-group">
<label><span class="required">*</span>访问地址</label>
<input v-model="toolForm.url" placeholder="例如https://www.qdintc.com" />
<input v-model="toolForm.url" placeholder="例如https://www.baidu.com" />
</div>
<div class="form-group">
<label><span class="required">*</span>网址名称</label>
<input v-model="toolForm.name" placeholder="例如:智聪官网" />
<input v-model="toolForm.name" placeholder="例如:网址1" />
</div>
<div class="form-group">
<label>网址描述</label>
<input v-model="toolForm.desc" placeholder="例如:智聪官网" />
<input v-model="toolForm.desc" placeholder="例如:网址描述" />
</div>
<div class="form-group">
<label>显示地址</label>
<input v-model="toolForm.displayUrl" placeholder="例如www.qdintc.com" />
<input v-model="toolForm.displayUrl" placeholder="例如www.baidu.com" />
</div>
<div class="form-group">
<label>图标文字</label>
@@ -117,8 +117,8 @@
<div class="form-group">
<label>图标颜色</label>
<div class="color-picker">
<div
v-for="(colorOption, index) in colorOptions"
<div
v-for="(colorOption, index) in colorOptions"
:key="index"
class="color-option"
:class="{ active: toolForm.color === colorOption.value }"
@@ -170,7 +170,7 @@ import ToolService from '../services/ToolService'
export default {
name: 'ManagePage',
components: { Page },
data() {
return {
loading: false,
@@ -233,7 +233,7 @@ export default {
currentCategory: null
}
},
computed: {
messageIcon() {
const icons = {
@@ -244,13 +244,13 @@ export default {
return icons[this.message.type] || '✓'
}
},
created() {
this.loadData()
},
methods: {
// 消息提示
showMessage(text, type = 'success') {
@@ -287,12 +287,12 @@ export default {
async loadData() {
try {
this.loading = true
const [categoriesRes, toolsRes] = await Promise.all([
ToolService.getCategories(),
ToolService.getTools()
])
if (categoriesRes && categoriesRes.code === 200 && toolsRes && toolsRes.code === 200) {
const categories = (categoriesRes.rows || []).map(cat => ({
id: cat.id,
@@ -301,7 +301,7 @@ export default {
sortOrder: cat.sortOrder || 0,
tools: []
}))
const tools = (toolsRes.rows || []).map(tool => ({
id: tool.id,
categoryId: tool.categoryId,
@@ -313,16 +313,16 @@ export default {
color: tool.color,
sortOrder: tool.sortOrder || 0
}))
tools.forEach(tool => {
const category = categories.find(cat => cat.id === tool.categoryId)
if (category) {
category.tools.push(tool)
}
})
categories.sort((a, b) => a.sortOrder - b.sortOrder)
this.categories = categories
}
} catch (error) {
@@ -363,7 +363,7 @@ export default {
this.showMessage('请填写分类标识', 'warning')
return
}
if (!this.categoryForm.title) {
this.showMessage('请填写分类名称', 'warning')
return
@@ -387,9 +387,9 @@ export default {
categoryTitle: this.categoryForm.title,
sortOrder: this.categoryForm.sortOrder
}
const response = await ToolService.createCategory(data)
if (response && response.code === 200) {
this.showMessage('添加成功')
this.closeCategoryDialog()
@@ -411,7 +411,7 @@ export default {
categoryTitle: this.categoryForm.title,
sortOrder: this.categoryForm.sortOrder
})
if (response && response.code === 200) {
this.showMessage('修改成功')
this.closeCategoryDialog()
@@ -426,10 +426,10 @@ export default {
},
deleteCategory(category) {
const message = category.tools && category.tools.length > 0
const message = category.tools && category.tools.length > 0
? `该分类下还有 ${category.tools.length} 个网址,确定要删除"${category.title}"吗?`
: `确定要删除分类"${category.title}"吗?`
this.showConfirm(message, () => {
this.deleteCategoryFromServer(category.id)
})
@@ -438,7 +438,7 @@ export default {
async deleteCategoryFromServer(id) {
try {
const response = await ToolService.deleteCategory([id])
if (response && response.code === 200) {
this.showMessage('删除成功')
this.loadData()
@@ -494,12 +494,12 @@ export default {
this.showMessage('请填写网址名称', 'warning')
return
}
if (!this.toolForm.url) {
this.showMessage('请填写访问地址', 'warning')
return
}
// 验证URL格式
if (!this.isValidUrl(this.toolForm.url)) {
this.showMessage('请输入完整的URL地址必须以http://或https://开头', 'warning')
@@ -516,7 +516,7 @@ export default {
this.addToolToServer()
}
},
// 验证URL格式
isValidUrl(url) {
if (!url) return false
@@ -535,7 +535,7 @@ export default {
color: this.toolForm.color,
sortOrder: this.toolForm.sortOrder
})
if (response && response.code === 200) {
this.showMessage('添加成功')
this.closeToolDialog()
@@ -562,7 +562,7 @@ export default {
color: this.toolForm.color,
sortOrder: this.toolForm.sortOrder
})
if (response && response.code === 200) {
this.showMessage('修改成功')
this.closeToolDialog()
@@ -585,7 +585,7 @@ export default {
async deleteToolFromServer(id) {
try {
const response = await ToolService.deleteTool([id])
if (response && response.code === 200) {
this.showMessage('删除成功')
this.loadData()
@@ -628,7 +628,7 @@ export default {
justify-content: center; // PC端居中
align-items: center;
z-index: 1000;
h1 {
margin: 0;
font-size: 36px; // 与HomePage保持一致
@@ -637,7 +637,7 @@ export default {
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
letter-spacing: 2px; // 添加字母间距
}
.back-btn {
position: absolute; // 绝对定位避免遮挡标题
right: 40px;
@@ -651,7 +651,7 @@ export default {
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
// 返回图标
&::before {
content: '◄';
@@ -660,17 +660,17 @@ export default {
display: inline-block;
transition: transform 0.3s ease;
}
&:hover {
transform: translateY(-3px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.5);
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
&::before {
transform: translateX(-4px);
}
}
&:active {
transform: translateY(-1px);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
@@ -688,7 +688,7 @@ export default {
padding: 20px 12px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
.section-header {
display: flex;
justify-content: space-between;
@@ -696,7 +696,7 @@ export default {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 2px solid #f0f0f0;
h2 {
margin: 0;
font-size: 20px;
@@ -717,17 +717,17 @@ export default {
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
&:active {
transform: translateY(0);
}
&.small {
padding: 6px 16px;
font-size: 13px;
@@ -738,7 +738,7 @@ export default {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 10px;
.category-item {
display: flex;
flex-direction: column;
@@ -748,17 +748,17 @@ export default {
border-radius: 6px;
border: 1px solid #e5e7eb;
transition: all 0.2s ease;
&:hover {
background: #f3f4f6;
border-color: @primary-color;
}
.category-info {
display: flex;
align-items: center;
gap: 8px;
.category-key {
padding: 3px 10px;
background: @primary-color;
@@ -768,7 +768,7 @@ export default {
font-weight: 500;
white-space: nowrap;
}
.category-name {
font-size: 14px;
color: @text-primary;
@@ -778,7 +778,7 @@ export default {
text-overflow: ellipsis;
white-space: nowrap;
}
.category-sort {
font-size: 12px;
color: @primary-color;
@@ -789,18 +789,18 @@ export default {
font-weight: 500;
}
}
.category-footer {
display: flex;
justify-content: space-between;
align-items: center;
.category-count {
font-size: 12px;
color: @text-tertiary;
white-space: nowrap;
}
.category-actions {
display: flex;
gap: 6px;
@@ -811,11 +811,11 @@ export default {
.tool-group {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
.tool-group-header {
display: flex;
justify-content: space-between;
@@ -824,19 +824,19 @@ export default {
padding: 12px 16px;
background: #f9fafb;
border-radius: 6px;
h3 {
margin: 0;
font-size: 16px;
color: @text-primary;
}
}
.tool-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 10px;
.tool-item {
display: flex;
flex-direction: column;
@@ -846,17 +846,17 @@ export default {
border-radius: 6px;
border: 1px solid #e5e7eb;
transition: all 0.2s ease;
&:hover {
background: #f3f4f6;
border-color: @primary-color;
}
.tool-main {
display: flex;
align-items: center;
gap: 10px;
.tool-icon {
width: 36px;
height: 36px;
@@ -869,11 +869,11 @@ export default {
font-weight: bold;
flex-shrink: 0;
}
.tool-info {
flex: 1;
min-width: 0;
.tool-name {
font-size: 13px;
font-weight: 500;
@@ -883,7 +883,7 @@ export default {
text-overflow: ellipsis;
white-space: nowrap;
}
.tool-desc {
font-size: 11px;
color: @text-secondary;
@@ -892,7 +892,7 @@ export default {
white-space: nowrap;
}
}
.tool-sort {
font-size: 11px;
color: @primary-color;
@@ -904,13 +904,13 @@ export default {
flex-shrink: 0;
}
}
.tool-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
.tool-url {
font-size: 10px;
color: @text-tertiary;
@@ -920,7 +920,7 @@ export default {
white-space: nowrap;
flex: 1;
}
.tool-actions {
display: flex;
gap: 6px;
@@ -941,12 +941,12 @@ button {
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: #66b1ff;
}
}
&.delete-btn {
padding: 5px 10px;
background: #f56c6c;
@@ -956,7 +956,7 @@ button {
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: #f78989;
}
@@ -975,7 +975,7 @@ button {
align-items: center;
justify-content: center;
z-index: 2000;
.dialog {
background: white;
border-radius: 12px;
@@ -983,29 +983,29 @@ button {
width: 90%;
max-width: 500px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
h3 {
margin: 0 0 20px;
font-size: 20px;
color: @text-primary;
}
.form-group {
margin-bottom: 16px;
label {
display: block;
margin-bottom: 8px;
font-size: 14px;
color: @text-secondary;
font-weight: 500;
.required {
color: #f56c6c;
margin-right: 4px;
}
}
input, select {
width: 100%;
padding: 10px 12px;
@@ -1014,31 +1014,31 @@ button {
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.2s ease;
&:focus {
outline: none;
border-color: @primary-color;
}
&:disabled {
background: #f5f7fa;
color: #999;
cursor: not-allowed;
}
}
select {
cursor: pointer;
}
}
// 颜色选择器样式
.color-picker {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 8px;
padding: 0 2px;
.color-option {
width: 100%;
aspect-ratio: 1;
@@ -1050,17 +1050,17 @@ button {
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
&.active {
border-color: @primary-color;
box-shadow: 0 0 0 2px white, 0 0 0 4px @primary-color;
}
.check-icon {
color: white;
font-size: 20px;
@@ -1069,13 +1069,13 @@ button {
}
}
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
button {
padding: 10px 24px;
border-radius: 6px;
@@ -1083,23 +1083,23 @@ button {
cursor: pointer;
transition: all 0.2s ease;
}
.cancel-btn {
background: white;
border: 1px solid @border-color;
color: @text-primary;
&:hover {
color: @primary-color;
border-color: @primary-color;
}
}
.confirm-btn {
background: @primary-color;
border: none;
color: white;
&:hover {
background: #5568d3;
}
@@ -1117,28 +1117,28 @@ button {
max-width: 400px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
text-align: center;
.confirm-icon {
font-size: 48px;
margin-bottom: 16px;
&.warning {
color: #e6a23c;
}
}
.confirm-message {
font-size: 16px;
color: @text-primary;
margin-bottom: 24px;
line-height: 1.6;
}
.confirm-actions {
display: flex;
gap: 12px;
justify-content: center;
button {
padding: 10px 32px;
border-radius: 6px;
@@ -1147,30 +1147,30 @@ button {
transition: all 0.2s ease;
min-width: 100px;
}
.cancel-btn {
background: white;
border: 1px solid @border-color;
color: @text-primary;
&:hover {
color: @primary-color;
border-color: @primary-color;
}
}
.confirm-btn {
background: @primary-color;
border: none;
color: white;
&:hover {
background: #5568d3;
}
&.danger {
background: #f56c6c;
&:hover {
background: #f78989;
}
@@ -1195,37 +1195,37 @@ button {
z-index: 3000;
min-width: 300px;
justify-content: center;
.message-icon {
font-size: 18px;
font-weight: bold;
}
&.success {
background: #f0f9ff;
border: 1px solid #67c23a;
color: #67c23a;
.message-icon {
color: #67c23a;
}
}
&.error {
background: #fef0f0;
border: 1px solid #f56c6c;
color: #f56c6c;
.message-icon {
color: #f56c6c;
}
}
&.warning {
background: #fdf6ec;
border: 1px solid #e6a23c;
color: #e6a23c;
.message-icon {
color: #e6a23c;
}
@@ -1253,32 +1253,32 @@ button {
.manage-header {
justify-content: flex-start; // 手机端左对齐
padding: 20px;
h1 {
font-size: 24px; // 手机端与HomePage保持一致
}
.back-btn {
right: 20px;
padding: 10px 20px;
font-size: 14px;
&::before {
font-size: 12px;
}
}
}
.manage-container {
padding: 16px 8px;
}
.category-list {
grid-template-columns: 1fr !important;
}
.tool-list {
grid-template-columns: 1fr !important;
}
}
</style>
</style>