Sfoglia il codice sorgente

feat:清理无关页面

lizhi 6 mesi fa
parent
commit
67ad8e2824
59 ha cambiato i file con 16 aggiunte e 37035 eliminazioni
  1. 0 1
      web/.husky/commit-msg
  2. 0 1
      web/.husky/pre-commit
  3. 0 2
      web/src/App.vue
  4. 0 3
      web/src/components/core/layouts/art-header-bar/index.vue
  5. 11 9
      web/src/components/custom/FollowDrawer.vue
  6. 0 128
      web/src/config/fastEnter.ts
  7. 1 2
      web/src/config/index.ts
  8. 0 25643
      web/src/mock/json/chinaMap.json
  9. BIN
      web/src/mock/json/chinaMap.json.gz
  10. 0 193
      web/src/mock/temp/articleList.ts
  11. 0 79
      web/src/mock/temp/commentDetail.ts
  12. 0 242
      web/src/mock/temp/commentList.ts
  13. 0 273
      web/src/mock/temp/formData.ts
  14. 0 1224
      web/src/mock/upgrade/changeLog.ts
  15. 0 42
      web/src/router/routesAlias.ts
  16. 3 3
      web/src/router/utils/registerRoutes.ts
  17. 1 1
      web/src/utils/sys/index.ts
  18. 0 269
      web/src/views/article/comment/index.vue
  19. 0 116
      web/src/views/article/detail/index.vue
  20. 0 380
      web/src/views/article/list/index.vue
  21. 0 359
      web/src/views/article/publish/index.vue
  22. 0 246
      web/src/views/change/log/index.vue
  23. 0 634
      web/src/views/examples/forms/search-bar.vue
  24. 0 64
      web/src/views/examples/tables/basic.vue
  25. 0 1536
      web/src/views/examples/tables/index.vue
  26. 0 136
      web/src/views/examples/tables/tree.vue
  27. 0 135
      web/src/views/examples/tabs/index.vue
  28. 0 15
      web/src/views/exception/403/index.vue
  29. 0 15
      web/src/views/exception/404/index.vue
  30. 0 15
      web/src/views/exception/500/index.vue
  31. 0 47
      web/src/views/outside/Iframe.vue
  32. 0 22
      web/src/views/result/fail/index.vue
  33. 0 21
      web/src/views/result/success/index.vue
  34. 0 287
      web/src/views/safeguard/server/index.vue
  35. 0 658
      web/src/views/system/menu/index.vue
  36. 0 5
      web/src/views/system/nested/menu1/index.vue
  37. 0 5
      web/src/views/system/nested/menu2/index.vue
  38. 0 5
      web/src/views/system/nested/menu3/index.vue
  39. 0 5
      web/src/views/system/nested/menu3/menu3-2/index.vue
  40. 0 215
      web/src/views/template/banners/index.vue
  41. 0 294
      web/src/views/template/calendar/index.vue
  42. 0 454
      web/src/views/template/cards/index.vue
  43. 0 383
      web/src/views/template/charts/index.vue
  44. 0 771
      web/src/views/template/chat/index.vue
  45. 0 17
      web/src/views/template/map/index.vue
  46. 0 307
      web/src/views/template/pricing/index.vue
  47. 0 124
      web/src/views/widgets/context-menu/index.vue
  48. 0 214
      web/src/views/widgets/count-to/index.vue
  49. 0 125
      web/src/views/widgets/drag/index.vue
  50. 0 115
      web/src/views/widgets/excel/index.vue
  51. 0 103
      web/src/views/widgets/fireworks/index.vue
  52. 0 175
      web/src/views/widgets/icon-list/index.vue
  53. 0 45
      web/src/views/widgets/icon-selector/index.vue
  54. 0 39
      web/src/views/widgets/image-crop/index.vue
  55. 0 136
      web/src/views/widgets/qrcode/index.vue
  56. 0 45
      web/src/views/widgets/text-scroll/index.vue
  57. 0 31
      web/src/views/widgets/video/index.vue
  58. 0 544
      web/src/views/widgets/wang-editor/index.vue
  59. 0 77
      web/src/views/widgets/watermark/index.vue

+ 0 - 1
web/.husky/commit-msg

@@ -1 +0,0 @@
-pnpm dlx commitlint --edit $1

+ 0 - 1
web/.husky/pre-commit

@@ -1 +0,0 @@
-pnpm run lint:lint-staged

+ 0 - 2
web/src/App.vue

@@ -30,7 +30,5 @@
     checkStorageCompatibility()
     // 提升暗黑主题下页面刷新视觉体验
     setThemeTransitionClass(false)
-    // 系统升级
-    systemUpgrade()
   })
 </script>

+ 0 - 3
web/src/components/core/layouts/art-header-bar/index.vue

@@ -24,9 +24,6 @@
           </div>
         </div>
 
-        <!-- 快速入口 -->
-        <ArtFastEnter v-if="shouldShowFastEnter && width >= headerBarFastEnterMinWidth" />
-
         <!-- 面包屑 -->
         <ArtBreadcrumb
           v-if="(shouldShowBreadcrumb && isLeftMenu) || (shouldShowBreadcrumb && isDualMenu)"

+ 11 - 9
web/src/components/custom/FollowDrawer.vue

@@ -57,7 +57,6 @@
   const props = defineProps<Props>()
   const timelineData = reactive<Api.Follow.timeLineItem[]>([])
   const delRes = (res:any) => {
-    console.log(`%c res == `, 'background:#41b883 ; padding:1px; color:#fff', res);
     if (res.length == 0) {
       return
     }
@@ -75,16 +74,19 @@
         timelineData[index].list.push(item)
       }
     }
-    console.log(`%c timelineData == `, 'background:#41b883 ; padding:1px; color:#fff', timelineData);
   }
 
-  watch(() => [props.type, props.first_id, props.second_id], async () => {
-    timelineData.splice(0, timelineData.length)
-    followApi.schoolAll(props.first_id, props.second_id).then((res:any) => delRes(res)).catch((e) => {
-      ElMessage.error('获取数据失败')
-      console.log(`%c e == `, 'background:#41b883 ; padding:1px; color:#fff', e);
-    })
-  })
+  watch(
+    () => [props.type, props.first_id, props.second_id],
+    async () => {
+      timelineData.splice(0, timelineData.length)
+      followApi.schoolAll(props.first_id, props.second_id).then((res:any) => delRes(res)).catch((e) => {
+        ElMessage.error('获取数据失败')
+        console.error(`%c e == `, 'background:#41b883 ; padding:1px; color:#fff', e);
+      })
+    },
+    {immediate: true}
+  )
 
 </script>
 

+ 0 - 128
web/src/config/fastEnter.ts

@@ -1,128 +0,0 @@
-/**
- * 快速入口配置
- * 包含:应用列表、快速链接等配置
- */
-import { RoutesAlias } from '@/router/routesAlias'
-import { WEB_LINKS } from '@/utils/constants'
-import type { FastEnterConfig } from '@/types/config'
-
-const fastEnterConfig: FastEnterConfig = {
-  // 显示条件(屏幕宽度)
-  minWidth: 1200,
-  // 应用列表
-  applications: [
-    {
-      name: '工作台',
-      description: '系统概览与数据统计',
-      icon: '&#xe721;',
-      iconColor: '#377dff',
-      path: RoutesAlias.Dashboard,
-      enabled: true,
-      order: 1
-    },
-    {
-      name: '分析页',
-      description: '数据分析与可视化',
-      icon: '&#xe812;',
-      iconColor: '#ff3b30',
-      path: RoutesAlias.Analysis,
-      enabled: true,
-      order: 2
-    },
-    {
-      name: '礼花效果',
-      description: '动画特效展示',
-      icon: '&#xe7ed;',
-      iconColor: '#7A7FFF',
-      path: RoutesAlias.Fireworks,
-      enabled: true,
-      order: 3
-    },
-    {
-      name: '聊天',
-      description: '即时通讯功能',
-      icon: '&#xe70a;',
-      iconColor: '#13DEB9',
-      path: RoutesAlias.Chat,
-      enabled: true,
-      order: 4
-    },
-    {
-      name: '官方文档',
-      description: '使用指南与开发文档',
-      icon: '&#xe788;',
-      iconColor: '#ffb100',
-      path: WEB_LINKS.DOCS,
-      enabled: true,
-      order: 5
-    },
-    {
-      name: '技术支持',
-      description: '技术支持与问题反馈',
-      icon: '&#xe86e;',
-      iconColor: '#ff6b6b',
-      path: WEB_LINKS.COMMUNITY,
-      enabled: true,
-      order: 6
-    },
-    {
-      name: '更新日志',
-      description: '版本更新与变更记录',
-      icon: '&#xe81c;',
-      iconColor: '#38C0FC',
-      path: RoutesAlias.ChangeLog,
-      enabled: true,
-      order: 7
-    },
-    {
-      name: '哔哩哔哩',
-      description: '技术分享与交流',
-      icon: '&#xe6b4;',
-      iconColor: '#FB7299',
-      path: WEB_LINKS.BILIBILI,
-      enabled: true,
-      order: 8
-    }
-  ],
-  // 快速链接
-  quickLinks: [
-    {
-      name: '登录',
-      path: RoutesAlias.Login,
-      enabled: true,
-      order: 1
-    },
-    {
-      name: '注册',
-      path: RoutesAlias.Register,
-      enabled: true,
-      order: 2
-    },
-    {
-      name: '忘记密码',
-      path: RoutesAlias.ForgetPassword,
-      enabled: true,
-      order: 3
-    },
-    {
-      name: '定价',
-      path: RoutesAlias.Pricing,
-      enabled: true,
-      order: 4
-    },
-    {
-      name: '个人中心',
-      path: RoutesAlias.UserCenter,
-      enabled: true,
-      order: 5
-    },
-    {
-      name: '留言管理',
-      path: RoutesAlias.Comment,
-      enabled: true,
-      order: 6
-    }
-  ]
-}
-
-export default Object.freeze(fastEnterConfig)

+ 1 - 2
web/src/config/index.ts

@@ -5,7 +5,6 @@
 import { MenuThemeEnum, MenuTypeEnum, SystemThemeEnum } from '@/enums/appEnum'
 import { SystemConfig } from '@/types/config'
 import { configImages } from './assets/images'
-import fastEnterConfig from './fastEnter'
 import { headerBarConfig } from './headerBar'
 
 const appConfig: SystemConfig = {
@@ -134,7 +133,7 @@ const appConfig: SystemConfig = {
     defaultTabStyle: 'tab-default' // 标签样式
   },
   // 快速入口配置
-  fastEnter: fastEnterConfig,
+  fastEnter: undefined,
   // 顶部栏功能配置
   headerBar: headerBarConfig
 }

File diff suppressed because it is too large
+ 0 - 25643
web/src/mock/json/chinaMap.json


BIN
web/src/mock/json/chinaMap.json.gz


File diff suppressed because it is too large
+ 0 - 193
web/src/mock/temp/articleList.ts


+ 0 - 79
web/src/mock/temp/commentDetail.ts

@@ -1,79 +0,0 @@
-export interface Comment {
-  id: number
-  author: string
-  content: string
-  timestamp: string
-  replies: Comment[]
-}
-
-export const commentList = ref<Comment[]>([
-  {
-    id: 1,
-    author: '白夜',
-    content: '黑神话悟空的打斗场面真的燃爆了!期待上线!',
-    timestamp: '2024-09-04 09:00',
-    replies: [
-      {
-        id: 101,
-        author: '星河',
-        content: '是啊,特别是那些技能特效,简直帅炸!',
-        timestamp: '2024-09-04 09:15',
-        replies: [
-          {
-            id: 201,
-            author: '光芒',
-            content: '希望优化能跟上,不然这么好的画面如果卡顿就可惜了。',
-            timestamp: '2024-09-04 09:30',
-            replies: []
-          }
-        ]
-      }
-    ]
-  },
-  {
-    id: 2,
-    author: '浮生',
-    content: '据说黑神话悟空需要很高的配置,不知道我的电脑能不能跑起来。',
-    timestamp: '2024-09-04 10:00',
-    replies: [
-      {
-        id: 102,
-        author: '晨曦',
-        content: '同担心啊,听说需要至少RTX 3070才能高效运行。',
-        timestamp: '2024-09-04 10:20',
-        replies: [
-          {
-            id: 202,
-            author: '流光',
-            content: '我是打算升级配置,等这款游戏就是了。',
-            timestamp: '2024-09-04 10:40',
-            replies: []
-          }
-        ]
-      }
-    ]
-  },
-  {
-    id: 3,
-    author: '风铃',
-    content: '130GB的存储要求有点夸张啊,不过画质这么好,也情有可原。',
-    timestamp: '2024-09-04 11:00',
-    replies: [
-      {
-        id: 103,
-        author: '云端',
-        content: '确实有点高,不过为了这种品质的游戏,值得。',
-        timestamp: '2024-09-04 11:15',
-        replies: [
-          {
-            id: 203,
-            author: '梦境',
-            content: '希望发售后能优化一下安装包体积。',
-            timestamp: '2024-09-04 11:30',
-            replies: []
-          }
-        ]
-      }
-    ]
-  }
-])

+ 0 - 242
web/src/mock/temp/commentList.ts

@@ -1,242 +0,0 @@
-export const commentList = reactive([
-  {
-    id: 1,
-    date: '2024-9-3',
-    content: '发现了一个超级好用的工具,开心',
-    collection: 5,
-    comment: 8,
-    userName: '匿名'
-  },
-  {
-    id: 2,
-    date: '2024-9-3',
-    content: '今天的代码写得很顺利!',
-    collection: 3,
-    comment: 2,
-    userName: 'Coder123'
-  },
-  {
-    id: 3,
-    date: '2024-9-4',
-    content: '遇到个bug,调试了一整天',
-    collection: 7,
-    comment: 10,
-    userName: 'DebugMaster'
-  },
-  {
-    id: 4,
-    date: '2024-9-4',
-    content: '学Node真的是一件很有趣的事',
-    collection: 9,
-    comment: 4,
-    userName: 'NodeLover'
-  },
-  {
-    id: 5,
-    date: '2024-9-5',
-    content: '今天的进度有点慢,需要加把劲了',
-    collection: 2,
-    comment: 3,
-    userName: '努力中的小白'
-  },
-  {
-    id: 6,
-    date: '2024-9-5',
-    content: '太好了,终于解决了一个难题!',
-    collection: 11,
-    comment: 5,
-    userName: '匿名'
-  },
-  {
-    id: 7,
-    date: '2024-9-6',
-    content: '学会了新的Node技巧,开心!',
-    collection: 4,
-    comment: 7,
-    userName: '开心每一天'
-  },
-  {
-    id: 8,
-    date: '2024-9-6',
-    content: '代码优化真的是一个细致活',
-    collection: 6,
-    comment: 4,
-    userName: '精益求精'
-  },
-  {
-    id: 9,
-    date: '2024-9-7',
-    content: '今天的工作太顺利了,完美!',
-    collection: 10,
-    comment: 9,
-    userName: '完美主义者'
-  },
-  {
-    id: 10,
-    date: '2024-9-7',
-    content: '需要多练习,才能掌握更多技能',
-    collection: 5,
-    comment: 6,
-    userName: '匿名'
-  },
-  {
-    id: 11,
-    date: '2024-9-8',
-    content: '每天进步一点点,终会成功',
-    collection: 8,
-    comment: 7,
-    userName: '逐梦者'
-  },
-  {
-    id: 12,
-    date: '2024-9-8',
-    content: '与其抱怨,不如努力改变',
-    collection: 12,
-    comment: 10,
-    userName: '改变命运'
-  },
-  {
-    id: 13,
-    date: '2024-9-9',
-    content: '今天尝试了新的库,感觉不错',
-    collection: 9,
-    comment: 8,
-    userName: '新手尝试'
-  },
-  {
-    id: 14,
-    date: '2024-9-9',
-    content: '写代码也需要灵感,今天灵感不错',
-    collection: 6,
-    comment: 5,
-    userName: '灵感源泉'
-  },
-  {
-    id: 15,
-    date: '2024-9-10',
-    content: '感谢社区的帮助,让我解决了问题',
-    collection: 7,
-    comment: 4,
-    userName: '受益匪浅'
-  },
-  {
-    id: 16,
-    date: '2024-9-10',
-    content: '学习的路上要保持耐心和恒心',
-    collection: 3,
-    comment: 2,
-    userName: '匿名'
-  },
-  {
-    id: 17,
-    date: '2024-9-11',
-    content: '今天学习了异步编程的知识,受益匪浅',
-    collection: 10,
-    comment: 9,
-    userName: '异步学习者'
-  },
-  {
-    id: 18,
-    date: '2024-9-11',
-    content: '今天的代码质量提升了不少',
-    collection: 11,
-    comment: 6,
-    userName: '代码匠人'
-  },
-  {
-    id: 19,
-    date: '2024-9-12',
-    content: '感觉学习编程真的很有成就感',
-    collection: 8,
-    comment: 7,
-    userName: '成就感满满'
-  },
-  {
-    id: 20,
-    date: '2024-9-12',
-    content: '要加倍努力,才能超越昨天的自己',
-    collection: 5,
-    comment: 4,
-    userName: '努力超越'
-  },
-  {
-    id: 21,
-    date: '2024-9-13',
-    content: '今天的代码写得很顺手,继续保持',
-    collection: 9,
-    comment: 8,
-    userName: '顺风顺水'
-  },
-  {
-    id: 22,
-    date: '2024-9-13',
-    content: '写代码也需要创意,今天很有创意',
-    collection: 7,
-    comment: 5,
-    userName: '创意无限'
-  },
-  {
-    id: 23,
-    date: '2024-9-14',
-    content: '遇到的难题解决了,感觉很有成就感',
-    collection: 10,
-    comment: 9,
-    userName: '匿名'
-  },
-  {
-    id: 24,
-    date: '2024-9-14',
-    content: '今天的编程练习很有收获',
-    collection: 8,
-    comment: 7,
-    userName: '收获满满'
-  },
-  {
-    id: 25,
-    date: '2024-9-15',
-    content: '学习编程的路上,有苦有甜',
-    collection: 6,
-    comment: 4,
-    userName: '苦乐编程'
-  },
-  {
-    id: 26,
-    date: '2024-9-15',
-    content: '今天的代码写得特别流畅,开心!',
-    collection: 11,
-    comment: 6,
-    userName: '流畅编程'
-  },
-  {
-    id: 27,
-    date: '2024-9-16',
-    content: '今天的编程练习让我更有信心',
-    collection: 9,
-    comment: 8,
-    userName: '信心满满'
-  },
-  {
-    id: 28,
-    date: '2024-9-16',
-    content: '今天的编程学习让我收获很多',
-    collection: 7,
-    comment: 5,
-    userName: '匿名'
-  },
-  {
-    id: 29,
-    date: '2024-9-17',
-    content: '编程是一门艺术,今天体会到了',
-    collection: 12,
-    comment: 10,
-    userName: '编程艺术家'
-  },
-  {
-    id: 30,
-    date: '2024-9-17',
-    content: '今天的代码写得很顺利,继续加油!',
-    collection: 10,
-    comment: 9,
-    userName: '匿名'
-  }
-])

+ 0 - 273
web/src/mock/temp/formData.ts

@@ -1,273 +0,0 @@
-import avatar1 from '@/assets/img/avatar/avatar1.webp'
-import avatar2 from '@/assets/img/avatar/avatar2.webp'
-import avatar3 from '@/assets/img/avatar/avatar3.webp'
-import avatar4 from '@/assets/img/avatar/avatar4.webp'
-import avatar5 from '@/assets/img/avatar/avatar5.webp'
-import avatar6 from '@/assets/img/avatar/avatar6.webp'
-import avatar7 from '@/assets/img/avatar/avatar7.webp'
-import avatar8 from '@/assets/img/avatar/avatar8.webp'
-import avatar9 from '@/assets/img/avatar/avatar9.webp'
-import avatar10 from '@/assets/img/avatar/avatar10.webp'
-
-export interface User {
-  id: number
-  username: string
-  gender: 1 | 0
-  mobile: string
-  email: string
-  dep: string
-  status: string
-  create_time: string
-  avatar: string
-}
-
-// 用户列表
-export const ACCOUNT_TABLE_DATA: User[] = [
-  {
-    id: 1,
-    username: 'alexmorgan',
-    gender: 1,
-    mobile: '18670001591',
-    email: 'alexmorgan@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-09-09 10:01:10',
-    avatar: avatar1
-  },
-  {
-    id: 2,
-    username: 'sophiabaker',
-    gender: 1,
-    mobile: '17766664444',
-    email: 'sophiabaker@company.com',
-    dep: '电商部',
-    status: '1',
-    create_time: '2020-10-10 13:01:12',
-    avatar: avatar2
-  },
-  {
-    id: 3,
-    username: 'liampark',
-    gender: 1,
-    mobile: '18670001597',
-    email: 'liampark@company.com',
-    dep: '人事部',
-    status: '1',
-    create_time: '2020-11-14 12:01:45',
-    avatar: avatar3
-  },
-  {
-    id: 4,
-    username: 'oliviagrant',
-    gender: 0,
-    mobile: '18670001596',
-    email: 'oliviagrant@company.com',
-    dep: '产品部',
-    status: '1',
-    create_time: '2020-11-14 09:01:20',
-    avatar: avatar4
-  },
-  {
-    id: 5,
-    username: 'emmawilson',
-    gender: 0,
-    mobile: '18670001595',
-    email: 'emmawilson@company.com',
-    dep: '财务部',
-    status: '1',
-    create_time: '2020-11-13 11:01:05',
-    avatar: avatar5
-  },
-  {
-    id: 6,
-    username: 'noahevan',
-    gender: 1,
-    mobile: '18670001594',
-    email: 'noahevan@company.com',
-    dep: '运营部',
-    status: '1',
-    create_time: '2020-10-11 13:10:26',
-    avatar: avatar6
-  },
-  {
-    id: 7,
-    username: 'avamartin',
-    gender: 1,
-    mobile: '18123820191',
-    email: 'avamartin@company.com',
-    dep: '客服部',
-    status: '2',
-    create_time: '2020-05-14 12:05:10',
-    avatar: avatar7
-  },
-  {
-    id: 8,
-    username: 'jacoblee',
-    gender: 1,
-    mobile: '18670001592',
-    email: 'jacoblee@company.com',
-    dep: '总经办',
-    status: '3',
-    create_time: '2020-11-12 07:22:25',
-    avatar: avatar8
-  },
-  {
-    id: 9,
-    username: 'miaclark',
-    gender: 0,
-    mobile: '18670001581',
-    email: 'miaclark@company.com',
-    dep: '研发部',
-    status: '4',
-    create_time: '2020-06-12 05:04:20',
-    avatar: avatar9
-  },
-  {
-    id: 10,
-    username: 'ethanharris',
-    gender: 1,
-    mobile: '13755554444',
-    email: 'ethanharris@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-12 16:01:10',
-    avatar: avatar10
-  },
-  {
-    id: 11,
-    username: 'isabellamoore',
-    gender: 1,
-    mobile: '13766660000',
-    email: 'isabellamoore@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-14 12:01:20',
-    avatar: avatar6
-  },
-  {
-    id: 12,
-    username: 'masonwhite',
-    gender: 1,
-    mobile: '18670001502',
-    email: 'masonwhite@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-14 12:01:20',
-    avatar: avatar7
-  },
-  {
-    id: 13,
-    username: 'charlottehall',
-    gender: 1,
-    mobile: '13006644977',
-    email: 'charlottehall@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-14 12:01:20',
-    avatar: avatar8
-  },
-  {
-    id: 14,
-    username: 'benjaminscott',
-    gender: 0,
-    mobile: '13599998888',
-    email: 'benjaminscott@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-14 12:01:20',
-    avatar: avatar9
-  },
-  {
-    id: 15,
-    username: 'ameliaking',
-    gender: 1,
-    mobile: '13799998888',
-    email: 'ameliaking@company.com',
-    dep: '研发部',
-    status: '1',
-    create_time: '2020-11-14 12:01:20',
-    avatar: avatar10
-  }
-]
-
-export interface Role {
-  roleName: string
-  roleCode?: string
-  des: string
-  date: string
-  enable: boolean
-}
-
-// 角色列表
-export const ROLE_LIST_DATA: Role[] = [
-  {
-    roleName: '超级管理员',
-    roleCode: 'R_SUPER',
-    des: '拥有系统全部权限',
-    date: '2025-05-15 12:30:45',
-    enable: true
-  },
-  {
-    roleName: '管理员',
-    roleCode: 'R_ADMIN',
-    des: '拥有系统管理权限',
-    date: '2025-05-15 12:30:45',
-    enable: true
-  },
-  {
-    roleName: '普通用户',
-    roleCode: 'R_USER',
-    des: '拥有系统普通权限',
-    date: '2025-05-15 12:30:45',
-    enable: true
-  },
-  {
-    roleName: '财务管理员',
-    roleCode: 'R_FINANCE',
-    des: '管理财务相关权限',
-    date: '2025-05-16 09:15:30',
-    enable: true
-  },
-  {
-    roleName: '数据分析师',
-    roleCode: 'R_ANALYST',
-    des: '拥有数据分析权限',
-    date: '2025-05-16 11:45:00',
-    enable: false
-  },
-  {
-    roleName: '客服专员',
-    roleCode: 'R_SUPPORT',
-    des: '处理客户支持请求',
-    date: '2025-05-17 14:30:22',
-    enable: true
-  },
-  {
-    roleName: '营销经理',
-    roleCode: 'R_MARKETING',
-    des: '管理营销活动权限',
-    date: '2025-05-17 15:10:50',
-    enable: true
-  },
-  {
-    roleName: '访客用户',
-    roleCode: 'R_GUEST',
-    des: '仅限浏览权限',
-    date: '2025-05-18 08:25:40',
-    enable: false
-  },
-  {
-    roleName: '系统维护员',
-    roleCode: 'R_MAINTAINER',
-    des: '负责系统维护和更新',
-    date: '2025-05-18 09:50:12',
-    enable: true
-  },
-  {
-    roleName: '项目经理',
-    roleCode: 'R_PM',
-    des: '管理项目相关权限',
-    date: '2025-05-19 13:40:35',
-    enable: true
-  }
-]

File diff suppressed because it is too large
+ 0 - 1224
web/src/mock/upgrade/changeLog.ts


+ 0 - 42
web/src/router/routesAlias.ts

@@ -8,53 +8,11 @@ export enum RoutesAlias {
   Login = '/auth/login', // 登录
   Register = '/auth/register', // 注册
   ForgetPassword = '/auth/forget-password', // 忘记密码
-  Exception403 = '/exception/403', // 403
-  Exception404 = '/exception/404', // 404
-  Exception500 = '/exception/500', // 500
-  Success = '/result/success', // 成功
-  Fail = '/result/fail', // 失败
   Dashboard = '/dashboard/console', // 工作台
-  Analysis = '/dashboard/analysis', // 分析页
-  Ecommerce = '/dashboard/ecommerce', // 电子商务
-  IconList = '/widgets/icon-list', // 图标列表
-  IconSelector = '/widgets/icon-selector', // 图标选择器
-  ImageCrop = '/widgets/image-crop', // 图片裁剪
-  Excel = '/widgets/excel', // Excel
-  Video = '/widgets/video', // 视频
-  CountTo = '/widgets/count-to', // 计数
-  WangEditor = '/widgets/wang-editor', // 富文本编辑器
-  Watermark = '/widgets/watermark', // 水印
-  ContextMenu = '/widgets/context-menu', // 上下文菜单
-  Qrcode = '/widgets/qrcode', // 二维码
-  Drag = '/widgets/drag', // 拖拽
-  TextScroll = '/widgets/text-scroll', // 文字滚动
-  Fireworks = '/widgets/fireworks', // 礼花效果
-  Chat = '/template/chat', // 聊天
-  Cards = '/template/cards', // 卡片
-  Banners = '/template/banners', // 横幅
-  Charts = '/template/charts', // 图表
-  Map = '/template/map', // 地图
-  Calendar = '/template/calendar', // 日历
-  Pricing = '/template/pricing', // 定价
-  ArticleList = '/article/list', // 文章列表
-  ArticleDetail = '/article/detail', // 文章详情
-  Comment = '/article/comment', // 评论
-  ArticlePublish = '/article/publish', // 文章发布
   User = '/system/user', // 账户
   Role = '/system/role', // 角色
   UserCenter = '/system/user-center', // 用户中心
   Menu = '/system/menu', // 菜单
-  NestedMenu1 = '/system/nested/menu1', // 嵌套菜单1
-  NestedMenu21 = '/system/nested/menu2', // 嵌套菜单2-1
-  NestedMenu31 = '/system/nested/menu3', // 嵌套菜单3-1
-  NestedMenu321 = '/system/nested/menu3/menu3-2', // 嵌套菜单3-2-1
-  Server = '/safeguard/server', // 服务器
-  ChangeLog = '/change/log', // 更新日志
-  ExamplesTabs = '/examples/tabs', // 标签页
-  ExamplesTablesBasic = '/examples/tables/basic', // 基础表格示例
-  ExamplesTables = '/examples/tables', // 高级表格示例
-  ExamplesTablesTree = '/examples/tables/tree', // 左右布局表格示例
-  ExamplesSearchBar = '/examples/forms/search-bar', // 搜索表单示例
   SchoolList = '/school/list', // 学校列表
   SchoolInfo = '/school/info', // 学校详情
   SchoolEdit = '/school/edit', // 编辑学校

+ 3 - 3
web/src/router/utils/registerRoutes.ts

@@ -215,9 +215,9 @@ function handleIframeRoute(
   route: AppRouteRecord,
   iframeRoutes: AppRouteRecord[]
 ): void {
-  converted.path = `/outside/iframe/${String(route.name)}`
-  converted.component = () => import('@/views/outside/Iframe.vue')
-  iframeRoutes.push(route)
+  // converted.path = `/outside/iframe/${String(route.name)}`
+  // converted.component = () => import('@/views/outside/Iframe.vue')
+  // iframeRoutes.push(route)
 }
 
 /**

+ 1 - 1
web/src/utils/sys/index.ts

@@ -2,5 +2,5 @@
  * 系统管理相关工具函数统一导出
  */
 
-export * from './upgrade'
+// export * from './upgrade'
 export { default as mittBus } from './mittBus'

+ 0 - 269
web/src/views/article/comment/index.vue

@@ -1,269 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1 class="title">留言墙</h1>
-    <p class="desc">每一份留言都记录了您的想法,也为我们提供了珍贵的回忆</p>
-
-    <div class="list">
-      <ul class="offset">
-        <li
-          class="comment-box"
-          v-for="item in commentList"
-          :key="item.id"
-          :style="{ background: randomColor() }"
-          @click="openDrawer(item)"
-        >
-          <p class="date">{{ item.date }}</p>
-          <p class="content">{{ item.content }}</p>
-          <div class="bottom">
-            <div class="left">
-              <span><i class="iconfont-sys">&#xe6eb;</i>{{ item.collection }}</span>
-              <span><i class="iconfont-sys">&#xe6e9;</i>{{ item.comment }}</span>
-            </div>
-            <div class="right">
-              <span>{{ item.userName }}</span>
-            </div>
-          </div>
-        </li>
-      </ul>
-    </div>
-
-    <ElDrawer
-      lDrawer
-      v-model="showDrawer"
-      :lock-scroll="false"
-      :size="360"
-      modal-class="comment-modal"
-    >
-      <template #header>
-        <h4>详情</h4>
-      </template>
-      <template #default>
-        <div class="drawer-default">
-          <div class="comment-box" :style="{ background: randomColor() }">
-            <p class="date">{{ clickItem.date }}</p>
-            <p class="content">{{ clickItem.content }}</p>
-            <div class="bottom">
-              <div class="left">
-                <span><i class="iconfont-sys">&#xe6eb;</i>{{ clickItem.collection }}</span>
-                <span><i class="iconfont-sys">&#xe6e9;</i>{{ clickItem.comment }}</span>
-              </div>
-              <div class="right">
-                <span>{{ clickItem.userName }}</span>
-              </div>
-            </div>
-          </div>
-
-          <!-- 评论组件 -->
-          <CommentWidget />
-        </div>
-      </template>
-      <template #footer>
-        <div>
-          <!-- <ElButton @click="cancelClick">cancel</ElButton> -->
-          <!-- <ElButton type="primary" @click="confirmClick">confirm</ElButton> -->
-        </div>
-      </template>
-    </ElDrawer>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { commentList } from '@/mock/temp/commentList'
-  const showDrawer = ref(false)
-
-  defineOptions({ name: 'ArticleComment' })
-
-  // const colorList = reactive([
-  //   'rgba(216, 248, 255, 0.8)',
-  //   'rgba(253, 223, 217, 0.8)',
-  //   'rgba(252, 230, 240, 0.8)',
-  //   'rgba(211, 248, 240, 0.8)',
-  //   'rgba(255, 234, 188, 0.8)',
-  //   'rgba(245, 225, 255, 0.8)',
-  //   'rgba(225, 230, 254, 0.8)'
-  // ])
-
-  const colorList = reactive([
-    '#D8F8FF',
-    '#FDDFD9',
-    '#FCE6F0',
-    '#D3F8F0',
-    '#FFEABC',
-    '#F5E1FF',
-    '#E1E6FE'
-  ])
-
-  let lastColor: string | null = null
-
-  const randomColor = () => {
-    let newColor: string
-
-    do {
-      const index = Math.floor(Math.random() * colorList.length)
-      newColor = colorList[index]
-    } while (newColor === lastColor)
-
-    lastColor = newColor
-    return newColor
-  }
-
-  const clickItem = ref({
-    id: 1,
-    date: '2024-9-3',
-    content: '加油!学好Node 自己写个小Demo',
-    collection: 5,
-    comment: 8,
-    userName: '匿名'
-  })
-
-  const openDrawer = (item: any) => {
-    showDrawer.value = true
-    clickItem.value = item
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    background-color: transparent !important;
-    box-shadow: none !important;
-
-    :deep(.comment-modal) {
-      background-color: transparent;
-    }
-
-    .title {
-      margin-top: 20px;
-      font-size: 36px;
-      font-weight: 500;
-    }
-
-    .desc {
-      margin-top: 15px;
-      font-size: 14px;
-      color: var(--art-text-gray-600);
-    }
-
-    .list {
-      margin-top: 40px;
-
-      .offset {
-        display: flex;
-        flex-wrap: wrap;
-        width: calc(100% + 16px);
-      }
-    }
-
-    .comment-box {
-      position: relative;
-      box-sizing: border-box;
-      width: calc(20% - 16px);
-      aspect-ratio: 16 / 12;
-      padding: 16px;
-      margin: 0 16px 16px 0;
-      cursor: pointer;
-      background-color: #eae2cb;
-      transition: all 0.3s;
-
-      &:hover {
-        transform: translateY(-5px);
-      }
-
-      .date {
-        font-size: 12px;
-        color: #949494;
-      }
-
-      .content {
-        margin-top: 16px;
-        font-size: 14px;
-        color: #333;
-      }
-
-      .bottom {
-        position: absolute;
-        bottom: 16px;
-        left: 0;
-        box-sizing: border-box;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        width: 100%;
-        padding: 0 16px;
-
-        .left {
-          display: flex;
-          align-items: center;
-
-          span {
-            display: flex;
-            align-items: center;
-            margin-right: 20px;
-            font-size: 12px;
-            color: #949494;
-
-            i {
-              margin-right: 5px;
-            }
-          }
-        }
-
-        .right {
-          span {
-            font-size: 14px;
-            color: #333;
-          }
-        }
-      }
-    }
-
-    .drawer-default {
-      .comment-box {
-        width: 100%;
-
-        &:hover {
-          transform: translateY(0);
-        }
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-notebook) {
-    .page-content {
-      .comment-box {
-        width: calc(25% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-ipad-pro) {
-    .page-content {
-      .comment-box {
-        width: calc(33.333% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-ipad) {
-    .page-content {
-      .comment-box {
-        width: calc(50% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-phone) {
-    .page-content {
-      .comment-box {
-        width: calc(100% - 16px);
-      }
-    }
-  }
-
-  .dark {
-    .page-content {
-      .comment-box {
-        color: #333 !important;
-      }
-    }
-  }
-</style>

+ 0 - 116
web/src/views/article/detail/index.vue

@@ -1,116 +0,0 @@
-<template>
-  <div class="article-detail page-content">
-    <div class="content">
-      <h1>{{ articleTitle }}</h1>
-      <div class="markdown-body" v-highlight v-html="articleHtml"></div>
-    </div>
-    <ArtBackToTop />
-  </div>
-</template>
-
-<script setup lang="ts">
-  import '@/assets/styles/markdown.scss'
-  import '@/assets/styles/one-dark-pro.scss'
-  import { useCommon } from '@/composables/useCommon'
-  import axios from 'axios'
-  // import 'highlight.js/styles/atom-one-dark.css';
-  // import 'highlight.js/styles/vs2015.css';
-
-  defineOptions({ name: 'ArticleDetail' })
-
-  const articleId = ref(0)
-  const router = useRoute()
-  const articleTitle = ref('')
-  const articleHtml = ref('')
-
-  onMounted(() => {
-    useCommon().scrollToTop()
-    articleId.value = Number(router.query.id)
-    getArticleDetail()
-  })
-
-  const getArticleDetail = async () => {
-    if (articleId.value) {
-      const res = await axios.get('https://www.qiniu.lingchen.kim/blog_detail.json')
-      if (res.data.code === 200) {
-        articleTitle.value = res.data.data.title
-        articleHtml.value = res.data.data.html_content
-      }
-    }
-  }
-</script>
-
-<style lang="scss">
-  .article-detail {
-    .content {
-      max-width: 800px;
-      margin: auto;
-      margin-top: 60px;
-
-      .markdown-body {
-        margin-top: 60px;
-
-        img {
-          width: 100%;
-          border: 1px solid var(--art-gray-200);
-        }
-
-        pre {
-          position: relative;
-
-          &:hover {
-            .copy-button {
-              opacity: 1;
-            }
-          }
-
-          &::before {
-            position: absolute;
-            top: 0;
-            left: 50px;
-            width: 1px;
-            height: 100%;
-            content: '';
-            background: #0a0a0e;
-          }
-        }
-
-        .code-wrapper {
-          overflow-x: auto;
-        }
-
-        .line-number {
-          position: sticky;
-          left: 0;
-          z-index: 2;
-          box-sizing: border-box;
-          display: inline-block;
-          width: 50px;
-          margin-right: 10px;
-          font-size: 14px;
-          color: #9e9e9e;
-          text-align: center;
-        }
-
-        .copy-button {
-          position: absolute;
-          top: 6px;
-          right: 6px;
-          z-index: 1;
-          width: 40px;
-          height: 40px;
-          font-size: 20px;
-          line-height: 40px;
-          color: #999;
-          text-align: center;
-          cursor: pointer;
-          background-color: #000;
-          border: none;
-          border-radius: 8px;
-          opacity: 0;
-          transition: all 0.2s;
-        }
-      }
-    }
-  }
-</style>

+ 0 - 380
web/src/views/article/list/index.vue

@@ -1,380 +0,0 @@
-<template>
-  <div class="page-content article-list">
-    <ElRow justify="space-between" :gutter="10">
-      <ElCol :lg="6" :md="6" :sm="14" :xs="16">
-        <ElInput
-          v-model="searchVal"
-          :prefix-icon="Search"
-          clearable
-          placeholder="输入文章标题查询"
-          @keyup.enter="searchArticle"
-        />
-      </ElCol>
-      <ElCol :lg="12" :md="12" :sm="0" :xs="0">
-        <div class="custom-segmented">
-          <ElSegmented v-model="yearVal" :options="options" @change="searchArticleByYear" />
-        </div>
-      </ElCol>
-      <ElCol :lg="6" :md="6" :sm="10" :xs="6" style="display: flex; justify-content: end">
-        <ElButton @click="toAddArticle">新增文章</ElButton>
-      </ElCol>
-    </ElRow>
-
-    <div class="list">
-      <div class="offset">
-        <div class="item" v-for="item in articleList" :key="item.id" @click="toDetail(item)">
-          <!-- 骨架屏 -->
-          <ElSkeleton animated :loading="isLoading" style="width: 100%; height: 100%">
-            <template #template>
-              <div class="top">
-                <ElSkeletonItem
-                  variant="image"
-                  style="width: 100%; height: 100%; border-radius: 10px"
-                />
-                <div style="padding: 16px 0">
-                  <ElSkeletonItem variant="p" style="width: 80%" />
-                  <ElSkeletonItem variant="p" style="width: 40%; margin-top: 10px" />
-                </div>
-              </div>
-            </template>
-
-            <template #default>
-              <div class="top">
-                <ElImage class="cover" :src="item.home_img" lazy fit="cover">
-                  <template #error>
-                    <div class="image-slot">
-                      <ElIcon><icon-picture /></ElIcon>
-                    </div>
-                  </template>
-                </ElImage>
-
-                <span class="type">{{ item.type_name }}</span>
-              </div>
-              <div class="bottom">
-                <h2>{{ item.title }}</h2>
-                <div class="info">
-                  <div class="text">
-                    <i class="iconfont-sys">&#xe6f7;</i>
-                    <span>{{ useDateFormat(item.create_time, 'YYYY-MM-DD') }}</span>
-                    <div class="line"></div>
-                    <i class="iconfont-sys">&#xe689;</i>
-                    <span>{{ item.count }}</span>
-                  </div>
-                  <ElButton size="small" @click.stop="toEdit(item)">编辑</ElButton>
-                </div>
-              </div>
-            </template>
-          </ElSkeleton>
-        </div>
-      </div>
-    </div>
-
-    <div style="margin-top: 16vh" v-if="showEmpty">
-      <ElEmpty :description="`未找到相关数据 ${EmojiText[0]}`" />
-    </div>
-
-    <div style="display: flex; justify-content: center; margin-top: 20px">
-      <ElPagination
-        size="default"
-        background
-        v-model:current-page="currentPage"
-        :page-size="pageSize"
-        :pager-count="9"
-        layout="prev, pager, next, total,jumper"
-        :total="total"
-        :hide-on-single-page="true"
-        @current-change="handleCurrentChange"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { Picture as IconPicture } from '@element-plus/icons-vue'
-
-  import { ref, onMounted, computed } from 'vue'
-  import { router } from '@/router'
-  import { useDateFormat } from '@vueuse/core'
-  import { Search } from '@element-plus/icons-vue'
-  import EmojiText from '@/utils/ui/emojo'
-  import { ArticleList } from '@/mock/temp/articleList'
-  import { useCommon } from '@/composables/useCommon'
-  import { RoutesAlias } from '@/router/routesAlias'
-
-  defineOptions({ name: 'ArticleList' })
-
-  const yearVal = ref('All')
-
-  const options = ['All', '2024', '2023', '2022', '2021', '2020', '2019']
-
-  const searchVal = ref('')
-  const articleList = ref<any[]>([])
-  const currentPage = ref(1)
-  const pageSize = ref(40)
-  // const lastPage = ref(0)
-  const total = ref(0)
-  const isLoading = ref(true)
-
-  const showEmpty = computed(() => {
-    return articleList.value.length === 0 && !isLoading.value
-  })
-
-  onMounted(() => {
-    getArticleList({ backTop: false })
-  })
-
-  // 搜索文章
-  const searchArticle = () => {
-    getArticleList({ backTop: true })
-  }
-
-  // 根据年份查询文章
-  const searchArticleByYear = () => {
-    getArticleList({ backTop: true })
-  }
-
-  const getArticleList = async ({ backTop = false }) => {
-    isLoading.value = true
-    // let year = yearVal.value
-
-    if (searchVal.value) {
-      yearVal.value = 'All'
-    }
-
-    if (yearVal.value === 'All') {
-      // year = ''
-    }
-
-    // const params = {
-    //   page: currentPage.value,
-    //   size: pageSize.value,
-    //   searchVal: searchVal.value,
-    //   year
-    // }
-
-    articleList.value = ArticleList
-    isLoading.value = false
-
-    if (backTop) {
-      useCommon().scrollToTop()
-    }
-
-    // const res = await ArticleService.getArticleList(params)
-    // if (res.code === ApiStatus.success) {
-    //   currentPage.value = res.currentPage
-    //   pageSize.value = res.pageSize
-    //   lastPage.value = res.lastPage
-    //   total.value = res.total
-    //   articleList.value = res.data
-
-    //   // setTimeout(() => {
-    //   isLoading.value = false
-    //   // }, 3000)
-
-    //   if (searchVal.value) {
-    //     searchVal.value = ''
-    //   }
-    // }
-  }
-
-  const handleCurrentChange = (val: number) => {
-    currentPage.value = val
-    getArticleList({ backTop: true })
-  }
-
-  const toDetail = (item: any) => {
-    router.push({
-      path: RoutesAlias.ArticleDetail,
-      query: {
-        id: item.id
-      }
-    })
-  }
-
-  const toEdit = (item: any) => {
-    router.push({
-      path: RoutesAlias.ArticlePublish,
-      query: {
-        id: item.id
-      }
-    })
-  }
-
-  const toAddArticle = () => {
-    router.push({
-      path: RoutesAlias.ArticlePublish
-    })
-  }
-</script>
-
-<style lang="scss" scoped>
-  .article-list {
-    .custom-segmented .el-segmented {
-      height: 40px;
-      padding: 6px;
-
-      --el-border-radius-base: 8px;
-    }
-
-    .list {
-      margin-top: 20px;
-
-      .offset {
-        display: flex;
-        flex-wrap: wrap;
-        width: calc(100% + 20px);
-
-        .item {
-          box-sizing: border-box;
-          width: calc(20% - 20px);
-          margin: 0 20px 20px 0;
-          cursor: pointer;
-          border: 1px solid var(--art-border-color);
-          border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
-
-          &:hover {
-            .el-button {
-              opacity: 1 !important;
-            }
-          }
-
-          .top {
-            position: relative;
-            aspect-ratio: 16/9.5;
-
-            .cover {
-              display: flex;
-              align-items: center;
-              justify-content: center;
-              width: 100%;
-              height: 100%;
-              object-fit: cover;
-              background: var(--art-gray-200);
-              border-radius: calc(var(--custom-radius) / 2 + 2px)
-                calc(var(--custom-radius) / 2 + 2px) 0 0;
-
-              .image-slot {
-                font-size: 26px;
-                color: var(--art-gray-400);
-              }
-            }
-
-            .type {
-              position: absolute;
-              top: 5px;
-              right: 5px;
-              padding: 5px 4px;
-              font-size: 12px;
-              color: rgba(#fff, 0.8);
-              background: rgba($color: #000, $alpha: 60%);
-              border-radius: 4px;
-            }
-          }
-
-          .bottom {
-            padding: 5px 10px;
-
-            h2 {
-              font-size: 16px;
-              font-weight: 500;
-              color: #333;
-
-              @include ellipsis();
-            }
-
-            .info {
-              display: flex;
-              justify-content: space-between;
-              width: 100%;
-              height: 25px;
-              margin-top: 6px;
-              line-height: 25px;
-
-              .text {
-                display: flex;
-                align-items: center;
-                color: var(--art-text-gray-600);
-
-                i {
-                  margin-right: 5px;
-                  font-size: 14px;
-                }
-
-                span {
-                  font-size: 13px;
-                  color: var(--art-gray-600);
-                }
-
-                .line {
-                  width: 1px;
-                  height: 12px;
-                  margin: 0 15px;
-                  background-color: var(--art-border-dashed-color);
-                }
-              }
-
-              .el-button {
-                opacity: 0;
-                transition: all 0.3s;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-notebook) {
-    .article-list {
-      .list {
-        .offset {
-          .item {
-            width: calc(25% - 20px);
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-ipad-pro) {
-    .article-list {
-      .list {
-        .offset {
-          .item {
-            width: calc(33.333% - 20px);
-
-            .bottom {
-              h2 {
-                font-size: 16px;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-ipad) {
-    .article-list {
-      .list {
-        .offset {
-          .item {
-            width: calc(50% - 20px);
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-phone) {
-    .article-list {
-      .list {
-        .offset {
-          .item {
-            width: calc(100% - 20px);
-          }
-        }
-      }
-    }
-  }
-</style>

+ 0 - 359
web/src/views/article/publish/index.vue

@@ -1,359 +0,0 @@
-<template>
-  <div class="article-edit">
-    <div>
-      <div class="editor-wrap">
-        <!-- 文章标题、类型 -->
-        <ElRow :gutter="10">
-          <ElCol :span="18">
-            <ElInput
-              v-model.trim="articleName"
-              placeholder="请输入文章标题(最多100个字符)"
-              maxlength="100"
-            />
-          </ElCol>
-          <ElCol :span="6">
-            <ElSelect v-model="articleType" placeholder="请选择文章类型" filterable>
-              <ElOption
-                v-for="item in articleTypes"
-                :key="item.id"
-                :label="item.name"
-                :value="item.id"
-              />
-            </ElSelect>
-          </ElCol>
-        </ElRow>
-
-        <!-- 富文本编辑器 -->
-        <ArtWangEditor class="el-top" v-model="editorHtml" />
-
-        <div class="form-wrap">
-          <h2>发布设置</h2>
-          <!-- 图片上传 TODO:封装成组件 -->
-          <ElForm>
-            <ElFormItem label="封面">
-              <div class="el-top upload-container">
-                <ElUpload
-                  class="cover-uploader"
-                  :action="uploadImageUrl"
-                  :headers="uploadHeaders"
-                  :show-file-list="false"
-                  :on-success="onSuccess"
-                  :on-error="onError"
-                  :before-upload="beforeUpload"
-                >
-                  <div v-if="!cover" class="upload-placeholder">
-                    <ElIcon class="upload-icon"><Plus /></ElIcon>
-                    <div class="upload-text">点击上传封面</div>
-                  </div>
-                  <img v-else :src="cover" class="cover-image" />
-                </ElUpload>
-                <div class="el-upload__tip">建议尺寸 16:9,jpg/png 格式</div>
-              </div>
-            </ElFormItem>
-            <ElFormItem label="可见">
-              <ElSwitch v-model="visible" />
-            </ElFormItem>
-          </ElForm>
-
-          <div style="display: flex; justify-content: flex-end">
-            <ElButton type="primary" @click="submit" style="width: 100px">
-              {{ pageMode === PageModeEnum.Edit ? '保存' : '发布' }}
-            </ElButton>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- <div class="outline-wrap">
-        <div class="item" v-for="(item, index) in outlineList" :key="index">
-          <p :class="`level${item.level}`">{{ item.text }}</p>
-        </div>
-      </div> -->
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { Plus } from '@element-plus/icons-vue'
-  import { ApiStatus } from '@/utils/http/status'
-  import { ElMessage } from 'element-plus'
-  import { useUserStore } from '@/store/modules/user'
-  import EmojiText from '@/utils/ui/emojo'
-  import { PageModeEnum } from '@/enums/formEnum'
-  import axios from 'axios'
-  import { useCommon } from '@/composables/useCommon'
-
-  defineOptions({ name: 'ArticlePublish' })
-
-  const route = useRoute()
-
-  const userStore = useUserStore()
-  let { accessToken } = userStore
-
-  // 上传路径
-  const uploadImageUrl = `${import.meta.env.VITE_API_URL}/api/common/upload`
-  // 传递 token
-  const uploadHeaders = { Authorization: accessToken }
-
-  let pageMode: PageModeEnum = PageModeEnum.Add // 页面类型 新增 | 编辑
-  const articleName = ref('') // 文章标题
-  const articleType = ref() // 文章类型
-  const articleTypes = ref() // 类型列表
-  const editorHtml = ref('') // 编辑器内容
-  const createDate = ref('') // 创建时间
-  const cover = ref('') // 图片
-  const visible = ref(true) // 可见
-  // const outlineList = ref()
-
-  onMounted(() => {
-    useCommon().scrollToTop()
-    getArticleTypes()
-    initPageMode()
-  })
-
-  // 初始化页面类型 新增 | 编辑
-  const initPageMode = () => {
-    const { id } = route.query
-    pageMode = id ? PageModeEnum.Edit : PageModeEnum.Add
-    if (pageMode === PageModeEnum.Edit && id) {
-      initEditArticle()
-    } else {
-      initAddArticle()
-    }
-  }
-
-  // 初始化编辑文章的逻辑
-  const initEditArticle = () => {
-    getArticleDetail()
-  }
-
-  // 初始化新增文章逻辑
-  const initAddArticle = () => {
-    createDate.value = formDate(useNow().value)
-  }
-
-  // 获取文章类型
-  const getArticleTypes = async () => {
-    try {
-      const response = await axios.get('https://www.qiniu.lingchen.kim/classify.json')
-      if (response.data.code === 200) {
-        articleTypes.value = response.data.data
-      }
-    } catch (error) {
-      console.error('Error fetching JSON data:', error)
-    }
-    // try {
-    //   const res = await ArticleService.getArticleTypes({})
-    //   if (res.code === ApiStatus.success) {
-    //     articleTypes.value = res.data
-    //   }
-    // } catch (err) { }
-  }
-
-  const getArticleDetail = async () => {
-    const res = await axios.get('https://www.qiniu.lingchen.kim/blog_list.json')
-
-    if (res.data.code === ApiStatus.success) {
-      let { title, blog_class, html_content } = res.data.data
-      articleName.value = title
-      articleType.value = Number(blog_class)
-      editorHtml.value = html_content
-    }
-  }
-
-  // const getOutline = (content: string) => {
-  //   const regex = /<h([1-3])>(.*?)<\/h\1>/g
-  //   const headings = []
-  //   let match
-
-  //   while ((match = regex.exec(content)) !== null) {
-  //     headings.push({ level: match[1], text: match[2] })
-  //   }
-  //   outlineList.value = headings
-  // }
-
-  // 提交
-  const submit = () => {
-    if (pageMode === PageModeEnum.Edit) {
-      editArticle()
-    } else {
-      addArticle()
-    }
-  }
-
-  // 格式化日期
-  const formDate = (date: string | Date): string => {
-    return useDateFormat(date, 'YYYY-MM-DD').value
-  }
-
-  // 验证输入
-  const validateArticle = () => {
-    if (!articleName.value) {
-      ElMessage.error(`请输入文章标题`)
-      return false
-    }
-
-    if (!articleType.value) {
-      ElMessage.error(`请选择文章类型`)
-      return false
-    }
-
-    if (editorHtml.value === '<p><br></p>') {
-      ElMessage.error(`请输入文章内容`)
-      return false
-    }
-
-    if (!cover.value) {
-      ElMessage.error(`请上传图片`)
-      return false
-    }
-
-    return true
-  }
-
-  // 添加文章
-  const addArticle = async () => {
-    try {
-      if (!validateArticle()) return
-      editorHtml.value = delCodeTrim(editorHtml.value)
-    } catch (err) {
-      console.error(err)
-    }
-  }
-
-  // 编辑文章
-  const editArticle = async () => {
-    try {
-      if (!validateArticle()) return
-
-      editorHtml.value = delCodeTrim(editorHtml.value)
-    } catch (err) {
-      console.error(err)
-    }
-  }
-
-  const delCodeTrim = (content: string): string => {
-    return content.replace(/(\s*)<\/code>/g, '</code>')
-  }
-
-  const onSuccess = (response: any) => {
-    cover.value = response.data.url
-    ElMessage.success(`图片上传成功 ${EmojiText[200]}`)
-  }
-
-  const onError = () => {
-    ElMessage.error(`图片上传失败 ${EmojiText[500]}`)
-  }
-
-  // 添加上传前的校验
-  const beforeUpload = (file: File) => {
-    const isImage = file.type.startsWith('image/')
-    const isLt2M = file.size / 1024 / 1024 < 2
-
-    if (!isImage) {
-      ElMessage.error('只能上传图片文件!')
-      return false
-    }
-    if (!isLt2M) {
-      ElMessage.error('图片大小不能超过 2MB!')
-      return false
-    }
-    return true
-  }
-</script>
-
-<style lang="scss" scoped>
-  .article-edit {
-    .editor-wrap {
-      max-width: 1000px;
-      margin: 20px auto;
-
-      .el-top {
-        margin-top: 10px;
-      }
-
-      .form-wrap {
-        padding: 20px;
-        margin-top: 20px;
-        background-color: var(--art-main-bg-color);
-        border: 1px solid var(--art-border-color);
-        border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
-
-        h2 {
-          margin-bottom: 20px;
-          font-size: 20px;
-          font-weight: 500;
-        }
-      }
-    }
-
-    .outline-wrap {
-      box-sizing: border-box;
-      width: 280px;
-      padding: 20px;
-      border: 1px solid #e3e3e3;
-      border-radius: 8px;
-
-      .item {
-        p {
-          height: 30px;
-          font-size: 13px;
-          line-height: 30px;
-          cursor: pointer;
-        }
-
-        .level3 {
-          padding-left: 10px;
-        }
-      }
-    }
-
-    .upload-container {
-      .cover-uploader {
-        position: relative;
-        overflow: hidden;
-        cursor: pointer;
-        border-radius: 6px;
-        transition: var(--el-transition-duration);
-
-        &:hover {
-          border-color: var(--el-color-primary);
-        }
-
-        .upload-placeholder {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          justify-content: center;
-          width: 260px;
-          height: 160px;
-          border: 1px dashed #d9d9d9;
-          border-radius: 6px;
-
-          .upload-icon {
-            font-size: 28px;
-            color: #8c939d;
-          }
-
-          .upload-text {
-            margin-top: 8px;
-            font-size: 14px;
-            color: #8c939d;
-          }
-        }
-
-        .cover-image {
-          display: block;
-          width: 260px;
-          height: 160px;
-          object-fit: cover;
-        }
-      }
-
-      .el-upload__tip {
-        margin-top: 8px;
-        font-size: 12px;
-        color: #666;
-      }
-    }
-  }
-</style>

+ 0 - 246
web/src/views/change/log/index.vue

@@ -1,246 +0,0 @@
-<template>
-  <div class="page-content">
-    <h3 class="table-title"><i class="iconfont-sys">&#xe74d;</i>更新日志</h3>
-
-    <!-- 桌面端表格显示 -->
-    <div class="desktop-view">
-      <ArtTable :data="upgradeLogList">
-        <ElTableColumn label="版本号" prop="version" width="100" />
-        <ElTableColumn label="内容">
-          <template #default="scope">
-            <div class="title">{{ scope.row.title }}</div>
-            <div v-if="scope.row.detail" style="margin-top: 10px">
-              <div class="detail-item" v-for="(item, index) in scope.row.detail" :key="index">
-                {{ index + 1 }}. {{ item }}
-              </div>
-            </div>
-          </template>
-        </ElTableColumn>
-        <ElTableColumn label="发布日期" prop="date" width="150" />
-        <ElTableColumn label="备注" prop="remark" width="260" />
-      </ArtTable>
-    </div>
-
-    <!-- 移动端卡片显示 -->
-    <div class="mobile-view">
-      <div class="changelog-cards">
-        <div v-for="item in upgradeLogList" :key="item.version" class="changelog-card">
-          <div class="card-header">
-            <div class="version-tag">{{ item.version }}</div>
-            <div class="date">{{ item.date }}</div>
-          </div>
-
-          <div class="card-content">
-            <h4 class="title">{{ item.title }}</h4>
-
-            <div v-if="item.detail && item.detail.length > 0" class="details">
-              <div class="detail-list">
-                <div v-for="(detail, index) in item.detail" :key="index" class="detail-item">
-                  <span class="detail-number">{{ index + 1 }}.</span>
-                  <span class="detail-text">{{ detail }}</span>
-                </div>
-              </div>
-            </div>
-
-            <div v-if="item.remark" class="remark">
-              {{ item.remark }}
-            </div>
-
-            <div v-if="item.requireReLogin" class="require-relogin">
-              <ElTag type="warning" size="small"> 需要重新登录 </ElTag>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { upgradeLogList } from '@/mock/upgrade/changeLog'
-
-  defineOptions({ name: 'ChangeLog' })
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    .table-title {
-      display: flex;
-      align-items: center;
-      padding: 10px 0 0;
-      font-size: 18px;
-      font-weight: 500;
-
-      i {
-        margin-right: 10px;
-        font-size: 24px;
-      }
-    }
-
-    .title {
-      color: var(--art-gray-800);
-    }
-
-    .detail-item {
-      color: var(--art-gray-600);
-    }
-
-    // 桌面端显示
-    .desktop-view {
-      display: block;
-    }
-
-    // 移动端样式
-    .mobile-view {
-      display: none;
-
-      .changelog-cards {
-        display: flex;
-        flex-direction: column;
-        gap: 16px;
-        margin-top: 20px;
-      }
-
-      .changelog-card {
-        padding: 16px;
-        background: var(--el-bg-color);
-        border: 1px solid var(--el-border-color-light);
-        border-radius: 8px;
-        transition: all 0.3s ease;
-      }
-
-      .card-header {
-        display: flex;
-        flex-wrap: wrap;
-        gap: 8px;
-        align-items: center;
-        justify-content: space-between;
-        margin-bottom: 12px;
-
-        .version-tag {
-          min-width: fit-content;
-          padding: 4px 12px;
-          font-size: 12px;
-          font-weight: 600;
-          color: white;
-          background: linear-gradient(
-            135deg,
-            var(--el-color-primary),
-            var(--el-color-primary-light-3)
-          );
-          border-radius: 20px;
-        }
-
-        .date {
-          min-width: fit-content;
-          font-size: 13px;
-          color: var(--art-gray-600);
-        }
-      }
-
-      .card-content {
-        .title {
-          margin-bottom: 12px;
-          font-size: 15px;
-          font-weight: 500;
-          line-height: 1.4;
-          color: var(--art-gray-800);
-        }
-
-        .details {
-          margin-bottom: 12px;
-
-          .detail-list {
-            padding: 12px;
-            background: rgba(var(--art-gray-200-rgb), 0.6);
-            border-radius: 6px;
-          }
-
-          .detail-item {
-            display: flex;
-            margin-bottom: 8px;
-            font-size: 13px;
-            line-height: 1.6;
-            color: var(--art-gray-600);
-
-            &:last-child {
-              margin-bottom: 0;
-            }
-
-            .detail-number {
-              min-width: 20px;
-            }
-
-            .detail-text {
-              flex: 1;
-              word-break: break-word;
-            }
-          }
-        }
-
-        .remark {
-          padding: 8px;
-          margin-bottom: 12px;
-          font-size: 12px;
-          color: var(--art-gray-600);
-          background: rgba(var(--art-gray-200-rgb), 0.6);
-          border-radius: 4px;
-        }
-
-        .remark,
-        .require-relogin {
-          margin-top: 12px;
-
-          :deep(.el-tag) {
-            font-size: 12px;
-            border-radius: 6px;
-
-            i {
-              margin-right: 4px;
-            }
-          }
-        }
-      }
-    }
-
-    // 移动端媒体查询
-    @media (width <= 768px) {
-      .desktop-view {
-        display: none;
-      }
-
-      .mobile-view {
-        display: block;
-      }
-
-      .table-title {
-        padding: 8px 0 0;
-        font-size: 16px;
-
-        i {
-          font-size: 20px;
-        }
-      }
-    }
-
-    // 平板端优化
-    @media (width <= 1024px) and (width >= 769px) {
-      .changelog-card {
-        .card-header {
-          .version-tag {
-            padding: 6px 14px;
-            font-size: 13px;
-          }
-
-          .date {
-            font-size: 14px;
-          }
-        }
-
-        .card-content .title {
-          font-size: 16px;
-        }
-      }
-    }
-  }
-</style>

+ 0 - 634
web/src/views/examples/forms/search-bar.vue

@@ -1,634 +0,0 @@
-<!-- 表格搜索栏示例 -->
-<template>
-  <div class="search-bar">
-    <h2 class="title">基础示例(默认收起)</h2>
-    <ArtSearchBar
-      ref="searchBarBasicRef"
-      v-model="formDataBasic"
-      :items="formItemsBasic"
-      @reset="handleBasicReset"
-      @search="handleBasicSearch"
-    >
-    </ArtSearchBar>
-
-    <h2 class="title m-15">完整示例(默认展开)</h2>
-    <ArtSearchBar
-      ref="searchBarAdvancedRef"
-      v-model="formDataAdvanced"
-      :items="formItemsAdvanced"
-      :rules="rulesAdvanced"
-      :defaultExpanded="true"
-      :labelWidth="labelWidthAdvanced"
-      :labelPosition="labelPositionAdvanced"
-      :span="spanAdvanced"
-      :gutter="gutterAdvanced"
-      @reset="handleAdvancedReset"
-      @search="handleAdvancedSearch"
-    >
-      <template #slots>
-        <ElInput v-model="formDataAdvanced.slots" placeholder="我是插槽渲染出来的组件" />
-      </template>
-    </ArtSearchBar>
-
-    <div class="code">
-      <pre><code>{{ formDataAdvanced }}</code></pre>
-    </div>
-
-    <div class="button-group">
-      <el-button @click="getLevelOptions"> 获取用户等级数据 </el-button>
-      <el-button @click="advancedValidate"> 校验表单 </el-button>
-      <el-button @click="advancedReset"> 重置 </el-button>
-      <el-button v-if="showUserName" @click="updateUserName"> 修改用户名 </el-button>
-      <el-button v-if="showUserName" @click="deleteUserName"> 删除用户名 </el-button>
-      <el-button @click="labelWidthAdvanced = 120"> 修改 label 宽度 </el-button>
-      <el-button @click="spanAdvanced = 8"> 设置一行显示的组件数 </el-button>
-      <el-button @click="gutterAdvanced = 50"> 修改 gutter </el-button>
-      <el-button @click="labelPositionAdvanced = 'left'"> label 左对齐 </el-button>
-      <el-button @click="labelPositionAdvanced = 'right'"> label 右对齐 </el-button>
-      <el-button @click="labelPositionAdvanced = 'top'"> label 顶部对齐 </el-button>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import ArtIconSelector from '@/components/core/base/art-icon-selector/index.vue'
-  import { SearchFormItem } from '@/components/core/forms/art-search-bar/index.vue'
-  import { IconTypeEnum } from '@/enums/appEnum'
-  import { ElMessage } from 'element-plus'
-
-  interface Emits {
-    (e: 'update:modelValue', value: Record<string, any>): void
-    (e: 'search', params: Record<string, any>): void
-    (e: 'reset'): void
-  }
-  const emit = defineEmits<Emits>()
-
-  // 表单数据双向绑定
-  const searchBarBasicRef = ref()
-  const searchBarAdvancedRef = ref()
-
-  // 基础示例表单数据
-  const formDataBasic = ref({
-    name: undefined,
-    phone: undefined,
-    level: undefined,
-    address: undefined,
-    date: undefined,
-    daterange: undefined,
-    status: undefined
-  })
-
-  // 完整示例表单数据
-  const formDataAdvanced = ref({
-    name: undefined,
-    phone: undefined,
-    level: undefined,
-    address: undefined,
-    slots: undefined,
-    date: undefined,
-    daterange: undefined,
-    cascader: undefined,
-    checkboxgroup: undefined,
-    userGender: undefined,
-    iconSelector: undefined,
-    status: undefined,
-    systemName: undefined
-  })
-
-  // 完整示例校验规则
-  const rulesAdvanced = {
-    name: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
-    phone: [
-      { required: true, message: '请输入手机号', trigger: 'blur' },
-      { min: 11, max: 11, message: '请输入11位手机号', trigger: 'blur' },
-      { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
-    ],
-    level: [{ required: true, message: '请选择等级', trigger: 'change' }],
-    address: [{ required: true, message: '请输入地址', trigger: 'blur' }]
-  }
-
-  const labelWidthAdvanced = ref(100)
-  const labelPositionAdvanced = ref<'right' | 'left' | 'top'>('right')
-  const spanAdvanced = ref(6)
-  const gutterAdvanced = ref(12)
-
-  // 动态 options
-  const levelOptions = ref<{ label: string; value: string; disabled?: boolean }[]>([])
-
-  // 共享的选项数据
-  const LEVEL_OPTIONS = [
-    { label: '普通用户', value: 'normal' },
-    { label: 'VIP用户', value: 'vip' },
-    { label: '高级VIP', value: 'svip' },
-    { label: '企业用户', value: 'enterprise', disabled: true }
-  ]
-
-  const GENDER_OPTIONS = [
-    { label: '男', value: '1' },
-    { label: '女', value: '2' }
-  ]
-
-  const DATE_SHORTCUTS = [
-    { text: '今日', value: new Date() },
-    { text: '昨日', value: () => new Date(Date.now() - 86400000) },
-    { text: '一周前', value: () => new Date(Date.now() - 604800000) }
-  ]
-
-  // 模拟接口返回用户等级
-  function fetchLevelOptions(): Promise<typeof levelOptions.value> {
-    return new Promise((resolve) => {
-      setTimeout(() => {
-        resolve(LEVEL_OPTIONS)
-      }, 500)
-    })
-  }
-
-  // 获取用户等级数据
-  async function getLevelOptions() {
-    levelOptions.value = await fetchLevelOptions()
-    if (levelOptions.value.length) {
-      ElMessage.success('成功获取到数据')
-    }
-  }
-
-  // 创建表单项的工厂函数
-  const createFormItem = (config: any) => config
-
-  // 基础表单项配置
-  const baseFormItems = {
-    username: createFormItem({
-      label: '用户名',
-      key: 'name',
-      type: 'input',
-      placeholder: '请输入用户名',
-      clearable: true
-    }),
-    phone: createFormItem({
-      label: '手机号',
-      key: 'phone',
-      type: 'input',
-      props: { placeholder: '请输入手机号', maxlength: '11' }
-    }),
-    level: createFormItem({
-      label: '用户等级',
-      key: 'level',
-      type: 'select',
-      props: {
-        placeholder: '请选择等级',
-        options: LEVEL_OPTIONS
-      }
-    }),
-    address: createFormItem({
-      label: '地址',
-      key: 'address',
-      type: 'input',
-      placeholder: '请输入地址'
-    }),
-    date: createFormItem({
-      label: '日期',
-      key: 'date',
-      type: 'datetime',
-      props: {
-        style: { width: '100%' },
-        placeholder: '请选择日期',
-        type: 'date',
-        valueFormat: 'YYYY-MM-DD',
-        shortcuts: DATE_SHORTCUTS
-      }
-    }),
-    gender: createFormItem({
-      label: '性别',
-      key: 'userGender',
-      type: 'radiogroup',
-      props: {
-        options: GENDER_OPTIONS
-      }
-    })
-  }
-
-  // 表单配置
-  const formItemsBasic = computed(() => [
-    baseFormItems.username,
-    {
-      label: '密码',
-      key: 'password',
-      type: 'input',
-      props: {
-        type: 'password',
-        placeholder: '请输入密码',
-        clearable: true
-      }
-    },
-    baseFormItems.phone,
-    baseFormItems.level,
-    baseFormItems.address,
-    baseFormItems.date,
-    baseFormItems.gender
-  ])
-
-  const userItem = ref<SearchFormItem>({
-    label: '用户名',
-    key: 'name',
-    type: 'input',
-    props: {
-      placeholder: '请输入用户名',
-      clearable: true
-    }
-  })
-
-  // 控制用户名字段是否显示
-  const showUserName = ref(true)
-
-  // 级联选择器数据
-  const cascaderOptions = [
-    {
-      value: 'guide',
-      label: '指南',
-      children: [
-        {
-          value: 'disciplines',
-          label: '规范',
-          children: [
-            { value: 'consistency', label: '一致性' },
-            { value: 'feedback', label: '反馈' },
-            { value: 'efficiency', label: '效率' },
-            { value: 'controllability', label: '可控性' }
-          ]
-        }
-      ]
-    },
-    {
-      value: 'components',
-      label: '组件',
-      children: [
-        {
-          value: 'basic',
-          label: '基础组件',
-          children: [
-            { value: 'button', label: '按钮' },
-            { value: 'form', label: '表单' },
-            { value: 'table', label: '表格' }
-          ]
-        }
-      ]
-    }
-  ]
-
-  // 树选择器数据
-  const treeSelectData = [
-    {
-      value: '1',
-      label: '一级 1',
-      children: [
-        {
-          value: '1-1',
-          label: '二级 1-1',
-          children: [{ value: '1-1-1', label: '三级 1-1-1' }]
-        }
-      ]
-    },
-    {
-      value: '2',
-      label: '一级 2',
-      children: [
-        {
-          value: '2-1',
-          label: '二级 2-1',
-          children: [{ value: '2-1-1', label: '三级 2-1-1' }]
-        },
-        {
-          value: '2-2',
-          label: '二级 2-2',
-          children: [{ value: '2-2-1', label: '三级 2-2-1' }]
-        }
-      ]
-    }
-  ]
-
-  // 复选框选项
-  const checkboxOptions = [
-    { label: '选项1', value: 'option1' },
-    { label: '选项2', value: 'option2' },
-    { label: '选项3', value: 'option3' },
-    { label: '选项4', value: 'option4' },
-    { label: '选项5(disabled)', value: 'option5', disabled: true }
-  ]
-
-  // 完整示例表单配置
-  const formItemsAdvanced = computed(() => [
-    ...(showUserName.value ? [userItem.value] : []),
-    {
-      ...baseFormItems.phone
-    },
-    {
-      ...baseFormItems.level,
-      props: { placeholder: '请选择等级', options: levelOptions.value }
-    },
-    baseFormItems.address,
-    baseFormItems.date,
-    // 日期时间
-    {
-      label: '日期时间',
-      key: 'datetime',
-      type: 'datetime',
-      props: {
-        style: { width: '100%' },
-        placeholder: '请选择日期时间',
-        type: 'datetime',
-        valueFormat: 'YYYY-MM-DD HH:mm:ss'
-      }
-    },
-    {
-      label: '日期范围',
-      key: 'daterange',
-      type: 'datetime',
-      props: {
-        type: 'daterange',
-        valueFormat: 'YYYY-MM-DD',
-        rangeSeparator: '至',
-        startPlaceholder: '开始日期',
-        endPlaceholder: '结束日期'
-      }
-    },
-    // 日期时间范围
-    {
-      label: '日期时间范围',
-      key: 'datetimerange',
-      type: 'datetime',
-      props: {
-        type: 'datetimerange',
-        valueFormat: 'YYYY-MM-DD HH:mm:ss',
-        rangeSeparator: '至',
-        startPlaceholder: '开始日期时间',
-        endPlaceholder: '结束日期时间'
-      }
-    },
-    // 时间选择
-    {
-      label: '时间选择',
-      key: 'timeselect',
-      type: 'timeselect',
-      props: {
-        placeholder: '请选择时间',
-        type: 'time',
-        valueFormat: 'HH:mm:ss'
-      }
-    },
-    // 时间选择器
-    {
-      label: '时间选择器',
-      key: 'timepicker',
-      type: 'timepicker',
-      props: {
-        style: { width: '100%' },
-        placeholder: '请选择时间',
-        type: 'time',
-        valueFormat: 'HH:mm:ss'
-      }
-    },
-    // 级联选择
-    {
-      label: '级联选择',
-      key: 'cascader',
-      type: 'cascader',
-      props: {
-        placeholder: '请选择级联选择器',
-        clearable: true,
-        style: { width: '100%' },
-        collapseTags: true,
-        maxCollapseTags: 1,
-        props: { multiple: true },
-        options: cascaderOptions
-      }
-    },
-    // 树型选择器
-    {
-      label: '树型选择器',
-      key: 'treeSelect',
-      type: 'treeselect',
-      props: {
-        showCheckbox: true,
-        multiple: true,
-        clearable: true,
-        data: treeSelectData
-      }
-    },
-    { label: '插槽', key: 'slots', type: 'input', placeholder: '请输入邮箱' },
-    {
-      label: '渲染组件',
-      key: 'iconSelector',
-      type: () => h(ArtIconSelector, { iconType: IconTypeEnum.UNICODE, width: '100%' }),
-      props: { placeholder: '请输入备注', type: 'textarea', rows: 4 }
-    },
-    {
-      label: '自定义组件',
-      key: 'customComponent',
-      type: () =>
-        h(
-          'div',
-          {
-            style:
-              'color: var(--art-gray-600); border: 1px solid var(--art-border-dashed-color); padding: 0px 15px; border-radius: 6px'
-          },
-          '我是一个自定义组件'
-        ),
-      props: {
-        placeholder: '请输入备注',
-        type: 'textarea',
-        rows: 4,
-        style: { width: '100%' }
-      }
-    },
-    {
-      label: '复选框',
-      key: 'checkboxgroup',
-      type: 'checkboxgroup',
-      span: 12,
-      props: {
-        options: checkboxOptions
-      }
-    },
-    {
-      ...baseFormItems.gender
-    },
-
-    {
-      label: '是否启用',
-      key: 'isEnabled',
-      type: 'switch',
-      props: {
-        placeholder: '请选择是否启用'
-      }
-    },
-    {
-      label: '年龄',
-      key: 'age',
-      type: 'number',
-      slots: {
-        suffix: () => h('span', { style: 'color: #909399; font-size: 12px' }, '岁')
-      }
-    },
-    {
-      label: '网站地址',
-      key: 'website',
-      type: 'input',
-      placeholder: '请输入网站名称',
-      slots: {
-        prepend: () => h('span', 'https://'),
-        append: () => h('span', '.com')
-      }
-    },
-    {
-      label: '事件演示',
-      key: 'event',
-      type: 'input',
-      props: {
-        placeholder: '输入内容触发事件,控制台查看',
-        clearable: true,
-        prefixIcon: 'Search',
-        // prefix: () => h('span', {}, '123'),
-        // 事件必须以 on 开头,然后驼峰式命名拼接 ElementPlus 事件名
-        onInput(val: string) {
-          console.log('输入事件', val)
-        },
-        onClear() {
-          console.log('清空事件')
-        }
-      }
-    },
-
-    {
-      label: '多行输入',
-      key: 'remark',
-      type: 'input',
-      props: {
-        placeholder: '请输入备注',
-        type: 'textarea',
-        rows: 2
-      }
-    },
-    {
-      label: '评分',
-      key: 'rate',
-      type: 'rate',
-      props: {
-        size: 'large',
-        placeholder: '请选择评分'
-      }
-    },
-    {
-      label: '禁用',
-      key: 'diaabled',
-      type: 'input',
-      placeholder: '我被禁用了',
-      disabled: true // 禁用
-    },
-    {
-      label: '滑块',
-      key: 'slider',
-      type: 'slider'
-      // props: {
-      //   step: 10,
-      //   showStops: true
-      // }
-    },
-
-    {
-      label: '隐藏',
-      key: 'email',
-      type: 'input',
-      hidden: true
-    },
-    // 根据条件隐藏
-    {
-      label: '根据条件隐藏',
-      key: 'systemName',
-      type: 'input',
-      hidden: formDataAdvanced.value.systemName === 'mac',
-      placeholder: '输入 mac 组件隐藏'
-    },
-    {
-      label: '栅格布局',
-      key: 'sg1',
-      type: 'input',
-      span: 12,
-      placeholder: '示例:栅格 span=12 占容器一半宽度,span=24 占满容器'
-    }
-  ])
-
-  // 统一的表单处理函数
-  const createFormHandler = (ref: any, formData: any, type: string) => ({
-    reset: () => {
-      console.log(`重置${type}表单`)
-      emit('reset')
-    },
-    search: async () => {
-      await ref.value.validate()
-      emit('search', formData.value)
-      console.log(`${type}表单数据`, formData.value)
-    },
-    validate: () => ref.value.validate()
-  })
-
-  // 基础表单处理器
-  const basicFormHandler = computed(() =>
-    createFormHandler(searchBarBasicRef, formDataBasic, '基础')
-  )
-
-  // 高级表单处理器
-  const advancedFormHandler = computed(() =>
-    createFormHandler(searchBarAdvancedRef, formDataAdvanced, '完整')
-  )
-
-  // 事件处理函数
-  const handleBasicReset = () => basicFormHandler.value.reset()
-  const handleBasicSearch = () => basicFormHandler.value.search()
-  const handleAdvancedReset = () => advancedFormHandler.value.reset()
-  const handleAdvancedSearch = () => advancedFormHandler.value.search()
-  const advancedValidate = () => advancedFormHandler.value.validate()
-  const advancedReset = () => searchBarAdvancedRef.value.reset()
-
-  const updateUserName = () => {
-    userItem.value = {
-      ...userItem.value,
-      label: '昵称',
-      props: {
-        placeholder: '请输入昵称'
-      }
-    }
-  }
-
-  const deleteUserName = () => {
-    showUserName.value = false
-    formDataAdvanced.value.name = undefined
-  }
-</script>
-
-<style scoped lang="scss">
-  .search-bar {
-    padding-bottom: 20px;
-
-    .title {
-      margin-bottom: 5px;
-      font-size: 18px;
-      font-weight: 500;
-
-      &.m-15 {
-        margin-top: 15px;
-      }
-    }
-
-    .code {
-      padding: 15px;
-      margin-top: 15px;
-      font-size: 14px;
-      background-color: var(--art-main-bg-color);
-      border: 1px solid var(--art-border-color);
-      border-radius: var(--el-border-radius-base);
-    }
-
-    .button-group {
-      margin-top: 15px;
-    }
-  }
-</style>

+ 0 - 64
web/src/views/examples/tables/basic.vue

@@ -1,64 +0,0 @@
-<!-- 基础表格 -->
-<template>
-  <div class="user-page art-full-height">
-    <ElCard class="art-table-card" shadow="never" style="margin-top: 0">
-      <!-- 表格 -->
-      <ArtTable
-        rowKey="id"
-        :show-table-header="false"
-        :loading="loading"
-        :data="data"
-        :columns="columns"
-        :pagination="pagination"
-        @pagination:size-change="handleSizeChange"
-        @pagination:current-change="handleCurrentChange"
-      >
-      </ArtTable>
-    </ElCard>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { useTable } from '@/composables/useTable'
-  import { UserService } from '@/api/usersApi'
-
-  defineOptions({ name: 'UserMixedUsageExample' })
-
-  const { data, columns, loading, pagination, handleSizeChange, handleCurrentChange } =
-    useTable<Api.User.UserListItem>({
-      core: {
-        apiFn: UserService.getUserList,
-        apiParams: {
-          current: 1,
-          size: 20,
-          name: '',
-          phone: '',
-          address: undefined
-        },
-        columnsFactory: () => [
-          {
-            prop: 'id',
-            label: 'ID'
-          },
-          {
-            prop: 'nickName',
-            label: '昵称'
-          },
-          {
-            prop: 'userGender',
-            label: '性别',
-            sortable: true,
-            formatter: (row) => row.sex || '未知'
-          },
-          {
-            prop: 'userPhone',
-            label: '手机号'
-          },
-          {
-            prop: 'userEmail',
-            label: '邮箱'
-          }
-        ]
-      }
-    })
-</script>

File diff suppressed because it is too large
+ 0 - 1536
web/src/views/examples/tables/index.vue


+ 0 - 136
web/src/views/examples/tables/tree.vue

@@ -1,136 +0,0 @@
-<template>
-  <div class="art-full-height">
-    <div class="tree-container">
-      <div class="left-sidebar">
-        <ElCard class="art-table-card" shadow="never" style="margin-top: 0">
-          <b>左右布局示例:</b>
-          <p style="margin-top: 20px">例如:分类、树形结构等</p>
-        </ElCard>
-      </div>
-
-      <div class="right-content art-full-height">
-        <UserSearch v-model="defaultFilter" :role-list="[]" />
-
-        <ElCard class="art-table-card" shadow="never">
-          <ArtTableHeader v-model:columns="columnChecks" @refresh="refreshData">
-            <template #left>
-              <ElButton v-ripple>新增用户</ElButton>
-            </template>
-          </ArtTableHeader>
-
-          <ArtTable
-            rowKey="id"
-            :loading="loading"
-            :data="data"
-            :columns="columns"
-            :pagination="pagination"
-            @pagination:size-change="handleSizeChange"
-            @pagination:current-change="handleCurrentChange"
-          >
-          </ArtTable>
-        </ElCard>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { useTable } from '@/composables/useTable'
-  import { UserService } from '@/api/usersApi'
-  import { ElButton, ElCard } from 'element-plus'
-  import UserSearch from '@/views/system/user/modules/user-search.vue'
-
-  defineOptions({ name: 'TreeTable' })
-
-  // 表单搜索初始值
-  const defaultFilter = ref({
-    name: undefined
-  })
-
-  const {
-    data,
-    columns,
-    columnChecks,
-    loading,
-    pagination,
-    refreshData,
-    handleSizeChange,
-    handleCurrentChange
-  } = useTable<Api.User.UserListItem>({
-    core: {
-      apiFn: UserService.getUserList,
-      apiParams: {
-        current: 1,
-        size: 20,
-        name: '',
-        phone: '',
-        address: undefined
-      },
-      columnsFactory: () => [
-        {
-          prop: 'id',
-          label: 'ID'
-        },
-        {
-          prop: 'nickName',
-          label: '昵称'
-        },
-        {
-          prop: 'userGender',
-          label: '性别',
-          sortable: true,
-          formatter: (row) => row.sex || '未知'
-        },
-        {
-          prop: 'userPhone',
-          label: '手机号'
-        },
-        {
-          prop: 'userEmail',
-          label: '邮箱'
-        }
-      ]
-    }
-  })
-</script>
-
-<style lang="scss" scoped>
-  .tree-container {
-    box-sizing: border-box;
-    display: flex;
-    gap: 16px;
-    height: 100%;
-
-    .left-sidebar {
-      flex-shrink: 0;
-      width: 230px;
-      height: 100%;
-    }
-
-    .right-content {
-      flex-grow: 1;
-      min-width: 0;
-      height: 100%;
-    }
-
-    .art-table-card {
-      display: flex;
-      flex-direction: column;
-      height: 100%;
-    }
-  }
-
-  @media screen and (max-width: $device-ipad) {
-    .tree-container {
-      display: block;
-      gap: 0;
-      height: auto;
-
-      .left-sidebar {
-        width: 100%;
-        height: auto;
-        margin-bottom: 20px;
-      }
-    }
-  }
-</style>

+ 0 - 135
web/src/views/examples/tabs/index.vue

@@ -1,135 +0,0 @@
-<template>
-  <div class="page-content">
-    <h3 class="page-title">标签页操作</h3>
-
-    <!-- 修改标签标题模块 -->
-    <ElCard class="module-card" header="修改标签标题" shadow="never">
-      <div style="display: flex; gap: 10px">
-        <ElInput
-          v-model="newTabTitle"
-          placeholder="请输入新的标签页标题"
-          clearable
-          style="width: 300px"
-        />
-
-        <ElButton type="primary" @click="handleUpdateTabTitle" :disabled="!newTabTitle.trim()">
-          修改
-        </ElButton>
-        <ElButton @click="handleResetTabTitle"> 重置 </ElButton>
-      </div>
-    </ElCard>
-
-    <!-- 获取标签页模块 -->
-    <ElCard class="module-card" header="获取标签页信息" shadow="never">
-      <div class="mb-4">
-        <p class="tab-info">当前标签页信息:{{ currentTab }}</p>
-      </div>
-      <ElRow :gutter="20">
-        <ElCol :span="24">
-          <ElButton type="success" plain @click="handleGetCurrentTabTitle(routePath)">
-            获取当前标签页信息
-          </ElButton>
-        </ElCol>
-      </ElRow>
-    </ElCard>
-
-    <!-- 关闭标签页模块 -->
-    <ElCard class="module-card" header="关闭标签页" shadow="never">
-      <ElRow :gutter="20">
-        <ElCol :span="24">
-          <ElButton type="danger" plain @click="handleCloseTab(routePath)"> 关闭当前标签 </ElButton>
-          <ElButton type="warning" plain @click="handleCloseOthersTab(routePath)">
-            关闭其他标签
-          </ElButton>
-          <ElButton type="danger" plain @click="handleCloseAllTab"> 关闭所有标签 </ElButton>
-        </ElCol>
-      </ElRow>
-    </ElCard>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { useWorktabStore } from '@/store/modules/worktab'
-  import { WorkTab } from '@/types'
-  import { ElMessage } from 'element-plus'
-  const worktabStore = useWorktabStore()
-  const currentTab = ref<WorkTab | null>(null)
-  const newTabTitle = ref('')
-  const routePath = '/examples/tabs'
-
-  /**
-   * 处理更新标签页标题
-   * 验证输入内容后调用store方法更新标题
-   */
-  const handleUpdateTabTitle = () => {
-    if (newTabTitle.value.trim()) {
-      worktabStore.updateTabTitle(routePath, newTabTitle.value.trim())
-    }
-  }
-
-  /**
-   * 处理重置标签页标题
-   * 将标题重置为默认值并清空输入框
-   */
-  const handleResetTabTitle = () => {
-    worktabStore.resetTabTitle(routePath)
-    newTabTitle.value = ''
-  }
-
-  /**
-   * 获取指定路径的标签页信息
-   * @param path - 标签页路径
-   */
-  const handleGetCurrentTabTitle = (path: string) => {
-    const tab = worktabStore.getTabTitle(path)
-    if (tab) {
-      currentTab.value = tab
-    } else {
-      ElMessage.warning('未找到标签信息')
-    }
-  }
-
-  /**
-   * 关闭指定路径的标签页
-   * @param path - 要关闭的标签页路径
-   */
-  const handleCloseTab = (path: string) => {
-    worktabStore.removeTab(path)
-  }
-
-  /**
-   * 关闭除指定路径外的其他所有标签页
-   * @param path - 要保留的标签页路径
-   */
-  const handleCloseOthersTab = (path: string) => {
-    worktabStore.removeOthers(path)
-  }
-
-  /**
-   * 关闭所有标签页
-   */
-  const handleCloseAllTab = () => {
-    worktabStore.removeAll()
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    .page-title {
-      margin-bottom: 20px;
-      font-size: 20px;
-      font-weight: 400;
-      color: var(--el-text-color-primary);
-    }
-
-    .module-card {
-      margin-bottom: 30px;
-    }
-
-    .tab-info {
-      margin: 0 0 10px;
-      font-size: 14px;
-      color: var(--art-gray-600);
-    }
-  }
-</style>

+ 0 - 15
web/src/views/exception/403/index.vue

@@ -1,15 +0,0 @@
-<template>
-  <ArtException
-    :data="{
-      title: '403',
-      desc: $t('exceptionPage.403'),
-      btnText: $t('exceptionPage.gohome'),
-      imgUrl
-    }"
-  />
-</template>
-
-<script setup lang="ts">
-  import imgUrl from '@imgs/svg/403.svg'
-  defineOptions({ name: 'Exception403' })
-</script>

+ 0 - 15
web/src/views/exception/404/index.vue

@@ -1,15 +0,0 @@
-<template>
-  <ArtException
-    :data="{
-      title: '404',
-      desc: $t('exceptionPage.404'),
-      btnText: $t('exceptionPage.gohome'),
-      imgUrl
-    }"
-  />
-</template>
-
-<script setup lang="ts">
-  import imgUrl from '@imgs/svg/404.svg'
-  defineOptions({ name: 'Exception404' })
-</script>

+ 0 - 15
web/src/views/exception/500/index.vue

@@ -1,15 +0,0 @@
-<template>
-  <ArtException
-    :data="{
-      title: '500',
-      desc: $t('exceptionPage.500'),
-      btnText: $t('exceptionPage.gohome'),
-      imgUrl
-    }"
-  />
-</template>
-
-<script setup lang="ts">
-  import imgUrl from '@imgs/svg/500.svg'
-  defineOptions({ name: 'Exception500' })
-</script>

+ 0 - 47
web/src/views/outside/Iframe.vue

@@ -1,47 +0,0 @@
-<template>
-  <div class="iframe-container" v-loading="isLoading">
-    <iframe
-      ref="iframeRef"
-      :src="iframeUrl"
-      frameborder="0"
-      class="iframe-content"
-      @load="handleIframeLoad"
-    ></iframe>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { getIframeRoutes } from '@/router/utils/menuToRouter'
-
-  const route = useRoute()
-  const isLoading = ref(true)
-  const iframeUrl = ref('')
-  const iframeRef = ref<HTMLIFrameElement | null>(null)
-
-  onMounted(() => {
-    const iframeRoute = getIframeRoutes().find((item: any) => item.path === route.path)
-
-    if (iframeRoute?.meta) {
-      iframeUrl.value = iframeRoute.meta.link || ''
-    }
-  })
-
-  const handleIframeLoad = () => {
-    isLoading.value = false
-  }
-</script>
-
-<style scoped>
-  .iframe-container {
-    box-sizing: border-box;
-    width: 100%;
-    height: 100%;
-  }
-
-  .iframe-content {
-    width: 100%;
-    height: 100%;
-    min-height: calc(100vh - 120px);
-    border: none;
-  }
-</style>

+ 0 - 22
web/src/views/result/fail/index.vue

@@ -1,22 +0,0 @@
-<template>
-  <ArtResultPage
-    type="fail"
-    title="提交失败"
-    message="请核对并修改以下信息后,再重新提交。"
-    iconCode="&#xe665;"
-  >
-    <template #content>
-      <p>您提交的内容有如下错误:</p>
-      <p><i class="icon iconfont-sys">&#xe71a;</i>您的账户已被冻结</p>
-      <p><i class="icon iconfont-sys">&#xe71a;</i>您的账户还不具备申请资格</p>
-    </template>
-    <template #buttons>
-      <el-button type="primary" v-ripple>返回修改</el-button>
-      <el-button v-ripple>查看</el-button>
-    </template>
-  </ArtResultPage>
-</template>
-
-<script setup lang="ts">
-  defineOptions({ name: 'ResultFail' })
-</script>

+ 0 - 21
web/src/views/result/success/index.vue

@@ -1,21 +0,0 @@
-<template>
-  <ArtResultPage
-    type="success"
-    title="提交成功"
-    message="提交结果页用于反馈一系列操作任务的处理结果,如果仅是简单操作,使用 Message 全局提示反馈即可。灰色区域可以显示一些补充的信息。"
-    iconCode="&#xe617;"
-  >
-    <template #content>
-      <p>已提交申请,等待部门审核。</p>
-    </template>
-    <template #buttons>
-      <el-button type="primary" v-ripple>返回修改</el-button>
-      <el-button v-ripple>查看</el-button>
-      <el-button v-ripple>打印</el-button>
-    </template>
-  </ArtResultPage>
-</template>
-
-<script setup lang="ts">
-  defineOptions({ name: 'ResultSuccess' })
-</script>

+ 0 - 287
web/src/views/safeguard/server/index.vue

@@ -1,287 +0,0 @@
-<template>
-  <div class="page-content server">
-    <div class="list">
-      <div class="middle">
-        <div class="item" v-for="item in serverList" :key="item.name">
-          <div class="header">
-            <span class="name">{{ item.name }}</span>
-            <span class="ip">{{ item.ip }}</span>
-          </div>
-          <div class="box">
-            <div class="left">
-              <img src="@imgs/safeguard/server.png" alt="服务器" />
-              <ElButtonGroup class="ml-4">
-                <ElButton type="primary" size="default">开机</ElButton>
-                <ElButton type="danger" size="default">关机</ElButton>
-                <ElButton type="warning" size="default">重启</ElButton>
-              </ElButtonGroup>
-            </div>
-            <div class="right">
-              <div>
-                <p>CPU</p>
-                <ElProgress :percentage="item.cup" :text-inside="true" :stroke-width="17" />
-              </div>
-              <div>
-                <p>RAM</p>
-                <ElProgress
-                  :percentage="item.memory"
-                  status="success"
-                  :text-inside="true"
-                  :stroke-width="17"
-                />
-              </div>
-              <div>
-                <p>SWAP</p>
-                <ElProgress
-                  :percentage="item.swap"
-                  status="warning"
-                  :text-inside="true"
-                  :stroke-width="17"
-                />
-              </div>
-              <div>
-                <p>DISK</p>
-                <ElProgress
-                  :percentage="item.disk"
-                  status="success"
-                  :text-inside="true"
-                  :stroke-width="17"
-                />
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { reactive, onMounted, onUnmounted } from 'vue'
-
-  defineOptions({ name: 'SafeguardServer' })
-
-  interface ServerInfo {
-    name: string
-    ip: string
-    cup: number
-    memory: number
-    swap: number
-    disk: number
-  }
-
-  const serverList = reactive<ServerInfo[]>([
-    {
-      name: '开发服务器',
-      ip: '192.168.1.100',
-      cup: 85,
-      memory: 65,
-      swap: 45,
-      disk: 92
-    },
-    {
-      name: '测试服务器',
-      ip: '192.168.1.101',
-      cup: 32,
-      memory: 78,
-      swap: 90,
-      disk: 45
-    },
-    {
-      name: '预发布服务器',
-      ip: '192.168.1.102',
-      cup: 95,
-      memory: 42,
-      swap: 67,
-      disk: 88
-    },
-    {
-      name: '线上服务器',
-      ip: '192.168.1.103',
-      cup: 58,
-      memory: 93,
-      swap: 25,
-      disk: 73
-    }
-  ])
-
-  // 生成随机数据的函数
-  function generateRandomValue(min = 0, max = 100): number {
-    return Math.floor(Math.random() * (max - min + 1)) + min
-  }
-
-  // 更新服务器数据
-  function updateServerData() {
-    serverList.forEach((server) => {
-      server.cup = generateRandomValue()
-      server.memory = generateRandomValue()
-      server.swap = generateRandomValue()
-      server.disk = generateRandomValue()
-    })
-  }
-
-  // 修改 timer 类型为 number | null
-  let timer: number | null = null
-
-  onMounted(() => {
-    timer = window.setInterval(updateServerData, 3000)
-  })
-
-  onUnmounted(() => {
-    if (timer !== null) {
-      window.clearInterval(timer)
-      timer = null
-    }
-  })
-</script>
-
-<style lang="scss" scoped>
-  .server {
-    .list {
-      width: 100%;
-
-      .middle {
-        display: flex;
-        flex-wrap: wrap;
-        width: calc(100% + 20px);
-
-        .item {
-          box-sizing: border-box;
-          width: calc(50% - 20px);
-          margin: 0 20px 20px 0;
-          border: 1px solid var(--el-border-color-light);
-          border-radius: 4px;
-
-          .header {
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            padding: 20px;
-            border-bottom: 1px solid var(--el-border-color-light);
-
-            .name {
-              font-size: 15px;
-              font-weight: 500;
-            }
-
-            .ip {
-              font-size: 14px;
-              color: var(--el-text-color-secondary);
-            }
-          }
-
-          .box {
-            display: flex;
-            align-items: center;
-            padding: 40px;
-
-            .left {
-              margin: 0 40px;
-
-              img {
-                display: block;
-                width: 190px;
-              }
-
-              .el-button-group {
-                display: flex;
-                justify-content: center;
-                margin-top: -10px;
-              }
-            }
-
-            .right {
-              flex: 1;
-              margin-top: 5px;
-
-              > div {
-                margin: 15px 0;
-
-                p {
-                  margin-bottom: 4px;
-                  font-size: 14px;
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  @media (max-width: $device-notebook) {
-    .server {
-      .list {
-        .middle {
-          .item {
-            .header {
-              padding: 10px 20px;
-            }
-
-            .box {
-              padding: 20px;
-
-              .left {
-                margin: 0 20px 0 0;
-              }
-
-              .right {
-                margin-top: 0;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  @media (max-width: $device-ipad-pro) {
-    .server {
-      .list {
-        .middle {
-          .item {
-            width: 100%;
-          }
-        }
-      }
-    }
-  }
-
-  @media (max-width: $device-phone) {
-    .server {
-      .list {
-        .middle {
-          .item {
-            width: 100%;
-
-            .header {
-              padding: 10px 20px;
-            }
-
-            .box {
-              display: block;
-              padding: 20px;
-
-              .left {
-                margin: 0;
-
-                img {
-                  width: 150px;
-                  margin: 0 auto;
-                }
-
-                .el-button-group {
-                  margin-top: 10px;
-                }
-              }
-
-              .right {
-                margin-top: 30px;
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-</style>

+ 0 - 658
web/src/views/system/menu/index.vue

@@ -1,658 +0,0 @@
-<template>
-  <div class="menu-page art-full-height">
-    <!-- 搜索栏 -->
-    <ArtSearchBar
-      v-model="formFilters"
-      :items="formItems"
-      :showExpand="false"
-      @reset="handleReset"
-      @search="handleSearch"
-    ></ArtSearchBar>
-
-    <ElCard class="art-table-card" shadow="never">
-      <!-- 表格头部 -->
-      <ArtTableHeader :showZebra="false" v-model:columns="columnChecks" @refresh="handleRefresh">
-        <template #left>
-          <!-- 按钮权限:后端控制模式,使用自定义指令 -->
-          <ElButton @click="showModel('menu', null, true)" v-ripple> 添加菜单 </ElButton>
-          <ElButton @click="toggleExpand" v-ripple>
-            {{ isExpanded ? '收起' : '展开' }}
-          </ElButton>
-          <!-- 按钮权限:前端控制模式,使用 hasAuth 方法 -->
-          <ElButton v-if="hasAuth('add')" @click="showModel('menu', null, true)" v-ripple>
-            添加菜单
-          </ElButton>
-        </template>
-      </ArtTableHeader>
-
-      <ArtTable
-        ref="tableRef"
-        rowKey="path"
-        :loading="loading"
-        :columns="columns"
-        :data="filteredTableData"
-        :stripe="false"
-      />
-
-      <ElDialog :title="dialogTitle" v-model="dialogVisible" width="700px" align-center>
-        <ElForm ref="formRef" :model="form" :rules="rules" label-width="85px">
-          <ElFormItem label="菜单类型">
-            <ElRadioGroup v-model="labelPosition" :disabled="disableMenuType">
-              <ElRadioButton value="menu" label="menu">菜单</ElRadioButton>
-              <ElRadioButton value="button" label="button">权限</ElRadioButton>
-            </ElRadioGroup>
-          </ElFormItem>
-
-          <template v-if="labelPosition === 'menu'">
-            <ElRow :gutter="20">
-              <ElCol :span="12">
-                <ElFormItem label="菜单名称" prop="name">
-                  <ElInput v-model="form.name" placeholder="菜单名称"></ElInput>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="12">
-                <ElFormItem label="路由地址" prop="path">
-                  <ElInput v-model="form.path" placeholder="路由地址"></ElInput>
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-            <ElRow :gutter="20">
-              <ElCol :span="12">
-                <ElFormItem label="权限标识" prop="label">
-                  <ElInput v-model="form.label" placeholder="权限标识"></ElInput>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="12">
-                <ElFormItem label="图标" prop="icon">
-                  <ArtIconSelector v-model="form.icon" :iconType="iconType" width="100%" />
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-
-            <ElRow :gutter="20">
-              <ElCol :span="12">
-                <ElFormItem label="菜单排序" prop="sort" style="width: 100%">
-                  <ElInputNumber
-                    v-model="form.sort"
-                    style="width: 100%"
-                    @change="handleChange"
-                    :min="1"
-                    controls-position="right"
-                  />
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="12">
-                <ElFormItem label="外部链接" prop="link">
-                  <ElInput
-                    v-model="form.link"
-                    placeholder="外部链接/内嵌地址(https://www.baidu.com)"
-                  ></ElInput>
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-
-            <ElRow :gutter="20">
-              <ElCol :span="5">
-                <ElFormItem label="是否启用" prop="isEnable">
-                  <ElSwitch v-model="form.isEnable"></ElSwitch>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="5">
-                <ElFormItem label="页面缓存" prop="keepAlive">
-                  <ElSwitch v-model="form.keepAlive"></ElSwitch>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="5">
-                <ElFormItem label="是否显示" prop="isHide">
-                  <ElSwitch v-model="form.isHide"></ElSwitch>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="5">
-                <ElFormItem label="是否内嵌" prop="isMenu">
-                  <ElSwitch v-model="form.isIframe"></ElSwitch>
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-          </template>
-
-          <template v-if="labelPosition === 'button'">
-            <ElRow :gutter="20">
-              <ElCol :span="12">
-                <ElFormItem label="权限名称" prop="authName">
-                  <ElInput v-model="form.authName" placeholder="权限名称"></ElInput>
-                </ElFormItem>
-              </ElCol>
-              <ElCol :span="12">
-                <ElFormItem label="权限标识" prop="authLabel">
-                  <ElInput v-model="form.authLabel" placeholder="权限标识"></ElInput>
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-            <ElRow :gutter="20">
-              <ElCol :span="12">
-                <ElFormItem label="权限排序" prop="authSort" style="width: 100%">
-                  <ElInputNumber
-                    v-model="form.authSort"
-                    style="width: 100%"
-                    @change="handleChange"
-                    :min="1"
-                    controls-position="right"
-                  />
-                </ElFormItem>
-              </ElCol>
-            </ElRow>
-          </template>
-        </ElForm>
-
-        <template #footer>
-          <span class="dialog-footer">
-            <ElButton @click="dialogVisible = false">取 消</ElButton>
-            <ElButton type="primary" @click="submitForm()">确 定</ElButton>
-          </span>
-        </template>
-      </ElDialog>
-    </ElCard>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { useMenuStore } from '@/store/modules/menu'
-  import type { FormInstance, FormRules } from 'element-plus'
-  import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
-  import { IconTypeEnum } from '@/enums/appEnum'
-  import { formatMenuTitle } from '@/router/utils/utils'
-  import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
-  import { useTableColumns } from '@/composables/useTableColumns'
-  import { ElPopover, ElButton } from 'element-plus'
-  import { AppRouteRecord } from '@/types/router'
-  import { useAuth } from '@/composables/useAuth'
-
-  defineOptions({ name: 'Menus' })
-
-  const { hasAuth } = useAuth()
-
-  const { menuList } = storeToRefs(useMenuStore())
-
-  const loading = ref(false)
-
-  // 定义表单搜索初始值
-  const initialSearchState = {
-    name: '',
-    route: ''
-  }
-
-  // 响应式表单数据
-  const formFilters = reactive({ ...initialSearchState })
-
-  // 增加实际应用的搜索条件状态
-  const appliedFilters = reactive({ ...initialSearchState })
-
-  // 重置表单
-  const handleReset = () => {
-    Object.assign(formFilters, { ...initialSearchState })
-    Object.assign(appliedFilters, { ...initialSearchState })
-    getTableData()
-  }
-
-  // 搜索处理
-  const handleSearch = () => {
-    // 将当前输入的筛选条件应用到实际搜索
-    Object.assign(appliedFilters, { ...formFilters })
-    getTableData()
-  }
-
-  // 表单配置项
-  const formItems = computed(() => [
-    {
-      label: '菜单名称',
-      key: 'name',
-      type: 'input',
-      props: {
-        clearable: true
-      }
-    },
-    {
-      label: '路由地址',
-      key: 'route',
-      type: 'input',
-      props: {
-        clearable: true
-      }
-    }
-  ])
-
-  // 构建菜单类型标签
-  const buildMenuTypeTag = (row: AppRouteRecord) => {
-    if (row.children && row.children.length > 0) {
-      return 'info'
-    } else if (row.meta?.link && row.meta?.isIframe) {
-      return 'success'
-    } else if (row.path) {
-      return 'primary'
-    } else if (row.meta?.link) {
-      return 'warning'
-    }
-  }
-
-  // 构建菜单类型文本
-  const buildMenuTypeText = (row: AppRouteRecord) => {
-    if (row.children && row.children.length > 0) {
-      return '目录'
-    } else if (row.meta?.link && row.meta?.isIframe) {
-      return '内嵌'
-    } else if (row.path) {
-      return '菜单'
-    } else if (row.meta?.link) {
-      return '外链'
-    }
-  }
-
-  // 动态列配置
-  const { columnChecks, columns } = useTableColumns(() => [
-    {
-      prop: 'meta.title',
-      label: '菜单名称',
-      minWidth: 120,
-      formatter: (row: AppRouteRecord) => {
-        return formatMenuTitle(row.meta?.title)
-      }
-    },
-    {
-      prop: 'type',
-      label: '菜单类型',
-      formatter: (row: AppRouteRecord) => {
-        return h(ElTag, { type: buildMenuTypeTag(row) }, () => buildMenuTypeText(row))
-      }
-    },
-    {
-      prop: 'path',
-      label: '路由',
-      formatter: (row: AppRouteRecord) => {
-        return row.meta?.link || row.path || ''
-      }
-    },
-    {
-      prop: 'meta.authList',
-      label: '可操作权限',
-      formatter: (row: AppRouteRecord) => {
-        return h(
-          'div',
-          {},
-          row.meta.authList?.map((item: { title: string; authMark: string }, index: number) => {
-            return h(
-              ElPopover,
-              {
-                placement: 'top-start',
-                title: '操作',
-                width: 200,
-                trigger: 'click',
-                key: index
-              },
-              {
-                default: () =>
-                  h('div', { style: 'margin: 0; text-align: right' }, [
-                    h(
-                      ElButton,
-                      {
-                        size: 'small',
-                        type: 'primary',
-                        onClick: () => showModel('button', item)
-                      },
-                      { default: () => '编辑' }
-                    ),
-                    h(
-                      ElButton,
-                      {
-                        size: 'small',
-                        type: 'danger',
-                        onClick: () => deleteAuth()
-                      },
-                      { default: () => '删除' }
-                    )
-                  ]),
-                reference: () => h(ElButton, { class: 'small-btn' }, { default: () => item.title })
-              }
-            )
-          })
-        )
-      }
-    },
-    {
-      prop: 'date',
-      label: '编辑时间',
-      formatter: () => '2022-3-12 12:00:00'
-    },
-    {
-      prop: 'status',
-      label: '隐藏菜单',
-      formatter: (row) => {
-        return h(ElTag, { type: row.meta.isHide ? 'danger' : 'info' }, () =>
-          row.meta.isHide ? '是' : '否'
-        )
-      }
-    },
-    {
-      prop: 'operation',
-      label: '操作',
-      width: 180,
-      formatter: (row: AppRouteRecord) => {
-        return h('div', [
-          // 这里写两组权限标识判断是为了方便演示,在实际开发中可以删除其中一组
-          // 前端模式权限标识
-          hasAuth('B_CODE1') &&
-            h(ArtButtonTable, {
-              type: 'add',
-              onClick: () => showModel('menu')
-            }),
-          hasAuth('B_CODE2') &&
-            h(ArtButtonTable, {
-              type: 'edit',
-              onClick: () => showDialog('edit', row)
-            }),
-          hasAuth('B_CODE3') &&
-            h(ArtButtonTable, {
-              type: 'delete',
-              onClick: () => deleteMenu()
-            }),
-          // 后端模式权限标识
-          hasAuth('add') &&
-            h(ArtButtonTable, {
-              type: 'add',
-              onClick: () => showModel('menu')
-            }),
-          hasAuth('edit') &&
-            h(ArtButtonTable, {
-              type: 'edit',
-              onClick: () => showDialog('edit', row)
-            }),
-          hasAuth('delete') &&
-            h(ArtButtonTable, {
-              type: 'delete',
-              onClick: () => deleteMenu()
-            })
-        ])
-      }
-    }
-  ])
-
-  const handleRefresh = () => {
-    getTableData()
-  }
-
-  const dialogVisible = ref(false)
-  const form = reactive({
-    // 菜单
-    name: '',
-    path: '',
-    label: '',
-    icon: '',
-    isEnable: true,
-    sort: 1,
-    isMenu: true,
-    keepAlive: true,
-    isHide: true,
-    link: '',
-    isIframe: false,
-    // 权限 (修改这部分)
-    authName: '',
-    authLabel: '',
-    authIcon: '',
-    authSort: 1
-  })
-  const iconType = ref(IconTypeEnum.UNICODE)
-
-  const labelPosition = ref('menu')
-  const rules = reactive<FormRules>({
-    name: [
-      { required: true, message: '请输入菜单名称', trigger: 'blur' },
-      { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
-    ],
-    path: [{ required: true, message: '请输入路由地址', trigger: 'blur' }],
-    label: [{ required: true, message: '输入权限标识', trigger: 'blur' }],
-    // 修改这部分
-    authName: [{ required: true, message: '请输入权限名称', trigger: 'blur' }],
-    authLabel: [{ required: true, message: '请输入权限权限标识', trigger: 'blur' }]
-  })
-
-  const tableData = ref<AppRouteRecord[]>([])
-
-  onMounted(() => {
-    getTableData()
-  })
-
-  const getTableData = () => {
-    loading.value = true
-    setTimeout(() => {
-      tableData.value = menuList.value
-      loading.value = false
-    }, 500)
-  }
-
-  // 过滤后的表格数据
-  const filteredTableData = computed(() => {
-    // 深拷贝函数,避免修改原数据
-    const deepClone = (obj: any): any => {
-      if (obj === null || typeof obj !== 'object') return obj
-      if (obj instanceof Date) return new Date(obj)
-      if (Array.isArray(obj)) return obj.map((item) => deepClone(item))
-
-      const cloned: any = {}
-      for (const key in obj) {
-        if (Object.prototype.hasOwnProperty.call(obj, key)) {
-          cloned[key] = deepClone(obj[key])
-        }
-      }
-      return cloned
-    }
-
-    // 递归搜索函数
-    const searchMenu = (items: AppRouteRecord[]): AppRouteRecord[] => {
-      const results: AppRouteRecord[] = []
-
-      for (const item of items) {
-        // 获取搜索关键词,转换为小写并去除首尾空格
-        const searchName = appliedFilters.name?.toLowerCase().trim() || ''
-        const searchRoute = appliedFilters.route?.toLowerCase().trim() || ''
-
-        // 获取菜单标题和路径,确保它们存在
-        const menuTitle = formatMenuTitle(item.meta?.title || '').toLowerCase()
-        const menuPath = (item.path || '').toLowerCase()
-
-        // 使用 includes 进行模糊匹配
-        const nameMatch = !searchName || menuTitle.includes(searchName)
-        const routeMatch = !searchRoute || menuPath.includes(searchRoute)
-
-        // 如果有子菜单,递归搜索
-        if (item.children && item.children.length > 0) {
-          const matchedChildren = searchMenu(item.children)
-          // 如果子菜单有匹配项,保留当前菜单并更新子菜单
-          if (matchedChildren.length > 0) {
-            const clonedItem = deepClone(item)
-            clonedItem.children = matchedChildren
-            results.push(clonedItem)
-            continue
-          }
-        }
-
-        // 当前菜单匹配条件则返回
-        if (nameMatch && routeMatch) {
-          results.push(deepClone(item))
-        }
-      }
-
-      return results
-    }
-
-    return searchMenu(tableData.value)
-  })
-
-  const isEdit = ref(false)
-  const formRef = ref<FormInstance>()
-  const dialogTitle = computed(() => {
-    const type = labelPosition.value === 'menu' ? '菜单' : '权限'
-    return isEdit.value ? `编辑${type}` : `新建${type}`
-  })
-
-  const showDialog = (type: string, row: AppRouteRecord) => {
-    showModel('menu', row, true)
-  }
-
-  const handleChange = () => {}
-
-  const submitForm = async () => {
-    if (!formRef.value) return
-
-    await formRef.value.validate(async (valid) => {
-      if (valid) {
-        try {
-          ElMessage.success(`${isEdit.value ? '编辑' : '新增'}成功`)
-          dialogVisible.value = false
-        } catch {
-          ElMessage.error(`${isEdit.value ? '编辑' : '新增'}失败`)
-        }
-      }
-    })
-  }
-
-  const showModel = (type: string, row?: any, lock: boolean = false) => {
-    dialogVisible.value = true
-    labelPosition.value = type
-    isEdit.value = false
-    lockMenuType.value = lock
-    resetForm()
-
-    if (row) {
-      isEdit.value = true
-      nextTick(() => {
-        // 回显数据
-        if (type === 'menu') {
-          // 菜单数据回显
-          form.name = formatMenuTitle(row.meta.title)
-          form.path = row.path
-          form.label = row.name
-          form.icon = row.meta.icon
-          form.sort = row.meta.sort || 1
-          form.isMenu = row.meta.isMenu
-          form.keepAlive = row.meta.keepAlive
-          form.isHide = row.meta.isHide || true
-          form.isEnable = row.meta.isEnable || true
-          form.link = row.meta.link
-          form.isIframe = row.meta.isIframe || false
-        } else {
-          // 权限按钮数据回显
-          form.authName = row.title
-          form.authLabel = row.authMark
-          form.authIcon = row.icon || ''
-          form.authSort = row.sort || 1
-        }
-      })
-    }
-  }
-
-  const resetForm = () => {
-    formRef.value?.resetFields()
-    Object.assign(form, {
-      // 菜单
-      name: '',
-      path: '',
-      label: '',
-      icon: '',
-      sort: 1,
-      isMenu: true,
-      keepAlive: true,
-      isHide: true,
-      link: '',
-      isIframe: false,
-      // 权限
-      authName: '',
-      authLabel: '',
-      authIcon: '',
-      authSort: 1
-    })
-  }
-
-  const deleteMenu = async () => {
-    try {
-      await ElMessageBox.confirm('确定要删除该菜单吗?删除后无法恢复', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      })
-
-      ElMessage.success('删除成功')
-    } catch (error) {
-      if (error !== 'cancel') {
-        ElMessage.error('删除失败')
-      }
-    }
-  }
-
-  const deleteAuth = async () => {
-    try {
-      await ElMessageBox.confirm('确定要删除该权限吗?删除后无法恢复', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      })
-
-      ElMessage.success('删除成功')
-    } catch (error) {
-      if (error !== 'cancel') {
-        ElMessage.error('删除失败')
-      }
-    }
-  }
-
-  // 修改计算属性,增加锁定控制参数
-  const disableMenuType = computed(() => {
-    // 编辑权限时锁定为权限类型
-    if (isEdit.value && labelPosition.value === 'button') return true
-    // 编辑菜单时锁定为菜单类型
-    if (isEdit.value && labelPosition.value === 'menu') return true
-    // 顶部添加菜单按钮时锁定为菜单类型
-    if (!isEdit.value && labelPosition.value === 'menu' && lockMenuType.value) return true
-    return false
-  })
-
-  // 添加一个控制变量
-  const lockMenuType = ref(false)
-
-  const isExpanded = ref(false)
-  const tableRef = ref()
-
-  const toggleExpand = () => {
-    isExpanded.value = !isExpanded.value
-    nextTick(() => {
-      if (tableRef.value && filteredTableData.value) {
-        // 递归处理所有行的展开/收起状态
-        const processRows = (rows: AppRouteRecord[]) => {
-          rows.forEach((row) => {
-            if (row.children && row.children.length > 0) {
-              tableRef.value.elTableRef.toggleRowExpansion(row, isExpanded.value)
-              processRows(row.children)
-            }
-          })
-        }
-        processRows(filteredTableData.value)
-      }
-    })
-  }
-</script>
-
-<style lang="scss" scoped>
-  .menu-page {
-    .svg-icon {
-      width: 1.8em;
-      height: 1.8em;
-      overflow: hidden;
-      vertical-align: -8px;
-      fill: currentcolor;
-    }
-
-    :deep(.small-btn) {
-      height: 30px !important;
-      padding: 0 10px !important;
-      font-size: 12px !important;
-    }
-  }
-</style>

+ 0 - 5
web/src/views/system/nested/menu1/index.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1>菜单-1</h1>
-  </div>
-</template>

+ 0 - 5
web/src/views/system/nested/menu2/index.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1>菜单-2-1</h1>
-  </div>
-</template>

+ 0 - 5
web/src/views/system/nested/menu3/index.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1>菜单-3-1</h1>
-  </div>
-</template>

+ 0 - 5
web/src/views/system/nested/menu3/menu3-2/index.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1>菜单-3-2-1</h1>
-  </div>
-</template>

+ 0 - 215
web/src/views/template/banners/index.vue

@@ -1,215 +0,0 @@
-<template>
-  <div class="banners">
-    <h1 class="page-title">基础 & 自定义按钮+背景色</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner
-          title="数据中心运行状态"
-          subtitle="系统访问量同比增长 23%,所有服务运行稳定,数据监控正常。"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner
-          title="欢迎使用 逐趣CRM"
-          subtitle="基于 Vue 3 + TypeScript + Element Plus 构建的现代化管理系统。"
-          titleColor="#333"
-          subtitleColor="#666"
-          backgroundColor="#D4F1F7"
-          :buttonConfig="{
-            show: true,
-            text: '开始探索',
-            color: 'rgb(var(--art-success))',
-            textColor: '#fff',
-            radius: '6px'
-          }"
-          @buttonClick="handleBannerClick"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">自定义图片 & 使用 slot 自定义内容</h1>
-
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner
-          title="探索星空计划"
-          subtitle="加入我们的天文观测活动,发现宇宙的奥秘"
-          backgroundColor="#FF8AAB"
-          :buttonConfig="{
-            show: true,
-            text: '立即参与',
-            color: '#FF5A89',
-            textColor: '#fff'
-          }"
-          :imageConfig="{
-            src: icon3
-          }"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner
-          backgroundColor="#70B1FF"
-          :imageConfig="{
-            src: icon5
-          }"
-        >
-          <template #title>
-            <h2 style="margin: 0; font-size: 1.6rem; color: #fff !important">智能组件系统</h2>
-          </template>
-
-          <template #subtitle>
-            <div style="margin-top: 12px">
-              <p style="position: relative; z-index: 10; font-style: italic"
-                >灵活配置,强大扩展,支持自定义插槽内容</p
-              >
-            </div>
-          </template>
-
-          <template #button>
-            <div style="margin-top: 12px">
-              <el-button type="primary" color="#04A1FF"> 查看文档 </el-button>
-            </div>
-          </template>
-        </ArtBasicBanner>
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">抽象配置方案(Preset 模式)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner v-bind="PresetBanners.marketing" />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12">
-        <ArtBasicBanner v-bind="PresetBanners.info" />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">卡片横幅</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="12" :lg="6">
-        <ArtCardBanner
-          title="系统运行正常"
-          description="所有核心服务运行稳定,响应时间在正常范围内。"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12" :lg="6">
-        <ArtCardBanner
-          :image="icon2"
-          title="重要消息通知"
-          description="您有 3 条待处理的重要消息,请及时查看。"
-          :button="{
-            show: true,
-            text: '查看详情',
-            color: 'rgb(var(--art-warning))',
-            textColor: '#fff'
-          }"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12" :lg="6">
-        <ArtCardBanner
-          :image="icon3"
-          title="数据分析报告"
-          description="本周业务数据分析报告已完成,请查看关键指标。"
-          :button="{
-            show: true,
-            text: '下载报告',
-            color: 'rgb(var(--art-error))',
-            textColor: '#fff'
-          }"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="12" :lg="6">
-        <ArtCardBanner
-          :image="icon4"
-          title="版本更新提醒"
-          description="逐趣CRM v2.1.0 已发布,包含性能优化和新功能。"
-          :button="{
-            show: true,
-            text: '立即更新',
-            color: 'rgb(var(--art-primary))',
-            textColor: '#fff'
-          }"
-          :cancelButton="{
-            show: true,
-            text: '稍后提醒',
-            color: '#eee',
-            textColor: '#333'
-          }"
-          @click="handleConfirm"
-          @cancel="handleCancel"
-        />
-      </ElCol>
-    </ElRow>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import icon2 from '@imgs/3d/icon2.webp'
-  import icon3 from '@imgs/3d/icon3.webp'
-  import icon4 from '@imgs/3d/icon4.webp'
-  import icon5 from '@imgs/3d/icon7.webp'
-  import { ElRow } from 'element-plus'
-
-  const handleBannerClick = () => {
-    console.log('banner clicked')
-  }
-
-  const handleConfirm = () => {
-    console.log('confirm clicked')
-  }
-
-  const handleCancel = () => {
-    console.log('cancel clicked')
-  }
-
-  const PresetBanners = {
-    marketing: {
-      title: '限时优惠活动',
-      subtitle: '精选商品 48 小时闪购,最高享受 7 折优惠,数量有限!',
-      titleColor: 'var(--art-gray-900)',
-      subtitleColor: 'var(--art-gray-900)',
-      backgroundColor: 'rgb(var(--art-success), 0.1)',
-      meteorConfig: { enabled: true, count: 15 },
-      buttonConfig: {
-        show: true,
-        text: '立即抢购',
-        color: 'rgb(var(--art-success), 0.9)',
-        textColor: '#fff'
-      }
-    },
-    info: {
-      title: '服务到期提醒',
-      subtitle: '您的高级服务将在 7 天后到期,请及时续费以继续享受完整功能。',
-      titleColor: 'var(--art-gray-900)',
-      subtitleColor: 'var(--art-gray-900)',
-      backgroundColor: 'rgb(var(--art-primary), 0.1)',
-      meteorConfig: { enabled: true, count: 15 },
-      buttonConfig: {
-        show: true,
-        text: '立即续费',
-        color: 'rgb(var(--art-secondary), 0.9)',
-        textColor: '#fff'
-      }
-    }
-  } as const
-</script>
-
-<style lang="scss" scoped>
-  .banners {
-    padding-top: 20px;
-
-    .page-title {
-      margin: 20px 0 15px;
-      font-size: 22px;
-      font-weight: 500;
-
-      &:first-child {
-        margin-top: 0;
-      }
-    }
-
-    .el-col {
-      margin-bottom: 20px;
-    }
-  }
-</style>

+ 0 - 294
web/src/views/template/calendar/index.vue

@@ -1,294 +0,0 @@
-<template>
-  <div class="page-content">
-    <!-- 日历主体 -->
-    <ElCalendar v-model="currentDate">
-      <template #date-cell="{ data }">
-        <div
-          class="calendar-cell"
-          :class="{ 'is-selected': data.isSelected }"
-          @click="handleCellClick(data.day)"
-        >
-          <!-- 日期显示 -->
-          <p class="calendar-date">{{ formatDate(data.day) }}</p>
-
-          <!-- 事件列表 -->
-          <div class="calendar-events">
-            <div
-              v-for="event in getEvents(data.day)"
-              :key="`${event.date}-${event.content}`"
-              class="calendar-event"
-              @click.stop="handleEventClick(event)"
-            >
-              <div class="event-tag" :class="[`${event.type || 'bg-primary'}`]">
-                {{ event.content }}
-              </div>
-            </div>
-          </div>
-        </div>
-      </template>
-    </ElCalendar>
-
-    <!-- 事件编辑弹窗 -->
-    <ElDialog v-model="dialogVisible" :title="dialogTitle" width="600px" @closed="resetForm">
-      <ElForm :model="eventForm" label-width="80px">
-        <ElFormItem label="活动标题" required>
-          <ElInput v-model="eventForm.content" placeholder="请输入活动标题" />
-        </ElFormItem>
-        <ElFormItem label="事件颜色">
-          <ElRadioGroup v-model="eventForm.type">
-            <ElRadio v-for="type in eventTypes" :key="type.value" :value="type.value">
-              {{ type.label }}
-            </ElRadio>
-          </ElRadioGroup>
-        </ElFormItem>
-        <ElFormItem label="开始日期" required>
-          <ElDatePicker
-            style="width: 100%"
-            v-model="eventForm.date"
-            type="date"
-            placeholder="选择日期"
-            format="YYYY-MM-DD"
-            value-format="YYYY-MM-DD"
-          />
-        </ElFormItem>
-        <ElFormItem label="结束日期">
-          <ElDatePicker
-            style="width: 100%"
-            v-model="eventForm.endDate"
-            type="date"
-            placeholder="选择结束日期"
-            format="YYYY-MM-DD"
-            value-format="YYYY-MM-DD"
-            :min-date="eventForm.date"
-          />
-        </ElFormItem>
-      </ElForm>
-      <template #footer>
-        <span class="dialog-footer">
-          <ElButton v-if="isEditing" type="danger" @click="handleDeleteEvent"> 删除 </ElButton>
-          <ElButton type="primary" @click="handleSaveEvent">
-            {{ isEditing ? '更新' : '添加' }}
-          </ElButton>
-        </span>
-      </template>
-    </ElDialog>
-  </div>
-</template>
-
-<script setup lang="ts">
-  // 类型定义
-  interface CalendarEvent {
-    date: string
-    endDate?: string
-    content: string
-    type?: 'bg-primary' | 'bg-success' | 'bg-warning' | 'bg-danger'
-  }
-
-  // 常量定义
-  const eventTypes = [
-    { label: '基本', value: 'bg-primary' },
-    { label: '成功', value: 'bg-success' },
-    { label: '警告', value: 'bg-warning' },
-    { label: '危险', value: 'bg-danger' }
-  ] as const
-
-  // 状态管理
-  const currentDate = ref(new Date('2025-02-07'))
-  const events = ref<CalendarEvent[]>([
-    { date: '2025-02-01', content: '产品需求评审', type: 'bg-primary' },
-    {
-      date: '2025-02-03',
-      endDate: '2025-02-05',
-      content: '项目周报会议(跨日期)',
-      type: 'bg-primary'
-    },
-    { date: '2025-02-10', content: '瑜伽课程', type: 'bg-success' },
-    { date: '2025-02-15', content: '团队建设活动', type: 'bg-primary' },
-    { date: '2025-02-20', content: '健身训练', type: 'bg-success' },
-    { date: '2025-02-20', content: '代码评审', type: 'bg-danger' },
-    { date: '2025-02-20', content: '团队午餐', type: 'bg-primary' },
-    { date: '2025-02-20', content: '项目进度汇报', type: 'bg-warning' },
-    { date: '2025-02-28', content: '月度总结会', type: 'bg-warning' }
-  ])
-
-  // 弹窗状态管理
-  const dialogVisible = ref(false)
-  const dialogTitle = ref('添加事件')
-  const editingEventIndex = ref<number>(-1)
-  const eventForm = ref<CalendarEvent>({
-    date: '',
-    endDate: '',
-    content: '',
-    type: 'bg-primary'
-  })
-
-  // 计算属性
-  const isEditing = computed(() => editingEventIndex.value >= 0)
-
-  // 工具函数
-  const formatDate = (date: string) => date.split('-')[2]
-
-  const getEvents = (day: string) => {
-    return events.value.filter((event) => {
-      const eventDate = new Date(event.date)
-      const currentDate = new Date(day)
-      const endDate = event.endDate ? new Date(event.endDate) : new Date(event.date)
-
-      return currentDate >= eventDate && currentDate <= endDate
-    })
-  }
-
-  const resetForm = () => {
-    eventForm.value = {
-      date: '',
-      endDate: '',
-      content: '',
-      type: 'bg-primary'
-    }
-    editingEventIndex.value = -1
-  }
-
-  // 事件处理函数
-  const handleCellClick = (day: string) => {
-    dialogTitle.value = '添加事件'
-    eventForm.value = {
-      date: day,
-      content: '',
-      type: 'bg-primary'
-    }
-    editingEventIndex.value = -1
-    dialogVisible.value = true
-  }
-
-  const handleEventClick = (event: CalendarEvent) => {
-    dialogTitle.value = '编辑事件'
-    eventForm.value = { ...event }
-    editingEventIndex.value = events.value.findIndex(
-      (e) => e.date === event.date && e.content === event.content
-    )
-    dialogVisible.value = true
-  }
-
-  const handleSaveEvent = () => {
-    if (!eventForm.value.content || !eventForm.value.date) {
-      return
-    }
-
-    if (isEditing.value) {
-      events.value[editingEventIndex.value] = { ...eventForm.value }
-    } else {
-      events.value.push({ ...eventForm.value })
-    }
-
-    dialogVisible.value = false
-    resetForm()
-  }
-
-  const handleDeleteEvent = () => {
-    if (isEditing.value) {
-      events.value.splice(editingEventIndex.value, 1)
-      dialogVisible.value = false
-      resetForm()
-    }
-  }
-</script>
-
-<style scoped lang="scss">
-  .page-content {
-    height: 100%;
-
-    :deep(.el-calendar) {
-      height: 100%;
-
-      .el-calendar__body {
-        height: calc(100% - 70px);
-      }
-
-      .el-calendar-table {
-        height: 100%;
-
-        .is-selected {
-          // 选中日期的背景颜色
-          background-color: var(--el-color-warning-light-9) !important;
-        }
-
-        .el-calendar-day {
-          height: 100%;
-
-          &:hover {
-            background-color: transparent !important;
-          }
-        }
-      }
-    }
-  }
-
-  .calendar-cell {
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    min-height: 120px;
-    max-height: 120px;
-    padding: 4px;
-    overflow: hidden;
-    cursor: pointer;
-
-    .calendar-date {
-      position: absolute;
-      top: 4px;
-      right: 4px;
-      font-size: 14px;
-    }
-
-    .calendar-events {
-      display: flex;
-      flex-direction: column;
-      gap: 4px;
-      width: 100%;
-      max-height: 85px;
-      padding-right: 4px;
-      margin-top: 24px;
-      overflow-y: auto;
-    }
-
-    .event-tag {
-      min-width: 100px;
-      padding: 6px 12px;
-      overflow: hidden;
-      font-size: 13px;
-      font-weight: 500;
-      line-height: 24px;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-      border-radius: 4px;
-
-      &:hover {
-        opacity: 0.8;
-      }
-
-      &::before {
-        position: absolute;
-        top: 0;
-        left: 0;
-        display: inline-block;
-        width: 4px;
-        height: 100%;
-        content: '';
-      }
-
-      &.event-continues::after {
-        position: absolute;
-        top: 50%;
-        right: 4px;
-        font-size: 12px;
-        content: '►';
-        transform: translateY(-50%);
-      }
-    }
-  }
-
-  :deep(.el-dialog__body) {
-    padding-top: 20px;
-  }
-</style>

+ 0 - 454
web/src/views/template/cards/index.vue

@@ -1,454 +0,0 @@
-<template>
-  <div class="cards">
-    <h1 class="page-title">统计卡片(文字)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in statsCards" :key="card.id">
-        <ArtStatsCard
-          :icon="card.icon"
-          :title="card.title"
-          :description="card.description"
-          :iconSize="card.iconSize"
-          :iconBgRadius="8"
-          iconColor="#fff"
-          :iconBgColor="card.iconBgColor"
-          :showArrow="card.showArrow"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">统计卡片(数字滚动)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in statsCards" :key="card.id">
-        <ArtStatsCard
-          :icon="card.icon"
-          :count="card.count"
-          :description="card.description"
-          :iconSize="card.iconSize"
-          :decimals="0"
-          :iconBgColor="card.iconBgColor"
-          :showArrow="card.showArrow"
-          separator=","
-          iconColor="#fff"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">统计卡片(自定义样式)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in statsCards" :key="card.id">
-        <ArtStatsCard
-          :icon="card.icon"
-          :title="card.title"
-          :description="card.description"
-          :iconColor="card.iconColor"
-          :textColor="card.textColor"
-          :backgroundColor="card.backgroundColor"
-          :showArrow="card.showArrow"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">进度卡片</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in progressCards" :key="card.id">
-        <ArtProgressCard :percentage="card.percentage" :title="card.title" :color="card.color" />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">进度卡片(icon)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in progressCards" :key="card.id">
-        <ArtProgressCard
-          :percentage="card.percentage"
-          :title="card.title"
-          :color="card.color"
-          :icon="card.icon"
-          :iconColor="card.iconColor"
-          :iconBgColor="card.iconBgColor"
-          :iconSize="card.iconSize"
-          :iconBgRadius="8"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">图表卡片(小图表)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtLineChartCard
-          :isMiniChart="true"
-          :value="2545"
-          label="新用户"
-          date="过去7天"
-          :percentage="1.2"
-          :height="9.5"
-          :chartData="[120, 132, 101, 134, 90, 230, 210]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtBarChartCard
-          :isMiniChart="true"
-          :value="15480"
-          label="浏览量"
-          date="过去 14 天"
-          :percentage="-4.15"
-          :height="9.5"
-          :barWidth="'45%'"
-          :chartData="[120, 100, 150, 140, 90, 120, 130]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtLineChartCard
-          :isMiniChart="true"
-          :value="2545"
-          label="粉丝数"
-          date="过去 30 天"
-          :percentage="1.2"
-          :height="9.5"
-          :showAreaColor="true"
-          :chartData="[150, 180, 160, 200, 180, 220, 240]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtDonutChartCard
-          :value="36358"
-          title="粉丝量"
-          :percentage="18"
-          percentageLabel="较去年"
-          :data="[50, 40]"
-          :height="9.5"
-          currentValue="2022"
-          previousValue="2021"
-          :radius="['50%', '70%']"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">图表卡片(大图表)</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtLineChartCard
-          :value="2545"
-          label="新用户"
-          :percentage="1.2"
-          :height="11"
-          :chartData="[120, 132, 101, 134, 90, 230, 210]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtBarChartCard
-          :value="15480"
-          label="浏览量"
-          :percentage="-4.15"
-          :height="11"
-          :chartData="[120, 100, 150, 140, 90, 120, 130, 110]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtLineChartCard
-          :value="2545"
-          label="粉丝数"
-          :percentage="1.2"
-          :height="11"
-          :showAreaColor="true"
-          :chartData="[150, 180, 160, 200, 180, 220, 240]"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :md="6">
-        <ArtDonutChartCard
-          :value="36358"
-          title="粉丝量"
-          :percentage="-18"
-          percentageLabel="较2021年"
-          :data="[70, 30]"
-          :height="11"
-          currentValue="12月"
-          previousValue="11月"
-        />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">数据列表卡片</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :lg="8">
-        <ArtDataListCard :list="dataList" title="待办事项" subtitle="今日待处理任务" />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :lg="8">
-        <ArtDataListCard
-          :maxCount="4"
-          :list="dataList"
-          title="最近活动"
-          subtitle="近期活动列表"
-          :showMoreButton="true"
-          @more="handleMore"
-        />
-      </ElCol>
-      <ElCol :xs="24" :sm="12" :lg="8">
-        <ArtTimelineListCard :list="timelineData" title="最近交易" subtitle="2024年12月20日" />
-      </ElCol>
-    </ElRow>
-
-    <h1 class="page-title">图片卡片</h1>
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :sm="12" :md="6" v-for="card in imageCards" :key="card.id">
-        <ArtImageCard
-          :imageUrl="card.imageUrl"
-          :title="card.title"
-          :category="card.category"
-          :readTime="card.readTime"
-          :views="card.views"
-          :comments="card.comments"
-          :date="card.date"
-          @click="handleImageCardClick(card)"
-        />
-      </ElCol>
-    </ElRow>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import cover1 from '@imgs/cover/img1.webp'
-  import cover2 from '@imgs/cover/img2.webp'
-  import cover3 from '@imgs/cover/img3.webp'
-  import cover4 from '@imgs/cover/img4.webp'
-
-  const statsCards = [
-    {
-      id: 1,
-      title: '销售产品',
-      count: 1235,
-      description: '鞋子、牛仔裤、派对服装、手表',
-      icon: '&#xe812;',
-      iconColor: 'rgb(var(--art-primary))',
-      iconSize: 20,
-      iconBgColor: 'rgb(var(--art-info))',
-      textColor: 'rgb(var(--art-primary))',
-      backgroundColor: 'rgb(var(--art-bg-primary))',
-      showArrow: false
-    },
-    {
-      id: 2,
-      title: '活跃用户',
-      count: 5000,
-      description: '日活跃用户超过5,000+',
-      icon: '&#xe724;',
-      iconColor: 'rgb(var(--art-warning))',
-      iconSize: 20,
-      iconBgColor: 'rgb(var(--art-success))',
-      textColor: 'rgb(var(--art-warning))',
-      backgroundColor: 'rgb(var(--art-bg-warning))',
-      showArrow: false
-    },
-    {
-      id: 3,
-      title: '总收入',
-      count: 35000,
-      description: '月收入超过¥350,000+',
-      icon: '&#xe70e;',
-      iconColor: 'rgb(var(--art-secondary))',
-      iconSize: 20,
-      iconBgColor: 'rgb(var(--art-secondary))',
-      textColor: 'rgb(var(--art-secondary))',
-      backgroundColor: 'rgb(var(--art-bg-secondary))',
-      showArrow: false
-    },
-    {
-      id: 4,
-      title: '客户评价',
-      count: 4800,
-      description: '平均评分4.8/5',
-      icon: '&#xe82d;',
-      iconColor: 'rgb(var(--art-error))',
-      iconSize: 20,
-      iconBgColor: 'rgb(var(--art-error))',
-      textColor: 'rgb(var(--art-error))',
-      backgroundColor: 'rgb(var(--art-bg-error))',
-      showArrow: false
-    }
-  ]
-
-  const progressCards = [
-    {
-      id: 1,
-      title: '完成进度',
-      percentage: 75,
-      color: 'rgb(var(--art-success))',
-      icon: '&#xe812;',
-      iconColor: 'rgb(var(--art-success))',
-      iconBgColor: 'rgb(var(--art-bg-success))',
-      iconSize: 20
-    },
-    {
-      id: 2,
-      title: '项目进度',
-      percentage: 65,
-      color: 'rgb(var(--art-primary))',
-      icon: '&#xe724;',
-      iconColor: 'rgb(var(--art-primary))',
-      iconBgColor: 'rgb(var(--art-bg-primary))',
-      iconSize: 20
-    },
-    {
-      id: 3,
-      title: '学习进度',
-      percentage: 45,
-      color: 'rgb(var(--art-error))',
-      icon: '&#xe724;',
-      iconColor: 'rgb(var(--art-error))',
-      iconBgColor: 'rgb(var(--art-bg-error))',
-      iconSize: 20
-    },
-    {
-      id: 4,
-      title: '任务进度',
-      percentage: 90,
-      color: 'rgb(var(--art-secondary))',
-      icon: '&#xe724;',
-      iconColor: 'rgb(var(--art-secondary))',
-      iconBgColor: 'rgb(var(--art-bg-secondary))',
-      iconSize: 20
-    }
-  ]
-
-  const imageCards = [
-    {
-      id: 1,
-      imageUrl: cover1,
-      title: 'AI技术在医疗领域的创新应用与发展前景',
-      category: '社交',
-      readTime: '2分钟',
-      views: 9125,
-      comments: 3,
-      date: '12月19日 周一'
-    },
-    {
-      id: 2,
-      imageUrl: cover2,
-      title: '大数据分析助力企业决策的实践案例',
-      category: '技术',
-      readTime: '3分钟',
-      views: 7234,
-      comments: 5,
-      date: '12月20日 周二'
-    },
-    {
-      id: 3,
-      imageUrl: cover3,
-      title: '区块链技术在供应链管理中的应用',
-      category: '科技',
-      readTime: '4分钟',
-      views: 5678,
-      comments: 8,
-      date: '12月21日 周三'
-    },
-    {
-      id: 4,
-      imageUrl: cover4,
-      title: '云计算技术发展趋势与未来展望',
-      category: '云技术',
-      readTime: '5分钟',
-      views: 4321,
-      comments: 6,
-      date: '12月22日 周四'
-    }
-  ]
-
-  const dataList = [
-    {
-      title: '新加坡之行',
-      status: '进行中',
-      time: '5分钟',
-      class: 'bg-primary',
-      icon: '&#xe6f2;'
-    },
-    {
-      title: '归档数据',
-      status: '进行中',
-      time: '10分钟',
-      class: 'bg-secondary',
-      icon: '&#xe806;'
-    },
-    {
-      title: '客户会议',
-      status: '待处理',
-      time: '15分钟',
-      class: 'bg-warning',
-      icon: '&#xe6fb;'
-    },
-    {
-      title: '筛选任务团队',
-      status: '进行中',
-      time: '20分钟',
-      class: 'bg-danger',
-      icon: '&#xe813;'
-    },
-    {
-      title: '发送信封给小王',
-      status: '已完成',
-      time: '20分钟',
-      class: 'bg-success',
-      icon: '&#xe70c;'
-    }
-  ]
-
-  const timelineData = [
-    {
-      time: '上午 09:30',
-      status: 'rgb(73, 190, 255)',
-      content: '收到 John Doe 支付的 385.90 美元'
-    },
-    {
-      time: '上午 10:00',
-      status: 'rgb(54, 158, 255)',
-      content: '新销售记录',
-      code: 'ML-3467'
-    },
-    {
-      time: '上午 12:00',
-      status: 'rgb(103, 232, 207)',
-      content: '向 Michael 支付了 64.95 美元'
-    },
-    {
-      time: '下午 14:30',
-      status: 'rgb(255, 193, 7)',
-      content: '系统维护通知',
-      code: 'MT-2023'
-    },
-    {
-      time: '下午 15:45',
-      status: 'rgb(255, 105, 105)',
-      content: '紧急订单取消提醒',
-      code: 'OR-9876'
-    },
-    {
-      time: '下午 17:00',
-      status: 'rgb(103, 232, 207)',
-      content: '完成每日销售报表'
-    }
-  ]
-
-  const handleMore = () => {}
-
-  const handleImageCardClick = (card: any) => {
-    console.log(card)
-  }
-</script>
-
-<style lang="scss" scoped>
-  .cards {
-    padding: 20px 0;
-
-    .page-title {
-      margin: 20px 0 15px;
-      font-size: 22px;
-      font-weight: 500;
-
-      &:first-child {
-        margin-top: 0;
-      }
-    }
-
-    .el-col {
-      margin-bottom: 20px;
-    }
-  }
-</style>

+ 0 - 383
web/src/views/template/charts/index.vue

@@ -1,383 +0,0 @@
-<!--
- * 部分属性配置:
- * 图表高度: height="12rem" | height="300px"
- * 柱状图宽度: barWidth="26%"
- * 圆角大小: :borderRadius="4" | :borderRadius="[6, 6, 0, 0]"
- * 颜色配置: :colors=['#409eff', '#95E0FB']
- * 是否显示图例: :showLegend="true"
- * 图例位置: legendPosition="bottom" | "top" | "left" | "right"
- * 是否显示提示框: :showTooltip="true"
- * 堆叠: :stack="true"
- * 数据点大小: :symbolSize="7"
- * 数据点类型: symbol="circle" | "rect" | "roundRect" | "triangle" | "diamond" | "pin" | "arrow" | "none"
--->
-<template>
-  <div class="charts">
-    <h1 class="page-title">图表</h1>
-
-    <ElRow :gutter="20">
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(单数据)</span>
-          </div>
-          <ArtBarChart
-            :data="singleBarData"
-            :xAxisData="xAxisData"
-            :showLegend="true"
-            legendPosition="right"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(多组数据)</span>
-          </div>
-          <ArtBarChart
-            :data="multiBarData"
-            :xAxisData="xAxisData"
-            :showLegend="true"
-            barWidth="26%"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(堆叠)</span>
-          </div>
-          <ArtBarChart
-            :data="stackBarData"
-            :xAxisData="stackXAxisData"
-            :showLegend="true"
-            :stack="true"
-            barWidth="26%"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>折线图</span>
-          </div>
-          <ArtLineChart
-            :data="[58, 15, 82, 35, 120, 62, 45]"
-            :xAxisData="['一月', '二月', '三月', '四月', '五月', '六月', '七月']"
-            symbol="none"
-            :symbolSize="7"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>折线图(渐变背景)</span>
-          </div>
-          <ArtLineChart
-            :data="[28, 45, 82, 35, 100, 32, 55]"
-            :xAxisData="['一月', '二月', '三月', '四月', '五月', '六月', '七月']"
-            :showAreaColor="true"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>折线图(多组数据)</span>
-          </div>
-          <ArtLineChart
-            :data="multiLineData"
-            :xAxisData="['1月', '2月', '3月', '4月', '5月', '6月']"
-            :showLegend="true"
-          />
-        </div>
-      </ElCol>
-
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(水平)</span>
-          </div>
-          <ArtHBarChart
-            :data="[50, 80, 120, 90, 60]"
-            :xAxisData="['产品A', '产品B', '产品C', '产品D', '产品E']"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(水平)</span>
-          </div>
-
-          <ArtHBarChart
-            :data="[
-              { name: '系列1', data: [10, 20, 30] },
-              { name: '系列2', data: [15, 25, 35] }
-            ]"
-            :xAxisData="['类目1', '类目2', '类目3']"
-            :showLegend="true"
-            barWidth="30%"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>柱状图(水平堆叠)</span>
-          </div>
-          <ArtHBarChart
-            :data="[
-              { name: '系列1', data: [10, 20, 30] },
-              { name: '系列2', data: [15, 25, 35] }
-            ]"
-            :xAxisData="['类目1', '类目2', '类目3']"
-            :showLegend="true"
-            :stack="true"
-            barWidth="30%"
-          />
-        </div>
-      </ElCol>
-
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>环形图</span>
-          </div>
-          <ArtRingChart
-            :data="[
-              { value: 35, name: '分类A' },
-              { value: 28, name: '分类B' },
-              { value: 42, name: '分类C' }
-            ]"
-            :radius="['54%', '70%']"
-            legendPosition="bottom"
-          />
-        </div>
-      </ElCol>
-
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>环形图</span>
-          </div>
-          <ArtRingChart
-            :data="[
-              { value: 35, name: '分类A' },
-              { value: 28, name: '分类B' },
-              { value: 42, name: '分类C' },
-              { value: 32, name: '分类D' },
-              { value: 26, name: '分类E' },
-              { value: 37, name: '分类F' }
-            ]"
-            :radius="['54%', '70%']"
-            :borderRadius="0"
-            :showLegend="true"
-            legendPosition="bottom"
-            centerText="¥300,458"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>饼图</span>
-          </div>
-          <ArtRingChart
-            :data="[
-              { value: 30, name: '分类A' },
-              { value: 25, name: '分类B' },
-              { value: 45, name: '分类C' }
-            ]"
-            :radius="['0%', '70%']"
-            :showLegend="true"
-            legendPosition="right"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>散点图</span>
-          </div>
-          <ArtScatterChart
-            :data="[
-              { value: [1, 3] },
-              { value: [2, 4] },
-              { value: [3, 5] },
-              { value: [4, 6] },
-              { value: [5, 7] },
-              { value: [6, 8] },
-              { value: [7, 7] },
-              { value: [8, 9] },
-              { value: [9, 8] },
-              { value: [10, 6] },
-              { value: [11, 7] },
-              { value: [12, 8] }
-            ]"
-          />
-        </div>
-      </ElCol>
-
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>雷达图</span>
-          </div>
-          <ArtRadarChart
-            :indicator="[
-              { name: '销售', max: 100 },
-              { name: '管理', max: 100 },
-              { name: '技术', max: 100 },
-              { name: '客服', max: 100 },
-              { name: '开发', max: 100 }
-            ]"
-            :data="[
-              {
-                name: '预算分配',
-                value: [80, 70, 90, 85, 75]
-              },
-              {
-                name: '实际开销',
-                value: [70, 75, 85, 80, 70]
-              }
-            ]"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>k线图</span>
-          </div>
-          <ArtKLineChart
-            :data="[
-              { time: '2024-01-01', open: 20, close: 23, high: 25, low: 18 },
-              { time: '2024-01-02', open: 23, close: 21, high: 24, low: 20 },
-              { time: '2024-01-03', open: 21, close: 25, high: 26, low: 21 }
-            ]"
-            :showDataZoom="false"
-            :dataZoomStart="0"
-            :dataZoomEnd="100"
-          />
-        </div>
-      </ElCol>
-      <ElCol :xs="24" :md="12" :lg="8">
-        <div class="card art-custom-card">
-          <div class="card-header">
-            <span>双向堆叠柱状图</span>
-          </div>
-          <ArtDualBarCompareChart
-            :positiveData="[50, 28, 80, 65, 68, 70, 60, 55]"
-            :negativeData="[50, 28, 40, 45, 38, 50, 42, 48]"
-            :xAxisData="[
-              '0-4岁',
-              '5-14岁',
-              '15-24岁',
-              '25-34岁',
-              '35-44岁',
-              '45-54岁',
-              '55-64岁',
-              '65岁以上'
-            ]"
-            positiveName="男性年龄分布"
-            negativeName="女性年龄分布"
-          />
-        </div>
-      </ElCol>
-    </ElRow>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import type { LineDataItem } from '@/types/component/chart'
-  import { ref } from 'vue'
-
-  // 多组数据折线图示例数据
-  const multiLineData: LineDataItem[] = [
-    {
-      name: '销售额',
-      data: [120, 132, 101, 134, 90, 130],
-      areaStyle: {
-        startOpacity: 0.1,
-        endOpacity: 0
-      }
-    },
-    {
-      name: '利润',
-      data: [80, 92, 71, 94, 60, 80],
-      areaStyle: {
-        startOpacity: 0.1,
-        endOpacity: 0
-      }
-    }
-  ]
-
-  // 单数据示例
-  const singleBarData = ref([120, 200, 150, 80, 70, 110, 130])
-  const xAxisData = ref(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
-
-  // 多数据示例
-  const multiBarData = ref([
-    {
-      name: '销售额',
-      data: [120, 200, 150, 80, 70, 110, 130]
-    },
-    {
-      name: '利润',
-      data: [20, 50, 30, 15, 10, 25, 35]
-    }
-  ])
-
-  // 堆叠柱状图示例
-  const stackBarData = ref([
-    {
-      name: 'Q1',
-      data: [20, 25, 30, 35, 40],
-      stack: 'total'
-    },
-    {
-      name: 'Q2',
-      data: [30, 35, 40, 45, 50],
-      stack: 'total'
-    }
-  ])
-
-  const stackXAxisData = ref(['产品A', '产品B', '产品C', '产品D', '产品E'])
-</script>
-
-<style lang="scss" scoped>
-  .charts {
-    padding-top: 20px;
-
-    .page-title {
-      margin: 20px 0 15px;
-      font-size: 22px;
-      font-weight: 500;
-
-      &:first-child {
-        margin-top: 0;
-      }
-    }
-
-    .card {
-      padding: 20px;
-      background-color: var(--art-main-bg-color);
-      border-radius: var(--custom-radius);
-
-      .card-header {
-        padding-bottom: 15px;
-
-        span {
-          font-size: 16px;
-          font-weight: 500;
-        }
-      }
-    }
-
-    .el-col {
-      margin-bottom: 20px;
-    }
-  }
-</style>

+ 0 - 771
web/src/views/template/chat/index.vue

@@ -1,771 +0,0 @@
-<template>
-  <div class="chat" :style="{ height: containerMinHeight }">
-    <ElRow>
-      <ElCol :span="12">
-        <div class="grid-content ep-bg-purple" />
-      </ElCol>
-      <ElCol :span="12">
-        <div class="grid-content ep-bg-purple-light" />
-      </ElCol>
-    </ElRow>
-    <div class="person-list">
-      <div class="person-item-header">
-        <div class="user-info">
-          <ElAvatar :size="50" :src="selectedPerson?.avatar" />
-          <div class="user-details">
-            <div class="name">{{ selectedPerson?.name }}</div>
-            <div class="email">{{ selectedPerson?.email }}</div>
-          </div>
-        </div>
-        <div class="search-box">
-          <ElInput v-model="searchQuery" placeholder="搜索联系人" prefix-icon="Search" clearable />
-        </div>
-        <ElDropdown trigger="click" placement="bottom-start">
-          <span class="sort-btn">
-            排序方式
-            <ElIcon class="el-icon--right">
-              <arrow-down />
-            </ElIcon>
-          </span>
-          <template #dropdown>
-            <ElDropdownMenu>
-              <ElDropdownItem>按时间排序</ElDropdownItem>
-              <ElDropdownItem>按名称排序</ElDropdownItem>
-              <ElDropdownItem>全部标为已读</ElDropdownItem>
-            </ElDropdownMenu>
-          </template>
-        </ElDropdown>
-      </div>
-      <ElScrollbar>
-        <div
-          v-for="item in personList"
-          :key="item.id"
-          class="person-item"
-          :class="{ active: selectedPerson?.id === item.id }"
-          @click="selectPerson(item)"
-        >
-          <div class="avatar-wrapper">
-            <ElAvatar :size="40" :src="item.avatar">
-              {{ item.name.charAt(0) }}
-            </ElAvatar>
-            <div class="status-dot" :class="{ online: item.online }"></div>
-          </div>
-          <div class="person-info">
-            <div class="info-top">
-              <span class="person-name">{{ item.name }}</span>
-              <span class="last-time">{{ item.lastTime }}</span>
-            </div>
-            <div class="info-bottom">
-              <span class="email">{{ item.email }}</span>
-            </div>
-          </div>
-        </div>
-      </ElScrollbar>
-    </div>
-    <div class="chat-modal">
-      <div class="header">
-        <div class="header-left">
-          <span class="name">Art Bot</span>
-          <div class="status">
-            <div class="dot" :class="{ online: isOnline, offline: !isOnline }"></div>
-            <span class="status-text">{{ isOnline ? '在线' : '离线' }}</span>
-          </div>
-        </div>
-        <div class="header-right">
-          <div class="btn">
-            <i class="iconfont-sys">&#xe776;</i>
-          </div>
-          <div class="btn">
-            <i class="iconfont-sys">&#xe778;</i>
-          </div>
-          <div class="btn">
-            <i class="iconfont-sys">&#xe6df;</i>
-          </div>
-        </div>
-      </div>
-      <div class="chat-container">
-        <!-- 聊天消息区域 -->
-        <div class="chat-messages" ref="messageContainer">
-          <template v-for="(message, index) in messages" :key="index">
-            <div :class="['message-item', message.isMe ? 'message-right' : 'message-left']">
-              <ElAvatar :size="32" :src="message.avatar" class="message-avatar" />
-              <div class="message-content">
-                <div class="message-info">
-                  <span class="sender-name">{{ message.sender }}</span>
-                  <span class="message-time">{{ message.time }}</span>
-                </div>
-                <div class="message-text">{{ message.content }}</div>
-              </div>
-            </div>
-          </template>
-        </div>
-
-        <!-- 聊天输入区域 -->
-        <div class="chat-input">
-          <ElInput
-            v-model="messageText"
-            type="textarea"
-            :rows="3"
-            placeholder="输入消息"
-            resize="none"
-            @keyup.enter.prevent="sendMessage"
-          >
-            <template #append>
-              <div class="input-actions">
-                <ElButton :icon="Paperclip" circle plain />
-                <ElButton :icon="Picture" circle plain />
-                <ElButton type="primary" @click="sendMessage" v-ripple>发送</ElButton>
-              </div>
-            </template>
-          </ElInput>
-          <div class="chat-input-actions">
-            <div class="left">
-              <i class="iconfont-sys">&#xe634;</i>
-              <i class="iconfont-sys">&#xe809;</i>
-            </div>
-            <ElButton type="primary" @click="sendMessage" v-ripple>发送</ElButton>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref, onMounted } from 'vue'
-  import { Picture, Paperclip } from '@element-plus/icons-vue'
-  import { mittBus } from '@/utils/sys'
-  import meAvatar from '@/assets/img/avatar/avatar5.webp'
-  import aiAvatar from '@/assets/img/avatar/avatar10.webp'
-  import avatar2 from '@/assets/img/avatar/avatar2.webp'
-  import avatar3 from '@/assets/img/avatar/avatar3.webp'
-  import avatar4 from '@/assets/img/avatar/avatar4.webp'
-  import avatar5 from '@/assets/img/avatar/avatar5.webp'
-  import avatar6 from '@/assets/img/avatar/avatar6.webp'
-  import avatar7 from '@/assets/img/avatar/avatar7.webp'
-  import avatar8 from '@/assets/img/avatar/avatar8.webp'
-  import avatar9 from '@/assets/img/avatar/avatar9.webp'
-  import avatar10 from '@/assets/img/avatar/avatar10.webp'
-  import { useCommon } from '@/composables/useCommon'
-
-  const { containerMinHeight } = useCommon()
-
-  const searchQuery = ref('')
-
-  // 抽屉显示状态
-  const isDrawerVisible = ref(false)
-  // 是否在线
-  const isOnline = ref(true)
-
-  interface Person {
-    id: number
-    name: string
-    email: string
-    avatar: string
-    online?: boolean
-    lastTime: string
-    unread?: number
-  }
-
-  const selectedPerson = ref<Person | null>(null)
-
-  const personList = ref<Person[]>([
-    {
-      id: 1,
-      name: '梅洛迪·梅西',
-      email: 'melody@altbox.com',
-      avatar: meAvatar,
-      online: true,
-      lastTime: '20小时前',
-      unread: 0
-    },
-    {
-      id: 2,
-      name: '马克·史密斯',
-      email: 'max@kt.com',
-      avatar: avatar2,
-      online: true,
-      lastTime: '2周前',
-      unread: 6
-    },
-    {
-      id: 3,
-      name: '肖恩·宾',
-      email: 'sean@dellito.com',
-      avatar: avatar3,
-      online: false,
-      lastTime: '5小时前',
-      unread: 5
-    },
-    {
-      id: 4,
-      name: '爱丽丝·约翰逊',
-      email: 'alice@domain.com',
-      avatar: avatar4,
-      online: true,
-      lastTime: '1小时前',
-      unread: 2
-    },
-    {
-      id: 5,
-      name: '鲍勃·布朗',
-      email: 'bob@domain.com',
-      avatar: avatar5,
-      online: false,
-      lastTime: '3天前',
-      unread: 1
-    },
-    {
-      id: 6,
-      name: '查理·戴维斯',
-      email: 'charlie@domain.com',
-      avatar: avatar6,
-      online: true,
-      lastTime: '10分钟前',
-      unread: 0
-    },
-    {
-      id: 7,
-      name: '戴安娜·普林斯',
-      email: 'diana@domain.com',
-      avatar: avatar7,
-      online: true,
-      lastTime: '15分钟前',
-      unread: 3
-    },
-    {
-      id: 8,
-      name: '伊桑·亨特',
-      email: 'ethan@domain.com',
-      avatar: avatar8,
-      online: true,
-      lastTime: '5分钟前',
-      unread: 0
-    },
-    {
-      id: 9,
-      name: '杰西卡·琼斯',
-      email: 'jessica@domain.com',
-      avatar: avatar9,
-      online: false,
-      lastTime: '1天前',
-      unread: 4
-    },
-    {
-      id: 10,
-      name: '彼得·帕克',
-      email: 'peter@domain.com',
-      avatar: avatar10,
-      online: true,
-      lastTime: '2小时前',
-      unread: 1
-    },
-    {
-      id: 11,
-      name: '克拉克·肯特',
-      email: 'clark@domain.com',
-      avatar: avatar3,
-      online: true,
-      lastTime: '30分钟前',
-      unread: 2
-    },
-    {
-      id: 12,
-      name: '布鲁斯·韦恩',
-      email: 'bruce@domain.com',
-      avatar: avatar5,
-      online: false,
-      lastTime: '3天前',
-      unread: 0
-    },
-    {
-      id: 13,
-      name: '韦德·威尔逊',
-      email: 'wade@domain.com',
-      avatar: avatar6,
-      online: true,
-      lastTime: '10分钟前',
-      unread: 5
-    }
-  ])
-
-  const selectPerson = (person: Person) => {
-    selectedPerson.value = person
-  }
-
-  // 消息相关数据
-  const messageText = ref('')
-  const messages = ref([
-    {
-      id: 1,
-      sender: 'Art Bot',
-      content: '你好!我是你的AI助手,有什么我可以帮你的吗?',
-      time: '10:00',
-      isMe: false,
-      avatar: aiAvatar
-    },
-    {
-      id: 2,
-      sender: 'Ricky',
-      content: '我想了解一下系统的使用方法。',
-      time: '10:01',
-      isMe: true,
-      avatar: meAvatar
-    },
-    {
-      id: 3,
-      sender: 'Art Bot',
-      content: '好的,我来为您介绍系统的主要功能。首先,您可以通过左侧菜单访问不同的功能模块...',
-      time: '10:02',
-      isMe: false,
-      avatar: aiAvatar
-    },
-    {
-      id: 4,
-      sender: 'Ricky',
-      content: '听起来很不错,能具体讲讲数据分析部分吗?',
-      time: '10:05',
-      isMe: true,
-      avatar: meAvatar
-    },
-    {
-      id: 5,
-      sender: 'Art Bot',
-      content: '当然可以。数据分析模块可以帮助您实时监控关键指标,并生成详细的报表...',
-      time: '10:06',
-      isMe: false,
-      avatar: aiAvatar
-    },
-    {
-      id: 6,
-      sender: 'Ricky',
-      content: '太好了,那我如何开始使用呢?',
-      time: '10:08',
-      isMe: true,
-      avatar: meAvatar
-    },
-    {
-      id: 7,
-      sender: 'Art Bot',
-      content: '您可以先创建一个项目,然后在项目中添加相关的数据源,系统会自动进行分析。',
-      time: '10:09',
-      isMe: false,
-      avatar: aiAvatar
-    },
-    {
-      id: 8,
-      sender: 'Ricky',
-      content: '明白了,谢谢你的帮助!',
-      time: '10:10',
-      isMe: true,
-      avatar: meAvatar
-    },
-    {
-      id: 9,
-      sender: 'Art Bot',
-      content: '不客气,有任何问题随时联系我。',
-      time: '10:11',
-      isMe: false,
-      avatar: aiAvatar
-    }
-  ])
-
-  const messageId = ref(10) // 用于生成唯一的消息ID
-
-  const userAvatar = ref(meAvatar) // 使用导入的头像
-
-  // 发送消息
-  const sendMessage = () => {
-    const text = messageText.value.trim()
-    if (!text) return
-
-    messages.value.push({
-      id: messageId.value++,
-      sender: 'Ricky',
-      content: text,
-      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
-      isMe: true,
-      avatar: userAvatar.value
-    })
-
-    messageText.value = ''
-    scrollToBottom()
-  }
-
-  // 滚动到底部
-  const messageContainer = ref<HTMLElement | null>(null)
-  const scrollToBottom = () => {
-    setTimeout(() => {
-      if (messageContainer.value) {
-        messageContainer.value.scrollTop = messageContainer.value.scrollHeight
-      }
-    }, 100)
-  }
-
-  const openChat = () => {
-    isDrawerVisible.value = true
-  }
-
-  onMounted(() => {
-    scrollToBottom()
-    mittBus.on('openChat', openChat)
-
-    selectedPerson.value = personList.value[0]
-  })
-</script>
-
-<style lang="scss">
-  .chat-modal {
-    .el-overlay {
-      background-color: rgb(0 0 0 / 20%) !important;
-    }
-  }
-</style>
-
-<style lang="scss" scoped>
-  .chat {
-    display: flex;
-    overflow: hidden;
-    background-color: var(--art-main-bg-color);
-    border: 1px solid var(--art-border-color);
-    border-radius: 10px;
-
-    .person-list {
-      box-sizing: border-box;
-      width: 360px;
-      height: 100%;
-      padding: 20px;
-      border-right: 1px solid var(--art-border-color);
-
-      .person-item-header {
-        padding-bottom: 20px;
-
-        .user-info {
-          display: flex;
-          gap: 12px;
-          align-items: center;
-
-          .user-details {
-            .name {
-              font-size: 16px;
-              font-weight: 500;
-              color: var(--art-gray-900);
-            }
-
-            .email {
-              margin-top: 4px;
-              font-size: 13px;
-              color: var(--art-gray-500);
-            }
-          }
-        }
-
-        .search-box {
-          margin-top: 12px;
-        }
-
-        .sort-btn {
-          margin-top: 20px;
-          cursor: pointer;
-        }
-      }
-
-      .person-item {
-        display: flex;
-        align-items: center;
-        padding: 12px;
-        cursor: pointer;
-        border-radius: 8px;
-        transition: all 0.3s ease;
-
-        &:hover,
-        &.active {
-          background-color: var(--el-fill-color-light);
-        }
-
-        .avatar-wrapper {
-          position: relative;
-          margin-right: 12px;
-
-          .status-dot {
-            position: absolute;
-            right: 1px;
-            bottom: 1px;
-            width: 9px;
-            height: 9px;
-            background-color: var(--el-color-error);
-            border-radius: 50%;
-
-            &.online {
-              background-color: var(--el-color-success);
-            }
-          }
-        }
-
-        .person-info {
-          flex: 1;
-          min-width: 0;
-
-          .info-top {
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            margin-bottom: 4px;
-
-            .person-name {
-              font-size: 14px;
-              font-weight: 500;
-              color: var(--el-text-color-primary);
-            }
-
-            .last-time {
-              font-size: 12px;
-              color: var(--el-text-color-secondary);
-            }
-          }
-
-          .info-bottom {
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-
-            .email {
-              overflow: hidden;
-              font-size: 12px;
-              color: var(--el-text-color-secondary);
-              text-overflow: ellipsis;
-              white-space: nowrap;
-            }
-
-            .unread-badge {
-              :deep(.el-badge__content) {
-                border: none;
-              }
-            }
-          }
-        }
-      }
-    }
-
-    .chat-modal {
-      box-sizing: border-box;
-      flex: 1;
-      height: 100%;
-    }
-
-    .header {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      padding: 16px 16px 0;
-      margin-bottom: 20px;
-
-      .header-left {
-        .name {
-          font-size: 16px;
-          font-weight: 500;
-        }
-
-        .status {
-          display: flex;
-          gap: 4px;
-          align-items: center;
-          margin-top: 6px;
-
-          .dot {
-            width: 8px;
-            height: 8px;
-            border-radius: 50%;
-
-            &.online {
-              background-color: var(--el-color-success);
-            }
-
-            &.offline {
-              background-color: var(--el-color-danger);
-            }
-          }
-
-          .status-text {
-            font-size: 12px;
-            color: var(--art-gray-600);
-          }
-        }
-      }
-
-      .header-right {
-        display: flex;
-        gap: 8px;
-        align-items: center;
-
-        .btn {
-          width: 42px;
-          height: 42px;
-          line-height: 42px;
-          text-align: center;
-          cursor: pointer;
-          border-radius: 50%;
-          transition: background-color 0.2s ease;
-
-          &:hover {
-            background-color: var(--art-gray-200);
-          }
-
-          i {
-            font-size: 20px;
-            color: var(--art-text-gray-700);
-          }
-        }
-      }
-    }
-
-    .chat-container {
-      display: flex;
-      flex-direction: column;
-      height: calc(100% - 85px);
-
-      .chat-messages {
-        flex: 1;
-        padding: 30px 16px;
-        overflow-y: auto;
-        border-top: 1px solid var(--el-border-color-lighter);
-
-        &::-webkit-scrollbar {
-          width: 5px !important;
-        }
-
-        .message-item {
-          display: flex;
-          flex-direction: row;
-          gap: 8px;
-          align-items: flex-start;
-          width: 100%;
-          margin-bottom: 30px;
-
-          .message-text {
-            font-size: 14px;
-            color: var(--art-gray-900);
-            border-radius: 6px;
-          }
-
-          &.message-left {
-            justify-content: flex-start;
-
-            .message-content {
-              align-items: flex-start;
-
-              .message-info {
-                flex-direction: row;
-              }
-
-              .message-text {
-                background-color: var(--art-gray-200);
-              }
-            }
-          }
-
-          &.message-right {
-            flex-direction: row-reverse;
-
-            .message-content {
-              align-items: flex-end;
-
-              .message-info {
-                flex-direction: row-reverse;
-              }
-
-              .message-text {
-                background-color: #e9f3ff;
-                background-color: rgb(var(--art-bg-secondary));
-              }
-            }
-          }
-
-          .message-avatar {
-            flex-shrink: 0;
-          }
-
-          .message-content {
-            display: flex;
-            flex-direction: column;
-            max-width: 70%;
-
-            .message-info {
-              display: flex;
-              gap: 8px;
-              margin-bottom: 4px;
-              font-size: 12px;
-
-              .message-time {
-                color: var(--el-text-color-secondary);
-              }
-
-              .sender-name {
-                font-weight: 500;
-              }
-            }
-
-            .message-text {
-              padding: 10px 14px;
-              line-height: 1.4;
-            }
-          }
-        }
-      }
-
-      .chat-input {
-        padding: 16px; // 增加填充以提升输入区域的布局
-
-        .input-actions {
-          display: flex;
-          gap: 8px;
-          padding: 8px 0;
-        }
-
-        .chat-input-actions {
-          display: flex;
-          align-items: center; // 修正为单数
-          justify-content: space-between;
-          margin-top: 12px;
-
-          .left {
-            display: flex;
-            align-items: center;
-
-            i {
-              margin-right: 20px;
-              font-size: 16px;
-              color: var(--art-gray-500);
-              cursor: pointer;
-            }
-          }
-
-          // 确保发送按钮与输入框对齐
-          el-button {
-            min-width: 80px;
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-ipad-pro) {
-    .chat {
-      flex-direction: column;
-
-      .person-list {
-        width: 100%;
-        height: 170px;
-        border-right: none;
-
-        .person-item-header {
-          display: none;
-        }
-      }
-
-      .chat-modal {
-        height: calc(70% - 30px);
-      }
-    }
-  }
-</style>

+ 0 - 17
web/src/views/template/map/index.vue

@@ -1,17 +0,0 @@
-<template>
-  <div class="page-content" element-loading-text="加载中...">
-    <ArtMapChart />
-  </div>
-</template>
-
-<script setup lang="ts">
-  const ArtMapChart = defineAsyncComponent(
-    () => import('@/components/core/charts/art-map-chart/index.vue')
-  )
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    height: 100%;
-  }
-</style>

+ 0 - 307
web/src/views/template/pricing/index.vue

@@ -1,307 +0,0 @@
-<template>
-  <div class="pricing-container">
-    <div class="pricing-header">
-      <h1 class="title">超过 53,476 位信赖的开发者</h1>
-      <h2 class="subtitle">以及众多科技巨头的选择</h2>
-      <div class="free-notice">
-        <p class="notice-text">本项目基于 MIT 协议开源免费,当前页面为定价模板,仅作演示用途</p>
-        <ElTag type="success" size="large" round>免费商用</ElTag>
-      </div>
-    </div>
-
-    <div class="pricing-cards">
-      <ElRow :gutter="20" justify="center">
-        <ElCol v-for="plan in pricingPlans" :key="plan.type" :xs="24" :sm="12" :md="6">
-          <ElCard class="pricing-card" :class="{ popular: plan.isPopular }" shadow="never">
-            <div class="card-header">
-              <h3>{{ plan.title }}</h3>
-              <p class="description">{{ plan.description }}</p>
-              <div class="price">
-                <span class="amount">¥{{ plan.price }}</span>
-                <span class="period">/一次性付款</span>
-              </div>
-            </div>
-
-            <div class="features">
-              <div v-for="(feature, index) in plan.features" :key="index" class="feature-item">
-                <ElIcon :class="feature.available ? 'available' : 'unavailable'">
-                  <Check v-if="feature.available" />
-                  <Close v-else />
-                </ElIcon>
-                <span>{{ feature.text }}</span>
-              </div>
-            </div>
-
-            <div class="card-footer">
-              <ElButton type="primary" class="purchase-btn" v-ripple>立即购买</ElButton>
-            </div>
-          </ElCard>
-        </ElCol>
-      </ElRow>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue'
-  import { Check, Close } from '@element-plus/icons-vue'
-
-  defineOptions({ name: 'Pricing' })
-
-  interface Feature {
-    text: string
-    available: boolean
-  }
-
-  interface PricingPlan {
-    type: string
-    title: string
-    description: string
-    price: number
-    isPopular: boolean
-    features: Feature[]
-  }
-
-  const pricingPlans = ref<PricingPlan[]>([
-    {
-      type: 'single',
-      title: '单次使用版',
-      description: '适用于单个最终产品,最终用户无需付费。',
-      price: 349,
-      isPopular: false,
-      features: [
-        { text: '完整源代码', available: true },
-        { text: '技术文档', available: true },
-        { text: 'SaaS应用授权', available: false },
-        { text: '单个项目使用', available: true },
-        { text: '一年技术支持', available: true },
-        { text: '一年免费更新', available: true }
-      ]
-    },
-    {
-      type: 'multiple',
-      title: '多次使用版',
-      description: '适用于无限个最终产品,最终用户无需付费。',
-      price: 629,
-      isPopular: false,
-      features: [
-        { text: '完整源代码', available: true },
-        { text: '技术文档', available: true },
-        { text: 'SaaS应用授权', available: false },
-        { text: '无限项目使用', available: true },
-        { text: '一年技术支持', available: true },
-        { text: '一年免费更新', available: true }
-      ]
-    },
-    {
-      type: 'extended',
-      title: '扩展授权版',
-      description: '适用于单个最终产品,最终用户需要付费。',
-      price: 2099,
-      isPopular: false,
-      features: [
-        { text: '完整源代码', available: true },
-        { text: '技术文档', available: true },
-        { text: 'SaaS应用授权', available: true },
-        { text: '单个项目使用', available: true },
-        { text: '一年技术支持', available: true },
-        { text: '一年免费更新', available: true }
-      ]
-    },
-    {
-      type: 'unlimited',
-      title: '无限授权版',
-      description: '适用于无限个最终产品,最终用户需要付费。',
-      price: 3499,
-      isPopular: false,
-      features: [
-        { text: '完整源代码', available: true },
-        { text: '技术文档', available: true },
-        { text: 'SaaS应用授权', available: true },
-        { text: '无限项目使用', available: true },
-        { text: '一年技术支持', available: true },
-        { text: '一年免费更新', available: true }
-      ]
-    }
-  ])
-</script>
-
-<style lang="scss" scoped>
-  .pricing-container {
-    padding: 6rem 5rem 0;
-    background-color: transparent !important;
-    border: none !important;
-
-    .pricing-header {
-      margin-bottom: 40px;
-      text-align: center;
-
-      .title {
-        margin-bottom: 0.5rem;
-        font-size: 2.5rem;
-        font-weight: 500;
-      }
-
-      .subtitle {
-        margin-bottom: 10px;
-        font-size: 1.4rem;
-        font-weight: 400;
-        color: var(--art-gray-600);
-      }
-
-      .free-notice {
-        display: flex;
-        gap: 8px;
-        align-items: center;
-        justify-content: center;
-        margin-top: 10px;
-
-        .notice-text {
-          font-size: 14px;
-          font-style: italic;
-          color: var(--art-gray-600);
-        }
-      }
-    }
-
-    .pricing-cards {
-      margin-top: 80px;
-
-      .el-col {
-        margin-bottom: 20px;
-      }
-
-      .pricing-card {
-        display: flex;
-        flex-direction: column;
-        height: 100%;
-        border-radius: 10px;
-
-        &.popular {
-          position: relative;
-          border: 2px solid var(--el-color-primary);
-
-          &::after {
-            position: absolute;
-            top: 10px;
-            right: 10px;
-            padding: 2px 8px;
-            font-size: 12px;
-            color: var(--el-color-primary);
-            content: '热门';
-            background-color: var(--el-color-primary-light-9);
-            border-radius: 12px;
-          }
-        }
-
-        .card-header {
-          margin-bottom: 20px;
-
-          h3 {
-            margin-bottom: 10px;
-            font-size: 1.3rem;
-            color: var(--art-text-gray-900) !important;
-          }
-
-          .description {
-            display: -webkit-box;
-            height: 40px;
-            padding-bottom: 20px;
-            margin-bottom: 20px;
-            overflow: hidden;
-            font-size: 14px;
-            color: var(--art-text-gray-600);
-            text-overflow: ellipsis;
-            border-bottom: 1px solid var(--art-border-color);
-            -webkit-box-orient: vertical;
-          }
-
-          .price {
-            margin-top: 30px;
-
-            .amount {
-              font-size: 1.8rem;
-              font-weight: 600;
-            }
-
-            .period {
-              margin-left: 10px;
-              font-size: 14px;
-              color: var(--art-text-gray-600);
-            }
-          }
-        }
-
-        .features {
-          flex-grow: 1;
-          margin-bottom: 20px;
-
-          .feature-item {
-            display: flex;
-            align-items: center;
-            margin-bottom: 10px;
-            font-size: 14px;
-
-            .el-icon {
-              margin-right: 10px;
-
-              &.available {
-                color: var(--el-color-primary);
-              }
-
-              &.unavailable {
-                color: var(--el-color-danger);
-              }
-            }
-          }
-        }
-
-        .card-footer {
-          margin-top: auto;
-          text-align: center;
-
-          .purchase-btn {
-            width: 100%;
-            height: 40px;
-          }
-        }
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-notebook) {
-    .pricing-container {
-      padding: 5rem 2rem 0 !important;
-
-      .pricing-cards {
-        margin-top: 0;
-      }
-    }
-  }
-
-  @media only screen and (max-width: $device-phone) {
-    .pricing-container {
-      .pricing-header {
-        .title {
-          font-size: 2rem;
-        }
-
-        .subtitle {
-          font-size: 1.5rem;
-        }
-
-        .free-notice {
-          margin-top: 15px;
-
-          .notice-text {
-            font-size: 13px;
-          }
-        }
-      }
-
-      .el-col {
-        margin-bottom: 20px;
-      }
-    }
-  }
-</style>

+ 0 - 124
web/src/views/widgets/context-menu/index.vue

@@ -1,124 +0,0 @@
-<template>
-  <div class="page-content">
-    <ElButton @contextmenu.prevent="showMenu"> 右键触发菜单 </ElButton>
-
-    <!-- 右键菜单组件 -->
-    <ArtMenuRight
-      ref="menuRef"
-      :menu-items="menuItems"
-      :menu-width="180"
-      :submenu-width="140"
-      :border-radius="10"
-      @select="handleSelect"
-      @show="onMenuShow"
-      @hide="onMenuHide"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref, computed, nextTick } from 'vue'
-  import ArtMenuRight from '@/components/core/others/art-menu-right/index.vue'
-  import type { MenuItemType } from '@/components/core/others/art-menu-right/index.vue'
-  import { ElMessage } from 'element-plus'
-
-  const menuRef = ref<InstanceType<typeof ArtMenuRight>>()
-  const lastAction = ref('')
-
-  // 右键菜单选项
-  const menuItems = computed((): MenuItemType[] => [
-    {
-      key: 'copy',
-      label: '复制',
-      icon: '&#xe7b2;'
-    },
-    {
-      key: 'paste',
-      label: '粘贴',
-      icon: '&#xe70b;'
-    },
-    {
-      key: 'cut',
-      label: '剪切',
-      icon: '&#xe7b8;',
-      showLine: true
-    },
-    {
-      key: 'export',
-      label: '导出选项',
-      icon: '&#xe78b;',
-      children: [
-        {
-          key: 'exportExcel',
-          label: '导出 Excel',
-          icon: '&#xe604;'
-        },
-        {
-          key: 'exportPdf',
-          label: '导出 PDF',
-          icon: '&#xe89e;'
-        }
-      ]
-    },
-    {
-      key: 'edit',
-      label: '编辑选项',
-      icon: '&#xe706;',
-      children: [
-        {
-          key: 'rename',
-          label: '重命名',
-          icon: '&#xe607;'
-        },
-        {
-          key: 'duplicate',
-          label: '复制副本',
-          icon: '&#xe608;'
-        }
-      ]
-    },
-    {
-      key: 'share',
-      label: '分享',
-      icon: '&#xe73b;',
-      showLine: true
-    },
-    {
-      key: 'delete',
-      label: '删除',
-      icon: '&#xe850;'
-    },
-    {
-      key: 'disabled',
-      label: '禁用选项',
-      icon: '&#xe619;',
-      disabled: true
-    }
-  ])
-
-  const handleSelect = (item: MenuItemType) => {
-    lastAction.value = `${item.label} (${item.key})`
-    ElMessage.success(`执行操作: ${item.label}`)
-    console.log('选择了菜单项:', item)
-  }
-
-  const showMenu = (e: MouseEvent) => {
-    console.log('触发右键菜单', e)
-    // 确保阻止默认行为
-    e.preventDefault()
-    e.stopPropagation()
-
-    // 延迟一帧执行,确保事件处理完成
-    nextTick(() => {
-      menuRef.value?.show(e)
-    })
-  }
-
-  const onMenuShow = () => {
-    console.log('菜单显示')
-  }
-
-  const onMenuHide = () => {
-    console.log('菜单隐藏')
-  }
-</script>

+ 0 - 214
web/src/views/widgets/count-to/index.vue

@@ -1,214 +0,0 @@
-<template>
-  <div class="page-content">
-    <div class="page-header">
-      <h1>基于 VueUse useTransition 的 Count-To 组件</h1>
-      <p>高性能数字滚动动画组件,支持完整的动画控制和事件监听</p>
-    </div>
-
-    <!-- 基础用法 -->
-    <div class="demo-section">
-      <h2>基础用法</h2>
-      <div class="number-display">
-        <ArtCountTo :target="1000" :duration="2000" />
-      </div>
-    </div>
-
-    <!-- 带前缀后缀 -->
-    <div class="demo-section">
-      <h2>带前缀后缀</h2>
-      <div class="number-display">
-        <ArtCountTo :target="20000" :duration="2500" prefix="¥" suffix="元" :decimals="2" />
-      </div>
-    </div>
-
-    <!-- 小数点和分隔符 -->
-    <div class="demo-section">
-      <h2>小数点和分隔符</h2>
-      <div class="number-display">
-        <ArtCountTo :target="2023.45" :duration="3000" :decimals="2" separator="," />
-      </div>
-    </div>
-
-    <!-- 动画效果对比 -->
-    <div class="demo-section">
-      <h2>动画效果对比</h2>
-      <div class="easing-demo">
-        <div class="easing-item" v-for="easing in easingTypes" :key="easing.type">
-          <div class="easing-label">{{ easing.name }}</div>
-          <div class="number-display">
-            <ArtCountTo :target="easingTarget" :duration="3000" :easing="easing.type" />
-          </div>
-        </div>
-      </div>
-      <div class="trigger-center">
-        <el-button @click="triggerEasing">触发所有动画</el-button>
-      </div>
-    </div>
-
-    <!-- 控制按钮 -->
-    <div class="demo-section">
-      <h2>控制按钮</h2>
-      <div class="number-display">
-        <ArtCountTo
-          ref="countToRef"
-          :target="controlTarget"
-          :duration="2000"
-          @started="handleAnimationStarted"
-          @finished="handleAnimationFinished"
-          @paused="handleAnimationPaused"
-          @reset="handleAnimationReset"
-        />
-      </div>
-
-      <div class="control-buttons">
-        <el-button @click="startCount">开始</el-button>
-        <el-button @click="pauseCount">暂停</el-button>
-        <el-button @click="resetCount">重置</el-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue'
-  import ArtCountTo from '@/components/core/text-effect/art-count-to/index.vue'
-
-  // 控制变量
-  const controlTarget = ref(0)
-  const countToRef = ref()
-
-  // 缓动动画目标值
-  const easingTarget = ref(0)
-
-  // 动画类型定义
-  const easingTypes = [
-    { name: 'Linear', type: 'linear' },
-    { name: 'Ease Out Cubic', type: 'easeOutCubic' },
-    { name: 'Ease Out Expo', type: 'easeOutExpo' },
-    { name: 'Ease Out Sine', type: 'easeOutSine' },
-    { name: 'Ease In Out', type: 'easeInOutCubic' },
-    { name: 'Ease In Quad', type: 'easeInQuad' }
-  ] as const
-
-  // 开始
-  const startCount = () => {
-    const newTarget = 5000
-    controlTarget.value = newTarget
-    countToRef.value?.start(newTarget)
-  }
-
-  // 暂停
-  const pauseCount = () => {
-    countToRef.value?.pause()
-  }
-
-  // 重置
-  const resetCount = () => {
-    countToRef.value?.reset()
-    controlTarget.value = 0
-  }
-
-  // 触发缓动效果演示
-  const triggerEasing = () => {
-    easingTarget.value = easingTarget.value === 0 ? 1000 : 0
-  }
-
-  // 监听动画事件
-  const handleAnimationStarted = (value: number) => {
-    console.log('动画开始,目标值:', value)
-  }
-
-  const handleAnimationFinished = (value: number) => {
-    console.log('动画完成,最终值:', value)
-  }
-
-  const handleAnimationPaused = (value: number) => {
-    console.log('动画暂停,当前值:', value)
-  }
-
-  const handleAnimationReset = () => {
-    console.log('动画已重置')
-  }
-</script>
-
-<style scoped lang="scss">
-  .page-content {
-    .page-header {
-      margin-bottom: 60px;
-      text-align: center;
-
-      h1 {
-        margin: 1rem 0 16px;
-        font-size: 1.5rem;
-        font-weight: 600;
-        line-height: 1.2;
-        color: #333;
-      }
-
-      p {
-        margin: 0;
-        font-size: 1rem;
-        line-height: 1.6;
-        color: #666;
-      }
-    }
-
-    .demo-section {
-      margin-bottom: 60px;
-
-      h2 {
-        margin: 0 0 24px;
-        font-size: 1.2rem;
-        font-weight: 500;
-        color: #333;
-      }
-    }
-
-    .number-display {
-      padding: 20px;
-      margin-bottom: 20px;
-      font-size: 2rem;
-      font-weight: 600;
-      font-variant-numeric: tabular-nums;
-      color: #495057;
-      text-align: center;
-      background: var(--art-gray-100);
-      border: 1px solid var(--art-border-color);
-      border-radius: 8px;
-    }
-
-    .control-buttons {
-      display: flex;
-      gap: 12px;
-      justify-content: center;
-    }
-
-    .easing-demo {
-      display: grid;
-      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
-      gap: 24px;
-      margin-bottom: 32px;
-
-      .easing-item {
-        text-align: center;
-
-        .easing-label {
-          margin-bottom: 12px;
-          font-size: 0.875rem;
-          font-weight: 500;
-          color: #666;
-        }
-
-        .number-display {
-          padding: 16px;
-          margin-bottom: 0;
-          font-size: 1.5rem;
-        }
-      }
-    }
-
-    .trigger-center {
-      text-align: center;
-    }
-  }
-</style>

+ 0 - 125
web/src/views/widgets/drag/index.vue

@@ -1,125 +0,0 @@
-<!-- https://vue-draggable-plus.pages.dev/ -->
-<template>
-  <div class="page-content">
-    <ElRow>
-      <ElCard shadow="never" style="width: 300px; margin-right: 20px">
-        <template #header>
-          <span class="card-header">基础示例</span>
-        </template>
-        <template #default>
-          <VueDraggable ref="el" v-model="userList">
-            <div class="demo1-item" v-for="item in userList" :key="item.name">
-              {{ item.name }}
-            </div>
-          </VueDraggable>
-        </template>
-      </ElCard>
-
-      <ElCard shadow="never" style="width: 300px">
-        <template #header>
-          <span class="card-header">过渡动画</span>
-        </template>
-        <template #default>
-          <VueDraggable v-model="userList" target=".sort-target" :scroll="true">
-            <TransitionGroup type="transition" tag="ul" name="fade" class="sort-target">
-              <li v-for="item in userList" :key="item.name" class="demo1-item">
-                {{ item.name }}
-              </li>
-            </TransitionGroup>
-          </VueDraggable>
-        </template>
-      </ElCard>
-    </ElRow>
-
-    <ElCard shadow="never">
-      <template #header>
-        <span class="card-header">表格拖拽排序</span>
-      </template>
-      <template #default>
-        <VueDraggable target="tbody" v-model="userList" :animation="150">
-          <ArtTable :data="userList">
-            <ElTableColumn label="姓名" prop="name" />
-            <ElTableColumn label="角色" prop="role" />
-          </ArtTable>
-        </VueDraggable>
-      </template>
-    </ElCard>
-
-    <ElCard shadow="never">
-      <template #header>
-        <span class="card-header">指定元素拖拽排序</span>
-      </template>
-      <template #default>
-        <VueDraggable target="tbody" handle=".handle" v-model="userList" :animation="150">
-          <ArtTable :data="userList">
-            <ElTableColumn label="姓名" prop="name" />
-            <ElTableColumn label="角色" prop="role" />
-            <ElTableColumn label="操作" width="100">
-              <ElButton size="default" class="handle"> 移动 </ElButton>
-            </ElTableColumn>
-          </ArtTable>
-        </VueDraggable>
-      </template>
-    </ElCard>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { VueDraggable } from 'vue-draggable-plus'
-
-  const userList = ref([
-    {
-      name: '孙悟空',
-      role: '斗战胜佛'
-    },
-    {
-      name: '猪八戒',
-      role: '净坛使者'
-    },
-    {
-      name: '沙僧',
-      role: '金身罗汉'
-    },
-    {
-      name: '唐僧',
-      role: '旃檀功德佛'
-    }
-  ])
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    .demo1-item {
-      padding: 10px;
-      margin-bottom: 10px;
-      cursor: move;
-      background-color: rgba(var(--art-gray-200-rgb), 0.8);
-      border-radius: 4px;
-    }
-
-    .el-card {
-      margin-bottom: 30px;
-
-      .card-header {
-        font-size: 16px;
-        font-weight: bold;
-      }
-    }
-  }
-
-  .fade-move,
-  .fade-enter-active,
-  .fade-leave-active {
-    transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
-  }
-
-  .fade-enter-from,
-  .fade-leave-to {
-    opacity: 0;
-    transform: scaleY(0.01) translate(30px, 0);
-  }
-
-  .fade-leave-active {
-    position: absolute;
-  }
-</style>

+ 0 - 115
web/src/views/widgets/excel/index.vue

@@ -1,115 +0,0 @@
-<template>
-  <div class="page-content">
-    <ArtExcelImport @import-success="handleImportSuccess" @import-error="handleImportError">
-      <template #import-text> 上传 Excel </template>
-    </ArtExcelImport>
-
-    <ArtExcelExport
-      style="margin-left: 10px"
-      :data="tableData"
-      filename="用户数据-1"
-      sheetName="用户列表"
-      type="success"
-      :headers="headers"
-      auto-index
-      :columns="columnConfig"
-      @export-success="handleExportSuccess"
-      @export-error="handleExportError"
-      @export-progress="handleProgress"
-    >
-      导出 Excel
-    </ArtExcelExport>
-
-    <ElButton type="danger" @click="handleClear" v-ripple>清除数据</ElButton>
-
-    <ArtTable :data="tableData" style="margin-top: 10px">
-      <ElTableColumn
-        v-for="key in Object.keys(headers)"
-        :key="key"
-        :prop="key"
-        :label="headers[key as keyof typeof headers]"
-      />
-    </ArtTable>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ElMessage } from 'element-plus'
-  interface TableData {
-    name: string
-    age: number
-    city: string
-  }
-
-  const handleImportSuccess = (data: any[]) => {
-    // 将导入的数据转换为正确的格式
-    const formattedData = data.map((item) => ({
-      name: item['姓名'],
-      age: Number(item['年龄']),
-      city: item['城市']
-    }))
-    tableData.value = formattedData
-
-    // tableData.value = data
-  }
-
-  const handleImportError = (error: Error) => {
-    // 处理导入错误
-    console.error('导入失败:', error)
-  }
-
-  // 使用类型化的ref
-  const tableData = ref<TableData[]>([
-    { name: '李四', age: 20, city: '上海' },
-    { name: '张三', age: 25, city: '北京' },
-    { name: '王五', age: 30, city: '广州' },
-    { name: '赵六', age: 35, city: '深圳' },
-    { name: '孙七', age: 28, city: '杭州' },
-    { name: '周八', age: 32, city: '成都' },
-    { name: '吴九', age: 27, city: '武汉' },
-    { name: '郑十', age: 40, city: '南京' },
-    { name: '刘一', age: 22, city: '重庆' },
-    { name: '陈二', age: 33, city: '西安' }
-  ])
-
-  // 自定义表头映射
-  const headers = {
-    name: '姓名',
-    age: '年龄',
-    city: '城市'
-  }
-
-  const columnConfig = {
-    name: {
-      title: '姓名',
-      width: 20,
-      formatter: (value: any) => value || '未知'
-    },
-    age: {
-      title: '年龄',
-      width: 10,
-      formatter: (value: any) => `${value}岁`
-    },
-    city: {
-      title: '城市',
-      width: 12,
-      formatter: (value: any) => `${value}市`
-    }
-  }
-
-  const handleExportSuccess = () => {
-    console.log('导出成功')
-  }
-
-  const handleExportError = (error: Error) => {
-    ElMessage.error(`导出失败: ${error.message}`)
-  }
-
-  const handleProgress = (progress: number) => {
-    console.log('导出进度:', progress)
-  }
-
-  const handleClear = () => {
-    tableData.value = []
-  }
-</script>

+ 0 - 103
web/src/views/widgets/fireworks/index.vue

@@ -1,103 +0,0 @@
-<template>
-  <div class="page-content">
-    <div class="action-buttons">
-      <ElButton :disabled="isLaunching" v-ripple @click="handleSingleLaunch"
-        >✨ 放个小礼花</ElButton
-      >
-      <ElButton :disabled="isLaunching" v-ripple @click="handleImageLaunch(bp)"
-        >🎉 打开幸运红包</ElButton
-      >
-      <ElButton :disabled="isLaunching" v-ripple @click="handleMultipleLaunch('')"
-        >🎆 璀璨烟火秀</ElButton
-      >
-      <ElButton :disabled="isLaunching" v-ripple @click="handleImageLaunch(sd)"
-        >❄️ 飘点小雪花</ElButton
-      >
-      <ElButton :disabled="isLaunching" v-ripple @click="handleMultipleLaunch(sd)"
-        >❄️ 浪漫暴风雪</ElButton
-      >
-    </div>
-
-    <ElDescriptions
-      title="礼花组件说明"
-      direction="vertical"
-      :column="1"
-      border
-      style="margin-top: 50px"
-    >
-      <ElDescriptionsItem label="显示时机">
-        礼花效果组件已全局注册,触发时机由配置文件控制。默认配置中的日期已过,不会在你使用过程中再次触发,无需担心。
-      </ElDescriptionsItem>
-      <ElDescriptionsItem label="礼花样式">
-        默认显示几何图形,可以配置图片,图片需要提前在
-        src/components/core/layouts/art-fireworks-effect/index.vue 文件预先定义
-      </ElDescriptionsItem>
-      <ElDescriptionsItem label="配置文件">
-        在 src/config/festival.ts 文件中,可以配置节日和对应的礼花样式
-      </ElDescriptionsItem>
-      <ElDescriptionsItem label="快捷键">
-        command + shift + p 或者 ctrl + shift + p
-      </ElDescriptionsItem>
-    </ElDescriptions>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { mittBus } from '@/utils/sys'
-
-  import bp from '@imgs/ceremony/hb.png'
-  import sd from '@imgs/ceremony/sd.png'
-
-  const timerRef = ref<ReturnType<typeof setInterval> | null>(null)
-  const isLaunching = ref(false)
-
-  const triggerFireworks = (count: number, src: string) => {
-    // 清除之前的定时器
-    if (timerRef.value) {
-      clearInterval(timerRef.value)
-      timerRef.value = null
-    }
-
-    isLaunching.value = true // 开始发射时设置状态
-
-    let fired = 0
-    timerRef.value = setInterval(() => {
-      mittBus.emit('triggerFireworks', src)
-      fired++
-
-      // 达到指定次数后清除定时器
-      if (fired >= count) {
-        clearInterval(timerRef.value!)
-        timerRef.value = null
-        isLaunching.value = false // 发射完成后解除禁用
-      }
-    }, 1000)
-  }
-
-  // 简化后的处理函数
-  const handleSingleLaunch = () => {
-    mittBus.emit('triggerFireworks')
-  }
-
-  const handleMultipleLaunch = (src: string) => {
-    triggerFireworks(10, src)
-  }
-
-  const handleImageLaunch = (src: string) => {
-    mittBus.emit('triggerFireworks', src)
-  }
-
-  // 组件卸载时清理定时器
-  onUnmounted(() => {
-    if (timerRef.value) {
-      clearInterval(timerRef.value)
-      timerRef.value = null
-    }
-  })
-</script>
-
-<style lang="scss" scoped>
-  .action-buttons {
-    margin-bottom: 20px;
-  }
-</style>

+ 0 - 175
web/src/views/widgets/icon-list/index.vue

@@ -1,175 +0,0 @@
-<template>
-  <div class="page-content">
-    <div class="form">
-      <ElSelect v-model="iconType" placeholder="Select" style="width: 240px">
-        <ElOption
-          v-for="item in options"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value"
-        />
-      </ElSelect>
-      <div class="colors-icon">
-        <ElCheckbox v-model="isColorsIcon" label="彩色图标" />
-      </div>
-    </div>
-    <div class="list">
-      <ul class="icon-list">
-        <li v-for="icon in systemIconClasses" :key="icon.className" @click="copyIcon(icon)">
-          <i
-            class="iconfont-sys"
-            v-if="iconType === 'unicode'"
-            v-html="icon.unicode"
-            :style="getIconStyle()"
-          ></i>
-          <i :class="`iconfont-sys ${icon.className}`" v-else :style="getIconStyle()"></i>
-          <span>{{ iconType === 'unicode' ? icon.unicode : icon.className }}</span>
-        </li>
-      </ul>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { extractIconClasses, IconfontType } from '@/utils/constants'
-  import { ElMessage } from 'element-plus'
-
-  const iconType = ref('unicode')
-  const options = [
-    {
-      value: 'unicode',
-      label: 'Unicode'
-    },
-    {
-      value: 'fontClass',
-      label: 'Font class'
-    }
-  ]
-  const systemIconClasses = ref<IconfontType[]>([])
-
-  const isColorsIcon = ref(false)
-
-  onMounted(() => {
-    systemIconClasses.value = extractIconClasses()
-  })
-
-  const copyIcon = (text: IconfontType) => {
-    if (!text) return
-
-    let copyipt = document.createElement('input')
-    copyipt.setAttribute(
-      'value',
-      (iconType.value === 'unicode' ? text.unicode : text.className) || ''
-    )
-    document.body.appendChild(copyipt)
-    copyipt.select()
-    document.execCommand('copy')
-    document.body.removeChild(copyipt)
-
-    ElMessage.success(`已复制`)
-  }
-
-  const getRandomColor = () => {
-    const colors = ['#2d8cf0', '#19be6b', '#ff9900', '#f24965', '#9463f7']
-    return colors[Math.floor(Math.random() * colors.length)]
-  }
-
-  const getIconStyle = () => {
-    return isColorsIcon.value ? { color: getRandomColor() } : { color: 'var(--art-text-gray-700)' }
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    width: 100%;
-    height: 100%;
-
-    $border-color: #eee;
-
-    .form {
-      display: flex;
-      align-items: center;
-
-      .colors-icon {
-        box-sizing: border-box;
-        height: var(--el-component-custom-height);
-        padding: 0 30px;
-        margin-left: 10px;
-        border: 1px solid var(--art-border-dashed-color);
-        border-radius: calc(var(--custom-radius) / 3 + 2px) !important;
-      }
-    }
-
-    .list {
-      margin-top: 20px;
-
-      .icon-list {
-        display: grid;
-        grid-template-columns: repeat(12, 1fr);
-        width: calc(100% + 16px);
-
-        li {
-          box-sizing: border-box;
-          display: flex;
-          flex-direction: column;
-          justify-content: center;
-          aspect-ratio: 1 / 1;
-          padding: 0 8px;
-          margin: 0 16px 16px 0;
-          overflow: hidden;
-          color: rgba(#fff, 0.8);
-          text-align: center;
-          border: 1px solid rgb(var(--art-gray-300-rgb), 0.8);
-          border-radius: 12px !important;
-
-          &:hover {
-            cursor: pointer;
-            background: var(--art-gray-100);
-          }
-
-          i {
-            font-size: 26px;
-            transition: color 0.3s ease;
-          }
-
-          span {
-            display: block;
-            margin-top: 10px;
-            font-size: 12px;
-            color: var(--art-text-gray-600);
-          }
-        }
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-notebook) {
-    .page-content {
-      .list {
-        .icon-list {
-          grid-template-columns: repeat(8, 1fr);
-        }
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-ipad-vertical) {
-    .page-content {
-      .list {
-        .icon-list {
-          grid-template-columns: repeat(5, 1fr);
-        }
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-phone) {
-    .page-content {
-      .list {
-        .icon-list {
-          grid-template-columns: repeat(3, 1fr);
-        }
-      }
-    }
-  }
-</style>

+ 0 - 45
web/src/views/widgets/icon-selector/index.vue

@@ -1,45 +0,0 @@
-<template>
-  <div class="page-content">
-    <div class="select">
-      <div class="item">
-        <h3>Unicode</h3>
-        <ArtIconSelector v-model="icon1" :iconType="IconTypeEnum.UNICODE" />
-      </div>
-      <div class="item">
-        <h3>ClassName</h3>
-        <ArtIconSelector v-model="icon2" :iconType="IconTypeEnum.CLASS_NAME" width="260px" />
-      </div>
-      <div class="item">
-        <h3>禁用</h3>
-        <ArtIconSelector
-          v-model="icon3"
-          :iconType="IconTypeEnum.CLASS_NAME"
-          width="260px"
-          disabled
-        />
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { IconTypeEnum } from '@/enums/appEnum'
-
-  const icon1 = ref('&#xe6b5;')
-  const icon2 = ref('iconsys-baitianmoshi3')
-  const icon3 = ref('iconsys-baitianmoshi3')
-</script>
-
-<style scoped lang="scss">
-  .select {
-    .item {
-      margin-bottom: 30px;
-
-      h3 {
-        padding-bottom: 10px;
-        font-size: 16px;
-        font-weight: 500;
-      }
-    }
-  }
-</style>

+ 0 - 39
web/src/views/widgets/image-crop/index.vue

@@ -1,39 +0,0 @@
-<template>
-  <div class="page-content">
-    <ArtCutterImg
-      v-model:imgUrl="imageUrl"
-      :boxWidth="540"
-      :boxHeight="300"
-      :cutWidth="360"
-      :cutHeight="200"
-      :quality="1"
-      :tool="true"
-      :watermarkText="'My Watermark'"
-      watermarkColor="#ff0000"
-      :showPreview="true"
-      :originalGraph="false"
-      :title="'图片裁剪'"
-      :previewTitle="'预览效果'"
-      @error="handleError"
-      @imageLoadComplete="handleLoadComplete"
-      @imageLoadError="handleLoadError"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-  import lockImg from '@imgs/lock/lock_screen_1.webp'
-  const imageUrl = ref(lockImg)
-
-  const handleError = (error: any) => {
-    console.error('裁剪错误:', error)
-  }
-
-  const handleLoadComplete = (result: any) => {
-    console.log('图片加载完成:', result)
-  }
-
-  const handleLoadError = (error: any) => {
-    console.error('图片加载失败:', error)
-  }
-</script>

+ 0 - 136
web/src/views/widgets/qrcode/index.vue

@@ -1,136 +0,0 @@
-<template>
-  <div class="page-content">
-    <ElRow :gutter="20">
-      <ElCol :span="6" v-for="preset in qrcodePresets" :key="preset.title">
-        <ElCard class="qrcode-card" shadow="never">
-          <template #header>
-            <div class="card-header">
-              <span>{{ preset.title }}</span>
-            </div>
-          </template>
-
-          <div class="qrcode-preview">
-            <QrcodeVue :value="qrValue" v-bind="preset.config" />
-          </div>
-        </ElCard>
-      </ElCol>
-    </ElRow>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import QrcodeVue from 'qrcode.vue'
-  import { ref, reactive, watch } from 'vue'
-  import type { Level, RenderAs, ImageSettings } from 'qrcode.vue'
-
-  // 二维码内容
-  const qrValue = ref('https://www.lingchen.kim')
-  const isShowLogo = ref(false)
-
-  // 预设二维码样式配置
-  const qrcodePresets = [
-    {
-      title: '渲染成 svg 标签',
-      config: {
-        size: 160,
-        level: 'H' as Level,
-        renderAs: 'svg' as RenderAs,
-        margin: 0,
-        background: '#ffffff',
-        foreground: '#000000'
-      }
-    },
-    {
-      title: '渲染成 canvas 标签',
-      config: {
-        size: 160,
-        level: 'H' as Level,
-        renderAs: 'canvas' as RenderAs,
-        margin: 0,
-        background: '#ffffff',
-        foreground: '#000000'
-      }
-    },
-    {
-      title: '自定义颜色',
-      config: {
-        size: 160,
-        level: 'H' as Level,
-        renderAs: 'canvas' as RenderAs,
-        margin: 0,
-        background: '#f0f0f0',
-        foreground: '#4080ff'
-      }
-    },
-    {
-      title: '带有Logo',
-      config: {
-        size: 160,
-        level: 'H' as Level,
-        renderAs: 'canvas' as RenderAs,
-        margin: 0,
-        background: '#ffffff',
-        foreground: '#000000',
-        imageSettings: {
-          src: 'https://www.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.png',
-          width: 40,
-          height: 40,
-          excavate: true
-        }
-      }
-    }
-  ]
-
-  // 二维码配置
-  const qrcodeConfig = reactive({
-    size: 160,
-    level: 'H' as Level,
-    renderAs: 'canvas' as RenderAs,
-    margin: 0,
-    background: '#ffffff',
-    foreground: '#000000',
-    imageSettings: {
-      src: 'https://www.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.png',
-      width: 40,
-      height: 40,
-      excavate: true
-    } as ImageSettings
-  })
-
-  // 监听是否显示 logo
-  watch(isShowLogo, (val) => {
-    if (!val) {
-      qrcodeConfig.imageSettings = {} as ImageSettings
-    } else {
-      qrcodeConfig.imageSettings = {
-        src: 'https://www.lingchen.kim/art-design-pro/assets/avatar-DJIoI-3F.png',
-        width: 40,
-        height: 40,
-        excavate: true
-      }
-    }
-  })
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    padding: 20px;
-
-    .qrcode-card {
-      margin-bottom: 20px;
-
-      .card-header {
-        font-size: 16px;
-        font-weight: bold;
-      }
-
-      .qrcode-preview {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        padding: 20px;
-        border-radius: 4px;
-      }
-    }
-  }
-</style>

+ 0 - 45
web/src/views/widgets/text-scroll/index.vue

@@ -1,45 +0,0 @@
-<template>
-  <div class="page-content">
-    <!-- 基础用法 -->
-    <ArtTextScroll
-      text="逐趣CRM 是一款专注于用户体验和视觉设计的后台管理系统模版 <a target='_blank' href='https://www.lingchen.kim/art-design-pro/docs/'>点击我 </a>访问官方文档"
-      showClose
-    />
-
-    <!-- 使用不同的类型 -->
-    <ArtTextScroll type="success" text="这是一条成功类型的滚动公告" showClose />
-
-    <ArtTextScroll type="warning" text="这是一条警告类型的滚动公告" showClose />
-
-    <ArtTextScroll type="danger" text="这是一条危险类型的滚动公告" showClose />
-
-    <ArtTextScroll type="info" text="这是一条信息类型的滚动公告" showClose />
-
-    <!-- 可关闭 -->
-    <ArtTextScroll text="这是一条可关闭的滚动公告" showClose @close="handleClose" />
-
-    <!-- 使用打字机效果 -->
-    <ArtTextScroll
-      text="这是一条打字机效果的滚动公告,打字机速度为 200 毫秒"
-      typewriter
-      :typewriter-speed="200"
-    />
-
-    <!-- 自定义速度和方向 -->
-    <ArtTextScroll text="这是一条速度较慢、向右滚动的公告" :speed="30" direction="right" />
-  </div>
-</template>
-
-<script setup lang="ts">
-  const handleClose = () => {
-    console.log('关闭')
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    :deep(.text-scroll-container) {
-      margin-bottom: 20px;
-    }
-  }
-</style>

+ 0 - 31
web/src/views/widgets/video/index.vue

@@ -1,31 +0,0 @@
-<template>
-  <div class="page-content">
-    <div class="video-container">
-      <ArtVideoPlayer
-        playerId="my-video-1"
-        :videoUrl="videoUrl"
-        :posterUrl="posterUrl"
-        :autoplay="false"
-        :volume="1"
-        :playbackRates="[0.5, 1, 1.5, 2]"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue'
-  import lockImg from '@imgs/lock/lock_screen_1.webp'
-
-  // 视频源和封面图片URL
-  const videoUrl = ref(
-    '//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo.mp4'
-  )
-  const posterUrl = ref(lockImg)
-</script>
-
-<style scoped>
-  .video-container {
-    max-width: 600px;
-  }
-</style>

+ 0 - 544
web/src/views/widgets/wang-editor/index.vue

@@ -1,544 +0,0 @@
-<template>
-  <div class="page-content">
-    <!-- 完整工具栏编辑器 -->
-    <ElCard class="editor-card" shadow="never">
-      <template #header>
-        <div class="card-header">
-          <span>🛠️ 完整工具栏编辑器</span>
-          <div class="header-buttons">
-            <el-button size="small" @click="clearFullEditor">清空</el-button>
-            <el-button size="small" @click="getFullEditorContent">获取内容</el-button>
-            <el-button size="small" @click="setFullEditorDemo">设置示例</el-button>
-          </div>
-        </div>
-      </template>
-
-      <ArtWangEditor
-        ref="fullEditorRef"
-        v-model="fullEditorHtml"
-        height="400px"
-        placeholder="请输入内容,体验完整的编辑功能..."
-        :exclude-keys="[]"
-      />
-    </ElCard>
-
-    <!-- 简化工具栏编辑器 -->
-    <el-card class="editor-card" shadow="never">
-      <template #header>
-        <div class="card-header">
-          <span>✨ 简化工具栏编辑器</span>
-          <div class="header-buttons">
-            <el-button size="small" @click="clearSimpleEditor">清空</el-button>
-            <el-button size="small" @click="getSimpleEditorContent">获取内容</el-button>
-            <el-button size="small" @click="setSimpleEditorDemo">设置示例</el-button>
-          </div>
-        </div>
-      </template>
-
-      <ArtWangEditor
-        ref="simpleEditorRef"
-        v-model="simpleEditorHtml"
-        height="400px"
-        placeholder="请输入内容,体验简化的编辑功能..."
-        :toolbar-keys="simpleToolbarKeys"
-      />
-    </el-card>
-
-    <!-- 内容对比预览 -->
-    <el-card class="preview-card" shadow="never">
-      <template #header>
-        <span>📖 内容预览对比</span>
-      </template>
-
-      <el-row :gutter="20">
-        <el-col :span="12">
-          <h3>完整编辑器内容</h3>
-          <el-tabs v-model="fullActiveTab">
-            <el-tab-pane label="渲染效果" name="preview">
-              <div class="content-preview" v-html="fullEditorHtml"></div>
-            </el-tab-pane>
-            <el-tab-pane label="HTML源码" name="html">
-              <el-input
-                v-model="fullEditorHtml"
-                type="textarea"
-                :rows="8"
-                placeholder="HTML源码"
-                readonly
-              />
-            </el-tab-pane>
-          </el-tabs>
-        </el-col>
-
-        <el-col :span="12">
-          <h3>简化编辑器内容</h3>
-          <el-tabs v-model="simpleActiveTab">
-            <el-tab-pane label="渲染效果" name="preview">
-              <div class="content-preview" v-html="simpleEditorHtml"></div>
-            </el-tab-pane>
-            <el-tab-pane label="HTML源码" name="html">
-              <el-input
-                v-model="simpleEditorHtml"
-                type="textarea"
-                :rows="8"
-                placeholder="HTML源码"
-                readonly
-              />
-            </el-tab-pane>
-          </el-tabs>
-        </el-col>
-      </el-row>
-    </el-card>
-
-    <!-- 使用说明 -->
-    <el-card class="usage-card" shadow="never">
-      <template #header>
-        <span>📚 使用说明</span>
-      </template>
-
-      <el-collapse v-model="activeCollapse">
-        <el-collapse-item title="基础用法" name="basic">
-          <pre><code class="language-vue">&lt;template&gt;
-  &lt;ArtWangEditor v-model="content" /&gt;
-&lt;/template&gt;
-
-&lt;script setup lang="ts"&gt;
-import { ref } from 'vue'
-
-const content = ref('&lt;p&gt;初始内容&lt;/p&gt;')
-&lt;/script&gt;</code></pre>
-        </el-collapse-item>
-
-        <el-collapse-item title="完整工具栏配置" name="full">
-          <pre><code class="language-vue">&lt;template&gt;
-  &lt;!-- 显示所有工具,不排除任何功能 --&gt;
-  &lt;ArtWangEditor
-    v-model="content"
-    :exclude-keys="[]"
-  /&gt;
-&lt;/template&gt;</code></pre>
-        </el-collapse-item>
-
-        <el-collapse-item title="简化工具栏配置" name="simple">
-          <pre><code class="language-vue">&lt;template&gt;
-  &lt;!-- 只显示基础编辑工具 --&gt;
-  &lt;ArtWangEditor
-    v-model="content"
-    :toolbar-keys="[
-      'bold', 'italic', 'underline', '|',
-      'bulletedList', 'numberedList', '|',
-      'insertLink', 'insertImage', '|',
-      'undo', 'redo'
-    ]"
-  /&gt;
-&lt;/template&gt;</code></pre>
-        </el-collapse-item>
-
-        <el-collapse-item title="自定义配置" name="config">
-          <pre><code class="language-vue">&lt;template&gt;
-  &lt;ArtWangEditor
-    v-model="content"
-    height="600px"
-    placeholder="请输入您的内容..."
-    :exclude-keys="['fontFamily', 'fontSize']"
-    :upload-config="{
-      maxFileSize: 5 * 1024 * 1024,
-      maxNumberOfFiles: 5
-    }"
-  /&gt;
-&lt;/template&gt;</code></pre>
-        </el-collapse-item>
-
-        <el-collapse-item title="组件方法调用" name="methods">
-          <pre><code class="language-vue">&lt;template&gt;
-  &lt;ArtWangEditor ref="editorRef" v-model="content" /&gt;
-  &lt;el-button @click="handleClear"&gt;清空&lt;/el-button&gt;
-  &lt;el-button @click="handleFocus"&gt;聚焦&lt;/el-button&gt;
-  &lt;el-button @click="handleGetContent"&gt;获取内容&lt;/el-button&gt;
-&lt;/template&gt;
-
-&lt;script setup lang="ts"&gt;
-import { ref } from 'vue'
-
-const editorRef = ref()
-const content = ref('')
-
-const handleClear = () =&gt; {
-  editorRef.value?.clear()
-}
-
-const handleFocus = () =&gt; {
-  editorRef.value?.focus()
-}
-
-const handleGetContent = () =&gt; {
-  const html = editorRef.value?.getHtml()
-  console.log('编辑器内容:', html)
-}
-&lt;/script&gt;</code></pre>
-        </el-collapse-item>
-
-        <el-collapse-item title="工具栏配置说明" name="toolbar-config">
-          <div class="toolbar-explanation">
-            <h4>完整工具栏 vs 简化工具栏</h4>
-            <el-row :gutter="16">
-              <el-col :span="12">
-                <h5>✅ 完整工具栏包含:</h5>
-                <ul>
-                  <li>文本格式:加粗、斜体、下划线、字体颜色、背景色</li>
-                  <li>段落格式:标题、引用、对齐方式、缩进</li>
-                  <li>列表:有序列表、无序列表、待办事项</li>
-                  <li>插入:链接、图片、表格、分割线、表情</li>
-                  <li>代码:代码块、行内代码</li>
-                  <li>操作:撤销、重做、全屏、清除格式</li>
-                </ul>
-              </el-col>
-              <el-col :span="12">
-                <h5>⚡ 简化工具栏包含:</h5>
-                <ul>
-                  <li>基础格式:加粗、斜体、下划线</li>
-                  <li>列表:有序列表、无序列表</li>
-                  <li>插入:链接、图片</li>
-                  <li>操作:撤销、重做</li>
-                </ul>
-                <p class="note">适用于简单的文本编辑场景,界面更清爽。</p>
-              </el-col>
-            </el-row>
-          </div>
-        </el-collapse-item>
-      </el-collapse>
-    </el-card>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { ref } from 'vue'
-  import { ElMessage } from 'element-plus'
-
-  // 编辑器引用
-  const fullEditorRef = ref()
-  const simpleEditorRef = ref()
-
-  // 标签页状态
-  const fullActiveTab = ref('preview')
-  const simpleActiveTab = ref('preview')
-  const activeCollapse = ref(['basic'])
-
-  // 简化工具栏配置
-  const simpleToolbarKeys = [
-    'bold',
-    'italic',
-    'underline',
-    '|',
-    'bulletedList',
-    'numberedList',
-    '|',
-    'insertLink',
-    'insertImage',
-    '|',
-    'undo',
-    'redo'
-  ]
-
-  // 完整编辑器内容
-  const fullEditorHtml = ref(`<h1>🎨 完整工具栏编辑器示例</h1>
-<p>这个编辑器包含所有功能,您可以体验丰富的格式编辑功能。</p>
-
-<h2>✨ 文本样式</h2>
-<p><strong>这是加粗的文字</strong></p>
-<p><em>这是斜体文字</em></p>
-<p><u>这是下划线文字</u></p>
-<p><span style="color: rgb(194, 79, 74);">这是彩色文字</span></p>
-
-<h2>📝 列表和待办</h2>
-<ul>
-  <li>无序列表项 1</li>
-  <li>无序列表项 2</li>
-</ul>
-
-<ol>
-  <li>有序列表项 1</li>
-  <li>有序列表项 2</li>
-</ol>
-
-<ul class="w-e-todo">
-  <li class="w-e-todo-item"><input type="checkbox" checked="true" readonly="true" disabled="disabled"><span>已完成的任务</span></li>
-  <li class="w-e-todo-item"><input type="checkbox" readonly="true" disabled="disabled"><span>待完成的任务</span></li>
-</ul>
-
-<h2>💬 引用和表格</h2>
-<blockquote>
-  这是一段引用文字,展示引用格式的效果。
-</blockquote>
-
-<table style="border-collapse: collapse; width: 100%;" border="1">
-  <thead>
-    <tr><th>功能</th><th>描述</th></tr>
-  </thead>
-  <tbody>
-    <tr><td>完整工具栏</td><td>包含所有编辑功能</td></tr>
-    <tr><td>自定义配置</td><td>支持灵活的工具栏配置</td></tr>
-  </tbody>
-</table>
-
-<h2>💻 代码块</h2>
-<pre><code class="language-javascript">// 完整编辑器支持代码高亮
-function createEditor() {
-  return new WangEditor({
-    container: '#editor',
-    toolbar: 'full' // 完整工具栏
-  });
-}</code></pre>
-
-<p>🔗 <a href="https://www.wangeditor.com/" target="_blank">访问官网了解更多</a></p>`)
-
-  // 简化编辑器内容
-  const simpleEditorHtml = ref(`<h1>✨ 简化工具栏编辑器示例</h1>
-<p>这个编辑器只包含基础的编辑功能,界面更加简洁。</p>
-
-<h2>基础文本格式</h2>
-<p><strong>加粗文字</strong></p>
-<p><em>斜体文字</em></p>
-<p><u>下划线文字</u></p>
-
-<h2>列表功能</h2>
-<ul>
-  <li>无序列表项 1</li>
-  <li>无序列表项 2</li>
-</ul>
-
-<ol>
-  <li>有序列表项 1</li>
-  <li>有序列表项 2</li>
-</ol>
-
-<h2>链接和图片</h2>
-<p>支持插入 <a href="https://www.wangeditor.com/" target="_blank">链接</a> 和图片。</p>
-
-<p>简化版编辑器专注于基础功能,适合简单的内容编辑需求。</p>`)
-
-  // 完整编辑器操作
-  const clearFullEditor = () => {
-    fullEditorRef.value?.clear()
-    ElMessage.success('完整编辑器已清空')
-  }
-
-  const getFullEditorContent = () => {
-    const content = fullEditorRef.value?.getHtml()
-    console.log('完整编辑器内容:', content)
-    ElMessage.success('完整编辑器内容已输出到控制台')
-  }
-
-  const setFullEditorDemo = () => {
-    const demoContent = `<h2>🎉 完整编辑器演示内容</h2>
-<p>这是通过方法设置的演示内容,展示完整编辑器的强大功能。</p>
-<ul>
-  <li>支持丰富的文本格式</li>
-  <li>包含表格、代码块等高级功能</li>
-  <li>提供完整的编辑体验</li>
-</ul>
-<table style="border-collapse: collapse; width: 100%;" border="1">
-  <tr><th>特性</th><th>状态</th></tr>
-  <tr><td>完整工具栏</td><td>✅ 已启用</td></tr>
-  <tr><td>高级功能</td><td>✅ 已启用</td></tr>
-</table>`
-
-    fullEditorRef.value?.setHtml(demoContent)
-    ElMessage.success('已设置完整编辑器演示内容')
-  }
-
-  // 简化编辑器操作
-  const clearSimpleEditor = () => {
-    simpleEditorRef.value?.clear()
-    ElMessage.success('简化编辑器已清空')
-  }
-
-  const getSimpleEditorContent = () => {
-    const content = simpleEditorRef.value?.getHtml()
-    console.log('简化编辑器内容:', content)
-    ElMessage.success('简化编辑器内容已输出到控制台')
-  }
-
-  const setSimpleEditorDemo = () => {
-    const demoContent = `<h2>⚡ 简化编辑器演示内容</h2>
-<p>这是通过方法设置的演示内容,展示简化编辑器的核心功能。</p>
-<ul>
-  <li><strong>基础格式</strong>:加粗、斜体、下划线</li>
-  <li><em>列表支持</em>:有序和无序列表</li>
-  <li><u>媒体插入</u>:链接和图片</li>
-</ul>
-<ol>
-  <li>界面简洁清爽</li>
-  <li>功能专注实用</li>
-  <li>适合快速编辑</li>
-</ol>
-<p>🔗 <a href="https://example.com" target="_blank">这是一个链接示例</a></p>`
-
-    simpleEditorRef.value?.setHtml(demoContent)
-    ElMessage.success('已设置简化编辑器演示内容')
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    padding: 20px;
-  }
-
-  .editor-card {
-    margin-bottom: 24px;
-
-    .card-header {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-
-      .header-buttons {
-        display: flex;
-        gap: 8px;
-      }
-    }
-  }
-
-  .editor-description {
-    padding: 12px 16px;
-    margin-bottom: 16px;
-    background-color: var(--el-bg-color-page);
-    border-left: 4px solid var(--el-color-primary);
-    border-radius: 8px;
-
-    p {
-      margin: 0;
-      font-size: 14px;
-      color: var(--el-text-color-regular);
-    }
-  }
-
-  .preview-card {
-    margin-bottom: 24px;
-
-    h3 {
-      margin: 0 0 16px;
-      font-size: 16px;
-      color: var(--el-text-color-primary);
-    }
-
-    .content-preview {
-      min-height: 200px;
-      max-height: 300px;
-      padding: 16px;
-      overflow-y: auto;
-      background-color: var(--el-bg-color);
-      border: 1px solid var(--el-border-color);
-      border-radius: 6px;
-
-      // 确保内容样式正确显示
-      :deep(h1),
-      :deep(h2),
-      :deep(h3) {
-        margin: 16px 0 8px;
-      }
-
-      :deep(p) {
-        margin: 8px 0;
-        line-height: 1.6;
-      }
-
-      :deep(table) {
-        margin: 16px 0;
-
-        th,
-        td {
-          padding: 8px 12px;
-        }
-      }
-
-      :deep(pre) {
-        padding: 12px;
-        margin: 16px 0;
-        overflow-x: auto;
-        background-color: var(--el-fill-color-light);
-        border-radius: 4px;
-      }
-
-      :deep(blockquote) {
-        padding-left: 16px;
-        margin: 16px 0;
-        color: var(--el-text-color-regular);
-        border-left: 4px solid var(--el-color-primary);
-      }
-    }
-  }
-
-  .usage-card {
-    :deep(.el-collapse-item__content) {
-      padding-bottom: 16px;
-    }
-
-    pre {
-      padding: 16px;
-      margin: 0;
-      overflow-x: auto;
-      background-color: var(--el-fill-color-light);
-      border-radius: 6px;
-
-      code {
-        font-family: Consolas, Monaco, 'Courier New', monospace;
-        font-size: 14px;
-        line-height: 1.5;
-      }
-    }
-
-    .toolbar-explanation {
-      h4 {
-        margin: 0 0 16px;
-        color: var(--el-text-color-primary);
-      }
-
-      h5 {
-        margin: 0 0 8px;
-        font-size: 14px;
-        color: var(--el-text-color-regular);
-      }
-
-      ul {
-        padding-left: 20px;
-        margin: 8px 0 16px;
-
-        li {
-          margin: 4px 0;
-          font-size: 13px;
-          color: var(--el-text-color-regular);
-        }
-      }
-
-      .note {
-        margin: 8px 0 0;
-        font-size: 12px;
-        font-style: italic;
-        color: var(--el-text-color-placeholder);
-      }
-    }
-  }
-
-  // 响应式设计
-  @media (width <= 768px) {
-    .page-content {
-      padding: 12px;
-    }
-
-    .card-header {
-      flex-direction: column;
-      gap: 12px;
-      align-items: stretch !important;
-
-      .header-buttons {
-        justify-content: center;
-      }
-    }
-
-    .preview-card {
-      :deep(.el-col) {
-        margin-bottom: 16px;
-      }
-    }
-  }
-</style>

+ 0 - 77
web/src/views/widgets/watermark/index.vue

@@ -1,77 +0,0 @@
-<template>
-  <div class="page-content">
-    <!-- 基础文字水印 -->
-    <ElCard class="card" shadow="never">
-      <template #header>基础文字水印</template>
-      <ElWatermark content="逐趣CRM" :font="{ color: 'rgba(128, 128, 128, 0.2)' }">
-        <div style="height: 200px"></div>
-      </ElWatermark>
-    </ElCard>
-
-    <!-- 多行文字水印 -->
-    <ElCard class="card" shadow="never">
-      <template #header>多行文字水印</template>
-      <ElWatermark
-        :content="['逐趣CRM', '专注用户体验,视觉设计']"
-        :font="{ fontSize: 16, color: 'rgba(128, 128, 128, 0.2)' }"
-      >
-        <div style="height: 200px"></div>
-      </ElWatermark>
-    </ElCard>
-
-    <!-- 图片水印 -->
-    <ElCard class="card" shadow="never">
-      <template #header>图片水印</template>
-      <ElWatermark :image="watermarkImage" :opacity="0.2" :width="80" :height="20">
-        <div style="height: 200px"></div>
-      </ElWatermark>
-    </ElCard>
-
-    <!-- 自定义样式水印 -->
-    <ElCard class="card" shadow="never">
-      <template #header>自定义样式水印</template>
-      <ElWatermark
-        content="逐趣CRM"
-        :font="{
-          fontSize: 20,
-          fontFamily: 'Arial',
-          color: 'rgba(255, 0, 0, 0.3)'
-        }"
-        :rotate="-22"
-        :gap="[100, 100]"
-      >
-        <div style="height: 200px"></div>
-      </ElWatermark>
-    </ElCard>
-
-    <ElButton
-      :type="settingStore.watermarkVisible ? 'danger' : 'primary'"
-      @click="handleWatermarkVisible"
-    >
-      {{ settingStore.watermarkVisible ? '隐藏全局水印' : '显示全局水印' }}
-    </ElButton>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { useSettingStore } from '@/store/modules/setting'
-
-  const settingStore = useSettingStore()
-
-  // 这里替换成你的实际logo图片地址
-  const watermarkImage = ref('https://element-plus.org/images/element-plus-logo.svg')
-
-  const handleWatermarkVisible = () => {
-    useSettingStore().setWatermarkVisible(!settingStore.watermarkVisible)
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    padding: 20px;
-
-    .el-card {
-      margin-bottom: 30px;
-    }
-  }
-</style>