Compare commits
185 Commits
Author | SHA1 | Date | |
---|---|---|---|
383d8deead | |||
fcf09d887b | |||
145baf5e16 | |||
866145591f | |||
33528d64a7 | |||
30559d749e | |||
57d07d3cd0 | |||
11f4587681 | |||
669cd7070f | |||
5487dc321e | |||
ec4906f441 | |||
ad8c187edd | |||
997df3a4d4 | |||
42560a4f2c | |||
8f9593957a | |||
0d26da85a1 | |||
a831d12cf8 | |||
55a634db68 | |||
ec7c877c93 | |||
382c0b79d0 | |||
167e9a55c1 | |||
3dcba6a1ef | |||
416faf42bf | |||
25f923e0c9 | |||
624984e8c0 | |||
8cd623996a | |||
c96a6ba984 | |||
6159caec26 | |||
4181110167 | |||
d359929003 | |||
6f2550a933 | |||
393cc3d9df | |||
7d1a250e06 | |||
0636256a32 | |||
e45fb54e92 | |||
c8db9d36da | |||
333d2e47fd | |||
c81b42a81d | |||
e9bfa60ea1 | |||
d102a29f04 | |||
6d7171dfba | |||
e8d6ca5262 | |||
43ef9854b8 | |||
070cfbad51 | |||
2863d607cf | |||
c04cbc444f | |||
ae189c4add | |||
6d603e0bc2 | |||
0a95e603bc | |||
0f88d0c35a | |||
bf563c3741 | |||
f49a425870 | |||
34af83bc6f | |||
0a43e45020 | |||
4389195b6f | |||
83b0171226 | |||
5ac854cb87 | |||
ba7f565e6a | |||
f48b00feb4 | |||
8d7795e53d | |||
69fc6b50d8 | |||
e2394c9bd8 | |||
31c56c7a4a | |||
bff4f8d890 | |||
abd974191c | |||
ed1e88bd55 | |||
62f92f1fb4 | |||
9424bb23d4 | |||
c371bc04b5 | |||
4a9069e13c | |||
85e6733e53 | |||
c9efa9ddf1 | |||
d5001e0e05 | |||
caa4ae66cc | |||
9635c2005f | |||
dc6b3506fb | |||
f4a64498bf | |||
dfa4a92d54 | |||
86d85d1166 | |||
1be504569c | |||
943993920b | |||
2abff2791f | |||
25d3cd09a1 | |||
e1a60fd9bc | |||
267b00d77a | |||
63fc76dc29 | |||
ec5239ab29 | |||
f4a475d2d6 | |||
2cb4dd0b18 | |||
c7e7ccc14c | |||
4c27108d68 | |||
0883cb8df5 | |||
65cd87264f | |||
00debaa771 | |||
5490cc7531 | |||
e2a58765f4 | |||
12d5af969c | |||
12d1cc4228 | |||
c29b9da955 | |||
bc45b0cb22 | |||
fa48449105 | |||
b0b687ec01 | |||
e8803a9f18 | |||
c3690aa235 | |||
376b5a2d54 | |||
2d42497a41 | |||
e1e14200cd | |||
15eeb6f5ee | |||
f7a70cbf85 | |||
e71936c3c4 | |||
1dfca31c33 | |||
e1c8663d47 | |||
98f5c40034 | |||
3bb892a507 | |||
cbc0da3ac8 | |||
7cc05ab132 | |||
a65ca6a6b8 | |||
d12c00cb4b | |||
58e6119436 | |||
0b195d16a1 | |||
4b73f0e310 | |||
3c2145a626 | |||
f432045ffe | |||
0cd7080cee | |||
05cc3f929a | |||
053501eff7 | |||
ab70388a53 | |||
f34202febb | |||
aafff004f4 | |||
4c034e2f9c | |||
0d535f80d5 | |||
323fc6cce0 | |||
60272a8f0a | |||
d74b6fe015 | |||
d1980c567c | |||
cef9ed6381 | |||
b850ae82ae | |||
243b2bbd6b | |||
cd63d1d30b | |||
0f9865d4d5 | |||
1a6e5d0240 | |||
e70834af8f | |||
bf10aa44da | |||
3e217c0db7 | |||
cff5c098d8 | |||
7344359d1e | |||
5b42efc166 | |||
e5dd7aa70e | |||
ce914d3275 | |||
695e583136 | |||
6f06a36e44 | |||
cf4ea2da16 | |||
8aa35a14fb | |||
6b52b7e87c | |||
6299b6d45f | |||
5fe08b0741 | |||
1ca2a17e4f | |||
a44a455eca | |||
dd1af6964d | |||
0ceaa5a583 | |||
c4a50b3642 | |||
12bbc843fb | |||
e2c2cb165a | |||
c6f27856ef | |||
608cd08ca5 | |||
6b6ddeb23b | |||
1c73834dc8 | |||
e10538baca | |||
e9c59a3634 | |||
2d6831fcb0 | |||
4164134a4e | |||
6c8529185a | |||
6a615bcc43 | |||
09e727dad5 | |||
5a16b62008 | |||
4d6380f141 | |||
11acb951d4 | |||
94472a4d8d | |||
74d4f87f4b | |||
67cee3b845 | |||
71b768b3a2 | |||
2cc080c579 | |||
5354f942e6 | |||
e0445a2737 | |||
8e9e4ca392 |
1
.gitattributes
vendored
@ -39,6 +39,7 @@ commit-msg text eol=lf
|
|||||||
*.woff2 binary
|
*.woff2 binary
|
||||||
*.eot binary
|
*.eot binary
|
||||||
*.otf binary
|
*.otf binary
|
||||||
|
*.spline binary
|
||||||
# Add more binary...
|
# Add more binary...
|
||||||
|
|
||||||
|
|
||||||
|
21
.gitattributes.MD
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
### .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,4 +1,11 @@
|
|||||||
# Project Conventions and Technical Guidelines
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
# GitHub Copilot Instructions
|
||||||
|
|
||||||
|
本文件定义了项目的代码生成规范,GitHub Copilot 和其他 AI 助手应遵循这些指令。
|
||||||
|
|
||||||
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.
|
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
@ -1,3 +0,0 @@
|
|||||||
```bash
|
|
||||||
ln -s .github/copilot-instructions.md .roorules
|
|
||||||
```
|
|
2
.github/workflows/lint.yaml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 🛠️ 设置Node环境
|
- name: 🛠️ 设置Node环境
|
||||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||||
- name: 🔍 静态代码分析
|
- name: 🔍 静态代码分析
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
- name: 📦 构建项目
|
- name: 📦 构建项目
|
||||||
|
23
.github/workflows/playwright.yaml
vendored
@ -7,6 +7,13 @@ env:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
run_cleanup:
|
||||||
|
description: '是否运行 Surge 清理 Job'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
surge:
|
surge:
|
||||||
@ -15,7 +22,7 @@ jobs:
|
|||||||
url: ${{ steps.surge_deploy.outputs.url }}
|
url: ${{ steps.surge_deploy.outputs.url }}
|
||||||
steps:
|
steps:
|
||||||
- name: ⚙️ 设置 Node 环境
|
- name: ⚙️ 设置 Node 环境
|
||||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||||
- name: 🔨 构建项目
|
- name: 🔨 构建项目
|
||||||
run: pnpm run build-only
|
run: pnpm run build-only
|
||||||
env:
|
env:
|
||||||
@ -23,22 +30,28 @@ jobs:
|
|||||||
- name: 🚀 部署到 Surge
|
- name: 🚀 部署到 Surge
|
||||||
id: surge_deploy
|
id: surge_deploy
|
||||||
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
|
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
|
||||||
uses: yanhao98/composite-actions/deploy-dist-to-surge@4470aa136359d05ae3b086d37cfaa33305448a5b
|
uses: yanhao98/composite-actions/deploy-dist-to-surge@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||||
|
|
||||||
playwright:
|
playwright:
|
||||||
needs: surge
|
needs: surge
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: mcr.microsoft.com/playwright:v1.51.1-noble
|
container: mcr.microsoft.com/playwright:v1.53.2-noble
|
||||||
steps:
|
steps:
|
||||||
- name: ⚙️ 设置 Node 环境
|
- name: ⚙️ 设置 Node 环境
|
||||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||||
# - name: 📥 安装 Playwright 浏览器
|
# - name: 📥 安装 Playwright 浏览器
|
||||||
# run: pnpm exec playwright install --with-deps
|
# run: pnpm exec playwright install --with-deps
|
||||||
- name: ▶️ 运行 Playwright 测试
|
- name: ▶️ 运行 Playwright 测试
|
||||||
run: npx playwright test
|
run: npx playwright test
|
||||||
env:
|
env:
|
||||||
BASE_URL: ${{ needs.surge.outputs.url }}
|
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 部署
|
- 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:
|
env:
|
||||||
SURGE_TOKEN: d843de16b331c626f10771245c56ed93
|
SURGE_TOKEN: d843de16b331c626f10771245c56ed93
|
||||||
|
2
.github/workflows/vercel.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: ⚙️ 设置 Node 环境
|
- name: ⚙️ 设置 Node 环境
|
||||||
uses: yanhao98/composite-actions/setup-node-environment@4470aa136359d05ae3b086d37cfaa33305448a5b
|
uses: yanhao98/composite-actions/setup-node-environment@b4a2caa64aca72f8aeada59d0df3181a12df8268
|
||||||
|
|
||||||
- name: 📥 拉取 Vercel 环境信息
|
- name: 📥 拉取 Vercel 环境信息
|
||||||
run: pnpm dlx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
run: pnpm dlx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
|
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
|
||||||
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
|
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
|
||||||
# 在这里添加你的 commit message 验证逻辑,例如 commitlint
|
echo "当前提交的文件是:$1"
|
||||||
# npx --no -- commitlint --edit "$1"
|
# npx --no -- commitlint --edit "$1"
|
||||||
|
pnpm exec commitlint --edit $1
|
||||||
echo "✅ [Commit-msg] commit-msg 钩子完成!"
|
echo "✅ [Commit-msg] commit-msg 钩子完成!"
|
||||||
|
2
.npmrc
@ -5,7 +5,7 @@ registry=https://registry.npmjs.org/
|
|||||||
# registry=https://nexus.oo1.dev/repository/npm/
|
# registry=https://nexus.oo1.dev/repository/npm/
|
||||||
|
|
||||||
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
|
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
|
||||||
use-node-version=22.14.0
|
use-node-version=22.15.0
|
||||||
node-mirror:release=https://npmmirror.com/mirrors/node/ # pnpm config set node-mirror:release=https://npmmirror.com/mirrors/node/
|
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:rc=https://npmmirror.com/mirrors/node-rc/
|
||||||
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/
|
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/
|
||||||
|
3
.vscode/settings.json
vendored
@ -27,8 +27,7 @@
|
|||||||
"source.fixAll.eslint": "never",
|
"source.fixAll.eslint": "never",
|
||||||
"source.fixAll.stylelint": "never",
|
"source.fixAll.stylelint": "never",
|
||||||
"source.fixAll.oxc": "never",
|
"source.fixAll.oxc": "never",
|
||||||
"source.organizeImports": "never",
|
"source.organizeImports": "never"
|
||||||
"source.fixAll": "never"
|
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"oxc.enable": true,
|
"oxc.enable": true,
|
||||||
|
30
README.md
@ -15,33 +15,9 @@ pnpm install --registry=https://nexus.oo1.dev/repository/npm
|
|||||||
pnpm run dev
|
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
|
## Links
|
||||||
|
|
||||||
- https://github.com/hyoban-template/shadcn-vue-unocss-starter$0
|
- https://github.com/hyoban-template/shadcn-vue-unocss-starter
|
||||||
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
|
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
|
||||||
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
|
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
|
||||||
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
|
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
|
||||||
@ -53,10 +29,12 @@ pnpm run dev
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
|
||||||
- https://biomejs.dev/zh-cn/internals/language-support/
|
- https://biomejs.dev/zh-cn/internals/language-support/
|
||||||
- https://github.dev/antfu-collective/vitesse/
|
- https://github.dev/antfu-collective/vitesse/
|
||||||
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
|
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
|
||||||
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)
|
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)
|
||||||
|
|
||||||
|
+ https://www.npmjs.com/package/npkill
|
||||||
|
9
commitlint.config.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { UserConfig } from '@commitlint/types';
|
||||||
|
|
||||||
|
|
||||||
|
const Configuration: UserConfig = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
formatter: '@commitlint/format',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Configuration;
|
@ -84,6 +84,8 @@ export default defineConfigWithVueTs(
|
|||||||
'import-x/newline-after-import': 'error',
|
'import-x/newline-after-import': 'error',
|
||||||
'import-x/first': 'error',
|
'import-x/first': 'error',
|
||||||
'import-x/no-named-as-default': 'off',
|
'import-x/no-named-as-default': 'off',
|
||||||
|
'import-x/no-duplicates': 'off',
|
||||||
|
'import-x/named': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// endregion <<< eslint-plugin-import-x <<<
|
// endregion <<< eslint-plugin-import-x <<<
|
||||||
@ -99,6 +101,8 @@ export default defineConfigWithVueTs(
|
|||||||
'perfectionist/sort-imports': ['error'],
|
'perfectionist/sort-imports': ['error'],
|
||||||
'perfectionist/sort-modules': 'off',
|
'perfectionist/sort-modules': 'off',
|
||||||
'perfectionist/sort-object-types': 'off',
|
'perfectionist/sort-object-types': 'off',
|
||||||
|
'perfectionist/sort-enums': 'off',
|
||||||
|
'perfectionist/sort-union-types': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// endregion <<< eslint-plugin-perfectionist <<<
|
// endregion <<< eslint-plugin-perfectionist <<<
|
||||||
|
140
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"packageManager": "pnpm@10.8.0",
|
"packageManager": "pnpm@10.12.4",
|
||||||
"name": "vue-ts-example",
|
"name": "vue-ts-example",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -44,119 +44,123 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alova/adapter-axios": "^2.0.13",
|
"@alova/adapter-axios": "^2.0.16",
|
||||||
"@formkit/auto-animate": "^0.8.2",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@intlify/unplugin-vue-i18n": "^6.0.6",
|
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||||
"@pinia/colada": "^0.14.2",
|
"@pinia/colada": "^0.17.1",
|
||||||
"@primeuix/themes": "^1.0.3",
|
"@primeuix/themes": "^1.1.2",
|
||||||
"@splinetool/runtime": "^1.9.82",
|
"@splinetool/runtime": "^1.10.22",
|
||||||
"@types/p5": "^1.7.6",
|
"@types/p5": "^1.7.6",
|
||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@unhead/vue": "^2.0.5",
|
"@unhead/vue": "^2.0.12",
|
||||||
"@vant/use": "^1.6.0",
|
"@vant/use": "^1.6.0",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.5.0",
|
||||||
"alova": "^3.2.10",
|
"alova": "^3.3.4",
|
||||||
"ant-design-vue": "~4.2.6",
|
"ant-design-vue": "~4.2.6",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.10.0",
|
||||||
"cesium": "^1.128.0",
|
"cesium": "^1.131.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"consola": "^3.4.2",
|
"consola": "^3.4.2",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"deep-freeze-es6": "^4.0.0",
|
"deep-freeze-es6": "^4.0.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lucide-vue-next": "^0.487.0",
|
"lucide-vue-next": "^0.525.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"p5": "^1.11.3",
|
"p5": "^2.0.3",
|
||||||
"page-stack-vue3": "^2.5.6",
|
"page-stack-vue3": "^2.5.6",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.4.1",
|
||||||
"plotly.js-dist-min": "^3.0.1",
|
"plotly.js-dist-min": "^3.0.1",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primelocale": "^2.1.2",
|
"primelocale": "^2.1.4",
|
||||||
"primevue": "^4.3.3",
|
"primevue": "^4.3.5",
|
||||||
"radash": "^12.1.0",
|
"radash": "^12.1.1",
|
||||||
"radix-vue": "^1.9.17",
|
"radix-vue": "^1.9.17",
|
||||||
"reka-ui": "^2.2.0",
|
"reka-ui": "^2.3.2",
|
||||||
"satellite.js": "^6.0.0",
|
"satellite.js": "^6.0.1",
|
||||||
"sortablejs": "^1.15.6",
|
"sortablejs": "^1.15.6",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tdesign-icons-vue-next": "^0.3.5",
|
"tdesign-icons-vue-next": "^0.3.6",
|
||||||
"three": "^0.175.0",
|
"three": "^0.178.0",
|
||||||
"ts-enum-util": "^4.1.0",
|
"ts-enum-util": "^4.1.0",
|
||||||
"utils4u": "^4.2.3",
|
"utils4u": "^4.2.3",
|
||||||
"vant": "^4.9.18",
|
"vant": "^4.9.20",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.17",
|
||||||
|
"vue-data-ui": "^2.12.7",
|
||||||
"vue-draggable-plus": "^0.6.0",
|
"vue-draggable-plus": "^0.6.0",
|
||||||
"vue-i18n": "^11.1.3",
|
"vue-i18n": "^11.1.9",
|
||||||
"vue-page-stack": "^3.2.0",
|
"vue-page-stack": "^3.2.0",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.2.8",
|
"@commitlint/cli": "^19.8.1",
|
||||||
"@faker-js/faker": "^9.6.0",
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
"@iconify-json/carbon": "^1.2.8",
|
"@eslint/compat": "^1.3.1",
|
||||||
|
"@faker-js/faker": "^9.9.0",
|
||||||
|
"@iconify-json/carbon": "^1.2.10",
|
||||||
"@iconify-json/logos": "^1.2.4",
|
"@iconify-json/logos": "^1.2.4",
|
||||||
"@iconify-json/mdi": "^1.2.3",
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
"@iconify/utils": "^2.3.0",
|
"@iconify/utils": "^2.3.0",
|
||||||
"@playwright/test": "^1.51.1",
|
"@playwright/test": "^1.53.2",
|
||||||
"@primevue/auto-import-resolver": "^4.3.3",
|
"@primevue/auto-import-resolver": "^4.3.5",
|
||||||
"@tsconfig/node22": "^22.0.1",
|
"@tsconfig/node22": "^22.0.2",
|
||||||
"@types/archiver": "^6.0.3",
|
"@types/archiver": "^6.0.3",
|
||||||
"@types/mockjs": "^1.0.10",
|
"@types/mockjs": "^1.0.10",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.16.3",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/plotly.js-dist-min": "^2.3.4",
|
"@types/plotly.js-dist-min": "^2.3.4",
|
||||||
"@types/three": "^0.175.0",
|
"@types/three": "^0.178.1",
|
||||||
"@vant/auto-import-resolver": "^1.3.0",
|
"@vant/auto-import-resolver": "^1.3.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.3",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
"@vitejs/plugin-vue-jsx": "^5.0.1",
|
||||||
"@vitest/eslint-plugin": "^1.1.40",
|
"@vitest/eslint-plugin": "^1.3.4",
|
||||||
"@vue/eslint-config-prettier": "^10.2.0",
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
"@vue/eslint-config-typescript": "^14.5.0",
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"depcheck": "^1.4.7",
|
"depcheck": "^1.4.7",
|
||||||
"eruda": "^3.4.1",
|
"eruda": "^3.4.3",
|
||||||
"eslint": "^9.24.0",
|
"eslint": "^9.30.1",
|
||||||
"eslint-plugin-import-x": "^4.10.2",
|
"eslint-plugin-import-x": "^4.16.1",
|
||||||
"eslint-plugin-oxlint": "^0.16.6",
|
"eslint-plugin-oxlint": "^1.6.0",
|
||||||
"eslint-plugin-perfectionist": "^4.11.0",
|
"eslint-plugin-perfectionist": "^4.15.0",
|
||||||
"eslint-plugin-unicorn": "^58.0.0",
|
"eslint-plugin-unicorn": "^59.0.1",
|
||||||
"eslint-plugin-vue": "^10.0.0",
|
"eslint-plugin-vue": "^10.3.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"less": "^4.3.0",
|
"less": "^4.3.0",
|
||||||
"lint-staged": "^15.5.0",
|
"lint-staged": "^16.1.2",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"npm-run-all2": "^7.0.2",
|
"naive-ui": "^2.42.0",
|
||||||
"oxlint": "^0.16.6",
|
"npm-run-all2": "^8.0.4",
|
||||||
"prettier": "3.5.3",
|
"oxlint": "^1.6.0",
|
||||||
"sass-embedded": "^1.86.3",
|
"prettier": "3.6.2",
|
||||||
"terser": "^5.39.0",
|
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"unocss": "66.1.0-beta.10",
|
"unocss": "66.3.3",
|
||||||
"unocss-preset-animations": "^1.1.1",
|
"unocss-preset-animations": "^1.2.1",
|
||||||
"unocss-preset-chinese": "^0.3.3",
|
"unocss-preset-chinese": "^0.3.3",
|
||||||
"unocss-preset-shadcn": "^0.5.0",
|
"unocss-preset-shadcn": "^0.5.0",
|
||||||
"unplugin-auto-import": "^19.1.2",
|
"unplugin-auto-import": "^19.3.0",
|
||||||
"unplugin-icons": "^22.1.0",
|
"unplugin-icons": "^22.1.0",
|
||||||
"unplugin-vue-components": "^28.5.0",
|
"unplugin-vue-components": "^28.8.0",
|
||||||
"unplugin-vue-macros": "^2.14.5",
|
"unplugin-vue-macros": "^2.14.5",
|
||||||
"unplugin-vue-markdown": "^28.3.1",
|
"unplugin-vue-markdown": "^29.1.0",
|
||||||
"unplugin-vue-router": "^0.12.0",
|
"unplugin-vue-router": "^0.14.0",
|
||||||
"vite": "^6.2.6",
|
"vfonts": "^0.0.3",
|
||||||
"vite-plugin-checker": "^0.9.1",
|
"vite": "^7.0.2",
|
||||||
|
"vite-plugin-checker": "^0.10.0",
|
||||||
"vite-plugin-fake-server": "^2.2.0",
|
"vite-plugin-fake-server": "^2.2.0",
|
||||||
"vite-plugin-purgecss-updated-v5": "^1.2.4",
|
"vite-plugin-image-tools": "^2.0.2",
|
||||||
"vite-plugin-singlefile": "^2.2.0",
|
"vite-plugin-purgecss-updated-v5": "^1.2.6",
|
||||||
"vite-plugin-static-copy": "^2.3.1",
|
"vite-plugin-singlefile": "^2.3.0",
|
||||||
"vite-plugin-vue-devtools": "^7.7.2",
|
"vite-plugin-static-copy": "^3.1.0",
|
||||||
|
"vite-plugin-vue-devtools": "^7.7.7",
|
||||||
"vite-plugin-vue-layouts": "^0.11.0",
|
"vite-plugin-vue-layouts": "^0.11.0",
|
||||||
"vite-plugin-vue-meta-layouts": "^0.5.1",
|
"vite-plugin-vue-meta-layouts": "^0.5.1",
|
||||||
"vite-plugin-webfont-dl": "^3.10.4",
|
"vite-plugin-webfont-dl": "^3.10.5",
|
||||||
"vue-component-type-helpers": "^2.2.8",
|
"vue-component-type-helpers": "^3.0.1",
|
||||||
"vue-tsc": "^2.2.8"
|
"vue-tsc": "^3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6852
pnpm-lock.yaml
generated
@ -15,9 +15,11 @@ const themeConfig = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider :theme="themeConfig">
|
<n-config-provider preflight-style-disabled>
|
||||||
<RouterView />
|
<a-config-provider :theme="themeConfig">
|
||||||
</a-config-provider>
|
<RouterView />
|
||||||
|
</a-config-provider>
|
||||||
|
</n-config-provider>
|
||||||
|
|
||||||
<DynamicDialog /> <ConfirmDialog /> <Toast />
|
<DynamicDialog /> <ConfirmDialog /> <Toast />
|
||||||
</template>
|
</template>
|
||||||
|
58
src/components/PinPuYi/CONST.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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 = '+/-',
|
||||||
|
}
|
13
src/components/PinPuYi/Helvetica.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@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; /* 推荐 */
|
||||||
|
}
|
805
src/components/PinPuYi/PinPuYi.vue
Normal file
@ -0,0 +1,805 @@
|
|||||||
|
<!-- 频谱仪 -->
|
||||||
|
<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>
|
BIN
src/components/PinPuYi/fonts/Helvetica-Light-05.eot
Normal file
2899
src/components/PinPuYi/fonts/Helvetica-Light-05.svg
Normal file
After Width: | Height: | Size: 447 KiB |
BIN
src/components/PinPuYi/fonts/Helvetica-Light-05.ttf
Normal file
BIN
src/components/PinPuYi/fonts/Helvetica-Light-05.woff
Normal file
BIN
src/components/PinPuYi/fonts/Helvetica-Light-05.woff2
Normal file
BIN
src/components/PinPuYi/imgs/屏幕_0.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/components/PinPuYi/imgs/屏幕_1.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/components/PinPuYi/imgs/屏幕_2.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/components/PinPuYi/imgs/屏幕_3.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/components/PinPuYi/pin-pu-yi-bg-clean.png
Normal file
After Width: | Height: | Size: 705 KiB |
BIN
src/components/PinPuYi/pin-pu-yi-bg.png
Normal file
After Width: | Height: | Size: 810 KiB |
@ -18,32 +18,32 @@ const readyPromise = new Promise<void>((resolve) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const 频谱瀑布图Layout = {
|
const 频谱瀑布图Layout = {
|
||||||
title: '频谱瀑布图',
|
title: { text: '频谱瀑布图' },
|
||||||
xaxis: {
|
xaxis: {
|
||||||
title: '频率 (Hz)',
|
title: { text: '频率 (Hz)' },
|
||||||
// range: [0, 22_050],
|
// range: [0, 22_050],
|
||||||
showgrid: false,
|
showgrid: false,
|
||||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||||
},
|
},
|
||||||
yaxis: {
|
yaxis: {
|
||||||
title: '时间步',
|
title: { text: '时间步' },
|
||||||
showgrid: false,
|
showgrid: false,
|
||||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||||
},
|
},
|
||||||
margin: { l: 60, r: 40, b: 40, t: 60 },
|
margin: { l: 60, r: 40, b: 40, t: 60 },
|
||||||
};
|
} satisfies Partial<import('plotly.js-dist-min').Layout>;
|
||||||
|
|
||||||
const 频谱图Layout = {
|
const 频谱图Layout = {
|
||||||
title: '频谱图',
|
title: { text: '频谱图' },
|
||||||
xaxis: {
|
xaxis: {
|
||||||
title: '频率 (Hz)',
|
title: { text: '频率 (Hz)' },
|
||||||
// range: [0, 22_050],
|
// range: [0, 22_050],
|
||||||
showgrid: true,
|
showgrid: true,
|
||||||
gridcolor: '#eee',
|
gridcolor: '#eee',
|
||||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||||
},
|
},
|
||||||
yaxis: {
|
yaxis: {
|
||||||
title: '幅度 (dB)',
|
title: { text: '幅度 (dB)' },
|
||||||
showgrid: true, // 显示网格线
|
showgrid: true, // 显示网格线
|
||||||
gridcolor: '#eee',
|
gridcolor: '#eee',
|
||||||
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
tickformat: ',d', // 设置为带逗号的整数格式 (推荐,更易读)
|
||||||
|
321
src/layouts/naive-ui/AppLayout.vue
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
<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,7 +1,12 @@
|
|||||||
import './styles';
|
import './styles';
|
||||||
|
|
||||||
|
import { LogLevels } from 'consola';
|
||||||
|
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import { setupPlugins } from './plugins';
|
import { setupPlugins } from './plugins';
|
||||||
|
|
||||||
const autoInstallModules = import.meta.glob('./plugins/*.ts', { eager: true });
|
const autoInstallModules = import.meta.glob('./plugins/*.ts', { eager: true });
|
||||||
|
|
||||||
setupPlugins(createApp(App), autoInstallModules).mount('#app');
|
setupPlugins(createApp(App), autoInstallModules).mount('#app');
|
||||||
|
|
||||||
|
consola.level = LogLevels.verbose;
|
||||||
|
3
src/pages/Page/MDPageImportMD.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# 测试
|
||||||
|
|
||||||
|
这个文件是被 import 的
|
11
src/pages/Page/MDPageImportMD.page.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import MD from './MDPageImportMD.md';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<MD />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
13
src/pages/Page/PinPuYi.page.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!--
|
||||||
|
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>
|
352
src/pages/Page/vue-data-ui.page.vue
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
<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>
|
11
src/pages/UI-components/NaiveUI/index.page.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<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 = () => {
|
const refresh = () => {
|
||||||
state.list.splice(0, state.list.length);
|
state.list.splice(0);
|
||||||
state.page = 0;
|
state.page = 0;
|
||||||
state.complete = false;
|
state.complete = false;
|
||||||
loadMore();
|
loadMore();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders';
|
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders';
|
||||||
import { setupLayouts } from 'virtual:meta-layouts';
|
import { setupLayouts } from 'virtual:meta-layouts';
|
||||||
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
|
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
|
||||||
import { createRouter, createWebHistory } from 'vue-router/auto';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
import { handleHotUpdate, routes } from 'vue-router/auto-routes';
|
import { handleHotUpdate, routes } from 'vue-router/auto-routes';
|
||||||
|
|
||||||
const setupLayoutsResult = setupLayouts(routes);
|
const setupLayoutsResult = setupLayouts(routes);
|
||||||
|
@ -13,4 +13,9 @@ import 'primeicons/primeicons.css';
|
|||||||
import './reset/reset-primevue.css';
|
import './reset/reset-primevue.css';
|
||||||
import './reset/reset-antdv.less';
|
import './reset/reset-antdv.less';
|
||||||
|
|
||||||
|
// 通用字体
|
||||||
|
import 'vfonts/Lato.css';
|
||||||
|
// 等宽字体
|
||||||
|
import 'vfonts/FiraCode.css';
|
||||||
|
//
|
||||||
import 'virtual:uno.css';
|
import 'virtual:uno.css';
|
||||||
|
11
src/types/global.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
declare global {
|
||||||
|
const $__DEV__: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface ComponentCustomProperties {
|
||||||
|
$__DEV__: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
@ -1,12 +1,4 @@
|
|||||||
declare global {
|
declare module '*.vue' {
|
||||||
const $__DEV__: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface ComponentCustomProperties {
|
|
||||||
$__DEV__: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
import type { ComponentOptions } from 'vue';
|
import type { ComponentOptions } from 'vue';
|
||||||
|
|
||||||
const Component: ComponentOptions;
|
const Component: ComponentOptions;
|
||||||
@ -19,5 +11,3 @@ declare module '*.md' {
|
|||||||
const Component: ComponentOptions;
|
const Component: ComponentOptions;
|
||||||
export default Component;
|
export default Component;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
|
4
typed-router.d.ts
vendored
@ -34,9 +34,12 @@ declare module 'vue-router/auto-routes' {
|
|||||||
'PageIframePageSpectrogram': RouteRecordInfo<'PageIframePageSpectrogram', '/Page/iframe-page/Spectrogram', Record<never, never>, Record<never, never>>,
|
'PageIframePageSpectrogram': RouteRecordInfo<'PageIframePageSpectrogram', '/Page/iframe-page/Spectrogram', Record<never, never>, Record<never, never>>,
|
||||||
'PageJSPage': RouteRecordInfo<'PageJSPage', '/Page/JSPage', 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>>,
|
'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>>,
|
'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>>,
|
'PageStyle': RouteRecordInfo<'PageStyle', '/Page/Style', Record<never, never>, Record<never, never>>,
|
||||||
'PageViteAssets': RouteRecordInfo<'PageViteAssets', '/Page/vite-assets', 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>>,
|
'PkgsUsageI18n': RouteRecordInfo<'PkgsUsageI18n', '/PkgsUsage/I18n', Record<never, never>, Record<never, never>>,
|
||||||
'PkgsUsageTsEnumUtil': RouteRecordInfo<'PkgsUsageTsEnumUtil', '/PkgsUsage/ts-enum-util', 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>>,
|
'UIComponentsAntdV': RouteRecordInfo<'UIComponentsAntdV', '/UI-components/AntdV', Record<never, never>, Record<never, never>>,
|
||||||
@ -44,6 +47,7 @@ declare module 'vue-router/auto-routes' {
|
|||||||
'UIComponentsInfiniteLoading': RouteRecordInfo<'UIComponentsInfiniteLoading', '/UI-components/infinite-loading', Record<never, never>, Record<never, never>>,
|
'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>>,
|
'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>>,
|
'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>>,
|
'UIComponentsPrimeVue': RouteRecordInfo<'UIComponentsPrimeVue', '/UI-components/PrimeVue', Record<never, never>, Record<never, never>>,
|
||||||
'UIComponentsShadcnVue': RouteRecordInfo<'UIComponentsShadcnVue', '/UI-components/ShadcnVue', 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>>,
|
'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 VueJsx from '@vitejs/plugin-vue-jsx';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import UnoCSS from 'unocss/vite';
|
import UnoCSS from 'unocss/vite';
|
||||||
import UnpluginAutoImport from 'unplugin-auto-import/vite';
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
|
||||||
import IconsResolver from 'unplugin-icons/resolver';
|
import IconsResolver from 'unplugin-icons/resolver';
|
||||||
import Icons from 'unplugin-icons/vite';
|
import Icons from 'unplugin-icons/vite';
|
||||||
import { AntDesignVueResolver, TDesignResolver } from 'unplugin-vue-components/resolvers';
|
import { AntDesignVueResolver, NaiveUiResolver, TDesignResolver } from 'unplugin-vue-components/resolvers';
|
||||||
import Components from 'unplugin-vue-components/vite';
|
import Components from 'unplugin-vue-components/vite';
|
||||||
import VueMacros from 'unplugin-vue-macros/vite';
|
import VueMacros from 'unplugin-vue-macros/vite';
|
||||||
import Markdown from 'unplugin-vue-markdown/vite';
|
import Markdown from 'unplugin-vue-markdown/vite';
|
||||||
@ -25,6 +25,7 @@ import { createUtils4uAutoImports } from 'utils4u/auto-imports';
|
|||||||
import { type PluginOption } from 'vite';
|
import { type PluginOption } from 'vite';
|
||||||
import { checker } from 'vite-plugin-checker';
|
import { checker } from 'vite-plugin-checker';
|
||||||
import { vitePluginFakeServer } from 'vite-plugin-fake-server';
|
import { vitePluginFakeServer } from 'vite-plugin-fake-server';
|
||||||
|
import VitePluginImageTools from 'vite-plugin-image-tools';
|
||||||
import pluginPurgeCss from 'vite-plugin-purgecss-updated-v5';
|
import pluginPurgeCss from 'vite-plugin-purgecss-updated-v5';
|
||||||
import { viteSingleFile } from 'vite-plugin-singlefile';
|
import { viteSingleFile } from 'vite-plugin-singlefile';
|
||||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||||
@ -65,7 +66,8 @@ export function Plugins() {
|
|||||||
|
|
||||||
// https://github.com/dishait/vite-plugin-vue-meta-layouts
|
// https://github.com/dishait/vite-plugin-vue-meta-layouts
|
||||||
MetaLayouts({
|
MetaLayouts({
|
||||||
defaultLayout: 'sakai-vue/AppLayout',
|
// defaultLayout: 'sakai-vue/AppLayout',
|
||||||
|
defaultLayout: 'naive-ui/AppLayout',
|
||||||
skipTopLevelRouteLayout: false, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134,默认为 false 关闭
|
skipTopLevelRouteLayout: false, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134,默认为 false 关闭
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ export function Plugins() {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
// https://github.com/antfu/unplugin-auto-import
|
// https://github.com/antfu/unplugin-auto-import
|
||||||
UnpluginAutoImport({
|
AutoImport({
|
||||||
dirs: [
|
dirs: [
|
||||||
// 'src/composables',
|
// 'src/composables',
|
||||||
'src/stores',
|
'src/stores',
|
||||||
@ -97,6 +99,7 @@ export function Plugins() {
|
|||||||
{
|
{
|
||||||
'consola/browser': ['consola'],
|
'consola/browser': ['consola'],
|
||||||
'vue-router/auto': ['useLink'],
|
'vue-router/auto': ['useLink'],
|
||||||
|
'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
resolvers: [TDesignResolver({ esm: true, library: 'mobile-vue' }), VantResolver({ importStyle: true })],
|
resolvers: [TDesignResolver({ esm: true, library: 'mobile-vue' }), VantResolver({ importStyle: true })],
|
||||||
@ -123,6 +126,7 @@ export function Plugins() {
|
|||||||
TDesignResolver({ esm: true, library: 'mobile-vue' }),
|
TDesignResolver({ esm: true, library: 'mobile-vue' }),
|
||||||
VantResolver({ importStyle: true }),
|
VantResolver({ importStyle: true }),
|
||||||
PrimeVueResolver(/* { components: { prefix: 'P' } } */),
|
PrimeVueResolver(/* { components: { prefix: 'P' } } */),
|
||||||
|
NaiveUiResolver(),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -190,6 +194,15 @@ export function Plugins() {
|
|||||||
{ dest: cesiumBaseUrl, src: `${cesiumSource}/Widgets` },
|
{ dest: cesiumBaseUrl, src: `${cesiumSource}/Widgets` },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
VitePluginImageTools({
|
||||||
|
quality: 80,
|
||||||
|
enableWebp: true,
|
||||||
|
enableDev: false,
|
||||||
|
enableDevWebp: false,
|
||||||
|
// 排除字体 SVG 文件和可能有问题的 SVG 文件
|
||||||
|
excludes: /fonts\/.*\.svg$|Helvetica.*\.svg$/,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 检查是否在VS Code终端中运行
|
// 检查是否在VS Code终端中运行
|
||||||
|
@ -36,6 +36,9 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
'satellite.js',
|
'satellite.js',
|
||||||
'ts-enum-util',
|
'ts-enum-util',
|
||||||
'unplugin-vue-router',
|
'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'],
|
exclude: ['quill', 'chart.js/auto'],
|
||||||
},
|
},
|
||||||
@ -53,7 +56,7 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
// https://cn.rollupjs.org/configuration-options/#output-assetfilenames
|
// https://cn.rollupjs.org/configuration-options/#output-assetfilenames
|
||||||
// output: env.VITE_SPLIT_CHUNKS === 'true' ? (await import('utils4u/rollup')).createSplitChunkOutput() : undefined,
|
// output: env.VITE_SPLIT_CHUNKS === 'true' ? (await import('utils4u/rollup')).createSplitChunkOutput() : undefined,
|
||||||
output: {
|
output: {
|
||||||
minifyInternalExports: false,
|
// minifyInternalExports: false,
|
||||||
// manualChunks: {
|
// manualChunks: {
|
||||||
// 'vendor/utils4u': ['utils4u', 'utils4u/vue-use', 'utils4u/primevue'],
|
// 'vendor/utils4u': ['utils4u', 'utils4u/vue-use', 'utils4u/primevue'],
|
||||||
// 'vendor/vue': ['vue'],
|
// 'vendor/vue': ['vue'],
|
||||||
@ -67,6 +70,14 @@ export default defineConfig(({ command, mode }) => {
|
|||||||
manualChunks: {
|
manualChunks: {
|
||||||
'vendor/Cesium': ['cesium'],
|
'vendor/Cesium': ['cesium'],
|
||||||
},
|
},
|
||||||
|
// advancedChunks: {
|
||||||
|
// groups: [
|
||||||
|
// {
|
||||||
|
// name: 'vendor/cesium',
|
||||||
|
// test: 'cesium',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourcemap: mode !== 'production' || env.VITE_SOURCE_MAP === 'true',
|
sourcemap: mode !== 'production' || env.VITE_SOURCE_MAP === 'true',
|
||||||
|