Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
9ada32765a |
1
.gitattributes
vendored
@ -39,7 +39,6 @@ commit-msg text eol=lf
|
||||
*.woff2 binary
|
||||
*.eot binary
|
||||
*.otf binary
|
||||
*.spline binary
|
||||
# Add more binary...
|
||||
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
### .gitattributes
|
||||
|
||||
如果你先推送了一个.jpg文件,然后再推送包含.gitattributes文件的更新,Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效,你可以通过以下步骤重新应用设置:
|
||||
|
||||
```bash
|
||||
1. 删除本地缓存的.jpg文件:git rm --cached <file>.jpg
|
||||
2. 重新添加该文件:git add <file>.jpg
|
||||
3. 再次提交并推送:git commit -m "Apply .gitattributes changes" && git push
|
||||
```
|
||||
|
||||
这样,Git将按照.gitattributes中的新规则处理该文件。
|
||||
|
||||
要验证.gitattributes文件中的二进制配置是否生效,可以使用以下方法:
|
||||
|
||||
```bash
|
||||
1. 推送后验证:先按照之前步骤重新添加并推送.jpg文件。
|
||||
2. 查看差异(diff):执行 git diff,检查文件是否有文本形式的改动。如果是二进制文件,Git不会显示具体内容的差异。
|
||||
3. Git日志验证:执行 git log -p <file>.jpg 查看提交的改动记录,确认文件未受行尾或编码处理的影响。
|
||||
```
|
||||
|
||||
如果以上测试显示该文件未发生不必要的改动,说明.gitattributes配置已生效。
|
9
.github/copilot-instructions.md
vendored
@ -1,11 +1,4 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# GitHub Copilot Instructions
|
||||
|
||||
本文件定义了项目的代码生成规范,GitHub Copilot 和其他 AI 助手应遵循这些指令。
|
||||
# Project Conventions and Technical Guidelines
|
||||
|
||||
This document outlines the core technical choices, coding conventions, and configuration details for this project. Adhering to these guidelines ensures consistency and leverages the project's setup effectively.
|
||||
|
||||
|
3
.github/copilot-instructions_MD
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
```bash
|
||||
ln -s .github/copilot-instructions.md .roorules
|
||||
```
|
2
.github/workflows/lint.yaml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛠️ 设置Node环境
|
||||
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
||||
- name: 🔍 静态代码分析
|
||||
run: pnpm run lint
|
||||
- name: 📦 构建项目
|
||||
|
23
.github/workflows/playwright.yaml
vendored
@ -7,13 +7,6 @@ env:
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_cleanup:
|
||||
description: '是否运行 Surge 清理 Job'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
surge:
|
||||
@ -22,7 +15,7 @@ jobs:
|
||||
url: ${{ steps.surge_deploy.outputs.url }}
|
||||
steps:
|
||||
- name: ⚙️ 设置 Node 环境
|
||||
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
||||
- name: 🔨 构建项目
|
||||
run: pnpm run build-only
|
||||
env:
|
||||
@ -30,28 +23,22 @@ jobs:
|
||||
- name: 🚀 部署到 Surge
|
||||
id: surge_deploy
|
||||
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
|
||||
uses: yanhao98/composite-actions/deploy-dist-to-surge@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||
uses: yanhao98/composite-actions/deploy-dist-to-surge@4470aa136359d05ae3b086d37cfaa33305448a5b
|
||||
|
||||
playwright:
|
||||
needs: surge
|
||||
runs-on: ubuntu-latest
|
||||
container: mcr.microsoft.com/playwright:v1.53.2-noble
|
||||
container: mcr.microsoft.com/playwright:v1.51.1-noble
|
||||
steps:
|
||||
- name: ⚙️ 设置 Node 环境
|
||||
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
||||
# - name: 📥 安装 Playwright 浏览器
|
||||
# run: pnpm exec playwright install --with-deps
|
||||
- name: ▶️ 运行 Playwright 测试
|
||||
run: npx playwright test
|
||||
env:
|
||||
BASE_URL: ${{ needs.surge.outputs.url }}
|
||||
|
||||
cleanup_surge:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [surge, playwright]
|
||||
if: github.event_name == 'push' || github.event.inputs.run_cleanup == true
|
||||
steps:
|
||||
- name: 🧹 清理 Surge 部署
|
||||
run: npx surge teardown ${{ needs.surge.outputs.url }} --token ${{ env.SURGE_TOKEN }}
|
||||
run: npx surge teardown ${{ needs.surge.outputs.url }} --token ${{ env.SURGE_TOKEN}}
|
||||
env:
|
||||
SURGE_TOKEN: d843de16b331c626f10771245c56ed93
|
||||
|
2
.github/workflows/vercel.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: ⚙️ 设置 Node 环境
|
||||
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
||||
|
||||
- name: 📥 拉取 Vercel 环境信息
|
||||
run: pnpm dlx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||
|
@ -1,6 +1,5 @@
|
||||
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
|
||||
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
|
||||
echo "当前提交的文件是:$1"
|
||||
# 在这里添加你的 commit message 验证逻辑,例如 commitlint
|
||||
# npx --no -- commitlint --edit "$1"
|
||||
pnpm exec commitlint --edit $1
|
||||
echo "✅ [Commit-msg] commit-msg 钩子完成!"
|
||||
|
2
.npmrc
@ -5,7 +5,7 @@ registry=https://registry.npmjs.org/
|
||||
# registry=https://nexus.oo1.dev/repository/npm/
|
||||
|
||||
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
|
||||
use-node-version=22.15.0
|
||||
use-node-version=22.14.0
|
||||
node-mirror:release=https://npmmirror.com/mirrors/node/ # pnpm config set node-mirror:release=https://npmmirror.com/mirrors/node/
|
||||
node-mirror:rc=https://npmmirror.com/mirrors/node-rc/
|
||||
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/
|
||||
|
3
.vscode/settings.json
vendored
@ -27,7 +27,8 @@
|
||||
"source.fixAll.eslint": "never",
|
||||
"source.fixAll.stylelint": "never",
|
||||
"source.fixAll.oxc": "never",
|
||||
"source.organizeImports": "never"
|
||||
"source.organizeImports": "never",
|
||||
"source.fixAll": "never"
|
||||
},
|
||||
"editor.formatOnSave": false,
|
||||
"oxc.enable": true,
|
||||
|
30
README.md
@ -15,9 +15,33 @@ pnpm install --registry=https://nexus.oo1.dev/repository/npm
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
### .gitattributes
|
||||
|
||||
如果你先推送了一个.jpg文件,然后再推送包含.gitattributes文件的更新,Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效,你可以通过以下步骤重新应用设置:
|
||||
|
||||
```bash
|
||||
1. 删除本地缓存的.jpg文件:git rm --cached <file>.jpg
|
||||
2. 重新添加该文件:git add <file>.jpg
|
||||
3. 再次提交并推送:git commit -m "Apply .gitattributes changes" && git push
|
||||
```
|
||||
|
||||
这样,Git将按照.gitattributes中的新规则处理该文件。
|
||||
|
||||
要验证.gitattributes文件中的二进制配置是否生效,可以使用以下方法:
|
||||
|
||||
```bash
|
||||
1. 推送后验证:先按照之前步骤重新添加并推送.jpg文件。
|
||||
2. 查看差异(diff):执行 git diff,检查文件是否有文本形式的改动。如果是二进制文件,Git不会显示具体内容的差异。
|
||||
3. Git日志验证:执行 git log -p <file>.jpg 查看提交的改动记录,确认文件未受行尾或编码处理的影响。
|
||||
```
|
||||
|
||||
如果以上测试显示该文件未发生不必要的改动,说明.gitattributes配置已生效。
|
||||
|
||||
## Links
|
||||
|
||||
- https://github.com/hyoban-template/shadcn-vue-unocss-starter
|
||||
- https://github.com/hyoban-template/shadcn-vue-unocss-starter$0
|
||||
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
|
||||
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
|
||||
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
|
||||
@ -29,12 +53,10 @@ pnpm run dev
|
||||
|
||||
---
|
||||
|
||||
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
|
||||
- https://biomejs.dev/zh-cn/internals/language-support/
|
||||
- https://github.dev/antfu-collective/vitesse/
|
||||
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
|
||||
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
|
||||
|
||||
---
|
||||
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)
|
||||
|
||||
+ https://www.npmjs.com/package/npkill
|
||||
|
@ -1,9 +0,0 @@
|
||||
import type { UserConfig } from '@commitlint/types';
|
||||
|
||||
|
||||
const Configuration: UserConfig = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
formatter: '@commitlint/format',
|
||||
};
|
||||
|
||||
export default Configuration;
|
@ -84,8 +84,6 @@ export default defineConfigWithVueTs(
|
||||
'import-x/newline-after-import': 'error',
|
||||
'import-x/first': 'error',
|
||||
'import-x/no-named-as-default': 'off',
|
||||
'import-x/no-duplicates': 'off',
|
||||
'import-x/named': 'off',
|
||||
},
|
||||
},
|
||||
// endregion <<< eslint-plugin-import-x <<<
|
||||
@ -101,8 +99,6 @@ export default defineConfigWithVueTs(
|
||||
'perfectionist/sort-imports': ['error'],
|
||||
'perfectionist/sort-modules': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
},
|
||||
},
|
||||
// endregion <<< eslint-plugin-perfectionist <<<
|
||||
|
142
package.json
@ -1,5 +1,5 @@
|
||||
{
|
||||
"packageManager": "pnpm@10.12.4",
|
||||
"packageManager": "pnpm@10.8.0",
|
||||
"name": "vue-ts-example",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
@ -44,123 +44,121 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/adapter-axios": "^2.0.16",
|
||||
"@alova/adapter-axios": "^2.0.13",
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||
"@pinia/colada": "^0.17.1",
|
||||
"@primeuix/themes": "^1.1.2",
|
||||
"@splinetool/runtime": "^1.10.22",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.6",
|
||||
"@pinia/colada": "^0.14.2",
|
||||
"@primeuix/themes": "^1.0.3",
|
||||
"@splinetool/runtime": "^1.9.82",
|
||||
"@types/p5": "^1.7.6",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@unhead/vue": "^2.0.12",
|
||||
"@unhead/vue": "^2.0.5",
|
||||
"@vant/use": "^1.6.0",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"alova": "^3.3.4",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"alova": "^3.2.10",
|
||||
"ant-design-vue": "~4.2.6",
|
||||
"axios": "^1.10.0",
|
||||
"cesium": "^1.131.0",
|
||||
"axios": "^1.8.4",
|
||||
"cesium": "^1.128.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"consola": "^3.4.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"deep-freeze-es6": "^4.0.1",
|
||||
"deep-freeze-es6": "^4.0.0",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"lucide-vue-next": "^0.487.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"p5": "^2.0.3",
|
||||
"p5": "^1.11.3",
|
||||
"page-stack-vue3": "^2.5.6",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"pinia": "^3.0.1",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"plotly.js-dist-min": "^3.0.1",
|
||||
"primeicons": "^7.0.0",
|
||||
"primelocale": "^2.1.4",
|
||||
"primevue": "^4.3.5",
|
||||
"radash": "^12.1.1",
|
||||
"primelocale": "^2.1.2",
|
||||
"primevue": "^4.3.3",
|
||||
"radash": "^12.1.0",
|
||||
"radix-vue": "^1.9.17",
|
||||
"reka-ui": "^2.3.2",
|
||||
"satellite.js": "^6.0.1",
|
||||
"reka-ui": "^2.2.0",
|
||||
"satellite.js": "^6.0.0",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tdesign-icons-vue-next": "^0.3.6",
|
||||
"three": "^0.178.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tdesign-icons-vue-next": "^0.3.5",
|
||||
"three": "^0.175.0",
|
||||
"ts-enum-util": "^4.1.0",
|
||||
"utils4u": "^4.2.3",
|
||||
"vant": "^4.9.20",
|
||||
"vue": "^3.5.17",
|
||||
"vue-data-ui": "^2.12.7",
|
||||
"vant": "^4.9.18",
|
||||
"vue": "^3.5.13",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-i18n": "^11.1.9",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vue-page-stack": "^3.2.0",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.0",
|
||||
"vuetify": "^3.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.8.1",
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@eslint/compat": "^1.3.1",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@iconify-json/carbon": "^1.2.10",
|
||||
"@eslint/compat": "^1.2.8",
|
||||
"@faker-js/faker": "^9.6.0",
|
||||
"@iconify-json/carbon": "^1.2.8",
|
||||
"@iconify-json/logos": "^1.2.4",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify/utils": "^2.3.0",
|
||||
"@playwright/test": "^1.53.2",
|
||||
"@primevue/auto-import-resolver": "^4.3.5",
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@primevue/auto-import-resolver": "^4.3.3",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@types/node": "^22.16.3",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/plotly.js-dist-min": "^2.3.4",
|
||||
"@types/three": "^0.178.1",
|
||||
"@types/three": "^0.175.0",
|
||||
"@vant/auto-import-resolver": "^1.3.0",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||
"@vitest/eslint-plugin": "^1.3.4",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||
"@vitest/eslint-plugin": "^1.1.40",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"@vue/eslint-config-typescript": "^14.6.0",
|
||||
"@vue/eslint-config-typescript": "^14.5.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"archiver": "^7.0.1",
|
||||
"depcheck": "^1.4.7",
|
||||
"eruda": "^3.4.3",
|
||||
"eslint": "^9.30.1",
|
||||
"eslint-plugin-import-x": "^4.16.1",
|
||||
"eslint-plugin-oxlint": "^1.6.0",
|
||||
"eslint-plugin-perfectionist": "^4.15.0",
|
||||
"eslint-plugin-unicorn": "^59.0.1",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"eruda": "^3.4.1",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-plugin-import-x": "^4.10.2",
|
||||
"eslint-plugin-oxlint": "^0.16.6",
|
||||
"eslint-plugin-perfectionist": "^4.11.0",
|
||||
"eslint-plugin-unicorn": "^58.0.0",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"less": "^4.3.0",
|
||||
"lint-staged": "^16.1.2",
|
||||
"lint-staged": "^15.5.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"naive-ui": "^2.42.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"oxlint": "^1.6.0",
|
||||
"prettier": "3.6.2",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"oxlint": "^0.16.6",
|
||||
"prettier": "3.5.3",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"terser": "^5.39.0",
|
||||
"typescript": "~5.8.3",
|
||||
"unocss": "66.3.3",
|
||||
"unocss-preset-animations": "^1.2.1",
|
||||
"unocss": "66.1.0-beta.10",
|
||||
"unocss-preset-animations": "^1.1.1",
|
||||
"unocss-preset-chinese": "^0.3.3",
|
||||
"unocss-preset-shadcn": "^0.5.0",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-auto-import": "^19.1.2",
|
||||
"unplugin-icons": "^22.1.0",
|
||||
"unplugin-vue-components": "^28.8.0",
|
||||
"unplugin-vue-components": "^28.5.0",
|
||||
"unplugin-vue-macros": "^2.14.5",
|
||||
"unplugin-vue-markdown": "^29.1.0",
|
||||
"unplugin-vue-router": "^0.14.0",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^7.0.2",
|
||||
"vite-plugin-checker": "^0.10.0",
|
||||
"unplugin-vue-markdown": "^28.3.1",
|
||||
"unplugin-vue-router": "^0.12.0",
|
||||
"vite": "^6.2.6",
|
||||
"vite-plugin-checker": "^0.9.1",
|
||||
"vite-plugin-fake-server": "^2.2.0",
|
||||
"vite-plugin-image-tools": "^2.0.2",
|
||||
"vite-plugin-purgecss-updated-v5": "^1.2.6",
|
||||
"vite-plugin-singlefile": "^2.3.0",
|
||||
"vite-plugin-static-copy": "^3.1.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.7",
|
||||
"vite-plugin-purgecss-updated-v5": "^1.2.4",
|
||||
"vite-plugin-singlefile": "^2.2.0",
|
||||
"vite-plugin-static-copy": "^2.3.1",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vite-plugin-vue-meta-layouts": "^0.5.1",
|
||||
"vite-plugin-webfont-dl": "^3.10.5",
|
||||
"vue-component-type-helpers": "^3.0.1",
|
||||
"vue-tsc": "^3.0.1"
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vite-plugin-webfont-dl": "^3.10.4",
|
||||
"vue-component-type-helpers": "^2.2.8",
|
||||
"vue-tsc": "^2.2.8"
|
||||
}
|
||||
}
|
||||
|
6904
pnpm-lock.yaml
generated
@ -15,11 +15,9 @@ const themeConfig = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-config-provider preflight-style-disabled>
|
||||
<a-config-provider :theme="themeConfig">
|
||||
<RouterView />
|
||||
</a-config-provider>
|
||||
</n-config-provider>
|
||||
<a-config-provider :theme="themeConfig">
|
||||
<RouterView />
|
||||
</a-config-provider>
|
||||
|
||||
<DynamicDialog /> <ConfirmDialog /> <Toast />
|
||||
</template>
|
||||
|
@ -1,58 +0,0 @@
|
||||
export enum E_选项卡菜单 {
|
||||
幅度单位列表 = '幅度单位列表',
|
||||
幅度菜单列表 = '幅度菜单列表',
|
||||
跨度菜单列表 = '跨度菜单列表',
|
||||
频率单位列表 = '频率单位列表',
|
||||
频率菜单列表 = '频率菜单列表',
|
||||
}
|
||||
|
||||
export enum E_幅度菜单列表的项目 {
|
||||
RefLevel = 'Ref Level',
|
||||
// Attenuation = 'Attenuation', // 如果衰减也需要类似交互,则取消注释
|
||||
}
|
||||
|
||||
export enum E_幅度单位 {
|
||||
dBm = 'dBm',
|
||||
mV = 'mV',
|
||||
uA = 'µA', // 使用 uA 表示微安
|
||||
uV = 'µV', // 使用 uV 表示微伏
|
||||
}
|
||||
|
||||
export enum E_跨度菜单列表的项目 {
|
||||
FullSpan = 'Full Span',
|
||||
LastSpan = 'Last Span',
|
||||
ZeroSpan = 'Zero Span',
|
||||
}
|
||||
|
||||
/* 值是在屏幕上显示的值。 */
|
||||
export enum E_选项卡菜单Freq菜单列表的项目 {
|
||||
CenterFreq = 'Center',
|
||||
StartFreq = 'Start',
|
||||
StopFreq = 'Stop',
|
||||
// CFStep = 'CFStep',
|
||||
// FreqOffset = 'FreqOffset',
|
||||
// SingnalTrack = 'SingnalTrack',
|
||||
// ScaleType = 'ScaleType',
|
||||
}
|
||||
|
||||
export enum E_Freq单位 {
|
||||
GHz = 'GHz',
|
||||
Hz = 'Hz',
|
||||
kHz = 'kHz',
|
||||
MHz = 'MHz',
|
||||
}
|
||||
|
||||
export enum E_数字键盘按键 {
|
||||
Dot = '.',
|
||||
Num0 = 0,
|
||||
Num1 = 1,
|
||||
Num2 = 2,
|
||||
Num3 = 3,
|
||||
Num4 = 4,
|
||||
Num5 = 5,
|
||||
Num6 = 6,
|
||||
Num7 = 7,
|
||||
Num8 = 8,
|
||||
Num9 = 9,
|
||||
PlusMinus = '+/-',
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Helvetica Custom';
|
||||
font-style: normal;
|
||||
font-weight: 400; /* 400 通常代表 'normal' 或 'regular' */
|
||||
src: url('fonts/Helvetica-Light-05.eot');
|
||||
src:
|
||||
url('fonts/Helvetica-Light-05.eot?#iefix') format('embedded-opentype'),
|
||||
url('fonts/Helvetica-Light-05.woff2') format('woff2'),
|
||||
url('fonts/Helvetica-Light-05.woff') format('woff'),
|
||||
url('fonts/Helvetica-Light-05.ttf') format('truetype'),
|
||||
url('fonts/Helvetica-Light-05.svg#Helvetica') format('svg');
|
||||
font-display: swap; /* 推荐 */
|
||||
}
|
@ -1,805 +0,0 @@
|
||||
<!-- 频谱仪 -->
|
||||
<script setup lang="ts">
|
||||
import { $enum } from 'ts-enum-util';
|
||||
|
||||
import {
|
||||
E_Freq单位,
|
||||
E_幅度单位,
|
||||
E_幅度菜单列表的项目,
|
||||
E_数字键盘按键,
|
||||
E_跨度菜单列表的项目,
|
||||
E_选项卡菜单,
|
||||
E_选项卡菜单Freq菜单列表的项目,
|
||||
} from './CONST';
|
||||
import 设备照片 from './pin-pu-yi-bg.png';
|
||||
|
||||
const state = reactive({
|
||||
选项卡当前显示: E_选项卡菜单.频率菜单列表,
|
||||
频谱仪状态: {
|
||||
频率输入状态: {
|
||||
选中的频率菜单列表的项目: null as E_选项卡菜单Freq菜单列表的项目 | null,
|
||||
输入的值临时输入字符串: null as null | string, // 用于暂存用户输入
|
||||
},
|
||||
幅度输入状态: {
|
||||
选中的幅度菜单列表的项目: null as E_幅度菜单列表的项目 | null,
|
||||
输入的值临时输入字符串: null as null | string, // 用于暂存用户输入
|
||||
refLevel: {
|
||||
value: 0,
|
||||
unit: E_幅度单位.dBm,
|
||||
},
|
||||
// attenuation: { value: 0, unit: E_幅度单位.dB }, // 如果需要衰减值
|
||||
},
|
||||
center: {
|
||||
value: 0,
|
||||
unit: E_Freq单位.Hz,
|
||||
},
|
||||
start: {
|
||||
value: 0,
|
||||
unit: E_Freq单位.Hz,
|
||||
},
|
||||
stop: {
|
||||
value: 0,
|
||||
unit: E_Freq单位.Hz,
|
||||
},
|
||||
span: {
|
||||
currentSpan: {
|
||||
start: 0,
|
||||
stop: 0,
|
||||
unit: E_Freq单位.Hz,
|
||||
},
|
||||
lastSpan: {
|
||||
start: 0,
|
||||
stop: 0,
|
||||
unit: E_Freq单位.Hz,
|
||||
},
|
||||
fullSpan: {
|
||||
start: 0,
|
||||
stop: 3,
|
||||
unit: E_Freq单位.GHz,
|
||||
},
|
||||
zeroSpan: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const 选项卡标题 = computed(() =>
|
||||
$enum.mapValue(state.选项卡当前显示).with({
|
||||
[E_选项卡菜单.频率菜单列表]: 'Freq/Channel',
|
||||
[E_选项卡菜单.频率单位列表]: '',
|
||||
[E_选项卡菜单.跨度菜单列表]: 'Span',
|
||||
[E_选项卡菜单.幅度菜单列表]: 'Amplitude',
|
||||
[E_选项卡菜单.幅度单位列表]: '',
|
||||
}),
|
||||
);
|
||||
|
||||
function 执行点击屏幕右边的按钮(按钮序号: number) {
|
||||
console.group('🔘 点击屏幕右边的按钮', {
|
||||
按钮序号,
|
||||
选项卡当前显示: state.选项卡当前显示,
|
||||
});
|
||||
$enum.visitValue(state.选项卡当前显示).with({
|
||||
[E_选项卡菜单.频率菜单列表]: () => {
|
||||
switch (按钮序号) {
|
||||
case 1: {
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.CenterFreq;
|
||||
console.debug('设置 Freq 输入状态为 CenterFreq');
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.StartFreq;
|
||||
console.debug('设置 Freq 输入状态为 StartFreq');
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = E_选项卡菜单Freq菜单列表的项目.StopFreq;
|
||||
console.debug('设置 Freq 输入状态为 StopFreq');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.debug('频率菜单列表 - 这个按键还没有实现');
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[E_选项卡菜单.频率单位列表]: () => {
|
||||
let selectedUnit: E_Freq单位 | null = null;
|
||||
switch (按钮序号) {
|
||||
case 1: {
|
||||
selectedUnit = E_Freq单位.GHz;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
selectedUnit = E_Freq单位.MHz;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
selectedUnit = E_Freq单位.kHz;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
selectedUnit = E_Freq单位.Hz;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.warn('无效的频率单位按钮序号:', 按钮序号);
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const tempInputString = state.频谱仪状态.频率输入状态.输入的值临时输入字符串;
|
||||
const selectedFreqItem = state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目;
|
||||
// 必须有临时输入值和选中的 Freq 项目才能确认
|
||||
if (!tempInputString || !selectedFreqItem) {
|
||||
console.warn('频率输入未完成或未选择项目,无法确认单位。');
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
const inputValue = Number.parseFloat(tempInputString);
|
||||
// 校验解析结果
|
||||
if (Number.isNaN(inputValue)) {
|
||||
console.warn('无法将临时输入解析为有效数字 (频率):', tempInputString);
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('确认频率单位:', selectedUnit, '值为:', inputValue);
|
||||
switch (selectedFreqItem) {
|
||||
case E_选项卡菜单Freq菜单列表的项目.CenterFreq: {
|
||||
state.频谱仪状态.center.value = inputValue;
|
||||
state.频谱仪状态.center.unit = selectedUnit;
|
||||
console.debug('更新 CenterFreq:', state.频谱仪状态.center);
|
||||
break;
|
||||
}
|
||||
case E_选项卡菜单Freq菜单列表的项目.StartFreq: {
|
||||
state.频谱仪状态.start.value = inputValue;
|
||||
state.频谱仪状态.start.unit = selectedUnit;
|
||||
console.debug('更新 StartFreq:', state.频谱仪状态.start);
|
||||
break;
|
||||
}
|
||||
case E_选项卡菜单Freq菜单列表的项目.StopFreq: {
|
||||
state.频谱仪状态.stop.value = inputValue;
|
||||
state.频谱仪状态.stop.unit = selectedUnit;
|
||||
console.debug('更新 StopFreq:', state.频谱仪状态.stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 = null;
|
||||
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 = null;
|
||||
state.选项卡当前显示 = E_选项卡菜单.频率菜单列表;
|
||||
console.debug('频率输入状态已重置, 切换回频率菜单列表');
|
||||
},
|
||||
[E_选项卡菜单.跨度菜单列表]: () => {
|
||||
switch (按钮序号) {
|
||||
case 1: {
|
||||
// Full Span
|
||||
state.频谱仪状态.span.lastSpan.start = state.频谱仪状态.start.value;
|
||||
state.频谱仪状态.span.lastSpan.stop = state.频谱仪状态.stop.value;
|
||||
state.频谱仪状态.span.lastSpan.unit = state.频谱仪状态.stop.unit;
|
||||
state.频谱仪状态.start.value = state.频谱仪状态.span.fullSpan.start;
|
||||
state.频谱仪状态.start.unit = state.频谱仪状态.span.fullSpan.unit;
|
||||
state.频谱仪状态.stop.value = state.频谱仪状态.span.fullSpan.stop;
|
||||
state.频谱仪状态.stop.unit = state.频谱仪状态.span.fullSpan.unit;
|
||||
state.频谱仪状态.center.value = (state.频谱仪状态.start.value + state.频谱仪状态.stop.value) / 2;
|
||||
state.频谱仪状态.center.unit = state.频谱仪状态.span.fullSpan.unit;
|
||||
state.频谱仪状态.span.zeroSpan = false;
|
||||
console.debug('设置为 Full Span');
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Zero Span
|
||||
state.频谱仪状态.span.lastSpan.start = state.频谱仪状态.start.value;
|
||||
state.频谱仪状态.span.lastSpan.stop = state.频谱仪状态.stop.value;
|
||||
state.频谱仪状态.span.lastSpan.unit = state.频谱仪状态.stop.unit;
|
||||
state.频谱仪状态.start.value = state.频谱仪状态.center.value;
|
||||
state.频谱仪状态.start.unit = state.频谱仪状态.center.unit;
|
||||
state.频谱仪状态.stop.value = state.频谱仪状态.center.value;
|
||||
state.频谱仪状态.stop.unit = state.频谱仪状态.center.unit;
|
||||
state.频谱仪状态.span.zeroSpan = true;
|
||||
console.debug('设置为 Zero Span');
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// Last Span
|
||||
state.频谱仪状态.start.value = state.频谱仪状态.span.lastSpan.start;
|
||||
state.频谱仪状态.start.unit = state.频谱仪状态.span.lastSpan.unit;
|
||||
state.频谱仪状态.stop.value = state.频谱仪状态.span.lastSpan.stop;
|
||||
state.频谱仪状态.stop.unit = state.频谱仪状态.span.lastSpan.unit;
|
||||
state.频谱仪状态.center.value = (state.频谱仪状态.start.value + state.频谱仪状态.stop.value) / 2;
|
||||
state.频谱仪状态.center.unit = state.频谱仪状态.span.lastSpan.unit;
|
||||
state.频谱仪状态.span.zeroSpan = false;
|
||||
console.debug('恢复到 Last Span');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.debug('跨度菜单列表 - 这个按键还没有实现');
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[E_选项卡菜单.幅度菜单列表]: () => {
|
||||
switch (按钮序号) {
|
||||
case 1: {
|
||||
// Ref Level
|
||||
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 = E_幅度菜单列表的项目.RefLevel;
|
||||
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = null;
|
||||
console.debug('设置 幅度输入状态为 RefLevel');
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Attenuation
|
||||
console.debug('点击了 幅度菜单列表 - Attenuation (功能待实现)');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.debug('幅度菜单列表 - 这个按键还没有实现');
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[E_选项卡菜单.幅度单位列表]: () => {
|
||||
let selectedUnit: E_幅度单位 | null = null;
|
||||
switch (按钮序号) {
|
||||
case 1: {
|
||||
selectedUnit = E_幅度单位.dBm;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
selectedUnit = E_幅度单位.mV;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
selectedUnit = E_幅度单位.uV;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
selectedUnit = E_幅度单位.uA;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.warn('无效的幅度单位按钮序号:', 按钮序号);
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const tempInputString = state.频谱仪状态.幅度输入状态.输入的值临时输入字符串;
|
||||
const selectedAmpItem = state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目;
|
||||
|
||||
if (!tempInputString || !selectedAmpItem) {
|
||||
console.warn('幅度输入未完成或未选择项目,无法确认单位。');
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
const inputValue = Number.parseFloat(tempInputString);
|
||||
if (Number.isNaN(inputValue)) {
|
||||
console.warn('无法将临时输入解析为有效数字 (幅度):', tempInputString);
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('确认幅度单位:', selectedUnit, '值为:', inputValue);
|
||||
if (selectedAmpItem === E_幅度菜单列表的项目.RefLevel) {
|
||||
state.频谱仪状态.幅度输入状态.refLevel.value = inputValue;
|
||||
state.频谱仪状态.幅度输入状态.refLevel.unit = selectedUnit;
|
||||
console.debug('更新 RefLevel:', state.频谱仪状态.幅度输入状态.refLevel);
|
||||
}
|
||||
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 = null;
|
||||
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = null;
|
||||
state.选项卡当前显示 = E_选项卡菜单.幅度菜单列表;
|
||||
console.debug('幅度输入状态已重置, 切换回幅度菜单列表');
|
||||
},
|
||||
});
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
function 执行点击数字按钮(按钮值: E_数字键盘按键) {
|
||||
console.group('🔢 点击数字按钮', { 按钮值 });
|
||||
|
||||
const isFreqInputActive = !!state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目;
|
||||
const isAmpInputActive = !!state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目;
|
||||
|
||||
if (!isFreqInputActive && !isAmpInputActive) {
|
||||
console.warn('请先选择一个频率或幅度菜单项');
|
||||
console.groupEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
let 当前临时输入: string;
|
||||
let 更新临时输入回调: (newVal: null | string) => void;
|
||||
let 后续操作菜单: E_选项卡菜单;
|
||||
|
||||
if (isFreqInputActive) {
|
||||
当前临时输入 = state.频谱仪状态.频率输入状态.输入的值临时输入字符串 ?? '';
|
||||
更新临时输入回调 = (newVal) => {
|
||||
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 = newVal;
|
||||
};
|
||||
后续操作菜单 = E_选项卡菜单.频率单位列表;
|
||||
} else {
|
||||
// isAmpInputActive
|
||||
当前临时输入 = state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 ?? '';
|
||||
更新临时输入回调 = (newVal) => {
|
||||
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 = newVal;
|
||||
};
|
||||
后续操作菜单 = E_选项卡菜单.幅度单位列表;
|
||||
}
|
||||
|
||||
switch (按钮值) {
|
||||
case E_数字键盘按键.Dot: {
|
||||
// 只有在当前临时输入不包含小数点时才允许添加
|
||||
if (当前临时输入.includes('.')) {
|
||||
console.debug('已存在小数点,忽略本次输入');
|
||||
} else {
|
||||
// 如果当前为空,则为 '0.',否则追加 '.'
|
||||
更新临时输入回调(当前临时输入 ? `${当前临时输入}.` : '0.');
|
||||
console.debug(
|
||||
'输入小数点,临时字符串:',
|
||||
isFreqInputActive
|
||||
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
|
||||
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case E_数字键盘按键.Num0:
|
||||
case E_数字键盘按键.Num1:
|
||||
case E_数字键盘按键.Num2:
|
||||
case E_数字键盘按键.Num3:
|
||||
case E_数字键盘按键.Num4:
|
||||
case E_数字键盘按键.Num5:
|
||||
case E_数字键盘按键.Num6:
|
||||
case E_数字键盘按键.Num7:
|
||||
case E_数字键盘按键.Num8:
|
||||
case E_数字键盘按键.Num9: {
|
||||
if (当前临时输入 === '0') {
|
||||
更新临时输入回调(String(按钮值));
|
||||
} else if (当前临时输入 === '-0') {
|
||||
更新临时输入回调(`-${String(按钮值)}`);
|
||||
} else {
|
||||
更新临时输入回调(当前临时输入 + String(按钮值));
|
||||
}
|
||||
console.debug(
|
||||
'输入数字,临时字符串:',
|
||||
isFreqInputActive
|
||||
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
|
||||
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case E_数字键盘按键.PlusMinus: {
|
||||
if (当前临时输入.startsWith('-')) {
|
||||
更新临时输入回调(当前临时输入.slice(1));
|
||||
} else if (当前临时输入 && 当前临时输入 !== '0' && 当前临时输入 !== '0.') {
|
||||
更新临时输入回调(`-${当前临时输入}`);
|
||||
} else if (当前临时输入 === '' || 当前临时输入 === '0' || 当前临时输入 === '0.') {
|
||||
更新临时输入回调('-');
|
||||
} else {
|
||||
console.debug('无法切换正负号');
|
||||
}
|
||||
console.debug(
|
||||
'切换正负号,临时字符串:',
|
||||
isFreqInputActive
|
||||
? state.频谱仪状态.频率输入状态.输入的值临时输入字符串
|
||||
: state.频谱仪状态.幅度输入状态.输入的值临时输入字符串,
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.warn('未知的数字键盘按键:', 按钮值);
|
||||
}
|
||||
}
|
||||
state.选项卡当前显示 = 后续操作菜单;
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
const img_modules = import.meta.glob<{ default: string }>('./imgs/*.png', {
|
||||
eager: true,
|
||||
});
|
||||
const 屏幕照片 = ref(img_modules['./imgs/屏幕_0.png']!.default);
|
||||
watch(
|
||||
() => state.频谱仪状态.center.value,
|
||||
() => {
|
||||
屏幕照片.value = img_modules['./imgs/屏幕_1.png']!.default;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => state.频谱仪状态.start.value,
|
||||
() => {
|
||||
屏幕照片.value = img_modules['./imgs/屏幕_2.png']!.default;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => state.频谱仪状态.stop.value,
|
||||
() => {
|
||||
屏幕照片.value = img_modules['./imgs/屏幕_3.png']!.default;
|
||||
},
|
||||
);
|
||||
watchEffect(() => {
|
||||
console.debug(`屏幕照片.value :>> `, 屏幕照片.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <pre
|
||||
v-if="$__DEV__"
|
||||
class="fixed bottom-10 right-10 top-10 z-[9999] overflow-auto overflow-y-auto bg-white p-[10px] text-[12px] text-black"
|
||||
>{{ { 选项卡菜单7个: $enum(E_选项卡菜单).getValues(), ...state } }}</pre
|
||||
> -->
|
||||
|
||||
<div class="wrp relative">
|
||||
<img :src="设备照片" alt="频谱仪设备" />
|
||||
|
||||
<div
|
||||
class="absolute left-[640px] top-[174px] flex w-[55px] flex-col gap-y-[4px] overflow-visible text-black"
|
||||
data-box="屏幕右边的按钮"
|
||||
>
|
||||
<!-- 上下还有一个 ESC / RETURN -->
|
||||
<ul class="flex h-[300px] flex-col justify-around gap-y-[4px] overflow-visible" data-box="屏幕右边按钮列表">
|
||||
<template v-for="n in 7" :key="`按钮${n}`">
|
||||
<li
|
||||
class="lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-visible rounded-[2px] text-[12px]"
|
||||
@click="执行点击屏幕右边的按钮(n)"
|
||||
>
|
||||
<button class="ppy-btn !h-full !w-full"></button>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="absolute left-[195px] top-[140px] h-[350px] w-[430px] bg-black" data-box="屏幕">
|
||||
<img :src="屏幕照片" alt="频谱仪屏幕" class="absolute left-0 top-0 !h-[330px] !w-[340px]" />
|
||||
|
||||
<!-- -->
|
||||
<!-- 屏幕左上角 Ref Level -->
|
||||
<div class="absolute left-[5px] top-[20px] text-left text-[10px] text-white" data-box="屏幕左上角RefLevel">
|
||||
<span>
|
||||
Ref {{ state.频谱仪状态.幅度输入状态.refLevel.value }}
|
||||
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-0 left-0 right-[85px] flex justify-between text-white" data-box="下面左Start 右Stop">
|
||||
<div class="flex-1 text-left">
|
||||
<span>Start </span>
|
||||
<span>{{ state.频谱仪状态.start.value }}</span>
|
||||
<span>{{ state.频谱仪状态.start.unit }}</span>
|
||||
</div>
|
||||
<div class="flex-1 text-right">
|
||||
<span>Stop </span>
|
||||
<span>{{ state.频谱仪状态.stop.value }}</span>
|
||||
<span>{{ state.频谱仪状态.stop.unit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 ||
|
||||
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目
|
||||
"
|
||||
class="lh-[1.2] absolute left-[40px] top-[140px] flex flex-col gap-y-[0px] bg-gray-500/100 text-white"
|
||||
data-box="正在输入的值"
|
||||
>
|
||||
<!-- 标题:显示当前选中的输入项 -->
|
||||
<div v-if="state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目">
|
||||
{{ state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 }}
|
||||
</div>
|
||||
<div v-else-if="state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目">
|
||||
{{ state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 }}
|
||||
</div>
|
||||
|
||||
<!-- 值:优先显示临时输入字符串,如果为空,则显示已确认的值 -->
|
||||
<div>
|
||||
<template
|
||||
v-if="
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 &&
|
||||
state.频谱仪状态.频率输入状态.输入的值临时输入字符串 !== null
|
||||
"
|
||||
>
|
||||
{{ state.频谱仪状态.频率输入状态.输入的值临时输入字符串 }}
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 &&
|
||||
state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 !== null
|
||||
"
|
||||
>
|
||||
{{ state.频谱仪状态.幅度输入状态.输入的值临时输入字符串 }}
|
||||
</template>
|
||||
<!-- 已确认的频率值 -->
|
||||
<template
|
||||
v-else-if="
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.CenterFreq
|
||||
"
|
||||
>
|
||||
{{ state.频谱仪状态.center.value }}
|
||||
{{ state.频谱仪状态.center.unit }}
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.StartFreq
|
||||
"
|
||||
>
|
||||
{{ state.频谱仪状态.start.value }} {{ state.频谱仪状态.start.unit }}
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
state.频谱仪状态.频率输入状态.选中的频率菜单列表的项目 === E_选项卡菜单Freq菜单列表的项目.StopFreq
|
||||
"
|
||||
>
|
||||
{{ state.频谱仪状态.stop.value }} {{ state.频谱仪状态.stop.unit }}
|
||||
</template>
|
||||
<!-- 已确认的幅度值 -->
|
||||
<template
|
||||
v-else-if="state.频谱仪状态.幅度输入状态.选中的幅度菜单列表的项目 === E_幅度菜单列表的项目.RefLevel"
|
||||
>
|
||||
{{ state.频谱仪状态.幅度输入状态.refLevel.value }}
|
||||
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
|
||||
</template>
|
||||
<template v-else>
|
||||
0.000000000
|
||||
<!-- 默认或回退显示 -->
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute bottom-[15px] right-0 top-0 flex w-[80px] flex-col gap-y-[4px] text-black"
|
||||
data-box="屏幕右侧的选项卡"
|
||||
>
|
||||
<div class="menu-item lh-[30px] h-[30px] overflow-hidden bg-[#AFAFAF] text-center" data-box="菜单标题">
|
||||
{{ 选项卡标题 }}
|
||||
</div>
|
||||
|
||||
<ul
|
||||
v-if="state.选项卡当前显示 === E_选项卡菜单.频率菜单列表"
|
||||
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
|
||||
data-box="频率菜单列表"
|
||||
>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>Center Freq</span>
|
||||
{{ state.频谱仪状态.center.value }}
|
||||
{{ state.频谱仪状态.center.unit }}
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>Start Freq</span>
|
||||
{{ state.频谱仪状态.start.value }}
|
||||
{{ state.频谱仪状态.start.unit }}
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>Stop Freq</span>
|
||||
{{ state.频谱仪状态.stop.value }}
|
||||
{{ state.频谱仪状态.stop.unit }}
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>#CF Step</span>
|
||||
<span>0.0000 hz</span>
|
||||
<span>X</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>#Freq Offset</span>
|
||||
<span>0.0000 Hz</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>#Singnal Track</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>#Scale Type</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul
|
||||
v-if="state.选项卡当前显示 === E_选项卡菜单.频率单位列表"
|
||||
class="flex flex-1 flex-col justify-around gap-y-[4px]"
|
||||
data-box="FreqChannel单位列表"
|
||||
>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>GHz</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>MHz</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>KHz</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>Hz</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
</ul>
|
||||
|
||||
<ul
|
||||
v-if="state.选项卡当前显示 === E_选项卡菜单.跨度菜单列表"
|
||||
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
|
||||
data-box="跨度菜单列表"
|
||||
>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_跨度菜单列表的项目.FullSpan }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_跨度菜单列表的项目.ZeroSpan }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_跨度菜单列表的项目.LastSpan }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
</ul>
|
||||
|
||||
<ul
|
||||
v-if="state.选项卡当前显示 === E_选项卡菜单.幅度菜单列表"
|
||||
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
|
||||
data-box="幅度菜单列表"
|
||||
>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>Ref Level</span>
|
||||
<span>
|
||||
{{ state.频谱仪状态.幅度输入状态.refLevel.value }}
|
||||
{{ state.频谱仪状态.幅度输入状态.refLevel.unit }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>#Attenuation</span>
|
||||
<!-- <span>TODO</span> -->
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
</ul>
|
||||
|
||||
<ul
|
||||
v-if="state.选项卡当前显示 === E_选项卡菜单.幅度单位列表"
|
||||
class="flex h-[300px] flex-col justify-around gap-y-[4px]"
|
||||
data-box="幅度单位列表"
|
||||
>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_幅度单位.dBm }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_幅度单位.mV }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_幅度单位.uV }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]">
|
||||
<span>{{ E_幅度单位.uA }}</span>
|
||||
</li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
<li class="menu-item lh-[1.2] flex flex-1 flex-col gap-y-[0px] overflow-hidden bg-[#AFAFAF]"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ppy-btns absolute left-[768px] top-[120px] h-[415px] !overflow-visible" data-box="机器右边按键">
|
||||
<!-- 左上角. 长的 -->
|
||||
<div class="flex flex-col gap-y-[18px] !overflow-visible">
|
||||
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.频率菜单列表">
|
||||
FREQ<br />Channel
|
||||
</button>
|
||||
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.跨度菜单列表">
|
||||
SPAN<br />X Scale
|
||||
</button>
|
||||
<button class="ppy-btn !w-[62px]" @click="state.选项卡当前显示 = E_选项卡菜单.幅度菜单列表">
|
||||
AMPLITUDE<br />Y Scale
|
||||
</button>
|
||||
<button class="ppy-btn !w-[62px] opacity-0">#</button>
|
||||
<button class="ppy-btn !w-[62px] opacity-0">#</button>
|
||||
</div>
|
||||
<div class="mt-[20px] grid grid-cols-3 gap-[16.5px] !overflow-visible" data-box="数字键盘区域">
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num7)">7</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num8)">8</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num9)">9</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num4)">4</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num5)">5</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num6)">6</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num1)">1</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num2)">2</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num3)">3</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Num0)">0</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.Dot)">.</button>
|
||||
<button class="ppy-btn" @click="执行点击数字按钮(E_数字键盘按键.PlusMinus)">+/-</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@import url('./Helvetica.css');
|
||||
|
||||
.wrp {
|
||||
font-family: 'Helvetica Custom';
|
||||
}
|
||||
|
||||
/* .wrp * {
|
||||
overflow: hidden;
|
||||
} */
|
||||
|
||||
img[alt='频谱仪设备'] {
|
||||
width: calc(2554px * 0.5);
|
||||
max-width: fit-content;
|
||||
height: calc(1626px * 0.5);
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
.ppy-btn {
|
||||
display: flex; /* 使用 flex 居中文本 */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 30px;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
color: #333; /* 深灰色文字 */
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
background-color: #e0e0e0; /* 按钮浅灰色背景 */
|
||||
border: 1px solid green;
|
||||
border: 1px solid #b0b0b0; /* 添加边框 */
|
||||
border-radius: 4px;
|
||||
box-shadow:
|
||||
1px 1px 2px rgb(0 0 0 / 20%),
|
||||
/* 轻微外阴影 */ -1px -1px 2px rgb(255 255 255 / 80%); /* 轻微外高光 */
|
||||
|
||||
transition: all 0.1s ease-in-out; /* 平滑过渡效果 */
|
||||
|
||||
/* width: 100%; */
|
||||
|
||||
/* 宽度占满 grid 单元格 */
|
||||
|
||||
/* height: 100%; */
|
||||
|
||||
/* 高度占满 grid 单元格 */
|
||||
|
||||
/* font-size: 20px; */
|
||||
|
||||
/* 调整字体大小 */
|
||||
|
||||
/* font-weight: bold; */
|
||||
|
||||
/* 字体加粗 */
|
||||
|
||||
/* border-radius: 4px; */
|
||||
|
||||
/* 按钮圆角 */
|
||||
}
|
||||
|
||||
.ppy-btn:active {
|
||||
background-color: #d5d5d5;
|
||||
border-color: #a0a0a0;
|
||||
box-shadow:
|
||||
inset 1px 1px 3px rgb(0 0 0 / 30%),
|
||||
inset -1px -1px 3px rgb(255 255 255 / 60%);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
/* @apply bg-gray-200/50 p-[2px]; */
|
||||
font-size: 12px;
|
||||
|
||||
/* border: 1px solid white; */
|
||||
}
|
||||
|
||||
[alt='频谱仪设备'] {
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
/* li,
|
||||
.ppy-btns,
|
||||
[data-box='屏幕'],
|
||||
[data-box='菜单标题'] {
|
||||
box-shadow: 0 0 0 1px rgba(255, 0, 0, 0.5);
|
||||
} */
|
||||
</style>
|
Before Width: | Height: | Size: 447 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 705 KiB |
Before Width: | Height: | Size: 810 KiB |
@ -18,32 +18,32 @@ const readyPromise = new Promise<void>((resolve) => {
|
||||
});
|
||||
|
||||
const 频谱瀑布图Layout = {
|
||||
title: { text: '频谱瀑布图' },
|
||||
title: '频谱瀑布图',
|
||||
xaxis: {
|
||||
title: { text: '频率 (Hz)' },
|
||||
title: '频率 (Hz)',
|
||||
// range: [0, 22_050],
|
||||
showgrid: false,
|
||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||
},
|
||||
yaxis: {
|
||||
title: { text: '时间步' },
|
||||
title: '时间步',
|
||||
showgrid: false,
|
||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||
},
|
||||
margin: { l: 60, r: 40, b: 40, t: 60 },
|
||||
} satisfies Partial<import('plotly.js-dist-min').Layout>;
|
||||
};
|
||||
|
||||
const 频谱图Layout = {
|
||||
title: { text: '频谱图' },
|
||||
title: '频谱图',
|
||||
xaxis: {
|
||||
title: { text: '频率 (Hz)' },
|
||||
title: '频率 (Hz)',
|
||||
// range: [0, 22_050],
|
||||
showgrid: true,
|
||||
gridcolor: '#eee',
|
||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||
},
|
||||
yaxis: {
|
||||
title: { text: '幅度 (dB)' },
|
||||
title: '幅度 (dB)',
|
||||
showgrid: true, // 显示网格线
|
||||
gridcolor: '#eee',
|
||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||
|
@ -1,321 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
|
||||
import { createGetRoutes } from '@/plugins/router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 响应式断点检测
|
||||
const isMobile = ref(false);
|
||||
const isTablet = ref(false);
|
||||
|
||||
// 检测屏幕尺寸
|
||||
const updateScreenSize = () => {
|
||||
const width = window.innerWidth;
|
||||
const wasMobile = isMobile.value;
|
||||
|
||||
isMobile.value = width < 768;
|
||||
isTablet.value = width >= 768 && width < 1024;
|
||||
|
||||
// 当从移动端切换到桌面端时,关闭抽屉并重置折叠状态
|
||||
if (wasMobile && !isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
collapsed.value = false;
|
||||
}
|
||||
// 当从桌面端切换到移动端时,关闭抽屉
|
||||
else if (!wasMobile && isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 侧边栏状态管理
|
||||
const collapsed = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// 初始化屏幕尺寸检测
|
||||
onMounted(() => {
|
||||
updateScreenSize();
|
||||
window.addEventListener('resize', updateScreenSize);
|
||||
|
||||
// 移动端默认收起侧边栏
|
||||
if (isMobile.value) {
|
||||
collapsed.value = true;
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateScreenSize);
|
||||
});
|
||||
|
||||
// 菜单项类型定义
|
||||
type MenuItemWithRoute = MenuOption & {
|
||||
routeName?: string;
|
||||
parentId?: string;
|
||||
originalPath?: string;
|
||||
};
|
||||
|
||||
// 生成菜单项
|
||||
const menuOptions = computed(() => {
|
||||
let flatArray: MenuItemWithRoute[] = createGetRoutes(router)()
|
||||
.filter((route) => !route.path.includes('/:'))
|
||||
.filter((route) => !route.meta.hidden)
|
||||
.map((route) => ({
|
||||
key: route.path,
|
||||
label: route.meta.title || `${(route.name as string) || route.path}`,
|
||||
routeName: route.name as string,
|
||||
}));
|
||||
|
||||
flatArray = flatArray.map((item) => {
|
||||
const originalPath = item.key as string; // 保存原始路径
|
||||
let id = item.key as string;
|
||||
if (flatArray.some((item) => (item.key as string).startsWith(`${id}/`))) {
|
||||
id = `${id}/index`;
|
||||
}
|
||||
// 去掉最前面的 /
|
||||
id = id.replace(/^\//, '');
|
||||
|
||||
let parentId = id.replace(/\/[^/]+$/, '');
|
||||
if (parentId === id) {
|
||||
parentId = '_ROOT_';
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
key: id,
|
||||
parentId,
|
||||
originalPath, // 保存原始路径用于后续映射
|
||||
};
|
||||
});
|
||||
|
||||
const groupItems: MenuItemWithRoute[] = [];
|
||||
for (const flatArrayItem of flatArray) {
|
||||
if (!groupItems.some((item) => item.key === flatArrayItem.parentId) && flatArrayItem.parentId !== '_ROOT_') {
|
||||
let groupItemParentId = flatArrayItem.parentId!.replace(/\/[^/]+$/, '');
|
||||
if (groupItemParentId === flatArrayItem.parentId) groupItemParentId = '_ROOT_';
|
||||
groupItems.push({
|
||||
key: flatArrayItem.parentId!,
|
||||
label: `Group ${flatArrayItem.parentId}`,
|
||||
parentId: groupItemParentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tree = arrayToTree([...flatArray, ...groupItems], {
|
||||
id: 'key',
|
||||
parentId: 'parentId',
|
||||
rootId: '_ROOT_',
|
||||
});
|
||||
|
||||
// 递归转换树形结构为 naive-ui menu 格式
|
||||
function convertToMenuOptions(tree: MenuItemWithRoute[]): MenuOption[] {
|
||||
return tree.map((item) => {
|
||||
const menuItem: MenuOption = {
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
};
|
||||
|
||||
if (item.children && item.children.length > 0) {
|
||||
menuItem.children = convertToMenuOptions(item.children);
|
||||
} else if (item.routeName) {
|
||||
// 叶子节点,存储路由映射
|
||||
menuRouteMap.set(item.key as string, item.routeName);
|
||||
// 同时存储路径到 key 的映射(用于高亮显示)
|
||||
if (item.originalPath) {
|
||||
pathToKeyMap.set(item.originalPath, item.key as string);
|
||||
}
|
||||
(menuItem as MenuItemWithRoute).routeName = item.routeName;
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
});
|
||||
}
|
||||
|
||||
// 清空之前的映射
|
||||
menuRouteMap.clear();
|
||||
pathToKeyMap.clear();
|
||||
const result = convertToMenuOptions(tree);
|
||||
|
||||
// 菜单生成后,重新设置当前选中的菜单项
|
||||
nextTick(() => {
|
||||
const currentPath = router.currentRoute.value.path;
|
||||
const menuKey = pathToKeyMap.get(currentPath);
|
||||
if (menuKey) {
|
||||
selectedKey.value = menuKey;
|
||||
} else {
|
||||
const pathWithoutSlash = currentPath.replace(/^\//, '');
|
||||
selectedKey.value = pathWithoutSlash;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// 当前选中的菜单项
|
||||
const selectedKey = ref<string>();
|
||||
|
||||
// 存储菜单项与路由名称的映射
|
||||
const menuRouteMap = new Map<string, string>();
|
||||
|
||||
// 存储路由路径与菜单 key 的映射(用于高亮显示)
|
||||
const pathToKeyMap = new Map<string, string>();
|
||||
|
||||
// 处理菜单点击
|
||||
const handleMenuSelect = (key: string, item: MenuOption) => {
|
||||
const routeName = menuRouteMap.get(key) || (item as MenuItemWithRoute).routeName;
|
||||
if (routeName) {
|
||||
router.push({ name: routeName as never });
|
||||
|
||||
// 移动端点击菜单项后自动收起侧边栏
|
||||
if (isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 监听路由变化,更新选中的菜单项
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(newPath) => {
|
||||
// 使用路径到 key 的映射来找到对应的菜单项
|
||||
const menuKey = pathToKeyMap.get(newPath);
|
||||
if (menuKey) {
|
||||
selectedKey.value = menuKey;
|
||||
} else {
|
||||
// 如果没有找到精确匹配,尝试去掉前面的 / 再匹配
|
||||
const pathWithoutSlash = newPath.replace(/^\//, '');
|
||||
selectedKey.value = pathWithoutSlash;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 切换侧边栏状态
|
||||
const toggleSidebar = () => {
|
||||
if (isMobile.value) {
|
||||
// 移动端使用抽屉模式
|
||||
drawerVisible.value = !drawerVisible.value;
|
||||
} else {
|
||||
// 桌面端使用折叠模式
|
||||
collapsed.value = !collapsed.value;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-layout :has-sider="!isMobile">
|
||||
<!-- 移动端抽屉 -->
|
||||
<n-drawer
|
||||
v-if="isMobile"
|
||||
v-model:show="drawerVisible"
|
||||
:width="280"
|
||||
placement="left"
|
||||
:trap-focus="false"
|
||||
:block-scroll="false"
|
||||
>
|
||||
<n-drawer-content title="菜单" :native-scrollbar="false">
|
||||
<n-menu :options="menuOptions" :value="selectedKey" @update:value="handleMenuSelect" />
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
|
||||
<!-- 桌面端侧边栏 -->
|
||||
<n-layout-sider
|
||||
v-if="!isMobile"
|
||||
:collapsed="collapsed"
|
||||
:native-scrollbar="false"
|
||||
bordered
|
||||
collapse-mode="width"
|
||||
:collapsed-width="64"
|
||||
:width="240"
|
||||
show-trigger
|
||||
@collapse="collapsed = true"
|
||||
@expand="collapsed = false"
|
||||
>
|
||||
<n-menu
|
||||
:collapsed="collapsed"
|
||||
:collapsed-width="64"
|
||||
:collapsed-icon-size="22"
|
||||
:options="menuOptions"
|
||||
:value="selectedKey"
|
||||
@update:value="handleMenuSelect"
|
||||
/>
|
||||
</n-layout-sider>
|
||||
|
||||
<n-layout>
|
||||
<n-layout-header
|
||||
bordered
|
||||
:style="{
|
||||
height: '64px',
|
||||
padding: isMobile ? '0 16px' : '0 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<n-button
|
||||
quaternary
|
||||
@click="toggleSidebar"
|
||||
:style="{
|
||||
marginRight: isMobile ? '8px' : '12px',
|
||||
padding: isMobile ? '8px' : '6px',
|
||||
}"
|
||||
:size="isMobile ? 'medium' : 'small'"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon :size="isMobile ? 20 : 18">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
v-if="!isMobile && collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"
|
||||
/>
|
||||
<path
|
||||
v-else-if="!isMobile && !collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h13v-2H3v2zm0-5h10v-2H3v2zm0-7v2h13V6H3zm18 9.59L17.42 12 21 8.41 19.59 7l-5 5 5 5L21 15.59z"
|
||||
/>
|
||||
<!-- 移动端始终显示菜单图标 -->
|
||||
<path v-else fill="currentColor" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
|
||||
</svg>
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<span
|
||||
:style="{
|
||||
fontSize: isMobile ? '16px' : '18px',
|
||||
fontWeight: '500',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}"
|
||||
>
|
||||
Vue TS Example
|
||||
</span>
|
||||
</n-layout-header>
|
||||
|
||||
<n-layout-content
|
||||
:content-style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
minHeight: 'calc(100vh - 64px - 72px)', // 减去头部和底部高度
|
||||
}"
|
||||
>
|
||||
<router-view />
|
||||
</n-layout-content>
|
||||
|
||||
<n-layout-footer
|
||||
bordered
|
||||
:style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
textAlign: 'center',
|
||||
}"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
color: 'var(--n-text-color-disabled)',
|
||||
fontSize: isMobile ? '12px' : '14px',
|
||||
}"
|
||||
>
|
||||
© 2025 Vue TS Example. All rights reserved.
|
||||
</span>
|
||||
</n-layout-footer>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</template>
|
@ -1,12 +1,7 @@
|
||||
import './styles';
|
||||
|
||||
import { LogLevels } from 'consola';
|
||||
|
||||
import App from './App.vue';
|
||||
import { setupPlugins } from './plugins';
|
||||
|
||||
const autoInstallModules = import.meta.glob('./plugins/*.ts', { eager: true });
|
||||
|
||||
setupPlugins(createApp(App), autoInstallModules).mount('#app');
|
||||
|
||||
consola.level = LogLevels.verbose;
|
||||
|
@ -1,3 +0,0 @@
|
||||
# 测试
|
||||
|
||||
这个文件是被 import 的
|
@ -1,11 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import MD from './MDPageImportMD.md';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<MD />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -1,13 +0,0 @@
|
||||
<!--
|
||||
http://localhost:4730/Page/PinPuYi
|
||||
-->
|
||||
<script setup lang="ts">
|
||||
definePage({ meta: { title: '频谱仪', hidden: !$__DEV__ } });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="true">
|
||||
<PinPuYi />
|
||||
</template>
|
||||
<template v-else> 🤡 </template>
|
||||
</template>
|
@ -1,352 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { VueUiXy, type VueUiXyConfig, type VueUiXyDatasetItem } from 'vue-data-ui';
|
||||
import 'vue-data-ui/style.css'; // If you are using multiple components, place styles import in your main
|
||||
|
||||
const config = computed<VueUiXyConfig>(() => {
|
||||
return {
|
||||
theme: undefined,
|
||||
responsive: false,
|
||||
customPalette: [],
|
||||
useCssAnimation: true,
|
||||
downsample: {
|
||||
threshold: 500,
|
||||
},
|
||||
chart: {
|
||||
fontFamily: 'inherit',
|
||||
backgroundColor: '#FFFFFFff',
|
||||
color: '#1A1A1Aff',
|
||||
height: 600,
|
||||
width: 1000,
|
||||
zoom: {
|
||||
show: true,
|
||||
color: '#CCCCCCff',
|
||||
highlightColor: '#4A4A4A',
|
||||
fontSize: 14,
|
||||
useResetSlot: false,
|
||||
startIndex: null,
|
||||
endIndex: null,
|
||||
enableRangeHandles: true,
|
||||
enableSelectionDrag: true,
|
||||
minimap: {
|
||||
show: true,
|
||||
smooth: false,
|
||||
selectedColor: '#1F77B4',
|
||||
selectedColorOpacity: 0.2,
|
||||
lineColor: '#1A1A1A',
|
||||
selectionRadius: 2,
|
||||
indicatorColor: '#1A1A1A',
|
||||
verticalHandles: false,
|
||||
},
|
||||
},
|
||||
padding: {
|
||||
top: 36,
|
||||
right: 24,
|
||||
bottom: 48,
|
||||
left: 48,
|
||||
},
|
||||
highlighter: {
|
||||
color: '#1A1A1Aff',
|
||||
opacity: 5,
|
||||
useLine: false,
|
||||
lineDasharray: 2,
|
||||
lineWidth: 1,
|
||||
},
|
||||
highlightArea: {
|
||||
show: false,
|
||||
from: 0,
|
||||
to: 0,
|
||||
color: '#CCCCCCff',
|
||||
opacity: 20,
|
||||
caption: {
|
||||
text: 'Caption',
|
||||
fontSize: 20,
|
||||
color: '#1A1A1Aff',
|
||||
bold: false,
|
||||
offsetY: 0,
|
||||
width: 'auto',
|
||||
padding: 3,
|
||||
textAlign: 'center',
|
||||
},
|
||||
},
|
||||
timeTag: {
|
||||
show: false,
|
||||
backgroundColor: '#e1e5e8ff',
|
||||
color: '#1A1A1Aff',
|
||||
fontSize: 12,
|
||||
circleMarker: {
|
||||
radius: 3,
|
||||
color: '#1A1A1Aff',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
stroke: '#e1e5e8ff',
|
||||
showVerticalLines: false,
|
||||
showHorizontalLines: false,
|
||||
position: 'middle',
|
||||
frame: {
|
||||
show: false,
|
||||
stroke: '#E1E5E8ff',
|
||||
strokeWidth: 2,
|
||||
strokeLinecap: 'round',
|
||||
strokeLinejoin: 'round',
|
||||
strokeDasharray: 0,
|
||||
},
|
||||
labels: {
|
||||
show: true,
|
||||
color: '#1A1A1Aff',
|
||||
fontSize: 16,
|
||||
axis: {
|
||||
yLabel: '',
|
||||
yLabelOffsetX: 0,
|
||||
xLabel: '',
|
||||
xLabelOffsetY: 14,
|
||||
fontSize: 12,
|
||||
},
|
||||
zeroLine: {
|
||||
show: true,
|
||||
},
|
||||
xAxis: {
|
||||
showBaseline: true,
|
||||
},
|
||||
yAxis: {
|
||||
showBaseline: true,
|
||||
commonScaleSteps: 10,
|
||||
useIndividualScale: false,
|
||||
stacked: false,
|
||||
gap: 12,
|
||||
labelWidth: 40,
|
||||
formatter: null,
|
||||
scaleMin: null,
|
||||
scaleMax: null,
|
||||
groupColor: '#1A1A1A',
|
||||
scaleLabelOffsetX: 0,
|
||||
scaleValueOffsetX: 0,
|
||||
},
|
||||
xAxisLabels: {
|
||||
color: '#1A1A1Aff',
|
||||
show: true,
|
||||
values: [],
|
||||
fontSize: 18,
|
||||
showOnlyFirstAndLast: false,
|
||||
showOnlyAtModulo: false,
|
||||
modulo: 12,
|
||||
yOffset: 8,
|
||||
rotation: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
comments: {
|
||||
show: true,
|
||||
showInTooltip: true,
|
||||
width: 200,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
labels: {
|
||||
fontSize: 16,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
},
|
||||
legend: {
|
||||
color: '#1A1A1Aff',
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
},
|
||||
title: {
|
||||
text: 'Title',
|
||||
color: '#1A1A1Aff',
|
||||
fontSize: 20,
|
||||
bold: true,
|
||||
textAlign: 'center',
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
subtitle: {
|
||||
color: '#CCCCCCff',
|
||||
text: '',
|
||||
fontSize: 16,
|
||||
bold: false,
|
||||
},
|
||||
show: true,
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
color: '#1A1A1Aff',
|
||||
backgroundColor: '#FFFFFFff',
|
||||
fontSize: 14,
|
||||
customFormat: null,
|
||||
borderRadius: 4,
|
||||
borderColor: '#e1e5e8',
|
||||
borderWidth: 1,
|
||||
backgroundOpacity: 30,
|
||||
position: 'center',
|
||||
offsetY: 24,
|
||||
showTimeLabel: true,
|
||||
showValue: true,
|
||||
showPercentage: false,
|
||||
roundingValue: 0,
|
||||
roundingPercentage: 0,
|
||||
},
|
||||
userOptions: {
|
||||
show: true,
|
||||
showOnChartHover: false,
|
||||
keepStateOnChartLeave: true,
|
||||
position: 'right',
|
||||
buttons: {
|
||||
tooltip: true,
|
||||
pdf: true,
|
||||
csv: true,
|
||||
img: true,
|
||||
table: true,
|
||||
labels: true,
|
||||
fullscreen: true,
|
||||
sort: false,
|
||||
stack: true,
|
||||
animation: false,
|
||||
annotator: true,
|
||||
},
|
||||
buttonTitles: {
|
||||
open: 'Open options',
|
||||
close: 'Close options',
|
||||
tooltip: 'Toggle tooltip',
|
||||
pdf: 'Download PDF',
|
||||
csv: 'Download CSV',
|
||||
img: 'Download PNG',
|
||||
table: 'Toggle table',
|
||||
labels: 'Toggle labels',
|
||||
fullscreen: 'Toggle fullscreen',
|
||||
stack: 'Toggle stack mode',
|
||||
annotator: 'Toggle annotator',
|
||||
},
|
||||
print: {
|
||||
allowTaint: false,
|
||||
backgroundColor: '#FFFFFFff',
|
||||
useCORS: false,
|
||||
onclone: null,
|
||||
scale: 2,
|
||||
logging: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
bar: {
|
||||
borderRadius: 2,
|
||||
useGradient: true,
|
||||
periodGap: 0.1,
|
||||
border: {
|
||||
useSerieColor: false,
|
||||
strokeWidth: 1,
|
||||
stroke: '#FFFFFFff',
|
||||
},
|
||||
labels: {
|
||||
show: true,
|
||||
offsetY: -8,
|
||||
rounding: 0,
|
||||
color: '#1A1A1Aff',
|
||||
formatter: null,
|
||||
},
|
||||
serieName: {
|
||||
show: false,
|
||||
offsetY: -6,
|
||||
useAbbreviation: true,
|
||||
abbreviationSize: 3,
|
||||
useSerieColor: true,
|
||||
color: '#1A1A1Aff',
|
||||
bold: false,
|
||||
},
|
||||
},
|
||||
line: {
|
||||
radius: 6,
|
||||
useGradient: false,
|
||||
strokeWidth: 2,
|
||||
cutNullValues: false,
|
||||
dot: {
|
||||
hideAboveMaxSerieLength: 62,
|
||||
useSerieColor: false,
|
||||
fill: '#FFFFFF',
|
||||
strokeWidth: 2,
|
||||
},
|
||||
labels: {
|
||||
show: true,
|
||||
offsetY: -16,
|
||||
rounding: 0,
|
||||
color: '#1A1A1Aff',
|
||||
formatter: null,
|
||||
},
|
||||
area: {
|
||||
useGradient: true,
|
||||
opacity: 20,
|
||||
},
|
||||
tag: {
|
||||
followValue: true,
|
||||
formatter: null,
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
plot: {
|
||||
radius: 6,
|
||||
useGradient: true,
|
||||
dot: {
|
||||
useSerieColor: true,
|
||||
fill: '#FFFFFF',
|
||||
strokeWidth: 0.5,
|
||||
},
|
||||
labels: {
|
||||
show: true,
|
||||
offsetY: -8,
|
||||
rounding: 0,
|
||||
color: '#1A1A1Aff',
|
||||
formatter: null,
|
||||
},
|
||||
tag: {
|
||||
followValue: true,
|
||||
formatter: null,
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
table: {
|
||||
responsiveBreakpoint: 400,
|
||||
rounding: 0,
|
||||
sparkline: true,
|
||||
showSum: true,
|
||||
columnNames: {
|
||||
period: 'Period',
|
||||
total: 'Total',
|
||||
},
|
||||
th: {
|
||||
backgroundColor: '#FAFAFAff',
|
||||
color: '#1A1A1Aff',
|
||||
outline: '',
|
||||
},
|
||||
td: {
|
||||
backgroundColor: '#FAFAFAff',
|
||||
color: '#1A1A1Aff',
|
||||
outline: '',
|
||||
},
|
||||
},
|
||||
showTable: false,
|
||||
};
|
||||
});
|
||||
|
||||
const dataset = computed<VueUiXyDatasetItem[]>(() => {
|
||||
return [
|
||||
{
|
||||
name: 'Serie name',
|
||||
series: [1, 9, 7, 2, 12, 16, 17, 30, 16, 23],
|
||||
color: '#1f77b4',
|
||||
type: 'line',
|
||||
shape: 'circle',
|
||||
useArea: false,
|
||||
useProgression: false,
|
||||
dataLabels: true,
|
||||
smooth: true,
|
||||
dashed: false,
|
||||
useTag: 'none',
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<!-- Using a wrapper is optional -->
|
||||
<div :style="{ width: '600px' }">
|
||||
<VueUiXy :config="config" :dataset="dataset" />
|
||||
</div>
|
||||
</template>
|
@ -27,7 +27,15 @@ import { Input as ShaInput } from '@/shadcn/components/ui/input';
|
||||
<ShadcnButton variant="ghost">Ghost</ShadcnButton>
|
||||
<ShadcnButton variant="link">Link</ShadcnButton>
|
||||
</div>
|
||||
<div class="demo-block">
|
||||
<VBtn>VBtn</VBtn>
|
||||
<v-btn variant="outlined"> Button </v-btn>
|
||||
<v-btn variant="tonal"> Button </v-btn>
|
||||
<v-btn variant="text"> Button </v-btn>
|
||||
<v-btn variant="plain"> Button </v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-block">
|
||||
<a-input placeholder="Ant Design" />
|
||||
<InputText placeholder="Primevue" />
|
||||
|
@ -1,11 +0,0 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-divider title-placement="left">
|
||||
<n-text depth="3"> Naive UI Components Divider </n-text>
|
||||
</n-divider>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -28,7 +28,7 @@ defineOptions({
|
||||
},
|
||||
});
|
||||
const refresh = () => {
|
||||
state.list.splice(0);
|
||||
state.list.splice(0, state.list.length);
|
||||
state.page = 0;
|
||||
state.complete = false;
|
||||
loadMore();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders';
|
||||
import { setupLayouts } from 'virtual:meta-layouts';
|
||||
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto';
|
||||
import { handleHotUpdate, routes } from 'vue-router/auto-routes';
|
||||
|
||||
const setupLayoutsResult = setupLayouts(routes);
|
||||
|
11
src/plugins/vuetify.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { createVuetify } from 'vuetify';
|
||||
import 'vuetify/styles';
|
||||
|
||||
export function install({ app }: { app: import('vue').App<Element> }) {
|
||||
const vuetify = createVuetify({
|
||||
// components,
|
||||
// directives,
|
||||
});
|
||||
|
||||
app.use(vuetify);
|
||||
}
|
@ -13,9 +13,4 @@ import 'primeicons/primeicons.css';
|
||||
import './reset/reset-primevue.css';
|
||||
import './reset/reset-antdv.less';
|
||||
|
||||
// 通用字体
|
||||
import 'vfonts/Lato.css';
|
||||
// 等宽字体
|
||||
import 'vfonts/FiraCode.css';
|
||||
//
|
||||
import 'virtual:uno.css';
|
||||
|
@ -1,11 +0,0 @@
|
||||
declare global {
|
||||
const $__DEV__: boolean;
|
||||
}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface ComponentCustomProperties {
|
||||
$__DEV__: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
@ -1,4 +1,12 @@
|
||||
declare module '*.vue' {
|
||||
declare global {
|
||||
const $__DEV__: boolean;
|
||||
}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface ComponentCustomProperties {
|
||||
$__DEV__: boolean;
|
||||
}
|
||||
|
||||
import type { ComponentOptions } from 'vue';
|
||||
|
||||
const Component: ComponentOptions;
|
||||
@ -11,3 +19,5 @@ declare module '*.md' {
|
||||
const Component: ComponentOptions;
|
||||
export default Component;
|
||||
}
|
||||
|
||||
export {};
|
4
typed-router.d.ts
vendored
@ -34,12 +34,9 @@ declare module 'vue-router/auto-routes' {
|
||||
'PageIframePageSpectrogram': RouteRecordInfo<'PageIframePageSpectrogram', '/Page/iframe-page/Spectrogram', Record<never, never>, Record<never, never>>,
|
||||
'PageJSPage': RouteRecordInfo<'PageJSPage', '/Page/JSPage', Record<never, never>, Record<never, never>>,
|
||||
'PageMDPage': RouteRecordInfo<'PageMDPage', '/Page/MDPage', Record<never, never>, Record<never, never>>,
|
||||
'PageMDPageImportMD': RouteRecordInfo<'PageMDPageImportMD', '/Page/MDPageImportMD', Record<never, never>, Record<never, never>>,
|
||||
'PageP5Js': RouteRecordInfo<'PageP5Js', '/Page/p5_js', Record<never, never>, Record<never, never>>,
|
||||
'PagePinPuYi': RouteRecordInfo<'PagePinPuYi', '/Page/PinPuYi', Record<never, never>, Record<never, never>>,
|
||||
'PageStyle': RouteRecordInfo<'PageStyle', '/Page/Style', Record<never, never>, Record<never, never>>,
|
||||
'PageViteAssets': RouteRecordInfo<'PageViteAssets', '/Page/vite-assets', Record<never, never>, Record<never, never>>,
|
||||
'PageVueDataUi': RouteRecordInfo<'PageVueDataUi', '/Page/vue-data-ui', Record<never, never>, Record<never, never>>,
|
||||
'PkgsUsageI18n': RouteRecordInfo<'PkgsUsageI18n', '/PkgsUsage/I18n', Record<never, never>, Record<never, never>>,
|
||||
'PkgsUsageTsEnumUtil': RouteRecordInfo<'PkgsUsageTsEnumUtil', '/PkgsUsage/ts-enum-util', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsAntdV': RouteRecordInfo<'UIComponentsAntdV', '/UI-components/AntdV', Record<never, never>, Record<never, never>>,
|
||||
@ -47,7 +44,6 @@ declare module 'vue-router/auto-routes' {
|
||||
'UIComponentsInfiniteLoading': RouteRecordInfo<'UIComponentsInfiniteLoading', '/UI-components/infinite-loading', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsInfiniteLoadingDetail': RouteRecordInfo<'UIComponentsInfiniteLoadingDetail', '/UI-components/infinite-loading/detail', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsInspiraUI': RouteRecordInfo<'UIComponentsInspiraUI', '/UI-components/InspiraUI', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsNaiveUI': RouteRecordInfo<'UIComponentsNaiveUI', '/UI-components/NaiveUI', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsPrimeVue': RouteRecordInfo<'UIComponentsPrimeVue', '/UI-components/PrimeVue', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsShadcnVue': RouteRecordInfo<'UIComponentsShadcnVue', '/UI-components/ShadcnVue', Record<never, never>, Record<never, never>>,
|
||||
'VueMacrosDefineRender': RouteRecordInfo<'VueMacrosDefineRender', '/VueMacros/DefineRender', Record<never, never>, Record<never, never>>,
|
||||
|
@ -11,11 +11,11 @@ import Vue from '@vitejs/plugin-vue';
|
||||
import VueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import path from 'node:path';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import UnpluginAutoImport from 'unplugin-auto-import/vite';
|
||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import { AntDesignVueResolver, NaiveUiResolver, TDesignResolver } from 'unplugin-vue-components/resolvers';
|
||||
import { AntDesignVueResolver, TDesignResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import VueMacros from 'unplugin-vue-macros/vite';
|
||||
import Markdown from 'unplugin-vue-markdown/vite';
|
||||
@ -25,12 +25,12 @@ import { createUtils4uAutoImports } from 'utils4u/auto-imports';
|
||||
import { type PluginOption } from 'vite';
|
||||
import { checker } from 'vite-plugin-checker';
|
||||
import { vitePluginFakeServer } from 'vite-plugin-fake-server';
|
||||
import VitePluginImageTools from 'vite-plugin-image-tools';
|
||||
import pluginPurgeCss from 'vite-plugin-purgecss-updated-v5';
|
||||
import { viteSingleFile } from 'vite-plugin-singlefile';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
import VueDevTools from 'vite-plugin-vue-devtools';
|
||||
import MetaLayouts from 'vite-plugin-vue-meta-layouts';
|
||||
import vuetify from 'vite-plugin-vuetify';
|
||||
import { ViteWebfontDownload } from 'vite-plugin-webfont-dl';
|
||||
|
||||
import { viteArchiverPlugin } from './vite.config.plugin.archiver';
|
||||
@ -66,8 +66,7 @@ export function Plugins() {
|
||||
|
||||
// https://github.com/dishait/vite-plugin-vue-meta-layouts
|
||||
MetaLayouts({
|
||||
// defaultLayout: 'sakai-vue/AppLayout',
|
||||
defaultLayout: 'naive-ui/AppLayout',
|
||||
defaultLayout: 'sakai-vue/AppLayout',
|
||||
skipTopLevelRouteLayout: false, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134,默认为 false 关闭
|
||||
}),
|
||||
|
||||
@ -80,8 +79,11 @@ export function Plugins() {
|
||||
headEnabled: true,
|
||||
}),
|
||||
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#automatic-imports
|
||||
vuetify({ autoImport: true /* styles: 'none' */ }), // Enabled by default
|
||||
|
||||
// https://github.com/antfu/unplugin-auto-import
|
||||
AutoImport({
|
||||
UnpluginAutoImport({
|
||||
dirs: [
|
||||
// 'src/composables',
|
||||
'src/stores',
|
||||
@ -99,7 +101,6 @@ export function Plugins() {
|
||||
{
|
||||
'consola/browser': ['consola'],
|
||||
'vue-router/auto': ['useLink'],
|
||||
'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
|
||||
},
|
||||
],
|
||||
resolvers: [TDesignResolver({ esm: true, library: 'mobile-vue' }), VantResolver({ importStyle: true })],
|
||||
@ -126,7 +127,6 @@ export function Plugins() {
|
||||
TDesignResolver({ esm: true, library: 'mobile-vue' }),
|
||||
VantResolver({ importStyle: true }),
|
||||
PrimeVueResolver(/* { components: { prefix: 'P' } } */),
|
||||
NaiveUiResolver(),
|
||||
],
|
||||
}),
|
||||
|
||||
@ -194,15 +194,6 @@ export function Plugins() {
|
||||
{ dest: cesiumBaseUrl, src: `${cesiumSource}/Widgets` },
|
||||
],
|
||||
}),
|
||||
|
||||
VitePluginImageTools({
|
||||
quality: 80,
|
||||
enableWebp: true,
|
||||
enableDev: false,
|
||||
enableDevWebp: false,
|
||||
// 排除字体 SVG 文件和可能有问题的 SVG 文件
|
||||
excludes: /fonts\/.*\.svg$|Helvetica.*\.svg$/,
|
||||
}),
|
||||
);
|
||||
|
||||
// 检查是否在VS Code终端中运行
|
||||
|
@ -36,9 +36,6 @@ export default defineConfig(({ command, mode }) => {
|
||||
'satellite.js',
|
||||
'ts-enum-util',
|
||||
'unplugin-vue-router',
|
||||
'unplugin-vue-router/runtime',
|
||||
'unplugin-vue-router/data-loaders/basic',
|
||||
'unplugin-vue-router/data-loaders/pinia-colada',
|
||||
],
|
||||
exclude: ['quill', 'chart.js/auto'],
|
||||
},
|
||||
@ -56,7 +53,7 @@ export default defineConfig(({ command, mode }) => {
|
||||
// https://cn.rollupjs.org/configuration-options/#output-assetfilenames
|
||||
// output: env.VITE_SPLIT_CHUNKS === 'true' ? (await import('utils4u/rollup')).createSplitChunkOutput() : undefined,
|
||||
output: {
|
||||
// minifyInternalExports: false,
|
||||
minifyInternalExports: false,
|
||||
// manualChunks: {
|
||||
// 'vendor/utils4u': ['utils4u', 'utils4u/vue-use', 'utils4u/primevue'],
|
||||
// 'vendor/vue': ['vue'],
|
||||
@ -70,14 +67,6 @@ export default defineConfig(({ command, mode }) => {
|
||||
manualChunks: {
|
||||
'vendor/Cesium': ['cesium'],
|
||||
},
|
||||
// advancedChunks: {
|
||||
// groups: [
|
||||
// {
|
||||
// name: 'vendor/cesium',
|
||||
// test: 'cesium',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
},
|
||||
sourcemap: mode !== 'production' || env.VITE_SOURCE_MAP === 'true',
|
||||
|