liuq 4 mesi fa
parent
commit
5e512d5334

+ 5 - 28
.gitignore

@@ -1,30 +1,7 @@
-# ---> Node
-# Logs
-logs
+node_modules/
+dist/
+.DS_Store
 *.log
-npm-debug.log*
-
-# Runtime data
-pids
-*.pid
-*.seed
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directory
-# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
-node_modules
+.vscode/
+.idea/
 

+ 287 - 2
README.md

@@ -1,3 +1,288 @@
-# simple_navigation_page
+# 韫珠科技内部导航页面
+
+一个简洁明了的内部导航系统,专为老年人友好设计。
+
+## 功能特点
+
+- ✅ 使用 JSON 配置文件,易于维护
+- ✅ 分组管理,支持排序
+- ✅ 每个链接支持图标、背景图、简介
+- ✅ **多种样式主题,可随时切换**(modern、card、list、minimal、tv)
+- ✅ 大字体、高对比度设计,适合老年人使用
+- ✅ 响应式布局,支持各种屏幕尺寸
+- ✅ 使用 Vue 3 最新框架
+
+## 项目结构
+
+```
+simple_navigation/
+├── src/
+│   ├── components/
+│   │   └── Navigation.vue    # 导航组件
+│   ├── data/
+│   │   └── navigation.json   # 导航配置数据
+│   ├── App.vue               # 主应用组件
+│   ├── main.js               # 入口文件
+│   └── style.css             # 全局样式
+├── index.html                # HTML 入口
+├── package.json              # 项目配置
+└── vite.config.js           # Vite 配置
+```
+
+## 安装和运行
+
+1. 安装依赖:
+```bash
+npm install
+```
+
+2. 启动开发服务器:
+```bash
+npm run dev
+```
+
+3. 构建生产版本:
+```bash
+npm run build
+```
+
+构建完成后,生产文件将输出到 `dist` 目录。
+
+4. 预览生产版本(本地测试):
+```bash
+npm run preview
+```
+
+## 部署说明
+
+### 方式一:静态网站托管服务(推荐)
+
+#### 1. Vercel 部署
+
+1. 安装 Vercel CLI:
+```bash
+npm i -g vercel
+```
+
+2. 在项目根目录执行:
+```bash
+vercel
+```
+
+3. 按照提示完成部署,或访问 [vercel.com](https://vercel.com) 通过网页界面部署。
+
+#### 2. Netlify 部署
+
+1. 安装 Netlify CLI:
+```bash
+npm i -g netlify-cli
+```
+
+2. 在项目根目录执行:
+```bash
+netlify deploy --prod
+```
+
+3. 或访问 [netlify.com](https://netlify.com) 通过网页界面拖拽 `dist` 文件夹部署。
+
+#### 3. GitHub Pages 部署
+
+1. 在 `vite.config.js` 中添加 base 配置:
+```javascript
+export default defineConfig({
+  base: '/your-repo-name/',  // 替换为你的仓库名
+  plugins: [vue()],
+  // ...
+})
+```
+
+2. 构建项目:
+```bash
+npm run build
+```
+
+3. 使用 GitHub Actions 自动部署,或手动将 `dist` 目录内容推送到 `gh-pages` 分支。
+
+#### 4. 其他静态托管服务
+
+- **Cloudflare Pages**:连接 GitHub 仓库自动部署
+- **阿里云 OSS**:上传 `dist` 目录内容到 OSS 并开启静态网站托管
+- **腾讯云 COS**:上传 `dist` 目录内容到 COS 并开启静态网站托管
+
+### 方式二:传统 Web 服务器部署
+
+#### 使用 Nginx
+
+1. 构建项目:
+```bash
+npm run build
+```
+
+2. 将 `dist` 目录内容复制到 Nginx 网站根目录(如 `/usr/share/nginx/html`):
+```bash
+cp -r dist/* /usr/share/nginx/html/
+```
+
+3. Nginx 配置示例(`/etc/nginx/sites-available/default`):
+```nginx
+server {
+    listen 80;
+    server_name your-domain.com;
+    root /usr/share/nginx/html;
+    index index.html;
+
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+}
+```
+
+4. 重启 Nginx:
+```bash
+sudo systemctl restart nginx
+```
+
+#### 使用 Apache
+
+1. 构建项目:
+```bash
+npm run build
+```
+
+2. 将 `dist` 目录内容复制到 Apache 网站根目录(如 `/var/www/html`):
+```bash
+cp -r dist/* /var/www/html/
+```
+
+3. 在网站根目录创建 `.htaccess` 文件:
+```apache
+<IfModule mod_rewrite.c>
+  RewriteEngine On
+  RewriteBase /
+  RewriteRule ^index\.html$ - [L]
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteRule . /index.html [L]
+</IfModule>
+```
+
+#### 使用 Node.js 简单服务器(开发/测试用)
+
+1. 安装 `serve`:
+```bash
+npm i -g serve
+```
+
+2. 构建并启动服务器:
+```bash
+npm run build
+serve -s dist -l 3000
+```
+
+### 方式三:Docker 部署
+
+1. 在项目根目录创建 `Dockerfile`:
+```dockerfile
+# 构建阶段
+FROM node:18-alpine as build
+WORKDIR /app
+COPY package*.json ./
+RUN npm install
+COPY . .
+RUN npm run build
+
+# 运行阶段
+FROM nginx:alpine
+COPY --from=build /app/dist /usr/share/nginx/html
+COPY nginx.conf /etc/nginx/conf.d/default.conf
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
+```
+
+2. 创建 `nginx.conf`:
+```nginx
+server {
+    listen 80;
+    server_name localhost;
+    root /usr/share/nginx/html;
+    index index.html;
+
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+}
+```
+
+3. 构建并运行 Docker 容器:
+```bash
+docker build -t navigation-app .
+docker run -d -p 80:80 navigation-app
+```
+
+### 部署注意事项
+
+1. **路由配置**:如果使用客户端路由,确保服务器配置了回退到 `index.html` 的规则(见上述 Nginx/Apache 配置)。
+
+2. **环境变量**:如果项目需要环境变量,在构建前设置好,或使用各平台的环境变量配置功能。
+
+3. **HTTPS**:生产环境建议启用 HTTPS,大多数托管服务都提供免费 SSL 证书。
+
+4. **CDN 加速**:对于静态资源,建议使用 CDN 加速访问。
+
+5. **缓存策略**:配置适当的缓存策略,提升用户体验。
+
+## JSON 配置说明
+
+### 全局配置
+- `style`: 页面样式主题(可选,默认:`modern`)
+  - `modern`: 现代风格 - 渐变背景、大卡片、丰富动画效果
+  - `card`: 卡片风格 - 简洁卡片布局、紧凑设计
+  - `list`: 列表风格 - 横向列表布局、清晰简洁
+  - `minimal`: 极简风格 - 最小化设计、专注内容
+  - `tv`: 电视风格 - 顶部标签栏切换分组,下方网格显示应用模块(类似智能电视界面)
+
+### 分组字段
+- `name`: 组名(必填)
+- `sort`: 排序值,数字越小越靠前(可选)
+- `icon`: 图标地址(可选)
+- `description`: 简介(可选)
+- `links`: 链接数组(必填)
+
+### 链接字段
+- `name`: 链接名称(必填)
+- `url`: 链接地址(必填)
+- `sort`: 排序值,数字越小越靠前(可选)
+- `icon`: 图标地址(可选)
+- `background`: 背景图地址(可选,仅在 modern 样式下生效)
+- `description`: 简介(可选)
+
+## 设计特点
+
+- **大字体**:标题 36-48px,链接名称 24-26px
+- **高对比度**:白色卡片配深色文字,清晰易读
+- **大按钮区域**:链接卡片最小高度 180-200px,易于点击
+- **简洁布局**:清晰的间距和分组,避免信息过载
+- **渐变背景**:美观的紫色渐变背景
+
+## 修改配置
+
+编辑 `src/data/navigation.json` 文件即可修改导航内容,无需修改代码。
+
+### 切换样式主题
+
+在 `navigation.json` 文件的顶部修改 `style` 字段即可切换不同的页面样式:
+
+```json
+{
+  "style": "modern",  // 可选值: "modern", "card", "list", "minimal", "tv"
+  "groups": [...]
+}
+```
+
+**样式说明:**
+- **modern**(现代风格):适合需要丰富视觉效果和动画的场景
+- **card**(卡片风格):适合需要清晰分类和详细信息的场景
+- **list**(列表风格):适合需要快速浏览和简洁展示的场景
+- **minimal**(极简风格):适合需要最小化干扰、专注内容的场景
+- **tv**(电视风格):适合大屏显示,顶部标签栏切换分组,下方网格展示应用模块
 
-simple_navigation_page

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>韫珠科技 - 内部导航</title>
+</head>
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.js"></script>
+</body>
+</html>
+

+ 1164 - 0
package-lock.json

@@ -0,0 +1,1164 @@
+{
+  "name": "yunzhu-navigation",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "yunzhu-navigation",
+      "version": "1.0.0",
+      "dependencies": {
+        "vue": "^3.4.21"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^5.0.4",
+        "vite": "^5.1.6"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.5"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "license": "MIT"
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+      "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+      "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+      "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+      "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+      "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+      "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+      "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+      "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+      "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+      "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+      "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+      "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+      "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+      "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+      "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+      "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+      "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+      "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+      "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+      "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "5.2.4",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
+      "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
+      "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.25",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz",
+      "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz",
+      "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.25",
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz",
+      "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz",
+      "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz",
+      "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/shared": "3.5.25"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz",
+      "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.25",
+        "@vue/runtime-core": "3.5.25",
+        "@vue/shared": "3.5.25",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz",
+      "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "vue": "3.5.25"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz",
+      "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==",
+      "license": "MIT"
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT"
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz",
+      "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.53.3",
+        "@rollup/rollup-android-arm64": "4.53.3",
+        "@rollup/rollup-darwin-arm64": "4.53.3",
+        "@rollup/rollup-darwin-x64": "4.53.3",
+        "@rollup/rollup-freebsd-arm64": "4.53.3",
+        "@rollup/rollup-freebsd-x64": "4.53.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+        "@rollup/rollup-linux-arm64-musl": "4.53.3",
+        "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+        "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-musl": "4.53.3",
+        "@rollup/rollup-openharmony-arm64": "4.53.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+        "@rollup/rollup-win32-x64-gnu": "4.53.3",
+        "@rollup/rollup-win32-x64-msvc": "4.53.3",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.4.21",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+      "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.25",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
+      "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.25",
+        "@vue/compiler-sfc": "3.5.25",
+        "@vue/runtime-dom": "3.5.25",
+        "@vue/server-renderer": "3.5.25",
+        "@vue/shared": "3.5.25"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 19 - 0
package.json

@@ -0,0 +1,19 @@
+{
+  "name": "yunzhu-navigation",
+  "version": "1.0.0",
+  "description": "韫珠科技内部导航页面",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "vue": "^3.4.21"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.0.4",
+    "vite": "^5.1.6"
+  }
+}
+

+ 99 - 0
src/App.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="app">
+    <header v-if="!isTVStyle" class="header">
+      <div class="header-content">
+        <div class="logo-container">
+          <div class="logo-circle"></div>
+        </div>
+        <h1 class="title">韫珠科技</h1>
+        <h2 class="subtitle">内部导航系统</h2>
+        <p class="description">快速访问常用系统和服务</p>
+      </div>
+    </header>
+    <Navigation />
+  </div>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import Navigation from './components/Navigation.vue'
+import navigationData from './data/navigation.json'
+
+const isTVStyle = computed(() => {
+  return navigationData.style === 'tv'
+})
+</script>
+
+<style scoped>
+.app {
+  width: 100%;
+}
+
+.header {
+  text-align: center;
+  color: white;
+  margin-bottom: 50px;
+  padding: 50px 20px;
+  position: relative;
+}
+
+.header-content {
+  position: relative;
+  z-index: 1;
+}
+
+.logo-container {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+}
+
+.logo-circle {
+  width: 80px;
+  height: 80px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.1));
+  backdrop-filter: blur(10px);
+  border: 3px solid rgba(255, 255, 255, 0.5);
+  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
+  animation: float 3s ease-in-out infinite;
+}
+
+@keyframes float {
+  0%, 100% {
+    transform: translateY(0px);
+  }
+  50% {
+    transform: translateY(-10px);
+  }
+}
+
+.title {
+  font-size: 56px;
+  font-weight: 700;
+  margin-bottom: 10px;
+  text-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 2px 10px rgba(0, 0, 0, 0.2);
+  letter-spacing: 2px;
+  background: linear-gradient(135deg, #ffffff 0%, #f0f0f0 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.subtitle {
+  font-size: 32px;
+  font-weight: 500;
+  margin-bottom: 15px;
+  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+  opacity: 0.95;
+  letter-spacing: 1px;
+}
+
+.description {
+  font-size: 20px;
+  opacity: 0.9;
+  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+  font-weight: 300;
+}
+</style>
+

+ 26 - 0
src/components/Navigation.vue

@@ -0,0 +1,26 @@
+<template>
+  <component :is="currentComponent" />
+</template>
+
+<script setup>
+import { computed } from 'vue'
+import navigationData from '../data/navigation.json'
+import NavigationModern from './NavigationModern.vue'
+import NavigationCard from './NavigationCard.vue'
+import NavigationList from './NavigationList.vue'
+import NavigationMinimal from './NavigationMinimal.vue'
+import NavigationTV from './NavigationTV.vue'
+
+const styleComponents = {
+  modern: NavigationModern,
+  card: NavigationCard,
+  list: NavigationList,
+  minimal: NavigationMinimal,
+  tv: NavigationTV
+}
+
+const currentComponent = computed(() => {
+  const style = navigationData.style || 'modern'
+  return styleComponents[style] || NavigationModern
+})
+</script>

+ 217 - 0
src/components/NavigationCard.vue

@@ -0,0 +1,217 @@
+<template>
+  <div class="navigation">
+    <div 
+      v-for="(group, index) in sortedGroups" 
+      :key="group.name"
+      class="group-container"
+      :style="{ animationDelay: `${index * 0.1}s` }"
+    >
+      <div class="group-header">
+        <h2 class="group-name">{{ group.name }}</h2>
+        <p v-if="group.description" class="group-description">{{ group.description }}</p>
+      </div>
+      
+      <div class="links-grid">
+        <a
+          v-for="(link, linkIndex) in sortedLinks(group.links)"
+          :key="link.name"
+          :href="link.url"
+          target="_blank"
+          rel="noopener noreferrer"
+          class="link-card"
+          :style="{ animationDelay: `${(index * 0.1) + (linkIndex * 0.05)}s` }"
+        >
+          <div class="card-header">
+            <div class="link-icon-wrapper">
+              <img 
+                v-if="link.icon" 
+                :src="link.icon" 
+                :alt="link.name"
+                class="link-icon"
+                @error="handleImageError"
+              />
+            </div>
+            <div class="link-name">{{ link.name }}</div>
+          </div>
+          <p v-if="link.description" class="link-description">{{ link.description }}</p>
+          <div class="link-footer">
+            <span class="link-arrow">访问 →</span>
+          </div>
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import navigationData from '../data/navigation.json'
+
+const groups = ref([])
+
+onMounted(() => {
+  groups.value = navigationData.groups || []
+})
+
+const sortedGroups = computed(() => {
+  return [...groups.value].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+})
+
+const sortedLinks = (links) => {
+  if (!links) return []
+  return [...links].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+}
+
+const handleImageError = (event) => {
+  event.target.style.display = 'none'
+}
+</script>
+
+<style scoped>
+.navigation {
+  display: flex;
+  flex-direction: column;
+  gap: 40px;
+}
+
+.group-container {
+  background: rgba(255, 255, 255, 0.98);
+  border-radius: 16px;
+  padding: 35px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
+  animation: fadeIn 0.5s ease-out both;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.group-header {
+  margin-bottom: 25px;
+  padding-bottom: 20px;
+  border-bottom: 2px solid #e2e8f0;
+}
+
+.group-name {
+  font-size: 32px;
+  color: #1a202c;
+  margin-bottom: 8px;
+  font-weight: 600;
+}
+
+.group-description {
+  font-size: 18px;
+  color: #718096;
+  font-weight: 400;
+}
+
+.links-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 20px;
+}
+
+.link-card {
+  display: flex;
+  flex-direction: column;
+  background: #ffffff;
+  border: 2px solid #e2e8f0;
+  border-radius: 12px;
+  padding: 25px;
+  text-decoration: none;
+  color: inherit;
+  transition: all 0.3s ease;
+  animation: fadeIn 0.5s ease-out both;
+  min-height: 160px;
+}
+
+.link-card:hover {
+  transform: translateY(-4px);
+  border-color: #667eea;
+  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
+}
+
+.link-icon-wrapper {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+  background: #f7fafc;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 10px;
+  flex-shrink: 0;
+}
+
+.link-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+.link-name {
+  font-size: 22px;
+  color: #2d3748;
+  font-weight: 600;
+  flex: 1;
+}
+
+.link-description {
+  font-size: 16px;
+  color: #718096;
+  line-height: 1.5;
+  margin-bottom: 15px;
+  flex: 1;
+}
+
+.link-footer {
+  margin-top: auto;
+  padding-top: 15px;
+  border-top: 1px solid #e2e8f0;
+}
+
+.link-arrow {
+  font-size: 14px;
+  color: #667eea;
+  font-weight: 500;
+  transition: transform 0.3s ease;
+}
+
+.link-card:hover .link-arrow {
+  transform: translateX(5px);
+}
+
+@media (max-width: 768px) {
+  .links-grid {
+    grid-template-columns: 1fr;
+  }
+  
+  .group-container {
+    padding: 25px 20px;
+  }
+  
+  .group-name {
+    font-size: 28px;
+  }
+  
+  .link-card {
+    min-height: 140px;
+    padding: 20px;
+  }
+}
+</style>
+

+ 245 - 0
src/components/NavigationList.vue

@@ -0,0 +1,245 @@
+<template>
+  <div class="navigation">
+    <div 
+      v-for="(group, index) in sortedGroups" 
+      :key="group.name"
+      class="group-container"
+      :style="{ animationDelay: `${index * 0.1}s` }"
+    >
+      <div class="group-header">
+        <div class="group-icon-wrapper">
+          <img 
+            v-if="group.icon" 
+            :src="group.icon" 
+            :alt="group.name"
+            class="group-icon"
+            @error="handleImageError"
+          />
+        </div>
+        <div class="group-info">
+          <h2 class="group-name">{{ group.name }}</h2>
+          <p v-if="group.description" class="group-description">{{ group.description }}</p>
+        </div>
+      </div>
+      
+      <div class="links-list">
+        <a
+          v-for="(link, linkIndex) in sortedLinks(group.links)"
+          :key="link.name"
+          :href="link.url"
+          target="_blank"
+          rel="noopener noreferrer"
+          class="link-item"
+          :style="{ animationDelay: `${(index * 0.1) + (linkIndex * 0.03)}s` }"
+        >
+          <div class="link-icon-wrapper">
+            <img 
+              v-if="link.icon" 
+              :src="link.icon" 
+              :alt="link.name"
+              class="link-icon"
+              @error="handleImageError"
+            />
+          </div>
+          <div class="link-content">
+            <div class="link-name">{{ link.name }}</div>
+            <div v-if="link.description" class="link-description">{{ link.description }}</div>
+          </div>
+          <div class="link-arrow">→</div>
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import navigationData from '../data/navigation.json'
+
+const groups = ref([])
+
+onMounted(() => {
+  groups.value = navigationData.groups || []
+})
+
+const sortedGroups = computed(() => {
+  return [...groups.value].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+})
+
+const sortedLinks = (links) => {
+  if (!links) return []
+  return [...links].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+}
+
+const handleImageError = (event) => {
+  event.target.style.display = 'none'
+}
+</script>
+
+<style scoped>
+.navigation {
+  display: flex;
+  flex-direction: column;
+  gap: 35px;
+}
+
+.group-container {
+  background: rgba(255, 255, 255, 0.95);
+  border-radius: 20px;
+  padding: 30px;
+  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.08);
+  animation: slideIn 0.4s ease-out both;
+}
+
+@keyframes slideIn {
+  from {
+    opacity: 0;
+    transform: translateX(-20px);
+  }
+  to {
+    opacity: 1;
+    transform: translateX(0);
+  }
+}
+
+.group-header {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  margin-bottom: 25px;
+  padding-bottom: 20px;
+  border-bottom: 2px solid #e2e8f0;
+}
+
+.group-icon-wrapper {
+  width: 60px;
+  height: 60px;
+  border-radius: 12px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px;
+  flex-shrink: 0;
+}
+
+.group-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  filter: brightness(0) invert(1);
+}
+
+.group-info {
+  flex: 1;
+}
+
+.group-name {
+  font-size: 28px;
+  color: #1a202c;
+  margin-bottom: 5px;
+  font-weight: 600;
+}
+
+.group-description {
+  font-size: 16px;
+  color: #718096;
+  font-weight: 400;
+}
+
+.links-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.link-item {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  background: #f7fafc;
+  border: 1px solid #e2e8f0;
+  border-radius: 12px;
+  padding: 20px 25px;
+  text-decoration: none;
+  color: inherit;
+  transition: all 0.3s ease;
+  animation: slideIn 0.4s ease-out both;
+}
+
+.link-item:hover {
+  background: #ffffff;
+  border-color: #667eea;
+  transform: translateX(5px);
+  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
+}
+
+.link-icon-wrapper {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+  background: #ffffff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 10px;
+  flex-shrink: 0;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.link-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+.link-content {
+  flex: 1;
+  min-width: 0;
+}
+
+.link-name {
+  font-size: 20px;
+  color: #2d3748;
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+
+.link-description {
+  font-size: 14px;
+  color: #718096;
+  line-height: 1.4;
+}
+
+.link-arrow {
+  font-size: 24px;
+  color: #667eea;
+  opacity: 0.6;
+  transition: all 0.3s ease;
+  flex-shrink: 0;
+}
+
+.link-item:hover .link-arrow {
+  opacity: 1;
+  transform: translateX(5px);
+}
+
+@media (max-width: 768px) {
+  .group-container {
+    padding: 20px;
+  }
+  
+  .group-name {
+    font-size: 24px;
+  }
+  
+  .link-item {
+    padding: 15px 20px;
+  }
+  
+  .link-name {
+    font-size: 18px;
+  }
+}
+</style>
+

+ 193 - 0
src/components/NavigationMinimal.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="navigation">
+    <div 
+      v-for="(group, index) in sortedGroups" 
+      :key="group.name"
+      class="group-container"
+      :style="{ animationDelay: `${index * 0.08}s` }"
+    >
+      <h2 class="group-name">{{ group.name }}</h2>
+      <p v-if="group.description" class="group-description">{{ group.description }}</p>
+      
+      <div class="links-grid">
+        <a
+          v-for="(link, linkIndex) in sortedLinks(group.links)"
+          :key="link.name"
+          :href="link.url"
+          target="_blank"
+          rel="noopener noreferrer"
+          class="link-card"
+          :style="{ animationDelay: `${(index * 0.08) + (linkIndex * 0.04)}s` }"
+        >
+          <div class="link-icon-wrapper">
+            <img 
+              v-if="link.icon" 
+              :src="link.icon" 
+              :alt="link.name"
+              class="link-icon"
+              @error="handleImageError"
+            />
+          </div>
+          <div class="link-name">{{ link.name }}</div>
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import navigationData from '../data/navigation.json'
+
+const groups = ref([])
+
+onMounted(() => {
+  groups.value = navigationData.groups || []
+})
+
+const sortedGroups = computed(() => {
+  return [...groups.value].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+})
+
+const sortedLinks = (links) => {
+  if (!links) return []
+  return [...links].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+}
+
+const handleImageError = (event) => {
+  event.target.style.display = 'none'
+}
+</script>
+
+<style scoped>
+.navigation {
+  display: flex;
+  flex-direction: column;
+  gap: 50px;
+}
+
+.group-container {
+  background: rgba(255, 255, 255, 0.9);
+  border-radius: 12px;
+  padding: 40px;
+  box-shadow: 0 1px 10px rgba(0, 0, 0, 0.05);
+  animation: fadeInUp 0.5s ease-out both;
+}
+
+@keyframes fadeInUp {
+  from {
+    opacity: 0;
+    transform: translateY(15px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.group-name {
+  font-size: 26px;
+  color: #1a202c;
+  margin-bottom: 8px;
+  font-weight: 500;
+  letter-spacing: -0.5px;
+}
+
+.group-description {
+  font-size: 15px;
+  color: #718096;
+  margin-bottom: 25px;
+  font-weight: 400;
+}
+
+.links-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  gap: 15px;
+}
+
+.link-card {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: #ffffff;
+  border: 1px solid #e2e8f0;
+  border-radius: 8px;
+  padding: 25px 20px;
+  text-decoration: none;
+  color: inherit;
+  transition: all 0.2s ease;
+  animation: fadeInUp 0.5s ease-out both;
+  min-height: 140px;
+  text-align: center;
+}
+
+.link-card:hover {
+  border-color: #667eea;
+  background: #f7fafc;
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
+}
+
+.link-icon-wrapper {
+  width: 56px;
+  height: 56px;
+  border-radius: 8px;
+  background: #f7fafc;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 12px;
+  margin-bottom: 12px;
+  transition: transform 0.2s ease;
+}
+
+.link-card:hover .link-icon-wrapper {
+  transform: scale(1.05);
+}
+
+.link-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+.link-name {
+  font-size: 16px;
+  color: #2d3748;
+  font-weight: 500;
+  line-height: 1.4;
+}
+
+.link-card:hover .link-name {
+  color: #667eea;
+}
+
+@media (max-width: 768px) {
+  .links-grid {
+    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+    gap: 12px;
+  }
+  
+  .group-container {
+    padding: 25px 20px;
+  }
+  
+  .link-card {
+    padding: 20px 15px;
+    min-height: 120px;
+  }
+  
+  .link-icon-wrapper {
+    width: 48px;
+    height: 48px;
+    margin-bottom: 10px;
+  }
+  
+  .link-name {
+    font-size: 14px;
+  }
+}
+</style>
+

+ 392 - 0
src/components/NavigationModern.vue

@@ -0,0 +1,392 @@
+<template>
+  <div class="navigation">
+    <div 
+      v-for="(group, index) in sortedGroups" 
+      :key="group.name"
+      class="group-container"
+      :style="{ animationDelay: `${index * 0.1}s` }"
+    >
+      <div class="group-header">
+        <div class="group-icon-wrapper">
+          <img 
+            v-if="group.icon" 
+            :src="group.icon" 
+            :alt="group.name"
+            class="group-icon"
+            @error="handleImageError"
+          />
+        </div>
+        <div class="group-info">
+          <h2 class="group-name">{{ group.name }}</h2>
+          <p v-if="group.description" class="group-description">{{ group.description }}</p>
+        </div>
+      </div>
+      
+      <div class="links-grid">
+        <a
+          v-for="(link, linkIndex) in sortedLinks(group.links)"
+          :key="link.name"
+          :href="link.url"
+          target="_blank"
+          rel="noopener noreferrer"
+          class="link-card"
+          :style="{ animationDelay: `${(index * 0.1) + (linkIndex * 0.05)}s` }"
+        >
+          <div class="link-background" :style="getBackgroundStyle(link)"></div>
+          <div class="link-gradient-overlay"></div>
+          <div class="link-content">
+            <div class="link-icon-wrapper">
+              <img 
+                v-if="link.icon" 
+                :src="link.icon" 
+                :alt="link.name"
+                class="link-icon"
+                @error="handleImageError"
+              />
+            </div>
+            <div class="link-text">
+              <h3 class="link-name">{{ link.name }}</h3>
+              <p v-if="link.description" class="link-description">{{ link.description }}</p>
+            </div>
+            <div class="link-arrow">→</div>
+          </div>
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import navigationData from '../data/navigation.json'
+
+const groups = ref([])
+
+onMounted(() => {
+  groups.value = navigationData.groups || []
+})
+
+const sortedGroups = computed(() => {
+  return [...groups.value].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+})
+
+const sortedLinks = (links) => {
+  if (!links) return []
+  return [...links].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+}
+
+const getBackgroundStyle = (link) => {
+  if (link.background) {
+    return {
+      backgroundImage: `url(${link.background})`,
+      opacity: 0.15
+    }
+  }
+  return {}
+}
+
+const handleImageError = (event) => {
+  event.target.style.display = 'none'
+}
+</script>
+
+<style scoped>
+.navigation {
+  display: flex;
+  flex-direction: column;
+  gap: 50px;
+}
+
+.group-container {
+  background: rgba(255, 255, 255, 0.95);
+  backdrop-filter: blur(20px);
+  border-radius: 24px;
+  padding: 45px;
+  box-shadow: 
+    0 20px 60px rgba(0, 0, 0, 0.15),
+    0 0 0 1px rgba(255, 255, 255, 0.5) inset;
+  animation: slideInUp 0.6s ease-out both;
+  transition: transform 0.3s ease, box-shadow 0.3s ease;
+}
+
+.group-container:hover {
+  transform: translateY(-2px);
+  box-shadow: 
+    0 25px 70px rgba(0, 0, 0, 0.2),
+    0 0 0 1px rgba(255, 255, 255, 0.6) inset;
+}
+
+@keyframes slideInUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.group-header {
+  display: flex;
+  align-items: center;
+  gap: 25px;
+  margin-bottom: 35px;
+  padding-bottom: 25px;
+  border-bottom: 3px solid;
+  border-image: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #f093fb 100%) 1;
+  position: relative;
+}
+
+.group-header::after {
+  content: '';
+  position: absolute;
+  bottom: -3px;
+  left: 0;
+  width: 100px;
+  height: 3px;
+  background: linear-gradient(90deg, #667eea, #764ba2);
+  border-radius: 2px;
+}
+
+.group-icon-wrapper {
+  width: 80px;
+  height: 80px;
+  border-radius: 20px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
+  padding: 15px;
+  transition: transform 0.3s ease;
+}
+
+.group-container:hover .group-icon-wrapper {
+  transform: scale(1.05) rotate(5deg);
+}
+
+.group-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  filter: brightness(0) invert(1);
+}
+
+.group-info {
+  flex: 1;
+}
+
+.group-name {
+  font-size: 40px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+  margin-bottom: 10px;
+  font-weight: 700;
+  letter-spacing: 0.5px;
+}
+
+.group-description {
+  font-size: 22px;
+  color: #666;
+  font-weight: 400;
+}
+
+.links-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: 30px;
+}
+
+.link-card {
+  position: relative;
+  display: block;
+  background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
+  border-radius: 20px;
+  padding: 35px;
+  text-decoration: none;
+  color: inherit;
+  box-shadow: 
+    0 8px 25px rgba(0, 0, 0, 0.08),
+    0 0 0 1px rgba(102, 126, 234, 0.1) inset;
+  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+  overflow: hidden;
+  min-height: 200px;
+  border: 2px solid transparent;
+  animation: slideInUp 0.6s ease-out both;
+}
+
+.link-card::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
+  opacity: 0;
+  transition: opacity 0.4s ease;
+}
+
+.link-card:hover {
+  transform: translateY(-8px) scale(1.02);
+  box-shadow: 
+    0 20px 40px rgba(102, 126, 234, 0.25),
+    0 0 0 1px rgba(102, 126, 234, 0.2) inset;
+  border-color: rgba(102, 126, 234, 0.3);
+}
+
+.link-card:hover::before {
+  opacity: 1;
+}
+
+.link-card:hover .link-arrow {
+  transform: translateX(5px);
+  opacity: 1;
+}
+
+.link-background {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-size: cover;
+  background-position: center;
+  z-index: 0;
+  opacity: 0.08;
+  transition: opacity 0.4s ease;
+}
+
+.link-card:hover .link-background {
+  opacity: 0.12;
+}
+
+.link-gradient-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(135deg, rgba(102, 126, 234, 0.03) 0%, rgba(118, 75, 162, 0.03) 100%);
+  z-index: 1;
+  pointer-events: none;
+}
+
+.link-content {
+  position: relative;
+  z-index: 2;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  height: 100%;
+}
+
+.link-icon-wrapper {
+  width: 70px;
+  height: 70px;
+  border-radius: 16px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.25);
+  padding: 15px;
+  transition: transform 0.3s ease;
+}
+
+.link-card:hover .link-icon-wrapper {
+  transform: scale(1.1) rotate(-5deg);
+}
+
+.link-icon {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  filter: brightness(0) invert(1);
+}
+
+.link-text {
+  flex: 1;
+}
+
+.link-name {
+  font-size: 28px;
+  color: #2d3748;
+  margin-bottom: 12px;
+  font-weight: 700;
+  line-height: 1.3;
+  transition: color 0.3s ease;
+}
+
+.link-card:hover .link-name {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.link-description {
+  font-size: 18px;
+  color: #718096;
+  line-height: 1.6;
+  font-weight: 400;
+}
+
+.link-arrow {
+  position: absolute;
+  bottom: 25px;
+  right: 25px;
+  font-size: 32px;
+  color: #667eea;
+  opacity: 0;
+  transform: translateX(-10px);
+  transition: all 0.3s ease;
+  font-weight: bold;
+}
+
+@media (max-width: 768px) {
+  .links-grid {
+    grid-template-columns: 1fr;
+  }
+  
+  .group-container {
+    padding: 30px 25px;
+  }
+  
+  .group-name {
+    font-size: 32px;
+  }
+  
+  .group-description {
+    font-size: 20px;
+  }
+  
+  .link-name {
+    font-size: 24px;
+  }
+  
+  .link-card {
+    padding: 30px;
+    min-height: 180px;
+  }
+}
+
+@media (min-width: 769px) {
+  .link-card {
+    min-height: 220px;
+    padding: 40px;
+  }
+  
+  .link-name {
+    font-size: 30px;
+  }
+  
+  .link-description {
+    font-size: 20px;
+  }
+}
+</style>
+

+ 468 - 0
src/components/NavigationTV.vue

@@ -0,0 +1,468 @@
+<template>
+  <div class="navigation-tv">
+    <!-- 应用网格 -->
+    <div class="apps-grid">
+      <a
+        v-for="(link, linkIndex) in sortedLinks(currentGroup.links)"
+        :key="link.name"
+        :href="link.url"
+        target="_blank"
+        rel="noopener noreferrer"
+        class="app-tile"
+        :style="getTileStyle(linkIndex)"
+        :class="{ 'focused': focusedIndex === linkIndex }"
+        @mouseenter="focusedIndex = linkIndex"
+        @mouseleave="focusedIndex = -1"
+      >
+        <div class="tile-background" :style="getBackgroundStyle(link, linkIndex)"></div>
+        <div class="tile-content">
+          <div 
+            class="tile-name" 
+            :style="getFontSize(link.name)"
+          >{{ link.name }}</div>
+        </div>
+      </a>
+    </div>
+
+    <!-- 底部导航栏 -->
+    <div class="bottom-nav-bar">
+      <div class="nav-title">
+        <div class="logo-circle"></div>
+        <div class="title-group">
+          <h1 class="title">韫珠科技</h1>
+          <p class="subtitle">内部导航系统</p>
+        </div>
+      </div>
+      <div class="nav-tabs">
+        <button
+          v-for="(group, index) in sortedGroups"
+          :key="group.name"
+          :class="['nav-tab', { active: activeTabIndex === index }]"
+          @click="switchTab(index)"
+        >
+          {{ group.name }}
+        </button>
+      </div>
+      <div class="nav-info">
+        <div class="time">{{ currentTime }}</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import navigationData from '../data/navigation.json'
+
+const groups = ref([])
+const activeTabIndex = ref(0)
+const focusedIndex = ref(-1)
+const currentTime = ref('')
+
+const sortedGroups = computed(() => {
+  return [...groups.value].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+})
+
+const currentGroup = computed(() => {
+  return sortedGroups.value[activeTabIndex.value] || { links: [] }
+})
+
+const sortedLinks = (links) => {
+  if (!links) return []
+  return [...links].sort((a, b) => (a.sort || 0) - (b.sort || 0))
+}
+
+const switchTab = (index) => {
+  activeTabIndex.value = index
+  focusedIndex.value = -1
+}
+
+const getFontSize = (name) => {
+  if (!name) return { fontSize: '40px' }
+  
+  // 计算字符长度(中文算1.5个字符)
+  let charCount = 0
+  for (let i = 0; i < name.length; i++) {
+    if (/[\u4e00-\u9fa5]/.test(name[i])) {
+      charCount += 1.5
+    } else {
+      charCount += 1
+    }
+  }
+  
+  // 根据字符数动态调整字体大小
+  // 字符少(1-4个字符):大字体 52-44px
+  // 字符中等(5-8个字符):中等字体 40-28px
+  // 字符多(9+个字符):小字体 26-20px
+  let fontSize
+  if (charCount <= 4) {
+    fontSize = 52 - (charCount - 1) * 2.5
+  } else if (charCount <= 8) {
+    fontSize = 40 - (charCount - 4) * 3
+  } else {
+    fontSize = Math.max(20, 28 - (charCount - 8) * 0.8)
+  }
+  
+  return {
+    fontSize: `${fontSize}px`
+  }
+}
+
+const getBackgroundStyle = (link, index) => {
+  // 如果有背景图片,优先使用背景图片
+  if (link.background && link.background.trim() !== '') {
+    return {
+      backgroundImage: `url(${link.background})`,
+      backgroundSize: 'cover',
+      backgroundPosition: 'center',
+      backgroundColor: 'rgba(0, 0, 0, 0.3)'
+    }
+  }
+  
+  // 如果没有背景图片,使用随机渐变背景
+  const gradients = [
+    'linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%)',
+    'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
+    'linear-gradient(135deg, #f6d365 0%, #fda085 100%)',
+    'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)',
+    'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)',
+    'linear-gradient(135deg, #fa709a 0%, #fee140 100%)',
+    'linear-gradient(135deg, #30cfd0 0%, #330867 100%)',
+    'linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)',
+    'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)',
+    'linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)',
+    'linear-gradient(135deg, #ff8a80 0%, #ea6100 100%)',
+    'linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%)',
+  ]
+  
+  // 使用索引确保相同系统总是使用相同的渐变
+  return {
+    background: gradients[index % gradients.length]
+  }
+}
+
+const getTileStyle = (index) => {
+  return {
+    animationDelay: `${index * 0.05}s`
+  }
+}
+
+
+const updateTime = () => {
+  const now = new Date()
+  const year = now.getFullYear()
+  const month = String(now.getMonth() + 1).padStart(2, '0')
+  const day = String(now.getDate()).padStart(2, '0')
+  const hours = String(now.getHours()).padStart(2, '0')
+  const minutes = String(now.getMinutes()).padStart(2, '0')
+  const seconds = String(now.getSeconds()).padStart(2, '0')
+  currentTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+}
+
+onMounted(() => {
+  groups.value = navigationData.groups || []
+  updateTime()
+  const timeInterval = setInterval(updateTime, 1000)
+  
+  // 添加全屏样式类
+  document.body.classList.add('tv-mode')
+  const app = document.getElementById('app')
+  if (app) {
+    app.classList.add('tv-mode')
+  }
+  
+  onUnmounted(() => {
+    clearInterval(timeInterval)
+    document.body.classList.remove('tv-mode')
+    const app = document.getElementById('app')
+    if (app) {
+      app.classList.remove('tv-mode')
+    }
+  })
+})
+</script>
+
+<style scoped>
+.navigation-tv {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: #0f1419;
+  background-image: 
+    radial-gradient(circle at 20% 30%, rgba(30, 144, 255, 0.1) 0%, transparent 50%),
+    radial-gradient(circle at 80% 70%, rgba(70, 130, 180, 0.08) 0%, transparent 50%),
+    linear-gradient(180deg, #0f1419 0%, #1a1f2e 100%);
+  display: flex;
+  flex-direction: column;
+  padding: 0;
+  overflow-y: auto;
+}
+
+.apps-grid {
+  flex: 1;
+  padding: 60px 40px 120px;
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+  gap: 20px;
+  max-width: 100%;
+  align-content: start;
+}
+
+.bottom-nav-bar {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: rgba(26, 31, 53, 0.95);
+  backdrop-filter: blur(20px);
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+  padding: 20px 40px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: 30px;
+  z-index: 100;
+  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.2);
+}
+
+.nav-title {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  flex-shrink: 0;
+}
+
+.logo-circle {
+  width: 60px;
+  height: 60px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.1));
+  backdrop-filter: blur(10px);
+  border: 2px solid rgba(255, 255, 255, 0.5);
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
+}
+
+.title-group {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.title {
+  font-size: 28px;
+  font-weight: 700;
+  color: #ffffff;
+  margin: 0;
+  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+  letter-spacing: 1px;
+}
+
+.subtitle {
+  font-size: 16px;
+  font-weight: 400;
+  color: rgba(255, 255, 255, 0.8);
+  margin: 0;
+  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
+}
+
+.nav-tabs {
+  display: flex;
+  gap: 30px;
+  flex: 1;
+  justify-content: center;
+}
+
+.nav-tab {
+  background: transparent;
+  border: none;
+  color: rgba(255, 255, 255, 0.6);
+  font-size: 24px;
+  font-weight: 500;
+  padding: 12px 0;
+  cursor: pointer;
+  position: relative;
+  transition: all 0.3s ease;
+  font-family: inherit;
+}
+
+.nav-tab:hover {
+  color: rgba(255, 255, 255, 0.9);
+}
+
+.nav-tab.active {
+  color: #ffffff;
+  font-weight: 600;
+}
+
+.nav-tab.active::after {
+  content: '';
+  position: absolute;
+  bottom: -22px;
+  left: 0;
+  right: 0;
+  height: 3px;
+  background: #3498db;
+  border-radius: 2px;
+}
+
+.nav-info {
+  display: flex;
+  align-items: center;
+  color: rgba(255, 255, 255, 0.9);
+  font-size: 16px;
+  flex-shrink: 0;
+}
+
+.time {
+  font-weight: 500;
+  letter-spacing: 0.5px;
+}
+
+
+.app-tile {
+  position: relative;
+  aspect-ratio: 1;
+  border-radius: 12px;
+  overflow: hidden;
+  text-decoration: none;
+  color: inherit;
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  animation: tileFadeIn 0.4s ease-out both;
+  cursor: pointer;
+}
+
+@keyframes tileFadeIn {
+  from {
+    opacity: 0;
+    transform: scale(0.9);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1);
+  }
+}
+
+.app-tile:hover,
+.app-tile.focused {
+  transform: scale(1.08) translateY(-5px);
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
+  z-index: 10;
+}
+
+.tile-background {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  transition: all 0.3s ease;
+}
+
+.tile-background::after {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.4);
+  transition: background 0.3s ease;
+}
+
+.app-tile:hover .tile-background,
+.app-tile.focused .tile-background {
+  transform: scale(1.05);
+}
+
+.app-tile:hover .tile-background::after,
+.app-tile.focused .tile-background::after {
+  background: rgba(0, 0, 0, 0.3);
+}
+
+.tile-content {
+  position: relative;
+  z-index: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  width: 100%;
+  padding: 20px;
+}
+
+.tile-name {
+  color: #ffffff;
+  font-weight: 700;
+  text-align: center;
+  line-height: 1.2;
+  text-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
+  word-break: break-word;
+  letter-spacing: 1px;
+  width: 100%;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .apps-grid {
+    padding: 40px 20px 140px;
+    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+    gap: 15px;
+  }
+
+  .bottom-nav-bar {
+    flex-direction: column;
+    gap: 15px;
+    padding: 15px 20px;
+    align-items: flex-start;
+  }
+
+  .nav-title {
+    width: 100%;
+    justify-content: center;
+  }
+
+  .nav-tabs {
+    width: 100%;
+    flex-wrap: wrap;
+    gap: 15px;
+    justify-content: flex-start;
+  }
+
+  .nav-tab {
+    font-size: 18px;
+    padding: 8px 0;
+  }
+
+  .nav-tab.active::after {
+    bottom: -15px;
+  }
+
+  .nav-info {
+    width: 100%;
+    justify-content: flex-end;
+  }
+
+  .title {
+    font-size: 24px;
+  }
+
+  .subtitle {
+    font-size: 14px;
+  }
+
+  .logo-circle {
+    width: 50px;
+    height: 50px;
+  }
+
+}
+
+@media (min-width: 1200px) {
+  .apps-grid {
+    grid-template-columns: repeat(6, 1fr);
+  }
+}
+</style>
+

+ 62 - 0
src/data/navigation.json

@@ -0,0 +1,62 @@
+{
+  "style": "tv",
+  "groups": [
+    {
+      "name": "系统导航",
+      "sort": 1,
+      "icon": "",
+      "description": "企业管理系统",
+      "links": [
+        {
+          "name": "实验室系统",
+          "url": "https://lab.example.com",
+          "sort": 1,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1532619675605-1d6c53d25d81?w=800&h=600&fit=crop",
+          "description": "实验室设备与实验管理"
+        },
+        {
+          "name": "人力资源管理系统",
+          "url": "https://hr.example.com",
+          "sort": 2,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1521737604893-d14cc237f11d?w=800&h=600&fit=crop",
+          "description": "员工信息、招聘、绩效管理"
+        },
+        {
+          "name": "工资发放系统",
+          "url": "https://salary.example.com",
+          "sort": 3,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1554224155-6726b3ff858f?w=800&h=600&fit=crop",
+          "description": "薪资计算与发放管理"
+        },
+        {
+          "name": "公司培训系统",
+          "url": "https://training.example.com",
+          "sort": 4,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=800&h=600&fit=crop",
+          "description": "员工培训课程与学习管理"
+        },
+        {
+          "name": "园区管理平台",
+          "url": "https://park.example.com",
+          "sort": 5,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=800&h=600&fit=crop",
+          "description": "智能安防警戒与园区管理"
+        },
+        {
+          "name": "供应链系统",
+          "url": "https://supplychain.example.com",
+          "sort": 6,
+          "icon": "",
+          "background": "https://images.unsplash.com/photo-1586528116311-ad8dd3c8310d?w=800&h=600&fit=crop",
+          "description": "供应链管理与物流跟踪"
+        }
+      ]
+    }
+  ]
+}
+

+ 6 - 0
src/main.js

@@ -0,0 +1,6 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import './style.css'
+
+createApp(App).mount('#app')
+

+ 62 - 0
src/style.css

@@ -0,0 +1,62 @@
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}
+
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
+  background-size: 400% 400%;
+  animation: gradientShift 15s ease infinite;
+  min-height: 100vh;
+  padding: 20px;
+  position: relative;
+  overflow-x: hidden;
+}
+
+body::before {
+  content: '';
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: 
+    radial-gradient(circle at 20% 50%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
+    radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
+  pointer-events: none;
+  z-index: 0;
+}
+
+@keyframes gradientShift {
+  0% {
+    background-position: 0% 50%;
+  }
+  50% {
+    background-position: 100% 50%;
+  }
+  100% {
+    background-position: 0% 50%;
+  }
+}
+
+#app {
+  max-width: 1400px;
+  margin: 0 auto;
+  position: relative;
+  z-index: 1;
+}
+
+/* TV 风格全屏模式 */
+body.tv-mode {
+  padding: 0;
+  overflow: hidden;
+}
+
+#app.tv-mode {
+  max-width: 100%;
+  margin: 0;
+  padding: 0;
+}
+

+ 11 - 0
vite.config.js

@@ -0,0 +1,11 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+export default defineConfig({
+  plugins: [vue()],
+  server: {
+    port: 3000,
+    open: true
+  }
+})
+