fix: 优化快速开始/便捷导航

This commit is contained in:
Guo Tiantian
2025-08-24 20:36:34 +08:00
parent d88b91c94e
commit b395a9bef7
8 changed files with 451 additions and 926 deletions
+2
View File
@@ -325,6 +325,7 @@ declare module 'vue' {
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
readonly ElNotification: UnwrapRef<typeof import('element-plus/es')['ElNotification']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
@@ -586,6 +587,7 @@ declare module 'vue' {
readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>
readonly useTimeAgo: UnwrapRef<typeof import('@vueuse/core')['useTimeAgo']>
readonly useTimeAgoIntl: UnwrapRef<typeof import('@vueuse/core')['useTimeAgoIntl']>
readonly useTimeout: UnwrapRef<typeof import('@vueuse/core')['useTimeout']>
readonly useTimeoutFn: UnwrapRef<typeof import('@vueuse/core')['useTimeoutFn']>
readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']>
-6
View File
@@ -5,7 +5,6 @@ export interface QuickLink {
title: string;
description: string;
icon: string;
color: string;
href: string;
action: 'navigate' | 'external';
id?: string;
@@ -35,7 +34,6 @@ class QuickStartManager {
title: "用户管理",
description: "管理系统用户信息",
icon: "User",
color: "#409EFF",
href: "/system/user",
action: "navigate"
},
@@ -44,7 +42,6 @@ class QuickStartManager {
title: "系统监控",
description: "监控系统状态",
icon: "Monitor",
color: "#909399",
href: "/monitor",
action: "navigate"
},
@@ -53,7 +50,6 @@ class QuickStartManager {
title: "百度搜索",
description: "访问百度搜索引擎",
icon: "Search",
color: "#3385FF",
href: "https://www.baidu.com",
action: "external"
},
@@ -62,7 +58,6 @@ class QuickStartManager {
title: "GitHub",
description: "访问代码托管平台",
icon: "Monitor",
color: "#24292e",
href: "https://github.com",
action: "external"
}
@@ -177,7 +172,6 @@ class QuickStartManager {
title: customTitle || route.meta?.title || route.name || '未命名页面',
description: customDescription || `快速访问 ${route.meta?.title || route.name || '页面'}`,
icon: routeIcon,
color: routeColor,
href: route.fullPath || route.path,
action: 'navigate',
id: `route-${route.path.replace(/\//g, '-')}-${Date.now()}`
-406
View File
@@ -1,406 +0,0 @@
# Dashboard 仪表板模块
## 概述
Dashboard 仪表板模块是系统的核心展示区域,提供了数据概览、快速导航、工作台等功能。包含工作台页面、数据分析页面以及可复用的组件库。
## 模块结构
```
dashboard/
├── workplace.vue # 工作台主页面
├── analysis.vue # 数据分析页面
├── components/ # 可复用组件
│ ├── QuickStart.vue # 快速开始组件
│ ├── AddQuickLinkDialog.vue # 添加快速链接对话框
│ └── index.ts # 组件导出文件
└── README.md # 模块说明文档
```
## 页面功能
### 1. 工作台页面 (workplace.vue)
**核心功能:**
- ✅ 用户欢迎信息展示
- ✅ 进行中的项目列表
- ✅ 动态时间线
- ✅ 快速开始/便捷导航
- ✅ 数据指数图表
- ✅ 团队成员展示
**布局特点:**
- 响应式栅格布局
- 左右分栏设计
- 卡片式组件展示
- 移动端适配
### 2. 数据分析页面 (analysis.vue)
**主要功能:**
- ✅ 数据统计图表
- ✅ 趋势分析展示
- ✅ 实时数据监控
- ✅ 可视化数据报表
## 组件功能
### 1. 快速开始组件 (QuickStart.vue)
#### 核心特性
- ✅ 快速链接管理和展示
- ✅ 支持内部路由跳转和外部链接访问
- ✅ 固定高度容器,内容可滚动
- ✅ 响应式设计,适配不同屏幕尺寸
#### 标签栏右键收藏
- ✅ 在任意标签页右击可收藏到快速开始
- ✅ 支持切换收藏状态(收藏/取消收藏)
- ✅ 自动获取路由配置的图标
- ✅ 智能图标匹配和颜色配置
- ✅ 实时状态显示和图标切换
#### 手动添加链接
- ✅ 通过对话框手动添加自定义链接
- ✅ 支持选择图标和自定义颜色
- ✅ 表单验证确保数据正确性
- ✅ 支持内部链接和外部链接
#### 右键管理链接
- ✅ 在快速链接上右击可编辑或删除
- ✅ 编辑功能自动预填充现有数据
- ✅ 删除功能带有确认对话框
- ✅ 防止误操作的安全提示
#### 数据持久化
- ✅ 使用 localStorage 保存用户配置
- ✅ 页面刷新后数据不丢失
- ✅ 支持导入/导出配置(可扩展)
### 2. 添加快速链接对话框 (AddQuickLinkDialog.vue)
#### 功能特性
- ✅ 支持新增和编辑两种模式
- ✅ 完整的表单验证
- ✅ 图标选择器(22个常用图标)
- ✅ 颜色自定义选择
- ✅ 链接类型区分(内部/外部)
## 使用指南
### 访问 Dashboard
#### 工作台页面
- **路径**`/dashboard/workplace`
- **功能**:个人工作台,包含项目概览、动态信息、快速导航等
- **适用场景**:日常工作的起始页面
#### 数据分析页面
- **路径**`/dashboard/analysis`
- **功能**:数据统计和分析展示
- **适用场景**:查看系统数据和业务指标
### 快速开始组件使用
#### 从标签栏收藏页面
**收藏页面:**
1. 在任意页面的标签上右击
2. 选择"收藏到快速开始"
3. 系统会自动获取路由图标和颜色
4. 前往工作台查看新添加的链接
**取消收藏:**
1. 在已收藏页面的标签上右击
2. 选择"取消收藏"(图标会显示为实心星星)
3. 链接立即从快速开始列表中移除
**自动图标获取:**
- **优先级1**:使用路由 `meta.icon` 配置
- **优先级2**:根据路径智能匹配预设图标
- **优先级3**:使用默认链接图标
#### 手动添加链接
1. 在快速开始组件中点击"添加"按钮
2. 填写链接信息:
- **标题**:链接显示名称
- **描述**:链接说明文字
- **链接地址**:内部路径或外部URL
- **链接类型**:内部链接或外部链接
- **图标**:从预设图标中选择
- **颜色**:自定义图标颜色
3. 点击确定保存
#### 管理快速链接
**编辑链接:**
1. 在快速开始组件中右击任意链接
2. 选择"编辑链接"
3. 对话框会自动填充现有的链接信息
4. 修改需要更改的字段
5. 点击"确定"保存修改
**删除链接:**
1. 在快速开始组件中右击任意链接
2. 选择"删除链接"
3. 在确认对话框中点击"确定"
#### 使用快速链接
- **内部链接**:点击后在当前窗口跳转
- **外部链接**:点击后在新标签页打开,带有"外链"标识
## 技术架构
### 目录结构
```text
dashboard/
├── workplace.vue # 工作台主页面
├── analysis.vue # 数据分析页面
├── components/ # 组件库
│ ├── QuickStart.vue # 快速开始组件
│ ├── AddQuickLinkDialog.vue # 添加链接对话框
│ └── index.ts # 组件导出
└── README.md # 模块文档
```
### 核心技术栈
- **Vue 3**: Composition API + `<script setup>`
- **TypeScript**: 完整的类型定义
- **Element Plus**: UI 组件库
- **ECharts**: 数据可视化图表
- **Pinia**: 状态管理(如需要)
- **Vue Router**: 路由管理
### 数据管理
#### 快速开始数据管理器
```typescript
// utils/quickStartManager.ts
import { quickStartManager } from '@/utils/quickStartManager';
// 获取所有链接
const links = quickStartManager.getQuickLinks();
// 添加链接
quickStartManager.addQuickLink(newLink);
// 检查链接是否存在
const exists = quickStartManager.isLinkExists(href);
// 监听数据变化
quickStartManager.addListener(callback);
```
#### 本地存储
- **存储方式**: localStorage
- **数据格式**: JSON
- **存储键**: `quick-start-links`
- **容错处理**: 存储失败时的降级方案
### 组件设计
#### 1. 工作台页面 (workplace.vue)
**技术特点:**
- 响应式栅格布局 (`ElRow` + `ElCol`)
- 组件化设计,功能模块独立
- 数据驱动的动态渲染
- 移动端适配
**主要组件:**
```vue
<template>
<div class="app-container">
<!-- 用户信息卡片 -->
<ElCard>用户欢迎信息</ElCard>
<ElRow :gutter="16">
<!-- 左侧内容区 -->
<ElCol :xl="16">
<ProjectList /> <!-- 项目列表 -->
<ActivityTimeline /> <!-- 动态时间线 -->
</ElCol>
<!-- 右侧侧边栏 -->
<ElCol :xl="8">
<QuickStart /> <!-- 快速开始 -->
<DataChart /> <!-- 数据图表 -->
<TeamMembers /> <!-- 团队成员 -->
</ElCol>
</ElRow>
</div>
</template>
```
#### 2. 快速开始组件 (QuickStart.vue)
**核心特性:**
- 虚拟滚动优化(大量链接时)
- 右键菜单交互
- 拖拽排序(可扩展)
- 实时数据同步
**组件架构:**
```typescript
// 数据响应式
const quickLinks = ref<QuickLink[]>([]);
const selectedLink = ref<QuickLink | null>(null);
// 事件处理
const handleQuickLinkClick = (item: QuickLink) => { /* 跳转逻辑 */ };
const handleEditLink = (item: QuickLink) => { /* 编辑逻辑 */ };
const handleDeleteLink = (item: QuickLink) => { /* 删除逻辑 */ };
// 生命周期
onMounted(() => quickStartManager.addListener(updateQuickLinks));
onUnmounted(() => quickStartManager.removeListener(updateQuickLinks));
```
### 图标系统
#### 支持的图标类型
**Element Plus 图标:**
- User, Setting, Document, DataAnalysis
- Monitor, Tools, Bell, Message, Search
- House, Files, Calendar, ChatDotRound
- Connection, DataBoard, Histogram, PieChart
- TrendCharts, Operation, Service, Guide, Link
## 性能优化
### 组件优化
- **虚拟滚动**: 大量快速链接时使用虚拟滚动
- **懒加载**: 图表组件按需加载
- **缓存策略**: localStorage 本地缓存
- **数据同步**: 事件监听机制
## 扩展功能
### 已规划的功能
1. **权限控制**: 根据用户角色显示不同的快速链接
2. **分组管理**: 将快速链接按类别分组显示
3. **拖拽排序**: 支持用户自定义链接顺序
4. **导入导出**: 支持配置的导入和导出
5. **使用统计**: 记录链接点击次数和使用频率
6. **云端同步**: 将配置同步到云端账户
7. **主题定制**: 支持自定义主题和样式
8. **快捷键**: 支持键盘快捷键操作
### 自定义配置
#### 修改默认链接
```typescript
// utils/quickStartManager.ts
private getDefaultLinks(): QuickLink[] {
return [
{
id: 'custom-link',
title: "自定义链接",
description: "这是一个自定义的快速链接",
icon: "Star",
color: "#409EFF",
href: "/custom-page",
action: "navigate"
}
// 更多自定义链接...
];
}
```
#### 扩展图标映射
```typescript
// utils/quickStartManager.ts
const iconMap = {
'/custom-module': { icon: 'CustomIcon', color: '#FF6B6B' },
// 添加更多路径映射...
};
```
## 开发指南
### 添加新页面
1. **创建页面组件**: `dashboard/new-page.vue`
2. **配置路由**: 在 `router/index.ts` 中添加路由配置
3. **添加图标映射**: 在 `quickStartManager.ts` 中添加路径映射
### 创建新组件
1. **创建组件文件**: `dashboard/components/NewComponent.vue`
2. **导出组件**: 在 `components/index.ts` 中导出
3. **在页面中使用**: 导入并使用组件
## 注意事项
### 性能注意事项
- **存储限制**: localStorage 有大小限制(通常5-10MB
- **内存管理**: 及时清理事件监听器,避免内存泄漏
- **渲染优化**: 大量数据时考虑虚拟滚动或分页
### 兼容性注意事项
- **浏览器支持**: 确保目标浏览器支持使用的 API
- **图标依赖**: 确保使用的图标已正确导入
- **路由匹配**: 内部链接需要确保对应路由存在
### 安全注意事项
- **外部链接**: 使用 `noopener,noreferrer` 参数
- **XSS 防护**: 对用户输入进行适当的转义
- **权限控制**: 敏感功能需要权限验证
## 测试
### 功能测试
访问 `/demo/quick-start-test` 进行完整的功能测试:
1. **收藏功能测试**
2. **编辑功能测试**
3. **删除功能测试**
4. **图标获取测试**
5. **数据持久化测试**
### 单元测试
```typescript
// tests/dashboard/QuickStart.spec.ts
import { mount } from '@vue/test-utils';
import QuickStart from '@/views/dashboard/components/QuickStart.vue';
describe('QuickStart Component', () => {
it('should render quick links', () => {
const wrapper = mount(QuickStart);
expect(wrapper.find('.quick-links-container').exists()).toBe(true);
});
});
```
## 更新日志
### v1.0.0 (最新)
- ✅ 新增编辑链接预填充功能
- ✅ 优化图标获取逻辑
- ✅ 支持切换收藏状态
- ✅ 完善错误处理和用户提示
- ✅ 新增右键删除链接功能
- ✅ 添加确认对话框
- ✅ 实现标签栏右键收藏功能
- ✅ 自动获取路由图标
- ✅ 数据持久化存储
- ✅ 基础快速开始组件
- ✅ 手动添加链接功能
- ✅ 内部和外部链接支持
@@ -45,48 +45,13 @@
</ElFormItem>
<ElFormItem label="图标" prop="icon">
<div class="icon-selector">
<div class="selected-icon">
<el-icon :size="24" :color="form.color">
<component :is="form.icon" />
</el-icon>
<span class="ml-2">{{ getIconName(form.icon) }}</span>
</div>
<ElButton size="small" @click="showIconPicker = true">选择图标</ElButton>
</div>
<IconSelect v-model="form.icon" width="400px" />
</ElFormItem>
<ElFormItem label="颜色" prop="color">
<ElColorPicker v-model="form.color" />
</ElFormItem>
</ElForm>
<!-- 图标选择器 -->
<ElDialog
v-model="showIconPicker"
title="选择图标"
width="600px"
append-to-body
>
<div class="icon-grid">
<div
v-for="icon in availableIcons"
:key="icon.name"
class="icon-item"
:class="{ active: form.icon === icon.name }"
@click="selectIcon(icon.name)"
>
<el-icon :size="24">
<component :is="icon.name" />
</el-icon>
<span class="icon-name">{{ icon.label }}</span>
</div>
</div>
<template #footer>
<ElButton @click="showIconPicker = false">取消</ElButton>
<ElButton type="primary" @click="showIconPicker = false">确定</ElButton>
</template>
</ElDialog>
<template #footer>
<span class="dialog-footer">
@@ -98,31 +63,8 @@
</template>
<script setup lang="ts">
import {
User,
Setting,
Document,
DataAnalysis,
Monitor,
Tools,
Bell,
Message,
Search,
House,
Files,
Calendar,
ChatDotRound,
Connection,
DataBoard,
Histogram,
PieChart,
TrendCharts,
Operation,
Service,
Guide,
Link
} from "@element-plus/icons-vue";
import { ElMessage, FormInstance, FormRules } from 'element-plus';
import IconSelect from '@/components/IconSelect/index.vue';
interface Props {
visible: boolean;
@@ -138,7 +80,6 @@ interface QuickLink {
title: string;
description: string;
icon: string;
color: string;
href: string;
action: 'navigate' | 'external';
}
@@ -147,7 +88,6 @@ const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const formRef = ref<FormInstance>();
const showIconPicker = ref(false);
// 对话框显示状态
const dialogVisible = computed({
@@ -162,37 +102,12 @@ const isEditMode = computed(() => !!props.editData);
const form = reactive<QuickLink>({
title: '',
description: '',
icon: 'User',
color: '#409EFF',
icon: 'user',
href: '',
action: 'navigate'
});
// 可用图标列表
const availableIcons = [
{ name: 'User', label: '用户' },
{ name: 'Setting', label: '设置' },
{ name: 'Document', label: '文档' },
{ name: 'DataAnalysis', label: '分析' },
{ name: 'Monitor', label: '监控' },
{ name: 'Tools', label: '工具' },
{ name: 'Bell', label: '通知' },
{ name: 'Message', label: '消息' },
{ name: 'Search', label: '搜索' },
{ name: 'House', label: '首页' },
{ name: 'Files', label: '文件' },
{ name: 'Calendar', label: '日历' },
{ name: 'ChatDotRound', label: '聊天' },
{ name: 'Connection', label: '连接' },
{ name: 'DataBoard', label: '数据' },
{ name: 'Histogram', label: '柱状图' },
{ name: 'PieChart', label: '饼图' },
{ name: 'TrendCharts', label: '趋势图' },
{ name: 'Operation', label: '操作' },
{ name: 'Service', label: '服务' },
{ name: 'Guide', label: '指南' },
{ name: 'Link', label: '链接' }
];
// 表单验证规则
const rules: FormRules = {
@@ -233,24 +148,14 @@ const rules: FormRules = {
]
};
// 获取图标名称
const getIconName = (iconName: string) => {
const icon = availableIcons.find(item => item.name === iconName);
return icon ? icon.label : iconName;
};
// 选择图标
const selectIcon = (iconName: string) => {
form.icon = iconName;
};
// 重置表单
const resetForm = () => {
Object.assign(form, {
title: '',
description: '',
icon: 'User',
color: '#409EFF',
icon: 'user',
href: '',
action: 'navigate'
});
@@ -286,7 +191,6 @@ watch(() => props.visible, (visible) => {
title: props.editData.title,
description: props.editData.description,
icon: props.editData.icon,
color: props.editData.color,
href: props.editData.href,
action: props.editData.action
});
@@ -307,7 +211,6 @@ watch(() => props.editData, (editData) => {
title: editData.title,
description: editData.description,
icon: editData.icon,
color: editData.color,
href: editData.href,
action: editData.action
});
@@ -325,54 +228,5 @@ watch(() => form.action, (newAction) => {
</script>
<style lang="scss" scoped>
.icon-selector {
display: flex;
align-items: center;
gap: 12px;
.selected-icon {
display: flex;
align-items: center;
padding: 8px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background-color: #f5f7fa;
}
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 12px;
max-height: 300px;
overflow-y: auto;
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 8px;
border: 1px solid #e4e7ed;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
border-color: #409EFF;
background-color: #f0f9ff;
}
&.active {
border-color: #409EFF;
background-color: #e1f3ff;
}
.icon-name {
font-size: 12px;
color: #606266;
margin-top: 4px;
text-align: center;
}
}
}
</style>
@@ -2,7 +2,14 @@
<ElCard class="mb-4" shadow="hover">
<template #header>
<div class="flex justify-between items-center">
<span class="font-bold text-16px">快速开始 / 便捷导航</span>
<div class="flex items-center gap-2">
<el-tooltip content="快速访问常用功能,支持内部路由跳转和外部链接打开。可以自定义添加、编辑和删除快捷方式。" placement="top">
<el-icon class="cursor-help" size="16">
<QuestionFilled />
</el-icon>
</el-tooltip>
<span class="font-bold text-16px">快速开始 / 便捷导航</span>
</div>
<ElButton size="small" type="primary" plain @click="handleAddQuickLink">
<el-icon>
<Plus />
@@ -12,7 +19,7 @@
</div>
</template>
<div class="quick-links-container">
<div class="quick-links-scroll">
<div class="quick-links-grid">
<div
v-for="(item, index) in quickLinks"
:key="index"
@@ -27,7 +34,7 @@
>
<div class="link-content-wrapper">
<div class="link-icon">
<el-icon :size="20" :color="item.color">
<el-icon :size="24">
<component :is="item.icon" />
</el-icon>
</div>
@@ -36,12 +43,6 @@
{{ item.title }}
<span v-if="item.action === 'external'" class="external-link-badge">外链</span>
</div>
<div class="link-description">{{ item.description }}</div>
</div>
<div class="link-arrow">
<el-icon size="14" color="#999">
<ArrowRight />
</el-icon>
</div>
</div>
@@ -84,7 +85,8 @@ import {
Plus,
ArrowRight,
Edit,
Delete
Delete,
QuestionFilled
} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from 'element-plus';
import { useRouter } from 'vue-router';
@@ -195,55 +197,39 @@ onUnmounted(() => {
<style lang="scss" scoped>
// 快速链接容器样式
.quick-links-container {
height: 280px; // 固定容器高度
overflow: hidden;
padding: 16px 0;
.quick-links-scroll {
height: 100%;
overflow-y: auto;
padding: 8px 0;
padding-right: 4px; // 为滚动条留出空间
// 自定义滚动条样式
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
&:hover {
background: #a8a8a8;
}
}
.quick-links-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.quick-link-item {
margin-bottom: 8px;
border-radius: 8px;
border: 1px solid transparent;
border-radius: 6px;
border: 1px solid #e4e7ed;
transition: all 0.3s ease;
width: 100%;
height: 100px;
max-width: 120px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #f5f7fa;
border-color: #e4e7ed;
transform: translateX(4px);
}
&:last-child {
margin-bottom: 0;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.link-content-wrapper {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 16px;
justify-content: center;
padding: 8px;
cursor: pointer;
width: 100%;
text-align: center;
}
.link-icon {
@@ -253,57 +239,40 @@ onUnmounted(() => {
width: 40px;
height: 40px;
border-radius: 8px;
background-color: #f0f2f5;
margin-right: 12px;
margin-bottom: 8px;
transition: all 0.3s ease;
}
.link-content {
flex: 1;
width: 100%;
display: flex;
justify-content: center;
.link-title {
font-size: 14px;
font-weight: 500;
margin-bottom: 2px;
line-height: 1.4;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
text-align: center;
.external-link-badge {
font-size: 10px;
font-weight: 400;
color: #fff;
background-color: #409EFF;
padding: 1px 4px;
border-radius: 2px;
line-height: 1.2;
}
}
.link-description {
font-size: 12px;
color: #909399;
line-height: 1.3;
}
}
.link-arrow {
display: flex;
align-items: center;
opacity: 0;
transition: all 0.3s ease;
}
&:hover {
.link-icon {
background-color: #e8f4ff;
}
.link-arrow {
opacity: 1;
transform: translateX(4px);
}
}
}
}
@@ -1,291 +0,0 @@
<template>
<div class="app-container">
<ElCard>
<template #header>
<div class="card-header">
<span class="font-bold text-18px">快速开始收藏功能测试</span>
</div>
</template>
<div class="content">
<ElAlert
title="功能说明"
type="info"
:closable="false"
show-icon
>
<p>在任意标签页上右击选择"收藏到快速开始"即可将当前页面添加到快速开始列表中</p>
<p>已收藏的页面会显示"已收藏"状态无法重复收藏</p>
</ElAlert>
<div class="mt-4">
<h3>测试步骤</h3>
<ol class="test-steps">
<li><strong>收藏功能</strong>在当前页面的标签上右击选择"收藏到快速开始"</li>
<li><strong>取消收藏</strong>在已收藏页面的标签上右击选择"取消收藏"</li>
<li><strong>自动图标</strong>收藏时会自动获取路由配置的图标</li>
<li><strong>查看收藏</strong>前往工作台页面查看快速开始组件</li>
<li><strong>编辑链接</strong>在快速开始组件中右击任意链接选择"编辑链接"</li>
<li><strong>删除链接</strong>在快速开始组件中右击任意链接选择"删除链接"</li>
<li><strong>验证跳转</strong>点击收藏的链接验证跳转功能</li>
</ol>
</div>
<div class="mt-4">
<h3>当前页面信息</h3>
<ElDescriptions :column="1" border>
<ElDescriptionsItem label="页面标题">
{{ $route.meta?.title || '快速开始测试页面' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="路由路径">
{{ $route.path }}
</ElDescriptionsItem>
<ElDescriptionsItem label="完整路径">
{{ $route.fullPath }}
</ElDescriptionsItem>
<ElDescriptionsItem label="路由图标">
<div class="flex items-center">
<el-icon v-if="$route.meta?.icon" :size="20" class="mr-2">
<component :is="$route.meta.icon" />
</el-icon>
<span>{{ $route.meta?.icon || '无图标配置' }}</span>
</div>
</ElDescriptionsItem>
<ElDescriptionsItem label="收藏状态">
<ElTag :type="isBookmarked ? 'success' : 'info'">
{{ isBookmarked ? '已收藏' : '未收藏' }}
</ElTag>
</ElDescriptionsItem>
</ElDescriptions>
</div>
<div class="mt-4">
<ElButton
type="primary"
@click="testBookmark"
:disabled="isBookmarked"
>
{{ isBookmarked ? '已收藏' : '手动收藏此页面' }}
</ElButton>
<ElButton
type="success"
@click="goToWorkplace"
class="ml-2"
>
前往工作台查看
</ElButton>
<ElButton
type="danger"
@click="removeBookmark"
:disabled="!isBookmarked"
class="ml-2"
>
删除收藏
</ElButton>
<ElButton
type="info"
@click="previewQuickLink"
class="ml-2"
>
预览收藏信息
</ElButton>
<ElButton
type="warning"
@click="testEditFunction"
class="ml-2"
>
测试编辑功能
</ElButton>
</div>
<div class="mt-4">
<h3>当前快速开始列表</h3>
<div class="quick-links-preview">
<div
v-for="(link, index) in quickLinks"
:key="index"
class="link-item"
>
<el-icon :color="link.color" class="mr-2">
<component :is="link.icon" />
</el-icon>
<span class="link-title">{{ link.title }}</span>
<ElTag size="small" class="ml-2">{{ link.action }}</ElTag>
</div>
</div>
</div>
</div>
</ElCard>
</div>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { quickStartManager, type QuickLink } from '@/utils/quickStartManager';
import { ElMessageBox } from 'element-plus';
defineOptions({
name: "QuickStartTest",
});
const route = useRoute();
const router = useRouter();
// 快速链接列表
const quickLinks = ref<QuickLink[]>(quickStartManager.getQuickLinks());
// 检查当前页面是否已收藏
const isBookmarked = computed(() => {
return quickStartManager.isLinkExists(route.fullPath);
});
// 手动收藏当前页面
const testBookmark = () => {
const quickLink = quickStartManager.createQuickLinkFromRoute(
route,
'快速开始测试页面',
'用于测试快速开始收藏功能的演示页面'
);
quickStartManager.addQuickLink(quickLink);
// 更新快速链接列表
quickLinks.value = quickStartManager.getQuickLinks();
};
// 删除收藏
const removeBookmark = () => {
const links = quickStartManager.getQuickLinks();
const targetLink = links.find(link => link.href === route.fullPath);
if (targetLink?.id) {
quickStartManager.removeQuickLink(targetLink.id);
// 更新快速链接列表
quickLinks.value = quickStartManager.getQuickLinks();
}
};
// 预览收藏信息
const previewQuickLink = () => {
const quickLink = quickStartManager.createQuickLinkFromRoute(
route,
'快速开始测试页面',
'用于测试快速开始收藏功能的演示页面'
);
ElMessageBox.alert(
`<div style="text-align: left;">
<p><strong>标题:</strong>${quickLink.title}</p>
<p><strong>描述:</strong>${quickLink.description}</p>
<p><strong>图标:</strong>${quickLink.icon}</p>
<p><strong>颜色:</strong><span style="color: ${quickLink.color};">${quickLink.color}</span></p>
<p><strong>链接:</strong>${quickLink.href}</p>
<p><strong>类型:</strong>${quickLink.action}</p>
</div>`,
'收藏信息预览',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '确定'
}
);
};
// 测试编辑功能
const testEditFunction = () => {
const links = quickStartManager.getQuickLinks();
if (links.length === 0) {
ElMessage.warning('没有可编辑的快速链接,请先添加一些链接');
return;
}
ElMessageBox.alert(
`<div style="text-align: left;">
<p><strong>测试说明:</strong></p>
<ol>
<li>前往工作台页面</li>
<li>在快速开始组件中右击任意链接</li>
<li>选择"编辑链接"</li>
<li>验证表单是否预填充了现有数据</li>
<li>修改任意字段后保存</li>
<li>确认修改是否生效</li>
</ol>
<p><strong>当前可编辑的链接:</strong></p>
<ul>
${links.slice(0, 3).map(link => `<li>${link.title} (${link.href})</li>`).join('')}
${links.length > 3 ? `<li>... 还有 ${links.length - 3} 个链接</li>` : ''}
</ul>
</div>`,
'编辑功能测试指南',
{
dangerouslyUseHTMLString: true,
confirmButtonText: '前往工作台测试',
cancelButtonText: '取消',
showCancelButton: true
}
).then(() => {
goToWorkplace();
}).catch(() => {
// 用户取消
});
};
// 前往工作台
const goToWorkplace = () => {
router.push('/dashboard/workplace');
};
// 监听快速链接变化
const updateQuickLinks = (links: QuickLink[]) => {
quickLinks.value = links;
};
onMounted(() => {
quickStartManager.addListener(updateQuickLinks);
});
onUnmounted(() => {
quickStartManager.removeListener(updateQuickLinks);
});
</script>
<style lang="scss" scoped>
.content {
padding: 20px 0;
}
.test-steps {
padding-left: 20px;
li {
margin-bottom: 8px;
line-height: 1.6;
}
}
.quick-links-preview {
max-height: 300px;
overflow-y: auto;
border: 1px solid var(--el-border-color);
border-radius: 4px;
padding: 12px;
.link-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--el-border-color-lighter);
&:last-child {
border-bottom: none;
}
.link-title {
flex: 1;
font-size: 14px;
}
}
}
</style>