687 Commits

Author SHA1 Message Date
21fb3dcda1 chore(deps): update dependency vite-plugin-vue-devtools to v8.0.2 2025-09-15 00:06:51 +08:00
866699665d chore(deps): update lint dependencies 2025-09-09 13:57:12 +08:00
严浩
b2ce4dde42 build: 更新 oxlint 命令参数
- 在 package.json 中更新了 lint:oxlint 脚本的命令参数
- 新增了 -D correctness 参数,以提高代码正确性检查的严格度
- 添加了 --ignore-path .gitignore 参数,忽略 .gitignore 中的文件
2025-09-09 11:49:09 +08:00
严浩
5e79ffe9be build(deps): 更新 eslint-plugin-unicorn 到 61.0.2
- 将 eslint-plugin-unicorn 从 60.0.0 升级到 61.0.2
- 修改 playwright 配置中的命令从 npm 到 pnpm
- 移除了多余的空格和换行符
2025-09-09 10:24:30 +08:00
严浩
46ca762a96 refactor(fake): 优化 XML 标签内容提取正则表达式
- 修改了 pickTag 函数中的正则表达式,以更准确地匹配标签内容
- 将 [\s\S]*? 替换为 [sS]*?,以提高匹配精度
- 此修改解决了潜在的命名空间与嵌套同名标签的复杂情形下的匹配问题
2025-09-09 09:31:00 +08:00
474a690417 chore(deps): update dependency @vitest/eslint-plugin to v1.3.8 2025-09-08 14:39:29 +08:00
38b9c28404 chore(deps): update dependency @types/node to v24.3.1 2025-09-08 08:49:34 +08:00
99dcb7723c chore(deps): update yanhao98/composite-actions digest to 25eb4dc 2025-09-08 08:49:10 +08:00
47390ab1f2 chore(deps): update dependency @intlify/unplugin-vue-i18n to v11 2025-09-08 00:47:12 +08:00
3af468f18e chore(deps): update dependency @types/three to ^0.180.0 2025-09-05 03:14:40 +08:00
严浩
cc0e5679a9 feat(xml): 增加 XML 解析和处理功能
- 添加了处理 XML 请求和响应的工具函数
- 实现了 GET 和 POST 请求的 XML 解析和返回
- 更新了 API 页面,增加了 XML POST 请求和解析的示例
- 优化了 XML 生成和转义逻辑,防止二次转义
2025-09-02 16:19:31 +08:00
严浩
b9e6a139b7 feat(fake-server): 模拟 XML 数据并增强 API 页面解析功能
- 新增 XML 模拟数据路由,提供示例 XML 文档
- 在 API 页面中集成 XML 请求和解析功能
- 使用 Axios 请求 XML 数据,并通过 DOMParser 解析
- 展示 XML 字符串和解析后的结果
2025-09-02 15:43:09 +08:00
严浩
f0a65d16f7 build(deps): pnpm dedupe
- 将 @primevue/auto-import-resolver 版本从 ^4.3.7 升级到 ^4.3.8
- 将 @babel/parser 版本从 7.27.7 升级到 7.28.3
2025-09-02 09:44:42 +08:00
严浩
119006133f build(deps): 更新项目依赖版本
- 更新 @pinia/colada 从 0.17.2 到 0.17.3
- 更新 @primevue/icons 从 4.3.7 到 4.3.8
- 更新 @vueuse/core 从 13.7.0 到 13.9.0
- 更新 p5 从 2.0.4 到 2.0.5
- 更新 primevue 从 4.3.7 到 4.3.8
- 更新 vue 从 3.5.18 到 3.5.20
- 更新 vue-data-ui 从 3.0.14 到 3.0.15
- 更新 @playwright/test 从 1.54.2 到 1.55.0
- 更新 @primevue/auto-import-resolver 从 4.3.7 到 4.3.8
- 更新 @primevue/metadata 从 4.3.7 到 4.3.8
- 更新 @vitejs/plugin-vue-jsx 从 5.0.1 到 5.1.1
- 更新 @vitest/eslint-plugin 从 1.3.5 到 1.3.6
- 更新 @vue/tsconfig 从 0.8.0 到 0.8.1
- 更新 eslint 从 9.33.0 到 9.34.0
- 更新 lint-staged 从 16.1.5 到 16.1.6
- 更新 unplugin-auto-import 从 20.0.0 到 20.1.0
- 更新 vite-plugin-static-copy 从 3.1.1 到 3.1.2
- 更新 vite-plugin-vue-devtools 从 8.0.0 到 8.0.1
- 更新 vue-component-type-helpers 从 3.0.5 到 3.0.6
- 更新 vue-tsc 从 3.0.5 到 3.0.6
2025-09-02 09:43:55 +08:00
严浩
9e82ae98e6 fix(vite): 更新 fake-server 插件配置
- 修改 fake-server 插件的 enableProd 参数为 !true,禁用生产环境中的 fake API
- 在 vite 配置中添加 eruda,用于调试
- 注释掉部分配置选项,保留作为参考
2025-09-02 09:32:29 +08:00
严浩
04e7376085 build(dependencies): 升级 pnpm 并添加图像优化插件
- 将 pnpm 版本从 10.15.0 升级到 10.15.1
- 添加 vite-plugin-image-optimizer 依赖
- 在 Vite 插件中集成图像优化功能
2025-09-02 09:03:50 +08:00
严浩
d83813f8f9 refactor: rolldown-vite 2025-09-01 18:36:46 +08:00
8b749beaf3 chore(deps): update dependency unplugin-auto-import to v20.1.0 2025-08-31 11:04:37 +08:00
adc95bf985 chore(deps): update dependency @vitejs/plugin-vue-jsx to v5.1.1 2025-08-31 11:02:07 +08:00
7cf0d22b24 chore(deps): update lint dependencies to v1.13.0 2025-08-29 22:16:53 +08:00
3e52fc491d chore(deps): update dependency @vue/tsconfig to ^0.8.0 2025-08-29 22:13:19 +08:00
9929a92a38 chore(deps): update dependency @vitejs/plugin-vue-jsx to v5.1.0 2025-08-29 11:23:41 +08:00
cc309e6fa8 chore(deps): update dependency vite-plugin-checker to v0.10.3 2025-08-29 11:20:49 +08:00
176f497f4d chore(deps): update all non-major dependencies 2025-08-27 17:12:33 +08:00
f25746c592 chore(deps): update dependency vite-plugin-vue-devtools to v8.0.1 2025-08-27 14:51:09 +08:00
63ea355c64 chore(deps): update dependency vite-plugin-static-copy to v3.1.2 2025-08-27 01:24:30 +08:00
98d697d735 chore(deps): update dependency vite-plugin-vue-meta-layouts to ^0.6.0 2025-08-26 23:01:27 +08:00
beaae87063 chore(deps): update dependency @types/node to v22.18.0 2025-08-26 19:50:48 +08:00
dbd10e0fe4 chore(deps): update dependency eslint to v9.34.0 2025-08-23 06:27:14 +08:00
47d933923c chore(deps): update playwright packages to v1.55.0 2025-08-21 05:48:49 +08:00
严浩
02e61dbcdd build(test): 配置 Vitest 并调整代码检查范围
- 更新 package.json 中 lint-staged 配置,扩大代码检查范围
- 修改 vitest.config.ts,优化 Vitest 配置参数
2025-08-20 13:59:34 +08:00
严浩
cd1735d6b7 build: 升级 Node.js 版本
- 将 .npmrc 文件中的 use-node-version 从 22.15.0 修改为 22.18.0
- 此次升级旨在使用最新版本的 Node.js,以获得更好的性能和安全性
2025-08-20 12:52:45 +08:00
严浩
642b8dbfba build: 升级 pnpm 包管理器版本
- 将 pnpm 版本从 10.14.0 升级到 10.15.0
2025-08-20 11:32:11 +08:00
严浩
85ab75a9e9 build(deps): 更新依赖并移除冗余包
- 更新 vitest、@vitest/eslint-plugin 等包至最新版本
- 移除 @napi-rs/wasm-runtime、@oxc-project/runtime 等冗余包
- 更新 lightningcss 相关包至 1.30.1 版本
- 移除 rolldown、rolldown-vite 等未使用的包
- 更新 renovate.json,添加 pnpmDedupe 选项
2025-08-20 10:03:56 +08:00
严浩
c762405682 build(deps): 更新 Vite 版本
- 在 pnpm overrides 中添加 Vite,确保使用正确的版本
2025-08-19 18:27:59 +08:00
严浩
868972e889 style: 更新 .editorconfig 文件配置
- 扩展配置到更多文件类型,包括 CSS 预处理器文件
- 添加 end_of_line 和 max_line_length 设置
- 统一使用 LF 换行符
- 设置最大行长度为 120 字符
2025-08-19 16:24:22 +08:00
严浩
e4287bb0e1 chore: 整理 2025-08-19 16:20:33 +08:00
严浩
0585f9abdb build: 添加 create-vue 依赖 2025-08-19 09:31:50 +08:00
严浩
4f5910147c build: 移除 tsconfig.app.json 中的 lib 配置项 2025-08-19 09:30:02 +08:00
严浩
95a3676fba build(deps): 降级 @types/node 和 @vue/tsconfig 版本
- 将 @types/node 版本从 ^24.3.0 降级到 ^22.17.2
- 将 @vue/tsconfig 版本从 ^0.8.0 降级到 ^0.7.0
2025-08-18 11:41:38 +08:00
严浩
46e5fd3e49 refactor(shadcn): 优化 Button 组件并更新相关依赖
- 更新 @types/node 版本至 24.3.0
- 更新 @vue/tsconfig 版本至 0.8.0
- 更新 eslint-plugin-oxlint 和 oxlint 版本至 1.12.0
- 优化 HCesiumManager 中的卫星实体创建逻辑
- 修复 ShadcnVue 页面中的 Button 组件用法
- 优化 Button 组件的 Props 类型定义
- 更新 buttonVariants 的导入路径
2025-08-18 10:18:03 +08:00
3b25b0dc7b chore(deps): update lint dependencies to v1.12.0 2025-08-17 20:50:04 +08:00
f4fea38dd5 chore(deps): update dependency @types/node to v24.3.0 2025-08-15 19:23:12 +08:00
严浩
ee58de3146 test(config): 更新 Playwright 配置以适应 VSCode 环境
- 添加代码判断是否在 VSCode 中运行,并设置相应的 baseURL
- 修改 webServer 配置,仅在 VSCode 环境中启动本地服务器
- 优化配置文件结构,提高可读性和维护性
2025-08-15 16:33:27 +08:00
严浩
b4c2711da6 build(deps): 更新项目依赖版本 2025-08-15 16:19:45 +08:00
0e97f523d8 chore(deps): update primevue pkgs 2025-08-14 18:53:32 +08:00
严浩
1d4f3b601a refactor(dev): 将 $__DEV__ 重命名为 __DEV__ 2025-08-14 14:31:19 +08:00
严浩
231249b357 ci: 更新 Playwright 测试工作流
- 在运行测试前添加环境变量输出,提高调试可读性
- 删除测试中不必要的日志输出
2025-08-14 14:18:10 +08:00
严浩
3ce9c29302 chore(deps): 更新依赖
更新了多个开发依赖项,包括 Vite 及其相关插件、unplugin 系列工具和 CI 工作流中使用的 composite-actions。

主要变更包括:
- 将 pnpm 版本升级至 10.14.0。
- 升级 Vite、@vitejs/plugin-vue 等核心构建工具。
- 升级 unplugin-auto-import, unplugin-vue-components, unplugin-vue-router 等插件。
- 更新 CI 工作流中的 GitHub Actions 版本以保持同步。
- 重新生成了因依赖更新而变化的 `typed-router.d.ts` 类型文件。
2025-08-14 09:57:47 +08:00
c632abf8a4 chore(deps): update dependency @types/node to ^22.17.1 2025-08-09 03:14:55 +08:00
67e4f3fe44 chore(deps): update dependency @types/three to ^0.179.0 2025-08-07 06:26:25 +08:00
1184096973 chore(deps): update dependency tdesign-icons-vue-next to ^0.3.7 2025-08-06 11:12:36 +08:00
6274d4f8aa chore(deps): update dependency unplugin-vue-router to ^0.15.0 2025-08-04 21:52:30 +08:00
e04af8a897 chore(deps): update playwright packages 2025-08-03 19:08:12 +08:00
c8a8444999 chore(deps): update lint dependencies 2025-08-02 03:17:45 +08:00
2374bd3eb5 chore(deps): update dependency unplugin-icons to ^22.2.0 2025-08-02 00:23:54 +08:00
c7986cb759 chore(deps): update dependency vite-plugin-checker to ^0.10.2 2025-07-31 03:15:59 +08:00
17b57099ab chore(deps): update dependency @types/node to ^22.17.0 2025-07-30 01:21:04 +08:00
严浩
8e8a272f2e chore(types): remove unnecessary export statements from global and unocss type definitions 2025-07-29 09:20:31 +08:00
严浩
8ca42a4cdd chore(deps): update eslint-plugin-unicorn to ^60.0.0 and vite-plugin-vue-devtools to ^8.0.0 2025-07-29 09:14:15 +08:00
8826aae6fa chore(deps): update lint dependencies 2025-07-26 00:51:11 +08:00
6c1433d39f chore(deps): update dependency vite-plugin-checker to ^0.10.1 2025-07-21 18:39:02 +08:00
7a1db29f4e chore(deps): update lint dependencies to ^1.7.0 2025-07-20 18:49:48 +08:00
ecdcf90dee chore(deps): update dependency @types/node to ^22.16.5 2025-07-19 13:00:18 +08:00
f214c7a567 chore(deps): update primevue pkgs 2025-07-16 18:41:09 +08:00
2de56fd2f8 chore(deps): update dependency @types/node to ^22.16.4 2025-07-15 16:17:33 +08:00
4d6e3149c6 chore(deps): update dependency eslint to ^9.31.0 2025-07-15 03:18:35 +08:00
483e2995db chore(deps): update playwright packages 2025-07-12 05:57:03 +08:00
383d8deead chore(deps): update dependency @types/node to ^22.16.3 2025-07-11 03:15:34 +08:00
fcf09d887b chore(deps): update dependency @types/three to ^0.178.1 2025-07-11 02:48:12 +08:00
145baf5e16 chore(deps): update dependency vite-plugin-checker to ^0.10.0 2025-07-10 18:56:25 +08:00
866145591f chore(deps): update lint dependencies to ^1.6.0 2025-07-10 16:30:47 +08:00
33528d64a7 chore(deps): update dependency @types/node to ^22.16.2 2025-07-09 08:59:11 +08:00
30559d749e chore(deps): update vite packages 2025-07-07 08:42:16 +08:00
57d07d3cd0 chore(deps): update yanhao98/composite-actions digest to b4a2caa 2025-07-07 07:29:42 +08:00
11f4587681 chore(deps): update all non-major dependencies 2025-07-06 16:21:06 +08:00
669cd7070f chore(deps): update lint dependencies 2025-07-04 13:41:31 +08:00
严浩
5487dc321e feat: enhance responsive design for sidebar and drawer in AppLayout 2025-07-04 12:34:55 +08:00
严浩
ec4906f441 feat: update layout configuration to use naive-ui/AppLayout 2025-07-04 12:06:40 +08:00
严浩
ad8c187edd chore: update GitHub Copilot instructions to define code generation standards 2025-07-04 10:15:09 +08:00
严浩
997df3a4d4 chore(deps): add naive-ui and vfonts dependencies; update styles and router for Naive UI components 2025-07-04 10:14:58 +08:00
42560a4f2c chore(deps): update dependency @splinetool/runtime to ^1.10.19 2025-07-02 12:00:38 +08:00
8f9593957a chore(deps): update lint dependencies to ^1.5.0 2025-07-02 09:31:33 +08:00
0d26da85a1 chore(deps): update dependency vue-tsc to v3 2025-07-02 09:29:20 +08:00
a831d12cf8 chore(deps): update dependency @types/node to ^22.16.0 2025-07-02 07:14:42 +08:00
55a634db68 chore(deps): update dependency @faker-js/faker to ^9.9.0 2025-07-02 04:58:04 +08:00
ec7c877c93 chore(deps): update lint dependencies 2025-07-02 04:53:09 +08:00
382c0b79d0 chore(deps): update dependency cesium to ^1.131.0 2025-07-02 01:56:02 +08:00
167e9a55c1 chore(deps): update dependency @types/three to ^0.178.0 2025-07-02 01:51:49 +08:00
3dcba6a1ef chore(deps): update dependency vue-component-type-helpers to v3 2025-07-01 23:38:51 +08:00
416faf42bf chore(deps): update dependency @splinetool/runtime to ^1.10.18 2025-07-01 18:18:56 +08:00
25f923e0c9 chore(deps): update mcr.microsoft.com/playwright docker tag to v1.53.2 2025-07-01 05:26:59 +08:00
624984e8c0 chore(deps): update dependency @playwright/test to ^1.53.2 2025-07-01 03:11:36 +08:00
8cd623996a chore(deps): update dependency @splinetool/runtime to ^1.10.17 2025-07-01 00:38:27 +08:00
c96a6ba984 chore(deps): update dependency three to ^0.178.0 2025-06-30 22:28:23 +08:00
6159caec26 chore(deps): update dependency alova to ^3.3.4 2025-06-30 22:23:13 +08:00
4181110167 chore(deps): update lint dependencies to ^1.4.0 2025-06-30 16:53:57 +08:00
d359929003 chore(deps): update dependency pinia-plugin-persistedstate to ^4.4.1 2025-06-30 16:49:10 +08:00
6f2550a933 chore(deps): update dependency unplugin-vue-markdown to ^29.1.0 2025-06-30 11:44:28 +08:00
393cc3d9df chore(deps): update dependency @vitejs/plugin-vue-jsx to ^5.0.1 2025-06-30 11:39:54 +08:00
7d1a250e06 chore(deps): update dependency unplugin-vue-markdown to v29 2025-06-30 08:43:26 +08:00
0636256a32 chore(deps): update dependency unplugin-vue-router to ^0.14.0 2025-06-30 06:17:41 +08:00
e45fb54e92 chore(deps): update dependency vue-data-ui to ^2.12.7 2025-06-30 03:13:16 +08:00
c8db9d36da chore(deps): update dependency unplugin-vue-router to ^0.13.0 2025-06-29 17:04:01 +08:00
333d2e47fd chore(deps): update dependency unplugin-vue-components to ^28.8.0 2025-06-29 11:18:35 +08:00
c81b42a81d chore(deps): update dependency pinia-plugin-persistedstate to ^4.4.0 2025-06-28 21:46:11 +08:00
e9bfa60ea1 chore(deps): update dependency @types/node to ^22.15.34 2025-06-28 16:08:49 +08:00
d102a29f04 chore(deps): update vite packages 2025-06-28 08:16:20 +08:00
6d7171dfba chore(deps): update dependency eslint to ^9.30.0 2025-06-28 05:42:22 +08:00
e8d6ca5262 chore(deps): update dependency lucide-vue-next to ^0.525.0 2025-06-28 03:12:15 +08:00
43ef9854b8 chore(deps): update dependency primelocale to ^2.1.4 2025-06-28 01:44:50 +08:00
070cfbad51 chore(deps): update dependency @splinetool/runtime to ^1.10.16 2025-06-28 01:39:29 +08:00
2863d607cf chore(deps): update dependency vue-data-ui to ^2.12.6 2025-06-27 22:45:08 +08:00
c04cbc444f chore(deps): update dependency lucide-vue-next to ^0.524.0 2025-06-27 20:16:29 +08:00
ae189c4add chore(deps): update dependency prettier to v3.6.2 2025-06-27 12:02:33 +08:00
6d603e0bc2 chore(deps): update dependency eslint-plugin-import-x to ^4.16.1 2025-06-27 11:57:14 +08:00
0a95e603bc chore(deps): update dependency @vitest/eslint-plugin to ^1.3.3 2025-06-27 06:13:23 +08:00
0f88d0c35a chore(deps): update pnpm to v10.12.4 2025-06-27 03:10:23 +08:00
bf563c3741 chore(deps): update dependency @splinetool/runtime to ^1.10.15 2025-06-26 21:36:12 +08:00
f49a425870 chore(deps): update dependency unocss to v66.3.2 2025-06-26 13:24:59 +08:00
34af83bc6f chore(deps): update dependency vite-plugin-purgecss-updated-v5 to ^1.2.6 2025-06-26 10:55:13 +08:00
0a43e45020 chore(deps): update dependency @eslint/compat to ^1.3.1 2025-06-25 22:30:19 +08:00
4389195b6f chore(deps): update dependency vite-plugin-static-copy to ^3.1.0 2025-06-25 20:09:27 +08:00
83b0171226 chore(deps): update dependency unocss to v66.3.1 2025-06-25 20:06:49 +08:00
5ac854cb87 chore(deps): update dependency vite-plugin-purgecss-updated-v5 to ^1.2.5 2025-06-25 17:21:29 +08:00
ba7f565e6a chore(deps): update dependency prettier to v3.6.1 2025-06-25 17:18:45 +08:00
f48b00feb4 chore(deps): update dependency eslint-plugin-import-x to ^4.16.0 2025-06-25 14:09:26 +08:00
8d7795e53d chore(deps): update dependency @types/node to ^22.15.33 2025-06-25 02:45:14 +08:00
69fc6b50d8 chore(deps): update all non-major dependencies 2025-06-25 00:26:18 +08:00
e2394c9bd8 chore(deps): update dependency lucide-vue-next to ^0.523.0 2025-06-24 22:03:09 +08:00
31c56c7a4a chore(deps): update dependency @unhead/vue to ^2.0.11 2025-06-24 22:00:25 +08:00
bff4f8d890 chore(deps): update pnpm to v10.12.3 2025-06-24 18:55:39 +08:00
abd974191c chore(deps): update dependency vue-data-ui to ^2.12.5 2025-06-24 13:51:35 +08:00
ed1e88bd55 chore(deps): update lint dependencies to ^1.3.0 2025-06-23 15:58:56 +08:00
严浩
62f92f1fb4 chore: remove copilot instructions file [skip ci] 2025-06-23 14:00:35 +08:00
9424bb23d4 chore(deps): update dependency vue-data-ui to ^2.12.3 2025-06-23 13:33:52 +08:00
c371bc04b5 chore(deps): update dependency prettier to v3.6.0 2025-06-23 11:29:12 +08:00
4a9069e13c chore(deps): update all non-major dependencies 2025-06-23 11:20:36 +08:00
85e6733e53 chore(deps): update lint dependencies 2025-06-22 16:28:20 +08:00
严浩
c9efa9ddf1 chore(config): comment out minifyInternalExports and manualChunks options 2025-06-22 14:40:48 +08:00
严浩
d5001e0e05 chore(deps): remove terser dependency 2025-06-21 23:23:33 +08:00
caa4ae66cc chore(deps): update dependency lucide-vue-next to ^0.522.0 2025-06-21 16:29:18 +08:00
9635c2005f chore(deps): update dependency lucide-vue-next to ^0.519.0 2025-06-21 05:57:23 +08:00
dc6b3506fb chore(deps): update dependency lucide-vue-next to ^0.518.0 2025-06-19 16:22:11 +08:00
f4a64498bf chore(deps): update playwright packages 2025-06-19 03:14:26 +08:00
dfa4a92d54 chore(deps): update dependency lucide-vue-next to ^0.517.0 2025-06-18 16:46:22 +08:00
86d85d1166 chore(deps): update dependency @primeuix/themes to ^1.1.2 2025-06-17 18:48:57 +08:00
1be504569c chore(deps): update dependency lucide-vue-next to ^0.516.0 2025-06-17 05:31:23 +08:00
943993920b chore(deps): update dependency @types/node to ^22.15.32 2025-06-16 19:27:50 +08:00
2abff2791f chore(deps): update dependency vite-plugin-vue-devtools to ^7.7.7 2025-06-15 19:35:09 +08:00
25d3cd09a1 chore(deps): update dependency vite-plugin-static-copy to ^3.0.2 2025-06-14 18:39:03 +08:00
e1a60fd9bc chore(deps): update dependency lucide-vue-next to ^0.515.0 2025-06-13 17:36:02 +08:00
267b00d77a chore(deps): update dependency @pinia/colada to ^0.17.1 2025-06-13 17:31:21 +08:00
63fc76dc29 chore(deps): update dependency primelocale to ^2.1.3 2025-06-12 19:29:20 +08:00
ec5239ab29 chore(deps): update lint dependencies to v1 2025-06-12 09:35:17 +08:00
f4a475d2d6 chore(deps): update dependency @types/node to ^22.15.31 2025-06-11 06:03:59 +08:00
2cb4dd0b18 chore(deps): update playwright packages 2025-06-11 03:15:17 +08:00
c7e7ccc14c chore(deps): update dependency lucide-vue-next to ^0.514.0 2025-06-11 03:10:30 +08:00
4c27108d68 chore(deps): update all non-major dependencies 2025-06-10 01:07:02 +08:00
严浩
0883cb8df5 fix: update layout titles to object format for consistency 2025-06-10 00:44:57 +08:00
严浩
65cd87264f feat: update multiple dependencies 2025-06-10 00:35:25 +08:00
00debaa771 chore(deps): update vite packages 2025-06-08 02:46:13 +08:00
5490cc7531 chore(deps): update dependency @types/node to ^22.15.30 2025-06-06 03:11:52 +08:00
e2a58765f4 chore(deps): update dependency @types/three to ^0.177.0 2025-06-04 00:37:04 +08:00
严浩
12d5af969c fix: disable specific eslint-plugin-import-x rules and update router import 2025-06-03 22:54:15 +08:00
12d1cc4228 chore(deps): update lint dependencies 2025-06-03 10:13:15 +08:00
c29b9da955 chore(deps): update dependency three to ^0.177.0 2025-06-01 05:53:14 +08:00
bc45b0cb22 chore(deps): update dependency @types/node to ^22.15.29 2025-05-31 03:14:38 +08:00
严浩
fa48449105 fix: update vite-plugin-image-tools to version 2.0.1 and configure additional options 2025-05-30 11:59:35 +08:00
b0b687ec01 chore(deps): update unplugin packages 2025-05-26 13:50:08 +08:00
严浩
e8803a9f18 feat: add vue-data-ui dependency and create vue-data-ui page 2025-05-26 12:57:24 +08:00
c3690aa235 chore(deps): update v0.x 2025-05-26 11:57:58 +08:00
376b5a2d54 chore(deps): update lint dependencies to ^0.16.12 2025-05-26 11:28:02 +08:00
2d42497a41 chore(deps): update lint dependencies 2025-05-26 09:52:25 +08:00
e1e14200cd chore(deps): update all non-major dependencies 2025-05-25 23:29:38 +08:00
15eeb6f5ee chore(deps): update vite packages 2025-05-22 11:03:48 +08:00
f7a70cbf85 chore(deps): update dependency @types/node to ^22.15.21 2025-05-21 20:01:34 +08:00
e71936c3c4 chore(deps): update lint dependencies 2025-05-21 02:16:43 +08:00
1dfca31c33 chore(deps): update lint dependencies to ^0.16.11 2025-05-19 14:11:17 +08:00
e1c8663d47 Merge pull request 'chore(deps): update dependency @types/node to ^22.15.19' (#253) from renovate/types into main
[skip ci]
2025-05-19 13:45:48 +08:00
98f5c40034 chore(deps): update dependency @types/node to ^22.15.19 2025-05-19 08:55:23 +08:00
3bb892a507 chore(deps): update dependency eslint-plugin-perfectionist to ^4.13.0 2025-05-15 21:35:19 +08:00
cbc0da3ac8 chore(deps): update dependency vite-plugin-image-tools to ^1.2.2 2025-05-15 18:52:35 +08:00
严浩
7cc05ab132 feat: .gitattributes.MD 2025-05-15 16:49:02 +08:00
a65ca6a6b8 chore(deps): update dependency @types/node to ^22.15.18 2025-05-14 13:23:59 +08:00
d12c00cb4b chore(deps): update vite packages 2025-05-13 07:56:51 +08:00
58e6119436 chore(deps): update dependency eslint-plugin-import-x to ^4.11.1 2025-05-12 20:19:14 +08:00
0b195d16a1 chore(deps): update lint dependencies to ^0.16.10 2025-05-12 02:49:37 +08:00
4b73f0e310 Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 3bf0746' (#247) from renovate/github-actions into main
[no ci]
2025-05-12 00:58:01 +08:00
3c2145a626 chore(deps): update yanhao98/composite-actions digest to 3bf0746 2025-05-11 22:10:33 +08:00
f432045ffe chore(deps): update dependency vite-plugin-static-copy to v3 2025-05-11 20:18:27 +08:00
0cd7080cee chore(deps): update dependency lint-staged to v16 2025-05-11 03:32:51 +08:00
05cc3f929a chore(deps): update v0.x 2025-05-10 01:57:03 +08:00
053501eff7 chore(deps): update dependency @types/node to ^22.15.17 2025-05-09 18:55:25 +08:00
严浩
ab70388a53 fix: 格式化 PinPuYi.vue 2025-05-09 16:51:03 +08:00
严浩
f34202febb feat: 频谱仪: 添加幅度菜单和单位支持 2025-05-09 16:48:07 +08:00
aafff004f4 chore(deps): update dependency vite to ^6.3.5 2025-05-08 21:51:14 +08:00
4c034e2f9c Merge pull request 'chore(deps): update lint dependencies' (#239) from renovate/lint into main
[skip ci ]
2025-05-08 20:38:38 +08:00
0d535f80d5 chore(deps): update lint dependencies 2025-05-08 19:36:43 +08:00
323fc6cce0 chore(deps): update dependency lucide-vue-next to ^0.508.0 2025-05-08 18:42:28 +08:00
60272a8f0a chore(deps): update dependency @types/node to ^22.15.16 2025-05-08 18:42:12 +08:00
d74b6fe015 chore(deps): update dependency unplugin-auto-import to ^19.2.0 2025-05-08 18:38:44 +08:00
d1980c567c chore(deps): update primevue pkgs 2025-05-08 18:38:28 +08:00
严浩
cef9ed6381 refactor: 优化频谱仪状态管理代码格式,提升可读性 2025-05-08 18:15:46 +08:00
严浩
b850ae82ae feat: 添加跨度菜单列表及其项目,更新频谱仪状态管理 2025-05-08 18:10:27 +08:00
严浩
243b2bbd6b fix: 移除不必要的 source.fixAll 配置 2025-05-08 14:07:40 +08:00
cd63d1d30b chore(deps): update dependency @types/node to ^22.15.14 2025-05-07 18:13:45 +08:00
0f9865d4d5 chore(deps): update dependency eslint-plugin-unicorn to ^59.0.1 2025-05-07 03:14:48 +08:00
1a6e5d0240 chore(deps): update lint dependencies to ^0.16.9 2025-05-06 13:50:00 +08:00
e70834af8f chore(deps): update dependency @types/node to ^22.15.11 2025-05-06 11:06:18 +08:00
bf10aa44da chore(deps): update lint dependencies 2025-05-06 08:18:40 +08:00
3e217c0db7 chore(deps): update dependency @types/node to ^22.15.7 2025-05-06 05:30:58 +08:00
cff5c098d8 chore(deps): update all non-major dependencies 2025-05-05 00:38:44 +08:00
严浩
7344359d1e chore(deps): 更新 eslint-plugin-unicorn 到 v59.0.0 2025-05-04 21:25:48 +08:00
5b42efc166 chore(deps): update vite packages 2025-05-03 16:52:33 +08:00
严浩
e5dd7aa70e chore: configure commitlint 2025-05-03 16:00:52 +08:00
ce914d3275 chore(deps): update dependency npm-run-all2 to v8 2025-05-03 03:28:48 +08:00
严浩
695e583136 feat: 添加对 .md 文件的类型声明 2025-05-02 23:10:38 +08:00
6f06a36e44 chore(deps): update lint dependencies to ^0.16.8 2025-04-29 18:49:09 +08:00
cf4ea2da16 chore(deps): update dependency @types/node to ^22.15.3 2025-04-29 13:04:42 +08:00
8aa35a14fb chore(deps): update dependency p5 to v2 2025-04-29 11:56:25 +08:00
6b52b7e87c chore(deps): update lint dependencies 2025-04-28 20:02:41 +08:00
严浩
6299b6d45f PinPuYi 2025-04-28 17:44:11 +08:00
严浩
5fe08b0741 PinPuYi 2025-04-28 17:02:47 +08:00
严浩
1ca2a17e4f 频谱仪: 屏幕照片 2025-04-28 15:41:26 +08:00
严浩
a44a455eca fix: 修改频率菜单和状态变量名称为中文 2025-04-28 14:05:24 +08:00
严浩
dd1af6964d feat(deps): add vite-plugin-image-tools dependency 2025-04-27 22:59:23 +08:00
0ceaa5a583 chore(deps): update dependency @intlify/unplugin-vue-i18n to ^6.0.8 2025-04-27 18:21:19 +08:00
严浩
c4a50b3642 临时屏幕.png 2025-04-27 18:08:35 +08:00
严浩
12bbc843fb PinPuYi.vue 2025-04-27 17:26:55 +08:00
严浩
e2c2cb165a PinPuYi.vue 2025-04-27 17:20:41 +08:00
严浩
c6f27856ef refactor(ci): split surge cleanup into separate job 2025-04-27 14:10:56 +08:00
608cd08ca5 chore(deps): update types 2025-04-25 20:22:47 +08:00
严浩
6b6ddeb23b PinPuYi.vue 2025-04-25 18:28:25 +08:00
严浩
1c73834dc8 PinPuYi.vue 2025-04-25 16:49:26 +08:00
严浩
e10538baca Add Helvetica-Light-05 font files in TTF, WOFF, and WOFF2 formats 2025-04-25 12:04:05 +08:00
严浩
e9c59a3634 PinPuYi.vue 2025-04-24 19:08:05 +08:00
严浩
2d6831fcb0 style(PinPuYi.vue): adjust image styling for better responsiveness 2025-04-24 17:51:46 +08:00
严浩
4164134a4e PinPuYi.vue 2025-04-24 17:50:59 +08:00
严浩
6c8529185a chore(vite.config): add unplugin-vue-router data loaders to optimizeDeps 2025-04-24 17:32:07 +08:00
严浩
6a615bcc43 chore(gitattributes): add .spline file type as binary 2025-04-24 17:27:04 +08:00
09e727dad5 chore(deps): update dependency tdesign-icons-vue-next to ^0.3.6 2025-04-24 10:39:43 +08:00
严浩
5a16b62008 chore(docs): add .gitattributes section to README and remove redundant content 2025-04-24 10:29:35 +08:00
4d6380f141 chore(deps): update dependency eslint-plugin-perfectionist to ^4.12.2 2025-04-24 07:34:15 +08:00
11acb951d4 chore(deps): update dependency three to ^0.176.0 2025-04-23 18:15:02 +08:00
94472a4d8d chore(deps): update lint dependencies 2025-04-22 11:33:38 +08:00
74d4f87f4b chore(deps): update v0.x 2025-04-22 09:35:50 +08:00
67cee3b845 chore(deps): update lint dependencies to ^0.16.7 2025-04-22 09:35:13 +08:00
71b768b3a2 chore(deps): update devdependencies 2025-04-22 08:42:06 +08:00
2cc080c579 chore(deps): update vite packages 2025-04-21 13:40:13 +08:00
5354f942e6 chore(deps): update dependency @intlify/unplugin-vue-i18n to ^6.0.7 2025-04-21 10:36:33 +08:00
e0445a2737 chore(deps): update playwright packages 2025-04-21 01:45:58 +08:00
8e9e4ca392 chore(deps): update dependency @types/node to ^22.14.1 2025-04-20 23:25:18 +08:00
严浩
aacca31384 feat: 更新随机图片选择逻辑,支持随机选择2或3 2025-04-20 22:11:51 +08:00
严浩
5f9eeddc77 feat: 添加随机图片功能 2025-04-20 22:09:08 +08:00
严浩
f4e248a2b1 feat: vite-assets 2025-04-20 22:06:49 +08:00
b85df83f8c chore(deps): update dependency @intlify/unplugin-vue-i18n to ^6.0.6 2025-04-20 12:06:24 +08:00
严浩
4433f01b81 fix: 补充 Plotly 嵌套 iframe 的注释和格式 2025-04-18 14:04:44 +08:00
严浩
defc842edf **/**.noformat.json 2025-04-18 13:47:58 +08:00
严浩
3daf4b2e21 feat: plotly-locale-zh-cn.js 2025-04-18 12:51:30 +08:00
严浩
edcf30a4d8 Iframe-PlotlyJs-Comp.vue 2025-04-18 12:47:31 +08:00
严浩
1f959b1d1b Iframe-PlotlyJs-Comp.vue 2025-04-18 12:05:53 +08:00
严浩
ebed0cf4bf Iframe-PlotlyJs.page.vue 2025-04-17 19:28:47 +08:00
严浩
95d5087b5e feat: add Plotly.js usage page 2025-04-17 14:57:17 +08:00
严浩
784c5ff56d chore: 添加 'unplugin-vue-router' 到优化依赖列表 2025-04-17 13:40:58 +08:00
42094e3571 chore(deps): update dependency @primeuix/themes to ^1.0.3 2025-04-16 21:46:04 +08:00
严浩
9f82dc6dcb Dom-Draggable.page.vue 2025-04-16 19:20:22 +08:00
严浩
b9ae95bfee dd 2025-04-16 18:11:40 +08:00
严浩
182d87c773 chore: 将变量名翻译为中文以提高可读性 2025-04-16 14:56:53 +08:00
严浩
5766bb828e chore: update satellite.js to version 6.0.0 and improve position validation in calculations 2025-04-15 17:02:52 +08:00
严浩
22e5c31f58 SortableJS.page.vue 2025-04-15 14:17:43 +08:00
b43b076b9d chore(deps): update lint dependencies to ^0.16.6 2025-04-14 21:49:05 +08:00
d47c02f743 chore(deps): update dependency vite to ^6.2.6 2025-04-14 21:46:01 +08:00
391e4fcb85 chore(deps): update dependency unplugin-vue-components to ^28.5.0 2025-04-14 21:45:25 +08:00
41b12e297b chore(deps): update yanhao98/composite-actions digest to 4470aa1 2025-04-14 15:16:46 +08:00
6fd22d573e chore(deps): update yanhao98/composite-actions digest to 2b8fce6 2025-04-11 19:46:41 +08:00
严浩
e9676c120d _星座样本IQ.no-lint.ts 2025-04-11 17:52:28 +08:00
严浩
09b1e481e5 waterfallwidget.js.grok-3-beta.md 2025-04-11 15:46:41 +08:00
严浩
82bbcc8ac6 waterfallwidget.js.暂存.md 2025-04-11 15:19:17 +08:00
严浩
a0fe6da933 playwright.yaml 2025-04-11 10:06:28 +08:00
严浩
704d7d5481 playwright.yaml 2025-04-11 09:56:19 +08:00
严浩
f65ef2770a playwright.yaml 2025-04-11 09:48:40 +08:00
严浩
8db3b59854 playwright.yaml 2025-04-11 09:34:20 +08:00
严浩
32005cd4d3 🧹 清理 Surge 部署 2025-04-11 09:14:10 +08:00
严浩
7a8a236243 增加对 .VSCodeCounter 文件的忽略 2025-04-10 16:34:43 +08:00
严浩
89ebb71b9d 频谱: iframe-page-comps/IframeSpectrogram.vue 2025-04-10 16:30:32 +08:00
严浩
106eba0053 ConstellationDiagram.html.md 2025-04-10 15:46:50 +08:00
严浩
6b99afc240 整理 2025-04-10 14:57:37 +08:00
严浩
95fddc8d20 整理文件 2025-04-10 14:55:44 +08:00
1a4a169222 chore(deps): update dependency vite-plugin-static-copy to ^2.3.1 2025-04-09 20:18:16 +08:00
2cec135a8f chore(deps): update all non-major dependencies 2025-04-09 19:14:37 +08:00
严浩
2290dc2edf 优化数据发送逻辑,确保使用原始数据对象,并增加 props.data 变化的日志记录 2025-04-09 18:17:00 +08:00
严浩
318fa4ea33 IframeConstellationDiagram.vue 2025-04-09 18:07:11 +08:00
严浩
4e56d311f1 html-page 2025-04-09 18:01:33 +08:00
严浩
0dcc47519f Spectrogram.html 2025-04-08 18:00:44 +08:00
严浩
ef48de3846 Spectrogram.html 2025-04-08 17:51:10 +08:00
严浩
1a7831c003 升级依赖 2025-04-08 15:48:43 +08:00
严浩
a6367058b5 语图page 2025-04-08 15:17:03 +08:00
mini2024
7abc6a8f65 fix: 添加 @ts-nocheck 注释以禁用 TypeScript 检查 2025-04-07 21:49:41 +08:00
严浩
371b7a71c9 星座图 2025-04-07 16:56:04 +08:00
严浩
c06cc44cbd vite optimizeDeps 2025-04-07 10:42:44 +08:00
严浩
2721b4c2f3 refactor(cesium): 重构 Cesium Viewer 状态管理和 Composable 2025-04-07 10:00:30 +08:00
严浩
d7d704d120 fix: 卫星列表初始化逻辑 2025-04-07 09:11:12 +08:00
mini2024
9e8affc52d chore: 更新项目配置并添加 p5.js 粒子演示 2025-04-06 21:38:06 +08:00
mini2024
aecfd22055 chore(husky): 更新钩子脚本注释和 emoji [no ci] 2025-04-06 19:49:41 +08:00
mini2024
bf68819b4a feat: 适配 FlowbiteSidebar 页面 iPhone 横屏安全区域 2025-04-06 19:18:00 +08:00
a5dd8f3e52 Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 7797ee9' (#183) from renovate/github-actions into main
[no ci]
2025-04-06 13:17:11 +08:00
0f8ad09ecd chore(deps): update yanhao98/composite-actions digest to 7797ee9 2025-04-06 06:08:15 +08:00
mini2024
3cad491aac Merge remote-tracking branch 'origin/main' 2025-04-06 03:03:17 +08:00
mini2024
f77b63cb8c chore: 移除 @unocss/preset-rem-to-px 依赖 2025-04-06 03:03:07 +08:00
20fa8e67d1 Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 90b5130' (#182) from renovate/github-actions into main
[no ci]
2025-04-06 02:58:27 +08:00
8c556dae8f chore(deps): update yanhao98/composite-actions digest to 90b5130 2025-04-06 02:12:19 +08:00
a42282212e chore(deps): update lint dependencies to ^0.16.4 (patch) (#175) [no ci]
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [eslint-plugin-oxlint](https://github.com/oxc-project/eslint-plugin-oxlint) | [`^0.16.3` -> `^0.16.4`](https://renovatebot.com/diffs/npm/eslint-plugin-oxlint/0.16.3/0.16.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [oxlint](https://oxc.rs) ([source](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`^0.16.3` -> `^0.16.4`](https://renovatebot.com/diffs/npm/oxlint/0.16.3/0.16.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.16.3/0.16.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/eslint-plugin-oxlint (eslint-plugin-oxlint)</summary>

### [`v0.16.4`](https://github.com/oxc-project/eslint-plugin-oxlint/releases/tag/v0.16.4)

[Compare Source](https://github.com/oxc-project/eslint-plugin-oxlint/compare/v0.16.3...v0.16.4)

*No significant changes*

#####     [View changes on GitHub](https://github.com/oxc-project/eslint-plugin-oxlint/compare/v0.16.3...v0.16.4)

</details>

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.16.4`](https://github.com/oxc-project/oxc/releases/tag/oxlint_v0.16.4): oxlint v0.16.4

[Compare Source](https://github.com/oxc-project/oxc/compare/oxlint_v0.16.3...oxlint_v0.16.4)

#### \[0.16.4] - 2025-04-01

-   [`da6336c`](https://github.com/oxc-project/oxc/commit/da6336c) language_server: \[**BREAKING**] Remove `enable` configuration, the client should shutdown the server instead ([#&#8203;9990](https://github.com/oxc-project/oxc/issues/9990)) (Sysix)

##### Features

-   [`06e3db9`](https://github.com/oxc-project/oxc/commit/06e3db9) linter: Support `multipleFileExtensions` option for `unicorn/filename-case` ([#&#8203;10118](https://github.com/oxc-project/oxc/issues/10118)) (shulaoda)
-   [`dbe0e46`](https://github.com/oxc-project/oxc/commit/dbe0e46) linter: Support `ignore` option for `unicorn/filename-case` ([#&#8203;10107](https://github.com/oxc-project/oxc/issues/10107)) (shulaoda)

##### Bug Fixes

-   [`0a33e27`](https://github.com/oxc-project/oxc/commit/0a33e27) editor: Update `initializationOptions` for a possible restart ([#&#8203;10121](https://github.com/oxc-project/oxc/issues/10121)) (Sysix)
-   [`ac780a2`](https://github.com/oxc-project/oxc/commit/ac780a2) editor: Repair filewatchers when no custom config provided ([#&#8203;10104](https://github.com/oxc-project/oxc/issues/10104)) (Sysix)
-   [`4303ace`](https://github.com/oxc-project/oxc/commit/4303ace) editor: Dont send `didChangeConfiguration` request to the server when it is shutdown ([#&#8203;10084](https://github.com/oxc-project/oxc/issues/10084)) (Sysix)
-   [`07f2a25`](https://github.com/oxc-project/oxc/commit/07f2a25) editor: Enable regex parsing in language server ([#&#8203;10035](https://github.com/oxc-project/oxc/issues/10035)) (camchenry)
-   [`fcf7702`](https://github.com/oxc-project/oxc/commit/fcf7702) language_server: Start from a default oxlint configuration + SafeFix for nested configuration ([#&#8203;10043](https://github.com/oxc-project/oxc/issues/10043)) (Sysix)
-   [`aba3654`](https://github.com/oxc-project/oxc/commit/aba3654) linter: Span disable directive correctly on next line ([#&#8203;10141](https://github.com/oxc-project/oxc/issues/10141)) (Ulrich Stark 🦀)
-   [`2c80858`](https://github.com/oxc-project/oxc/commit/2c80858) linter: Enable multi-file analysis for nested configs ([#&#8203;10089](https://github.com/oxc-project/oxc/issues/10089)) (camchenry)

##### Performance

-   [`566be59`](https://github.com/oxc-project/oxc/commit/566be59) linter: Replace `phf_set` with `array` in `eslint/func-names` ([#&#8203;10119](https://github.com/oxc-project/oxc/issues/10119)) (shulaoda)
-   [`5e14fe9`](https://github.com/oxc-project/oxc/commit/5e14fe9) linter: Inline `PRE_DEFINE_VAR` and use `array` format ([#&#8203;10079](https://github.com/oxc-project/oxc/issues/10079)) (shulaoda)

##### Refactor

-   [`327be53`](https://github.com/oxc-project/oxc/commit/327be53) editor: `LanguageClient` can be undefined ([#&#8203;10112](https://github.com/oxc-project/oxc/issues/10112)) (Sysix)
-   [`5ec477c`](https://github.com/oxc-project/oxc/commit/5ec477c) editor: Make `onConfigChange` async ([#&#8203;10110](https://github.com/oxc-project/oxc/issues/10110)) (Sysix)
-   [`a278d73`](https://github.com/oxc-project/oxc/commit/a278d73) editor: Use warning background when the plugin is deactived ([#&#8203;10085](https://github.com/oxc-project/oxc/issues/10085)) (Sysix)
-   [`c0e5251`](https://github.com/oxc-project/oxc/commit/c0e5251) language_server: Set `null` as a default value for `configPath` ([#&#8203;10047](https://github.com/oxc-project/oxc/issues/10047)) (Sysix)
-   [`09c0ac6`](https://github.com/oxc-project/oxc/commit/09c0ac6) linter: Improve `unicorn/filename-case` ([#&#8203;10117](https://github.com/oxc-project/oxc/issues/10117)) (shulaoda)
-   [`d8e49a1`](https://github.com/oxc-project/oxc/commit/d8e49a1) linter: Compute lintable extensions at compile time ([#&#8203;10090](https://github.com/oxc-project/oxc/issues/10090)) (camchenry)
-   [`b3ec235`](https://github.com/oxc-project/oxc/commit/b3ec235) linter: Use items of `oxc_ast::ast` module directly ([#&#8203;10100](https://github.com/oxc-project/oxc/issues/10100)) (Ulrich Stark 🦀)
-   [`93e6c0b`](https://github.com/oxc-project/oxc/commit/93e6c0b) linter: Use `FormalParameter::has_modifier` to detect parameter properties ([#&#8203;10097](https://github.com/oxc-project/oxc/issues/10097)) (Ulrich Stark 🦀)

##### Testing

-   [`410b8d6`](https://github.com/oxc-project/oxc/commit/410b8d6) editor: Use debug build of the language server ([#&#8203;10083](https://github.com/oxc-project/oxc/issues/10083)) (Sysix)
-   [`500add0`](https://github.com/oxc-project/oxc/commit/500add0) editor: Add test for `oxc.fixAll` command ([#&#8203;10045](https://github.com/oxc-project/oxc/issues/10045)) (Sysix)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzAuMSIsInVwZGF0ZWRJblZlciI6IjM5LjIzMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Reviewed-on: https://git.1-h.cc/examples/vue-ts-example/pulls/175
Co-authored-by: Renovate Bot <renovatebot@oo1.dev>
Co-committed-by: Renovate Bot <renovatebot@oo1.dev>
2025-04-06 01:44:29 +08:00
0344b51b40 chore(deps): update dependency typescript to ~5.8.3 2025-04-05 21:44:17 +08:00
mini2024
5e652561a1 feat: gitattributes [no ci] 2025-04-05 21:18:02 +08:00
mini2024
4ee2048ae4 fix: 在 lint-staged 中添加 oxlint 修复命令 2025-04-05 16:31:42 +08:00
mini2024
f0c6290e89 chore: 添加 Vercel 工作流文档链接 [no ci] 2025-04-05 15:43:22 +08:00
42a367e83b chore(deps): update yanhao98/composite-actions digest to 2a7a660 2025-04-04 22:22:08 +08:00
bd4dd95c2a chore(deps): update dependency @primeuix/themes to ^1.0.1 2025-04-04 00:21:10 +08:00
mini2024
cc3567d022 fix: 更新 Vercel 工作流 [no ci] 2025-04-03 23:53:29 +08:00
mini2024
5b28d93122 fix: 在 Vercel 工作流中启用 Vue 生产环境开发工具 2025-04-03 22:37:28 +08:00
严浩
ecd82d30a4 fix: 更新 lint-staged 配置,支持 .tsx 文件格式 2025-04-03 15:26:27 +08:00
严浩
5a69629f35 feat: 添加地面站实体创建逻辑,优化管理器功能 2025-04-03 15:12:11 +08:00
严浩
aacd168450 refactor: 更新地面站和卫星状态接口,优化类型命名和属性结构 2025-04-03 13:32:12 +08:00
严浩
b742597f61 fix: 更新卫星和地面站管理器中的选项解构,确保 showOrbit 属性的正确性 2025-04-03 13:29:25 +08:00
严浩
61e6b21d90 refactor: 重命名卫星和地面站管理器函数,优化代码可读性 2025-04-03 13:22:35 +08:00
严浩
64e82fbb7f refactor: 重命名管理器类并更新相关类型导入,优化代码结构 2025-04-03 13:17:34 +08:00
严浩
29678d8f61 fix: 修复卫星和地面站管理器中的多余括号和注释,优化代码可读性 2025-04-03 12:47:15 +08:00
严浩
8198fefe91 Refactor: 重构 HCesiumManager 2025-04-03 12:35:24 +08:00
b1c8597936 chore(deps): update yanhao98/composite-actions digest to 2d4be1a 2025-04-03 01:54:34 +08:00
mini2024
fbff6e0542 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	.github/workflows/playwright.yaml
#	.github/workflows/vercel.yaml
2025-04-03 00:47:24 +08:00
fb71cf676c Merge pull request 'chore(deps): update yanhao98/composite-actions digest to 2d4be1a' (#173) from renovate/github-actions into main
[no ci]
2025-04-03 00:39:53 +08:00
mini2024
3d20ff6e83 feat: 优化 GitHub Actions 工作流中的步骤名称以增强可读性 [skip ci] 2025-04-03 00:39:18 +08:00
mini2024
bcec165c2f feat: 为 GitHub Actions 步骤添加名称以增强可读性 2025-04-03 00:27:03 +08:00
严浩
7261a45cab feat: 更新地面站和卫星状态管理为使用 Set 2025-04-02 19:09:00 +08:00
严浩
02ce0fa9a0 h-cesium-viewer 2025-04-02 16:39:15 +08:00
严浩
098a769dbd feat: 为卫星实体和覆盖范围添加随机基色 2025-04-02 15:33:33 +08:00
严浩
db9d394171 feat: 添加卫星覆盖范围计算功能 2025-04-02 15:17:47 +08:00
175ed3450d chore(deps): update yanhao98/composite-actions digest to 2d4be1a 2025-04-02 15:08:11 +08:00
严浩
751b890a80 fix: 添加 import-x/no-unresolved 规则的注释,提供更多上下文信息 [no ci] 2025-04-02 11:38:14 +08:00
严浩
1fecb5cb92 refactor: 重新组织 ESLint 插件配置,添加区域注释以提高可读性 2025-04-02 11:31:45 +08:00
严浩
2bc762a89b feat: 更新 ESLint 配置 2025-04-02 11:27:09 +08:00
9ed4c0bc6b chore(deps): update dependency primelocale to ^2.1.2 2025-04-02 10:01:34 +08:00
9daafb9157 chore(deps): update yanhao98/composite-actions digest to 502d85b 2025-04-02 09:54:08 +08:00
严浩
162f9a9a13 feat: 添加 vite-plugin-checker 插件,配置 ESLint 和 Vue 类型检查 2025-04-02 09:51:35 +08:00
严浩
15980dd551 feat: 添加 husky 钩子,支持 commit-msg 和 post-merge 自动提示,更新 pre-commit 钩子中文提示 2025-04-01 19:18:31 +08:00
严浩
da515f4dfc feat: 添加卫星管理功能,新增 SatelliteOptions 接口,更新相关组件以支持卫星实体的添加和移除 2025-04-01 18:00:25 +08:00
严浩
13681003fb fix: 更新代码注释规范,强调注释应解释逻辑和设计意图,禁止描述代码状态或历史 2025-04-01 18:00:18 +08:00
严浩
21660cd9fa fix: 整理代码 2025-04-01 16:34:05 +08:00
严浩
99b5aeb042 feat: 重构地面站管理逻辑,新增 GroundStationState 接口,优化组件间数据传递 2025-04-01 16:20:22 +08:00
严浩
c829eaaf71 fix: 更新代码注释规范,强调注释应解释复杂逻辑和设计意图,而非重复代码或描述位置 2025-04-01 16:15:39 +08:00
严浩
f32c52867f fix: 更新代码注释规范,强调注释应解释复杂逻辑而非重复代码 2025-04-01 15:28:35 +08:00
严浩
5c92482122 fix: 修正移除站点逻辑,确保从选中项中正确移除已删除的站点 2025-04-01 14:32:32 +08:00
严浩
302151eea8 fix: 修正页面加载路径,确保测试正确访问首页 2025-04-01 12:14:45 +08:00
严浩
9166bca070 fix: 调整类型检查步骤的位置,确保构建后进行类型检查 2025-04-01 11:58:55 +08:00
严浩
8f2a77702b feat: InspiraUI 2025-04-01 11:55:16 +08:00
严浩
db2c210a09 fix: message.not-used [no ci] 2025-04-01 09:23:16 +08:00
严浩
7072f171e0 refactor: 重构 Cesium 相关代码 2025-04-01 01:51:02 +08:00
严浩
2c6a4287d2 feat: 整理 HCesiumViewerCls 相关 2025-03-31 20:43:13 +08:00
严浩
4636f9fde4 feat: 添加 Cesium Viewer 组件及相关功能,优化项目结构和配置 2025-03-31 19:43:56 +08:00
32bc83e16e chore(deps): update dependency vite to ^6.2.4 2025-03-31 18:35:50 +08:00
严浩
12d5ee979f feat: 优化响应式布局,使用 CSS 支持的单位调整屏幕高度和宽度 2025-03-31 15:30:45 +08:00
严浩
3edb7392ce feat: 全局 FluidCursor 2025-03-31 15:25:40 +08:00
严浩
b16c038be9 feat: 删除 OrbPro 相关文件和依赖,清理代码库 2025-03-31 15:15:54 +08:00
严浩
aea90d1f0d feat: 优化首页模板,移除不必要的条件渲染,调整样式以适应全屏 2025-03-31 14:47:13 +08:00
严浩
334b2485f5 feat: 添加 InspiraUI 组件和模式背景功能,更新依赖项 2025-03-31 14:11:30 +08:00
严浩
6e18e0f737 feat: 添加 headless 测试支持和修复接口请求页面的路径 2025-03-31 12:36:39 +08:00
mini2024
5ba87212b9 feat: 添加 oxlint 配置文件并更新 lint 脚本 2025-03-31 01:41:17 +08:00
mini2024
3adafb58d5 chore: 移除不必要的 oxlint lint 脚本 2025-03-31 00:57:16 +08:00
mini2024
2fd7f2fcbc feat: 更新配置和依赖项,启用 oxc 插件,移除 cdnImport 插件 2025-03-31 00:55:50 +08:00
mini2024
0cc1730b2c feat: 更新依赖 2025-03-31 00:38:50 +08:00
mini2024
8d0ed93ee0 feat: 添加 InspiraUI 组件 2025-03-30 23:45:01 +08:00
mini2024
05d6a71da8 feat: <Isolate /> 2025-03-30 22:58:32 +08:00
严浩
4b109a0829 chore(deps): 更新多个依赖项版本 2025-03-28 09:58:16 +08:00
mini2024
0cf585be90 手动修复 2025-03-28 00:43:59 +08:00
mini2024
ba91e845d5 帮我把 @unhead/vue 的版本升级到 v2.0.2
---
title: Migrate Your TypeScript App To Unhead v2
description: Learn about how to migrate to TypeScript Unhead v2 from v1.
navigation:
  title: Upgrade Guide
---

## Introduction

With the release of Unhead v2, we now have first-class support for other frameworks. However, this guide will focus on
the changes that affec TypeScript users.

The high-level of Unhead v2 was to remove deprecations and remove the implicit context implementation.

### Legacy Support

Unhead v2 is mostly fully backwards compatible with Unhead v1.

While not recommended, if upgrading is not possible for you, you can change your imports to the following:

```diff [TypeScript]
-import { createServerHead, useHead } from 'unhead'
+import { createServerHead, useHead } from 'unhead/legacy'
```

This will be removed in a future minor version, so you should lock your dependencies to the version that works for you.

## Client / Server Subpath Exports

🚦 Impact Level: Critical

**⚠️ Breaking Changes:**

- `createServerHead()`{lang="ts"} and `createHead()`{lang="ts"} exports from `unhead` are removed

The path where you import `createHead` from has been updated to be a subpath export.

Please follow the updated installation instructions or simply update the import to use the subpath.

**Client bundle:**

```diff
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/client'

// avoids bundling server plugins
createHead()
```

**Server bundle:**

```diff
-import { createServerHead } from 'unhead'
+import { createHead } from 'unhead/server'

// avoids bundling server plugins
-createServerHead()
+createHead()
```

## Removed Implicit Context

🚦 Impact Level: Critical

**⚠️ Breaking Changes:**

- `getActiveHead()`{lang="ts"}, `activeHead`{lang="ts"} exports are removed

The implicit context implementation kept a global instance of Unhead available so that you could use the `useHead()`{lang="ts"} composables
anywhere in your application.

```ts
useHead({
  title: 'This just worked!'
})
```

While safe client side, this was a leaky abstraction server side and led to memory leaks in some cases.

In v2, the core composables no longer have access to the Unhead instance. Instead, you must pass the Unhead instance to the composables.

::note
Passing the instance is only relevant if you're importing from `unhead`. In JavaScript frameworks we tie the context to the framework itself so you
don't need to worry about this.
::

::code-group

```ts [TypeScript v2]
import { useHead } from 'unhead'

// example of getting the instance
const unheadInstance = useMyApp().unhead
useHead(unheadInstance, {
  title: 'Looks good'
})
```

```ts [TypeScript v1]
import { useHead } from 'unhead'

useHead({
  title: 'Just worked! But with SSR issues'
})
```

::

## Removed `vmid`, `hid`, `children`, `body`

🚦 Impact Level: High

For legacy support with Vue Meta we allowed end users to provide deprecated properties:  `vmid`, `hid`, `children` and `body`.

You must either update these properties to the appropriate replacement, remove them, or you can use the `DeprecationsPlugin`.

**Meta tags with `vmid`, `hid`**

These are already deduped magically so you can safely remove them there.

```diff
useHead({
  meta: [{
    name: 'description',
-   vmid: 'description'
-   hid: 'description'
  }]
})
```

**Other Tags with `vmid`, `hid`**

Use `key` if you need the deduplication feature. This is useful for tags that may change from server to client
rendering.

```diff
useHead({
  script: [{
-   vmid: 'my-key'
-   hid: 'my-key'
+   key: 'my-key',
  }]
})
```

**Using `children`**

The `children` key is a direct replacement of `innerHTML` which you should use instead.

::Caution
When migrating your code ensure that you're not dynamically setting `innerHTML` as this can lead to XSS vulnerabilities.
::

```diff
useHead({
  script: [
      {
-        children: '..'
+        innerHTML: '..'
      }
   ]
})
```

**Using `body`**

The `body` key should be updated to use the Tag Position feature.

```diff
useHead({
  script: [
      {
-        body: true
+        tagPosition: 'bodyClose'
      }
   ]
})
```

**Use Deprecations Plugin**

```ts
import { createHead } from 'unhead'
import { DeprecationsPlugin } from 'unhead/plugins'

const unhead = createHead({
  plugins: [DeprecationsPlugin]
})
```

## Opt-in Template Params & Tag Alias Sorting

🚦 Impact Level: High

To reduce the bundle size and improve performance, we've moved the template params and tag alias sorting to optional plugins.

If you'd like to continue using these, please opt-in to the plugins.

```ts
import { AliasSortingPlugin, TemplateParamsPlugin } from 'unhead/plugins'

createHead({
  plugins: [TemplateParamsPlugin, AliasSortingPlugin]
})
```

## Vue 2 Support

🚦 Impact Level: Critical

Unhead v2 no longer supports Vue v2. If you're using Vue v2, you will need to lock your dependencies to the latest v1 version of Unhead.

## Promise Input Support

🚦 Impact Level: Medium

If you have promises as input they will no longer be resolved, either await the promise before passing it along or register the optional promises plugin.

**Option 1: Await Promise**

```diff
useHead({
  link: [
    {
-     href: import('~/assets/MyFont.css?url'),
+     href: await import('~/assets/MyFont.css?url'),
      rel: 'stylesheet',
      type: 'text/css'
    }
  ]
})
```

**Option 2: Promise Plugin**

```ts
import { PromisePlugin } from 'unhead/plugins'

const unhead = createHead({
  plugins: [PromisePlugin]
})
```

## Updated `useScript()`{lang="ts"}

🚦 Impact Level: High

**⚠️ Breaking Changes:**

- Script instance is no longer augmented as a proxy and promise
- `script.proxy`{lang="ts"} is rewritten for simpler, more stable behavior
- `stub()`{lang="ts"} and runtime hook `script:instance-fn` are removed

**Replacing promise usage**

If you're using the script as a promise you should instead opt to use the `onLoaded()` functions.

```diff
const script = useScript()

-script.then(() => console.log('loaded')
+script.onLoaded(() => console.log('loaded'))
```

**Replacing proxy usage**

If you're accessing the underlying API directly from the script instance, you will now need to only access it from the `.proxy`.

```diff
const script = useScript('..', {
  use() { return { foo: [] } }
})

-script.foo.push('bar')
+script.proxy.foo.push('bar')
```

**Replacing `stub()`**

If you were using stub for anything you should replace this with either custom `use()` behavior.

```diff
const script = useScript('...', {
-  stub() { return { foo: import.meta.server ? [] : undefined } }
})

+script.proxy = {} // your own implementation
```

## Tag Sorting Updated

🚦 Impact Level: :UBadge{color="success" variant="subtle" size="sm" label="Low"}

An optional [Capo.js](https://rviscomi.github.io/capo.js/) plugin was added to Unhead, in v2 we make this the default sorting behavior.

::warning
As all head tags may be re-ordered this will break any snapshot tests that you have in place and in some rare cases may lead to performance regressions.
::

You can opt-out of Capo.js sorting by providing the option.

```ts
createHead({
  disableCapoSorting: true,
})
```

## Default SSR Tags

🚦 Impact Level: Low

When SSR Unhead will now insert important default tags for you:
- `<meta charset="utf-8">`
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
- `<html lang="en">`

If you were previously relying on these being left empty, you may need to either disable them by using `disableDefaultTags` or insert tags
to override them.

```ts
import { createHead, useHead } from 'unhead/server'

// disable when creating the head instance
const head = createHead({
  disableDefaults: true,
})

useHead(head, {
  htmlAttrs: {
    lang: 'fr'
  }
})
```

## CJS Exports Removed

🚦 Impact Level: Low

CommonJS exports have been removed in favor of ESM only.

```diff
-const { createHead } = require('unhead/client')
+import { createHead } from 'unhead/client'
```

## Deprecated `@unhead/schema`

🚦 Impact Level: Low

The `@unhead/schema` package is now deprecated and will be removed in a future version. You should instead import
the schema from `unhead/types` or `unhead`.

```diff
-import { HeadTag } from '@unhead/schema'
+import { HeadTag } from 'unhead/types'
```
2025-03-28 00:06:28 +08:00
mini2024
ea50699dc1 feat: 移动 depcheck 工作流到 lint.yaml,删除旧的 depcheck.yaml 文件 2025-03-27 22:55:36 +08:00
严浩
dcbdba3a55 docs: 添加 Vuetify 相关链接到 UseIntersectionObserverInfiniteLoading 组件 2025-03-27 11:24:16 +08:00
a6389aee36 chore(deps): update dependency eslint-plugin-oxlint to ^0.15.15 2025-03-26 20:32:09 +08:00
a05ca276ca chore(deps): update dependency eslint-plugin-unicorn to v58 2025-03-26 20:29:15 +08:00
6dc43a1812 chore(deps): update yanhao98/composite-actions digest to 33959a3 2025-03-26 01:51:21 +08:00
严浩
fa8d02f08d feat: 移除 size-limit 依赖及相关工作流配置 2025-03-25 11:42:13 +08:00
严浩
68406f5681 feat: 添加 size-limit 依赖 2025-03-25 10:54:13 +08:00
严浩
fb095ea5be fix: createUtils4uAutoImports 2025-03-25 10:49:06 +08:00
严浩
b475a7bbdd feat: 添加构建大小统计到 depcheck 工作流,并移除 playwright 工作流中的重复统计 2025-03-25 10:39:18 +08:00
严浩
2df09cf33f feat: 整理目录结构 2025-03-25 10:36:19 +08:00
mini2024
2482608a2e feat: 添加 FlowbiteSidebar 组件及路由定义 2025-03-25 01:03:03 +08:00
严浩
4ac0e0c138 fix: 修复导入名称以匹配 vite-plugin-webfont-dl 的更新 2025-03-24 10:36:14 +08:00
a8a06c589c chore(deps): update unplugin packages 2025-03-24 03:14:23 +08:00
mini2024
295f2d0c95 feat: 添加主题配置支持并优化 UI 组件页面的暗黑模式样式 2025-03-24 01:19:00 +08:00
mini2024
cf95ef7e8e feat: 添加输入组件并更新 UI 组件页面布局 2025-03-24 00:54:43 +08:00
mini2024
22d3eedea0 feat: UIComponentsComponents 2025-03-24 00:22:24 +08:00
mini2024
e42241ddc5 feat: unocss-preset-shadcn 2025-03-23 22:10:38 +08:00
mini2024
e66dc097f7 chore: 更新 Vercel 命令为 pnpm dlx 以提高执行效率 2025-03-23 02:19:13 +08:00
7abce90d75 chore(deps): update dependency eslint-plugin-vue to v10 2025-03-23 02:08:50 +08:00
cc17419836 chore(deps): update yanhao98/composite-actions digest to ae93019 2025-03-23 02:06:25 +08:00
mini2024
e83f6c9247 docs(vite.config.plugin.archiver): 添加相关参考链接
在vite.config.plugin.archiver.ts文件中添加了两个参考链接,以便开发者更好地理解插件的实现背景和用途。
2025-03-23 00:55:18 +08:00
mini2024
051244dde6 refactor(archiver): 优化打包插件逻辑并支持更多压缩格式
重构打包插件以支持多种压缩格式(zip, tar, tgz),并增加时间戳选项。同时,将插件调用移至VS Code终端检测逻辑中,确保仅在VS Code环境中执行
2025-03-23 00:46:56 +08:00
mini2024
e4f2ad3110 feat: 添加构建后自动打包dist目录为zip文件的功能
引入archiver依赖,新增vite插件viteArchiverPlugin,用于在构建完成后自动将dist目录打包成zip文件。同时更新.gitignore和package.json以支持该功能。
2025-03-23 00:29:19 +08:00
mini2024
6883cd2dfe feat: 禁用 vite-plugin-purgecss 2025-03-22 23:58:21 +08:00
mini2024
bee73670b7 feat: 更新 Playwright 工作流,增加以KB为单位的构建大小统计 2025-03-22 23:54:24 +08:00
mini2024
d23fcd72c7 feat: 更新 Playwright 工作流以计算构建大小;添加 vite-plugin-purgecss-updated-v5 插件 2025-03-22 23:50:24 +08:00
mini2024
9e6d526ada feat: 添加构建大小计算步骤;更新依赖项,新增 vite-plugin-singlefile 插件 2025-03-22 23:44:06 +08:00
mini2024
974f05719e feat: 添加 StyleLayer 组件,展示 @layer 优先级规则及样式示例;新增 style.page.vue 页面 2025-03-22 18:29:23 +08:00
4db3a84f61 chore(deps): update dependency prettier to v3.5.3 2025-03-17 13:09:49 +08:00
严浩
c66070a30f feat: 更新 cesium-helper 的 README.md,优化链接和格式
[skip ci]
2025-03-17 10:51:30 +08:00
mini2024
01adfcd908 chore: 更新 package.json,优化依赖命令并添加 knip 工具 2025-03-16 19:36:49 +08:00
mini2024
eafe01fcb3 feat: 更新 cesium-helper 的 README.md,添加 vue-vite-cesium-demo 链接
[skip ci]
2025-03-16 17:36:59 +08:00
a5be55dcf7 chore(deps): update dependency @types/node to ^22.13.10 2025-03-16 17:28:13 +08:00
0f04b268b6 chore(deps): update yanhao98/composite-actions digest to a7c66fb 2025-03-16 17:25:25 +08:00
mini2024
74631308f6 feat: 更新 cesium-helper 的 README.md,添加新的链接
[skip ci]
2025-03-16 17:24:56 +08:00
ffdc49ec2a chore(deps): update dependency typescript to ~5.8.2 2025-03-14 20:24:25 +08:00
严浩
29c9eee458 feat: 更新 README.md,添加卫星相关链接 2025-03-14 17:58:09 +08:00
严浩
f7137cb6e5 feat: 更新工具提示,添加 REV_AT_EPOCH 的校验和说明 2025-03-14 15:28:56 +08:00
严浩
9f0bed4513 demo_03_卫星加站点 2025-03-14 14:42:06 +08:00
严浩
8963fe99ff feat: 更新 utils4u 依赖至 4.2.1 版本 2025-03-14 14:04:26 +08:00
严浩
31909c906d feat: 添加 eslint-plugin-unicorn 依赖,更新 ESLint 配置以支持新规则 2025-03-14 12:50:54 +08:00
严浩
8b12d3e3dd feat: 更新 vue-component-type-helpers 依赖至 2.2.8 版本 2025-03-14 11:43:21 +08:00
严浩
e528005e7c docs: 更新 README 文件,修改项目配置和 TLE 数据链接 2025-03-13 16:52:03 +08:00
严浩
7d98cb0074 feat: 添加卫星数量动态选择按钮,优化布局和交互 2025-03-12 19:15:46 +08:00
严浩
091b3d1e29 feat: 添加卫星数据加载按钮,优化初始加载逻辑 2025-03-12 19:12:40 +08:00
严浩
8b702826b3 feat: 更新 TLE 数据处理和示例,完善卫星轨道参数解析逻辑 2025-03-12 19:02:11 +08:00
严浩
447907aa7c feat: 优化卫星页面加载和状态管理,引入加载动画和响应式数据处理 2025-03-12 18:33:26 +08:00
严浩
38f35328b3 refactor: 重构卫星选择器组件,优化布局和交互细节 2025-03-12 14:34:20 +08:00
严浩
4fba5091be fix: 修复卫星名称提取逻辑,增强类型安全性 2025-03-12 14:32:16 +08:00
严浩
92d795ba88 refactor: 重构卫星选择器为独立组件,优化代码结构和性能 2025-03-12 14:29:38 +08:00
严浩
ff46fdd637 feat: 优化卫星选择器标题显示,增强用户交互体验 2025-03-12 14:06:19 +08:00
严浩
ab38d2a3f6 feat: 优化卫星选择器并完善 Cesium 页面交互 2025-03-12 12:12:41 +08:00
80fb2f66ee chore(deps): update dependency @vueuse/core to v13 2025-03-12 07:59:00 +08:00
严浩
203ab3da7d feat: 优化卫星实体类,更新 TLE 处理逻辑并增强类型定义 2025-03-11 18:21:40 +08:00
严浩
a2f07c7d76 feat: 添加卫星实体类及相关功能,优化 Cesium 初始化和时间线设置 2025-03-11 16:01:55 +08:00
严浩
ec49e340ea fix: 更新接口请求页面的 URL 路径 2025-03-11 14:09:38 +08:00
严浩
4542944f52 整理 2025-03-10 13:15:38 +08:00
严浩
a7b10809c1 feat: 添加假 API 接口以获取用户信息,并更新 Cesium 示例代码 2025-03-10 12:00:45 +08:00
严浩
ca1521328c chore: 更新 setup-node-environment 版本并添加静态代码分析工作流 2025-03-10 11:02:45 +08:00
mini2024
8733c3e42e chore(deps): 升级 oxlint 2025-03-09 23:30:29 +08:00
严浩
35a658bfe4 feat: /cesium 2025-03-07 15:27:21 +08:00
严浩
c8f2fe83fa feat: 添加额外的离线地图链接以丰富文档内容 2025-03-06 17:36:22 +08:00
严浩
9eaf7f5791 feat: 添加 Cesium 配置文件和离线地图支持,优化初始化选项 2025-03-06 16:33:33 +08:00
严浩
1d251b4e1c feat: 添加离线地图链接并优化 Cesium 初始化配置 2025-03-06 15:50:02 +08:00
严浩
3acea1577b refactor: 重命名变量以提高代码可读性 2025-03-05 12:50:26 +08:00
严浩
8c1b8987c5 feat: 更新轨道演示功能,添加未来位置注释并优化相机跟踪设置 2025-03-05 12:30:42 +08:00
严浩
4fd1e1b992 feat: 添加轨道生成演示功能 2025-03-05 11:40:55 +08:00
mini2024
2b7186ef69 chore: eslint 配置 2025-03-05 00:57:59 +08:00
mini2024
feb7659b75 feat: 在轨道生成演示中添加确认提示,增强用户交互体验 2025-03-05 00:48:02 +08:00
mini2024
dc318c04e3 feat: 添加 Cesium 相关功能和配置,更新样式及文档 2025-03-05 00:42:30 +08:00
mini2024
49a7ef0dae fix: ERR_PNPM_LOCKFILE_CONFIG_MISMATCH 2025-03-02 16:48:49 +08:00
mini2024
41a036e60f chore: 更新依赖项版本 2025-03-01 18:30:11 +08:00
mini2024
c7047146e4 chore: eslint.config.ts 2025-03-01 18:20:11 +08:00
严浩
94b71c5c47 feat: 添加 HAPopconfirm 组件并更新 index.page.vue 以集成确认功能 2025-02-28 14:47:10 +08:00
严浩
8c855b5b3d feat: 更新 ESLint 配置,添加宏定义顺序和导入后换行规则 2025-02-28 11:56:18 +08:00
严浩
4a4118c2dc 重命名 APopconfirmButton 为 HAPopconfirmButton 并更新相关引用 2025-02-28 11:41:02 +08:00
严浩
7375b66360 更新 AntdV 组件页面布局 2025-02-28 09:54:06 +08:00
mini2024
5ada043ee9 feat: 添加 APopconfirmButton 组件并更新 index.page.vue 以集成确认删除功能 2025-02-28 00:35:13 +08:00
ff4718c54b chore(deps): pin dependency prettier to v3.4.2 2025-02-27 13:12:20 +08:00
严浩
1f2911c096 feat: update ESLint configuration and package dependencies, add Ant Design Vue 2025-02-27 12:28:58 +08:00
8aef352614 chore(deps): update dependency pinia to v3 2025-02-27 05:46:06 +08:00
mini2024
97431f932f feat: add unocss-preset-chinese and update related configurations 2025-02-25 00:27:05 +08:00
严浩
4552bbca34 chore: update Node.js version and dependencies to latest releases 2025-02-21 10:03:18 +08:00
055b5d2a9e chore(deps): update unocss monorepo to v66 2025-02-18 19:36:22 +08:00
严浩
a70e33f209 fix: remove duplicate entry for utils4u in package.json 2025-02-13 19:13:50 +08:00
严浩
3b22aba719 fix: pnpm-lock.yaml is not up to date with <ROOT>/package.json 2025-02-12 14:27:37 +08:00
严浩
cb6cf464ee Merge branch 'sat' 2025-02-11 19:28:55 +08:00
严浩
d394592a6b fix(SAT): update ISS data source assignment and correct CESIUM_BASE_URL to HTTPS 2025-02-11 18:58:13 +08:00
严浩
d3548f1105 chore: update dev script to allow Vite to bind to all network interfaces 2025-02-11 18:36:10 +08:00
严浩
6a3560a7f8 chore(deps): update orbpro to version 1.126.0; set CESIUM_BASE_URL in SAT page 2025-02-11 18:22:06 +08:00
严浩
e14c897b2c chore: update Vercel workflow to include 'sat' branch for deployment 2025-02-10 17:35:06 +08:00
严浩
19e46190f1 feat(SAT): add CesiumViewer component and utility functions; update dependencies 2025-02-10 17:34:24 +08:00
64fa5bbc93 chore(deps): update dependency vercel to v41 2025-02-09 07:23:37 +08:00
8962d65ed3 chore(deps): update dependency utils4u to v4 2025-02-09 07:22:28 +08:00
严浩
f26342ddfc chore(ci): update Vercel workflow to trigger on main and sat branches 2025-02-08 13:45:38 +08:00
503d1b27ef chore(deps): update vite packages 2025-02-08 11:47:41 +08:00
严浩
c465fd6fec chore: refactor countdown logic and update utils4u auto imports 2025-02-08 10:39:36 +08:00
严浩
d533738e0a chore(deps): update vercel to ^41.0.2 2025-02-07 18:27:58 +08:00
严浩
d4b8585143 feat(SAT): add SAT page and viewer component; update dependencies 2025-02-07 18:26:25 +08:00
1d5a387ae0 chore(deps): update pnpm to v10 2025-02-06 07:46:22 +08:00
63d21602fc chore(deps): update all non-major dependencies 2025-02-05 21:00:52 +08:00
74452b748a chore(deps): update dependency unplugin-vue-router to ^0.11.1 2025-02-05 13:43:58 +08:00
严浩
72c638894f chore(ci): enable Playwright browser installation in workflow 2025-02-05 12:14:07 +08:00
f98225aebf chore(deps): update node.js to v22.13.1 2025-02-04 08:19:02 +08:00
581dd237ca chore(deps): update dependency unplugin-vue-markdown to ^28.1.0 2025-02-01 21:00:52 +08:00
1fe225cf05 chore(deps): update all non-major dependencies to ^65.4.2 2025-01-30 19:13:16 +08:00
f3b4ee26fe chore(deps): update dependency @types/node to ^22.10.7 2025-01-30 10:47:42 +08:00
e03064cddd chore(deps): update dependency taze to ^18.2.0 2025-01-29 13:55:07 +08:00
c34dbceb2c chore(deps): update v0.x to v65 2025-01-28 05:46:15 +08:00
01525ddbc2 chore(deps): update dependency @types/node to ^22.10.6 2025-01-28 03:16:57 +08:00
525fb84811 更新 package.json 2025-01-24 14:33:53 +08:00
7239da28b1 chore(deps): update v0.x 2025-01-24 09:09:35 +08:00
mini2024
14fdb7ef1f chore(ci): reorganize CI workflows and add depcheck and playwright jobs 2025-01-24 00:28:13 +08:00
a7312f402f chore(deps): update dependency typescript to ~5.7.3 2025-01-23 03:15:31 +08:00
f6414e5ce0 chore(deps): update vite packages 2025-01-22 21:18:15 +08:00
af32fe53a8 chore(deps): update v0.x 2025-01-22 21:14:40 +08:00
c8ae17353e chore(deps): update v0.x to ^0.65.4 2025-01-22 19:00:06 +08:00
2d59658f1d chore(deps): update dependency vite-plugin-webfont-dl to ^3.10.4 2025-01-22 18:56:55 +08:00
严浩
536f879930 feat: 移除不再使用的 ESLint 插件和类型定义,优化依赖项 2025-01-22 12:01:38 +08:00
严浩
53188ff9cf feat: 更新 ESLint 配置,支持更多语言,调整 npm 镜像源 2025-01-22 11:54:35 +08:00
mini2024
b0cd8b7c61 feat: 添加 eslint-plugin-perfectionist 插件,优化导入排序规则 2025-01-21 23:05:11 +08:00
a7b6d6ea75 chore(deps): update all non-major dependencies 2025-01-20 18:36:08 +08:00
cc914c9a80 chore(deps): update node.js to v22.13.0 2025-01-20 08:34:01 +08:00
严浩
83117bbf4b feat: 优化菜单项,添加路由名称和标题支持,更新首页元信息 2025-01-08 17:41:36 +08:00
严浩
943eac62df feat: 添加日期选择器和分页器组件,优化页面布局和用户交互 2025-01-08 17:23:15 +08:00
严浩
f86afbdb2c feat: 更新确认对话框的拒绝按钮属性,优化用户交互体验 [no ci] 2025-01-08 15:25:45 +08:00
严浩
7258853b57 feat: 添加路由元信息支持,优化无限加载组件的状态管理和错误处理 2025-01-08 14:48:01 +08:00
严浩
d4b34a4f1f feat: 更新 utils4u、vercel 和 vite-plugin-vue-devtools 依赖至最新版本 2025-01-08 10:57:55 +08:00
严浩
df0ccdaed1 feat: 添加 terser 作为构建工具的压缩选项 2025-01-08 10:25:05 +08:00
严浩
3292800f0d feat: 优化插件设置逻辑,支持自动安装模块并增强调试信息 2025-01-08 10:17:07 +08:00
严浩
b70da8aad4 feat: 重构插件初始化逻辑,简化应用启动流程并添加调试工具支持 2025-01-08 09:35:21 +08:00
严浩
be321165c8 feat: 更新依赖版本,包括 @unhead/vue、tdesign-icons-vue-next 和 utils4u,调整 VueMacros 路由路径 2025-01-07 18:45:13 +08:00
严浩
cfd71c8364 feat: 更新 pnpm 依赖至 9.15.3 版本 [no ci] 2025-01-07 13:59:57 +08:00
严浩
f76bb5d0e2 feat: 更新 utils4u 依赖至 3.1.0 版本 2025-01-07 13:59:14 +08:00
严浩
1a0688bf54 feat: 添加字体页面,展示相关链接和信息 2025-01-06 10:44:38 +08:00
严浩
72a51b7fc7 feat: 更新 CI 工作流,添加构建项目的注释 2025-01-05 23:03:31 +08:00
严浩
b638926537 feat: 更新 CI 工作流,调整构建和类型检查顺序 2025-01-05 23:02:24 +08:00
严浩
6309f1a7d7 feat: 更新触摸模拟器脚本链接,替换为 unpkg 源 2025-01-05 22:16:04 +08:00
严浩
1713315c08 feat: 优化样式和简化 DatePicker 组件的代码 2025-01-03 18:26:42 +08:00
严浩
a1cf330f9a feat: 更新 unocss 配置,添加新的颜色变量和文本颜色定义 2025-01-03 18:25:37 +08:00
严浩
4750d0dd43 feat: 删除不必要的页面和组件,更新路由类型定义,添加新链接到文档 2025-01-03 17:12:06 +08:00
严浩
181213526e feat: 添加 JSPage 页面,支持 JavaScript 文件,更新 ESLint 和 TypeScript 配置 2025-01-03 17:06:47 +08:00
严浩
f5c0cb8665 feat: 重构页面结构,删除不必要的页面,添加新页面,优化暗黑主题样式 2025-01-03 16:47:30 +08:00
严浩
263f3f6dfa feat: 更新 DatePicker 组件,添加手动输入和输入 ID,优化标签关联 2025-01-03 16:00:40 +08:00
严浩
6f35ac8105 feat: 更新依赖项版本,添加 vue-tsc 作为覆盖项,修复 primevue 版本号 2025-01-03 14:03:12 +08:00
严浩
44b55323a6 feat: 更新加载更多提示文本,升级依赖项以修复潜在问题 2025-01-03 11:29:51 +08:00
严浩
09d2779f7c feat: 更新 TypeScript 配置文件,添加引用链接以便于维护和参考 2025-01-02 23:12:58 +08:00
严浩
47e2c5d813 feat: 更新 Vite 配置,添加手动分块注释以优化构建输出 2025-01-02 23:09:12 +08:00
严浩
78ec619f23 feat: 添加 .editorconfig 文件,更新 ESLint 配置,修改 TypeScript 配置以支持 Node 22,优化依赖项版本 2025-01-02 22:22:51 +08:00
严浩
d132ae7484 feat: 修复无限加载页面的错误处理逻辑,优化数据加载和缓存更新 2025-01-02 17:12:08 +08:00
严浩
24966a3b7d feat: 重构缓存逻辑,优化数据更新和清空缓存的实现 2025-01-02 16:52:56 +08:00
严浩
d30163351a feat: 添加深冻结库,重构无限加载页面,优化缓存逻辑并新增详情页 2025-01-02 16:18:37 +08:00
严浩
49da2ee1d8 feat: 修改用户数据返回类型,优化用户信息展示逻辑 2025-01-02 14:46:17 +08:00
严浩
087c593b58 feat: 移除未使用的提示服务功能,优化按钮逻辑以支持不同的提示类型 2025-01-02 11:49:44 +08:00
严浩
ce16720368 feat: 更新样式和组件,修复文件上传相关问题,升级依赖版本 2025-01-02 11:49:44 +08:00
严浩
a440113044 feat: 在发送验证码成功后添加提示消息 2025-01-02 11:49:44 +08:00
严浩
ed9e505f0c feat: 优化样式,修复文件上传组件的布局问题并增强消息文本的换行处理 2025-01-01 23:43:31 +08:00
严浩
b46edb4aeb feat: 优化发送验证码按钮,增加加载状态并调整倒计时逻辑 2024-12-30 14:39:54 +08:00
严浩
0b09c3dbb6 feat: 修复 flex 布局子元素宽度超出父元素的问题 2024-12-30 14:29:43 +08:00
严浩
ee5db6d91c feat: 添加新的页面组件 src/pages/a/b/index.page.vue 2024-12-30 12:58:01 +08:00
严浩
7df5552b05 feat: 删除不再使用的 Vue 组件,清理代码库 2024-12-30 12:15:38 +08:00
严浩
1c53703092 feat: 更新 openToast 函数,优化消息摘要格式以提高可读性 2024-12-30 10:59:50 +08:00
严浩
6918d26da2 feat: 优化 ToastService 消息添加逻辑,支持多种消息类型 2024-12-30 10:56:51 +08:00
严浩
15d7bd3806 feat: 优化 ToastService 消息格式,调整参数顺序以提升可读性 2024-12-30 10:54:06 +08:00
严浩
3c2af936cb feat: 添加链接到相关项目和资源,丰富页面内容 2024-12-29 21:17:33 +08:00
严浩
8ea637d7a0 feat: 添加对 100dvh 支持的样式调整,优化布局适应性 2024-12-29 02:01:39 +08:00
严浩
a09fb23df0 feat: 更新页面模板,修复模板内容显示问题 2024-12-29 01:32:17 +08:00
严浩
eff30bd6c0 feat: 更新组件样式和结构,优化函数式组件展示 2024-12-27 15:02:29 +08:00
严浩
6b6e313195 feat: 重构菜单项生成逻辑,优化路由处理并支持分组结构 2024-12-27 14:56:49 +08:00
严浩
c6594a1254 feat: 重构菜单项生成逻辑,支持分组和树形结构 2024-12-27 14:49:49 +08:00
严浩
865ecf9156 暂存 2024-12-27 13:09:01 +08:00
严浩
56791aa06c chore: 注释掉不再使用的 createSplitChunkOutput 导入 2024-12-27 00:04:41 +08:00
严浩
b6a51c1245 feat: 更新路由插件,重构路由获取逻辑并添加新的路由页面 2024-12-27 00:02:11 +08:00
严浩
bc8fc96580 feat: 更新 package.json 和 pnpm-lock.yaml,添加新的大小检查命令和依赖项版本更新 2024-12-26 22:54:37 +08:00
严浩
8d048038ee feat: 添加第二布局页面,更新路由配置以支持新布局 2024-12-26 18:51:16 +08:00
严浩
f3e6dbcc0e feat: 添加 @formkit/auto-animate 插件 2024-12-26 18:38:35 +08:00
严浩
c2d942554d chore: 清理 CI 配置,移除不必要的 .npmrc 修改步骤 2024-12-26 17:39:19 +08:00
严浩
97f6575f49 fix: 修测试用例 2024-12-26 17:37:47 +08:00
严浩
1dac41ce88 feat: 更新 VSCode 设置,更新 package.json 脚本,添加新功能页面和重构组件 2024-12-26 16:25:39 +08:00
严浩
4e3633d7ea feat: 添加 vite-plugin-vue-meta-layouts 插件,优化路由菜单项生成逻辑 2024-12-26 15:53:53 +08:00
严浩
d18799e8cb feat: 更新 touch-emulator 脚本链接至新的 CDN 地址 2024-12-26 15:04:24 +08:00
严浩
02048f1266 feat: 更新 pnpm-lock.yaml 中的依赖版本,升级 ms 至 2.1.3 和 @vue/language-core 至 2.2.0 2024-12-26 13:11:28 +08:00
严浩
83d7f6b72e feat: 更新 Vite 版本至 6.0.6 2024-12-26 13:05:14 +08:00
严浩
1856bd4fc8 feat: 重构 AppMenu 组件以使用 PanelMenu,优化菜单项生成逻辑,更新无限加载页面按钮样式,调整 Vite 插件配置 2024-12-26 00:20:33 +08:00
严浩
49e18facda feat: 更新 VSCode 配置,添加 oxc 插件,调整 eslint 配置,优化构建和类型检查命令 2024-12-25 23:44:18 +08:00
严浩
eed0f4f3f4 feat: 重构 AppMenu 组件以动态生成菜单项,更新路由和样式,添加返回按钮 2024-12-25 19:19:21 +08:00
严浩
78fe5b0c37 feat: 在 unocss 配置中添加主题使用说明注释 2024-12-25 14:39:46 +08:00
严浩
cf87a60357 feat: 更新样式导入,调整 nprogress 和 unocss 重置样式的引用 2024-12-25 14:37:52 +08:00
严浩
82d89363b1 feat: 更新 AppConfigurator 组件,添加新的 Sakai 链接,调整页面模板和路由配置,更新 unocss 配置以添加颜色 2024-12-25 14:36:51 +08:00
严浩
e91582db23 feat: 添加新的 Sakai Vue 布局和样式,重构基础样式,更新 README,调整插件配置 2024-12-25 14:14:55 +08:00
严浩
99c8e8d892 feat: 更新路由名称映射,调整插件配置 2024-12-25 11:12:29 +08:00
严浩
7a682b1ea6 feat: 更新页面模板内容,调整 VSCode 设置,更新依赖管理器版本并添加新脚本 2024-12-25 11:00:19 +08:00
严浩
03d22e09a0 feat: 更新 README 和样式文件,调整依赖安装路径,重构基础样式并添加重置样式 2024-12-25 09:49:29 +08:00
严浩
ea2eddb4c3 feat: 重构样式文件,删除不必要的样式并更新路径配置 2024-12-25 09:38:29 +08:00
严浩
1b62a08c4d feat: 更新 Layouts 配置,添加 pagesDirs 选项以指定页面目录 2024-12-25 00:35:16 +08:00
严浩
cce5d94017 feat: 添加默认布局和第二布局,更新路由配置以支持新布局 2024-12-25 00:33:35 +08:00
严浩
bfbeb326bc feat: 添加 vite-plugin-vue-layouts 插件并更新 vite 版本至 6.0.5 2024-12-25 00:06:46 +08:00
严浩
400de0f222 chore: 更新依赖项版本以保持兼容性和稳定性 2024-12-24 23:20:26 +08:00
严浩
9d421c5bfe feat: 重构插件安装方式,统一使用 install 方法并移除不必要的 setup 函数 2024-12-24 23:13:09 +08:00
严浩
e9f5aa56a6 feat: 更新 ReusableTemplate 组件,增加对默认插槽的支持和修复插槽属性传递 2024-12-24 18:14:17 +08:00
严浩
b3e670ee50 feat: 为 Cascadeselect 组件添加最大高度和垂直滚动支持 2024-12-23 18:58:47 +08:00
严浩
01d619a857 fix: 更新类型声明,将模块名称从 '@vue/runtime-dom' 更改为 'vue' 2024-12-23 12:19:01 +08:00
严浩
68e28f7f30 feat: 添加滚动行为和优化路由插件注册顺序 2024-12-23 10:56:41 +08:00
严浩
f74f76750d fix: 修复 ProgressSpinner 组件的类名,添加强制应用样式 2024-12-23 10:52:21 +08:00
严浩
de9260dcf5 feat: 更新 index.html 和样式文件,优化样式导入和结构 2024-12-23 10:44:47 +08:00
严浩
4037060842 feat: 将主样式文件从 CSS 更改为 LESS 以支持更灵活的样式管理 2024-12-23 10:24:03 +08:00
严浩
32dd090d31 fix: 禁用 VITE_SPLIT_CHUNKS 以优化构建性能 2024-12-23 10:16:32 +08:00
严浩
20fadd2cfd feat: 重构样式导入,优化样式文件结构 2024-12-23 10:09:08 +08:00
严浩
b7e09dd383 fix: 测试样式导入顺序问题 2024-12-20 18:21:31 +08:00
严浩
c48340d45e feat: 添加 PrimeVue 相关样式导入以支持主题和重置样式 2024-12-20 18:18:53 +08:00
严浩
76aa6eac79 feat: 在可重用模板中添加标题以增强可读性 2024-12-20 16:33:59 +08:00
严浩
49fc15b1b6 fix: 修改用户数据加载延迟时间和新鲜度设置 2024-12-20 15:33:55 +08:00
严浩
50e0c5ebc6 feat: 添加暗色模式支持,重构用户数据加载逻辑 2024-12-20 15:28:24 +08:00
严浩
89eae5bcbb defineColadaLoader 2024-12-20 14:53:31 +08:00
严浩
a8ce2f39e2 fix: 注释掉被墙的 Typekit 脚本以避免加载错误 2024-12-20 14:16:47 +08:00
严浩
487b6bd315 feat: 添加 Pinia 插件支持并重构主应用初始化逻辑 2024-12-19 23:09:17 +08:00
严浩
5f81c647fa chore: 添加 auto-imports.d.ts 到 .gitignore 2024-12-19 22:58:40 +08:00
严浩
cb779c0234 refactor: 移除不再使用的 utils4u 导入,更新为直接使用 useCountdown 2024-12-19 22:54:25 +08:00
严浩
319ec789f4 chore(deps): 更新 PrimeVue 版本至 4.2.6,删除不再使用的补丁文件 2024-12-19 18:07:24 +08:00
严浩
c9bde0b7da feat: 更新 utils4u 依赖至 2.18.1,并添加 PrimeVue 服务的全局类型声明 2024-12-19 13:11:42 +08:00
严浩
6cd44c4b34 chore(deps): 更新 utils4u 和 vant 依赖至最新版本并调整导入方式 2024-12-19 12:34:07 +08:00
严浩
b578c18e43 fix: 修正 utils4u 导入路径 2024-12-18 19:02:11 +08:00
严浩
1cec4cdb2f chore(deps): 更新 utils4u 依赖至 2.16.0 2024-12-18 19:01:23 +08:00
严浩
1d2d19df65 fix: type 2024-12-18 18:42:06 +08:00
严浩
aa84274f0a feat: 添加热模块替换支持到计数器状态管理 2024-12-18 18:08:22 +08:00
严浩
41ecc9ff56 chore(deps): 更新 npm registry 配置并升级部分依赖版本 2024-12-18 12:17:26 +08:00
严浩
a5c1cf38f6 feat: 添加全局错误处理逻辑,优化 PrimeVue 插件导入和路由设置 2024-12-17 09:21:11 +08:00
严浩
1dc0230a3e chore: 更新 npm registry 配置并升级部分依赖版本 2024-12-17 00:32:07 +08:00
5f561d3a5f chore(deps): update node.js to v22.12.0 2024-12-16 10:26:56 +08:00
严浩
86943c1e58 feat: 添加文件上传数量限制,优化禁用状态逻辑并显示上传文件信息 2024-12-16 01:09:34 +08:00
严浩
511f07f778 feat: 优化假数据上传逻辑,简化响应处理并添加调试信息 2024-12-16 00:54:28 +08:00
严浩
5f0eaa2579 feat: 更新上传假数据处理逻辑,添加原始响应方法并支持选择禁用选项 2024-12-16 00:38:02 +08:00
严浩
abe0262130 feat: upload-demo.vue 2024-12-16 00:21:03 +08:00
严浩
86d0e5622d feat: 重构 PrimeVue 相关插件,优化对话框和通知服务的使用,更新样式和组件结构 2024-12-15 18:45:27 +08:00
严浩
0be091c32f fix: 更新 CI 工作流,修改构建命令以确保类型定义文件存在 2024-12-15 00:51:09 +08:00
严浩
efc52343f2 chore: 更新依赖项版本,提升项目稳定性 2024-12-15 00:41:52 +08:00
严浩
baa100d6ed fix: 修正 CI 工作流中的 Vue 类型检查命令格式 2024-12-15 00:36:00 +08:00
严浩
b8ac17b600 feat: 更新 App.vue 模板,合并对话框和提示组件的渲染 2024-12-14 21:55:53 +08:00
严浩
553d3be256 feat: 添加 useDialogRef 函数,优化对话框引用管理,更新对话框组件逻辑 2024-12-14 21:54:51 +08:00
严浩
b9ad180d4c feat: 优化确认对话框的属性设置,简化代码结构 2024-12-14 20:49:49 +08:00
严浩
b1c0efb853 feat: 添加确认对话框组件,优化提示和对话框服务的交互逻辑 2024-12-14 20:01:38 +08:00
严浩
9ed80478e6 fix: 修正 DynamicDialogOptions.data 的注释格式 [no ci] 2024-12-13 10:15:45 +08:00
严浩
722f880f81 feat: 重构 DynamicDialog 组件,优化数据注入和事件处理逻辑 2024-12-13 09:40:29 +08:00
严浩
6ceb3f0766 feat: 添加 DynamicDialog 和 Toast 组件,重构 PrimeVue 相关逻辑和样式 2024-12-12 23:34:20 +08:00
严浩
22ff97d003 feat: 添加 Toast 和 Dialog 服务,优化组件交互和样式 2024-12-12 19:20:49 +08:00
严浩
dbcc28fb42 feat: 优化 UseIntersectionObserverInfiniteLoading 组件的加载逻辑和模板结构 2024-12-11 15:48:35 +08:00
严浩
aa902ed159 Revert "feat: 使用 radash 的 throttle 包装 load 函数,优化加载频率并重构 refresh 方法"
This reverts commit 40bf214da7.
2024-12-11 15:44:43 +08:00
严浩
40bf214da7 feat: 使用 radash 的 throttle 包装 load 函数,优化加载频率并重构 refresh 方法 2024-12-11 15:42:49 +08:00
严浩
3a9b19d64f feat: 添加 UseIntersectionObserverInfiniteLoading 组件的使用示例,优化模板结构 [no-ci] 2024-12-11 15:31:03 +08:00
严浩
294c73df09 feat: 调整 Toast 组件样式,优化消息文本的上边距 2024-12-11 15:00:25 +08:00
严浩
b166cc9d60 feat: 更新 CI 工作流,修改 Surge 部署步骤以使用主分支 2024-12-10 09:54:34 +08:00
严浩
f314b9c023 feat: 调整组件样式,优化图标和布局的尺寸,更新无限加载组件的刷新方法 2024-12-08 18:00:21 +08:00
严浩
1685a34bb2 feat: 重构 ToastService,添加消息管理功能并更新按钮事件处理 2024-12-08 17:43:40 +08:00
严浩
b18bab4d7c feat: 更新无限加载组件,添加刷新功能和错误处理,优化加载状态管理 2024-12-08 16:53:50 +08:00
严浩
cd1cd72002 feat: 更新无限加载组件,支持分页加载和错误处理 2024-12-08 00:23:28 +08:00
严浩
5f98fe12ba feat: 更新无限加载组件 2024-12-08 00:04:31 +08:00
严浩
2574e38d9a feat: 添加无限加载组件和示例页面 2024-12-07 23:11:08 +08:00
严浩
12a02eb193 feat: 添加 useIntersectionObserverList 页面,支持懒加载和可见性检测 2024-12-07 19:28:37 +08:00
严浩
b309260e8f 00 2024-12-07 02:37:21 +08:00
34b55ce71a chore(deps): update dependency typescript to ~5.7.2 2024-12-07 01:45:37 +08:00
严浩
190b5159bc feat: 更新README.md,添加primevue-scopedtokens链接 [no ci] 2024-12-04 18:39:02 +08:00
严浩
71c2e6408a feat: 更新依赖项版本和CI配置 2024-12-04 14:36:30 +08:00
严浩
ce2f1f6a27 feat: 更新图标组件和Vite配置以支持自定义类名 2024-12-04 14:00:24 +08:00
严浩
2479c80cd1 feat: 更新样式和脚本以改善应用布局和响应式设计 2024-12-04 12:01:21 +08:00
严浩
32902909c2 [no ci] 2024-12-03 10:16:41 +08:00
严浩
3676127dc9 feat: 更新依赖项版本以提升项目稳定性和功能 2024-11-27 17:34:03 +08:00
081ae50e09 chore(deps): update yanhao98/composite-actions digest to 6af4afe 2024-11-26 23:30:38 +08:00
严浩
adf5fd2b4b feat: 更新依赖项版本以保持项目的最新状态 2024-11-26 23:08:39 +08:00
严浩
fdcc4aa18c feat: 在 primevue.vue 中添加类型忽略注释以处理缺失的类型 2024-11-26 12:49:36 +08:00
严浩
7e06011c05 feat: 在 typed-router.d.ts 中添加 '$Path' 路由记录信息 2024-11-26 12:39:37 +08:00
严浩
fea7e3d20e feat: 添加 404 页面组件,显示路径不存在的消息 2024-11-26 11:03:41 +08:00
严浩
8d765091d8 feat: 更新 tsconfig 和 Vite 插件配置,添加类型定义文件以增强类型支持 2024-11-25 17:01:57 +08:00
严浩
b6854ae632 feat: 更新 PrimeVue 配置以支持多语言,移除不必要的代码 2024-11-25 16:53:41 +08:00
严浩
3883fad98d feat: 重构 Vite 配置,提取插件配置到单独文件并简化 tsconfig 配置 2024-11-25 16:44:36 +08:00
严浩
bb27e0107d fix: 更新 nprogress 样式表链接以使用新的 CDN 2024-11-25 16:27:44 +08:00
严浩
84003f22d3 chore(deps): 更新依赖项 2024-11-25 15:57:36 +08:00
严浩
1473a58e06 fix: 修正组件类名并更新按钮文本以提高可读性 2024-11-22 16:13:56 +08:00
严浩
f708970fa4 feat: 添加 primelocale 依赖并更新 PrimeVue 组件以支持多语言选择 2024-11-22 16:11:41 +08:00
严浩
e39fea979d feat: 更新 PrimeVue 组件,添加城市选择下拉框并优化按钮布局 2024-11-22 12:45:08 +08:00
严浩
2bbe9c7a59 feat: 添加 PrimeVue 组件并更新样式,增强按钮交互体验 2024-11-22 12:34:43 +08:00
严浩
30721736a4 chore(deps): 更新依赖项 @vueuse/core、alova、@types/node 和 typescript 到最新版本 2024-11-22 11:55:12 +08:00
8cbb8deceb chore(deps): update all non-major dependencies 2024-11-20 21:20:37 +08:00
6239cfd928 chore(deps): update yanhao98/composite-actions digest to fdab136 2024-11-20 20:03:04 +08:00
0e991ee39e chore(deps): update all non-major dependencies 2024-11-20 19:03:22 +08:00
26d9cbaaff chore(deps): update all non-major dependencies 2024-11-20 16:15:09 +08:00
严浩
b597d71eac feat(vscode): 更新设置以格式化修改部分并添加i18n-ally语言路径 2024-11-15 11:35:04 +08:00
严浩
6b5d34d183 feat(i18n): 添加注释以说明国际化组件的类型安全示例 2024-11-07 18:45:38 +08:00
严浩
6b657f3f2b feat(i18n): 更新国际化组件,优化语言文件和结构,增强类型支持 2024-11-07 15:26:33 +08:00
严浩
3eed9a44df feat(i18n): 重构国际化组件,添加新的语言支持和功能 2024-11-07 12:36:37 +08:00
严浩
a4cc30f816 feat(i18n): 移除国际化 2024-11-07 10:38:42 +08:00
严浩
4dd414e4bf feat(i18n): 添加国际化消息支持,更新首页组件以显示重要消息 2024-11-06 19:01:12 +08:00
严浩
b717b6252c feat(i18n): 更新语言文件导入逻辑,增强类型支持 2024-11-06 18:45:37 +08:00
严浩
ec1d932250 feat(i18n): 添加中文和英文语言文件,集成国际化支持 2024-11-06 18:28:53 +08:00
严浩
40752e555f feat(i18n): 添加国际化支持,更新语言切换功能 2024-11-06 17:44:32 +08:00
严浩
e2f60c32a3 chore(npmrc): 更新 node-mirror:release 配置注释 2024-11-05 16:19:02 +08:00
严浩
9f5e3703e1 chore(npmrc): 添加多个 npm 镜像源 2024-11-05 15:07:38 +08:00
2cc4d30046 chore(deps): update all non-major dependencies 2024-11-01 13:50:11 +08:00
3f16456c95 chore(deps): update dependency npm-run-all2 to ^6.2.4 2024-11-01 12:44:52 +08:00
ffb2646375 chore(deps): update dependency @types/node to ^22.8.4 2024-10-31 09:32:53 +08:00
b1dac39295 chore(deps): update dependency @types/node to ^22.7.6 2024-10-31 08:27:34 +08:00
99d9ae9cd2 更新 README.md 2024-10-29 15:18:09 +08:00
35bd3ff9c1 更新 README.md 2024-10-27 23:38:13 +08:00
bd78a7b598 chore: 更新 CI 流程,优化依赖检查和类型检查步骤 2024-10-19 16:34:06 +08:00
f302a56d2c chore: 更新 pre-commit 钩子,添加运行提示图标 2024-10-19 15:44:24 +08:00
3d03311ed7 chore: 更新 pre-commit 钩子,添加 TypeScript 格式化设置并清理 VSCode 配置 2024-10-19 15:41:38 +08:00
严浩
3ffb874049 chore: 在 CI 流程中添加 taze 依赖检查步骤 2024-10-18 17:06:19 +08:00
107b0a2d98 chore(deps): update all non-major dependencies 2024-10-17 12:45:29 +08:00
严浩
b3fc1e8999 chore: 简化 CI 流程中的 Vue 类型检查步骤 2024-10-17 12:41:49 +08:00
严浩
823f767fc9 docs: 更新 README.md,添加 .gitattributes 使用说明 2024-10-17 12:01:34 +08:00
严浩
a5ec941074 chore: 添加 .gitattributes 文件以配置文本和二进制文件处理 2024-10-17 09:14:56 +08:00
23fbb10a3c chore(deps): update dependency node to v20.18.0 2024-10-17 08:14:54 +08:00
235 changed files with 333358 additions and 6180 deletions

View File

@@ -1,14 +0,0 @@
// https://github.com/depcheck/depcheck?tab=readme-ov-file#api
// node .depcheck.js
import depcheck from 'depcheck';
depcheck(process.cwd(), {
ignorePatterns: ['tsconfig.json'],
}).then((unused) => {
console.debug(`unused.dependencies :>> `, unused.dependencies); // an array containing the unused dependencies
console.debug(`unused.devDependencies :>> `, unused.devDependencies); // an array containing the unused devDependencies
console.debug(`unused.missing :>> `, unused.missing); // a lookup containing the dependencies missing in `package.json` and where they are used
console.debug(`unused.using :>> `, unused.using); // a lookup indicating each dependency is used by which files
console.debug(`unused.invalidFiles :>> `, unused.invalidFiles); // files that cannot access or parse
console.debug(`unused.invalidDirs :>> `, unused.invalidDirs); // directories that cannot access
});

View File

@@ -1,8 +0,0 @@
# https://github.com/depcheck/depcheck#usage
# pnpm add json -g
# pnpm exec depcheck --json | json
# pnpm exec depcheck
# ignores: ["eslint", "babel-*"]
# skip-missing: true

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
max_line_length = 120

2
.env
View File

@@ -1,6 +1,6 @@
VITE_BASE=/
VITE_SPLIT_CHUNKS=true
VITE_SPLIT_CHUNKS=false
VITE_SOURCE_MAP=true
VITE_BUILD_TIME=
VITE_BUILD_COMMIT=

82
.gitattributes vendored Normal file
View File

@@ -0,0 +1,82 @@
# 如果你先推送了一个.jpg文件然后再推送包含.gitattributes文件的更新Git不会自动重新处理之前的.jpg文件的属性。为使.gitattributes中的新规则生效你可以通过以下步骤重新应用设置
# 1. 删除本地缓存的.jpg文件git rm --cached <file>.jpg
# 2. 重新添加该文件git add <file>.jpg
# 3. 再次提交并推送git commit -m "Apply .gitattributes changes" && git push
# 这样Git将按照.gitattributes中的新规则处理该文件。
* text=auto
# Force the following filetypes to have unix eols, so Windows does not break them
*.* text eol=lf
# Separate configuration for files without suffix
LICENSE text eol=lf
Dockerfile text eol=lf
pre-commit text eol=lf
commit-msg text eol=lf
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.ico binary
*.jpg binary
*.jpeg binary
*.png binary
*.pdf binary
*.doc binary
*.docx binary
*.ppt binary
*.pptx binary
*.xls binary
*.xlsx binary
*.exe binary
*.jar binary
*.ttf binary
*.woff binary
*.woff2 binary
*.eot binary
*.otf binary
*.spline binary
# Add more binary...
# >>> huggingface >>>
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
# <<< huggingface <<<

21
.gitattributes.MD Normal file
View 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配置已生效。

15
.github/.depcheck.js vendored Normal file
View File

@@ -0,0 +1,15 @@
// https://github.com/depcheck/depcheck?tab=readme-ov-file#api
// node .github/.depcheck.js
import depcheck from 'depcheck';
const unused = await depcheck(process.cwd(), {
ignorePatterns: [],
});
console.debug(`unused.dependencies :>>`, unused.dependencies); // an array containing the unused dependencies
console.debug(`unused.devDependencies :>>`, unused.devDependencies); // an array containing the unused devDependencies
console.debug(`unused.missing :>>`, unused.missing); // a lookup containing the dependencies missing in `package.json` and where they are used
console.debug(`unused.using :>>`, unused.using); // a lookup indicating each dependency is used by which files
console.debug(`unused.invalidFiles :>>`, unused.invalidFiles); // files that cannot access or parse
console.debug(`unused.invalidDirs :>>`, unused.invalidDirs); // directories that cannot access

9
.github/.depcheckrc.yaml vendored Normal file
View File

@@ -0,0 +1,9 @@
# https://github.com/depcheck/depcheck#usage
# #####
# pnpm exec depcheck --config .github/.depcheckrc.yaml
# #####
# pnpm add json -g
# pnpm exec depcheck --json | json
ignores: ['@iconify-json/*']
skip-missing: true

60
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,60 @@
---
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.
## 🚀 Core Technologies & Conventions
* **Framework:** Vue 3
* **Language:** TypeScript
* **API Style:** **Strictly use the Composition API with `<script setup>` syntax.** The Options API and the standard `<script>` block with the `setup()` function are **not** used in this project.
* **Single File Component (SFC) Structure:** Follow this order within `.vue` files:
1. `<script setup lang="ts">` (Imports, logic, composables, etc.)
2. `<template>` (HTML structure)
3. `<style>` (Preferably `<style scoped>`)
* **Code Comments:**
* All comments within the codebase **must be written in Chinese**. (代码中的所有注释**必须使用中文**。)
* **注释应解释代码的“为什么”(设计意图、选择某方案的原因)或复杂逻辑/算法的“如何”,而不是解释“是什么”(重复代码功能)或“在哪里”(描述定义位置)。**
* **严禁添加仅说明代码被移动、重构或提取到其他文件的注释。** 例如,避免 `// XxxState 接口已移至 types.ts``// 提取为 useXYZ composable` 这样的注释。依赖代码结构、类型系统和 IDE 功能(如 Go To Definition来理解这些信息而不是通过注释。Focus comments on the *logic* itself, not its location or history.
* **同样地,严禁添加描述代码是“新增”、“修改”、“临时”或标记其开发状态的注释。** 例如,避免 `// 新增功能``// (新增)``// 临时修复``// TODO: 优化`(除非 TODO 指向具体的技术债务或未完成的逻辑,而不仅仅是标记状态)等注释。注释的目标是解释代码的内在逻辑或设计决策,而不是其变更历史或当前状态。
## 📦 UI Libraries & Components
* **UI Libraries:** This project utilizes both **Ant Design Vue** and **PrimeVue**.
* **Component Auto-Import:** Components from Ant Design Vue, PrimeVue, and those defined in `src/components/**/*.vue` (excluding filenames starting with `__`) are **automatically imported** via `unplugin-vue-components`.
* **Ant Design Vue:** Use components directly (e.g., `<a-button>`, `<a-table>`). Icons are also resolved (e.g., `<UserOutlined />`). CSS-in-JS is used; no manual style import is needed.
* **PrimeVue:** Use components directly with their standard names (e.g., `<Button>`, `<InputText>`, `<DataTable>`).
* **Custom Components:** Local components (e.g., `<MyCustomComponent />` defined in `src/components/MyCustomComponent.vue`) should be used directly in templates without explicit imports in `<script setup>`.
* **Custom Icons:** Custom SVG icons are available via `unplugin-icons` with the prefix `icon-`. Usage: `<icon-your-svg-file-name />`.
* **No Auto-Import Comments for Components:** When using auto-imported components (UI libraries, local components), **do not** add comments indicating they are auto-imported. Assume this behavior is known. Trust the tooling.
## ✨ API Auto-Import (unplugin-auto-import)
* **Automatic API Availability:** This project leverages `unplugin-auto-import` extensively. APIs from the following libraries/modules are globally available and **SHOULD NOT be explicitly imported** in `<script setup>` blocks unless necessary for specific type augmentation or rare cases:
* `vue` (e.g., `ref`, `computed`, `watch`, `onMounted`, `defineProps`, `defineEmits`, etc.)
* `pinia` (e.g., `defineStore`, `storeToRefs`)
* `vue-router/auto` (e.g., `useRouter`, `useRoute`, `useLink`) - Provided by `unplugin-vue-router` integration.
* `@vueuse/core` (Common utility functions)
* `vue-i18n` (e.g., `useI18n`)
* `consola/browser` (Available as the `consola` global for logging)
* **Auto-Imported Directories:** All exports from files within `src/stores/**` and `src/utils/**` are also auto-imported globally. Functions, variables, or stores defined and exported in these directories can be used directly without explicit import statements.
* **Template Usage:** Auto-import functionality also applies within the `<template>` block where appropriate (e.g., accessing store state or using certain utility functions directly).
* **Important:** Rely on the auto-import mechanism for the specified libraries and directories. Explicitly importing these APIs clutters the code unnecessarily.
* **No Auto-Import Explanation Comments:** When using auto-imported APIs (from libraries like Vue, Pinia, VueUse, or custom directories like `src/utils`, `src/stores`), simply use them directly. **Absolutely avoid adding comments that merely state an item is auto-imported** (e.g., DO NOT add comments like "// xxx is auto-imported from src/utils"). Focus comments on explaining complex logic or intent.
## ✅ Summary of Key Rules & Conventions
* **Always** use `<script setup lang="ts">`.
* Composition API is the **only** accepted style.
* Code comments **must be in Chinese** and explain **why/how (logic, intent)**, **strictly avoiding** comments that explain **what (code repetition), where (definition location), refactoring history, code status (e.g., 'new', 'modified'), or auto-import mechanisms.**
* Vue 3 Composition API, Pinia, Vue Router (auto), VueUse functions, `consola`, and exports from `src/stores` & `src/utils` are globally available via auto-import; **do not** import them explicitly.
* Ant Design Vue, PrimeVue, and local `src/components` components are auto-imported; use them directly in templates without explicit imports.
* **Crucially: Do not add comments solely to explain that an API or component is auto-imported OR where code has been moved/refactored OR the status of the code (e.g., 'new').** Trust the auto-import setup and IDE navigation. Focus on logic.
* Use custom SVG icons via the `<icon-name />` syntax.
* Maintain the specified SFC structure: `<script setup>`, `<template>`, `<style>`.

View File

@@ -1,71 +0,0 @@
# https://cn.vitejs.dev/guide/static-deploy
on:
push:
jobs:
build-and-deploy-to-vercel:
runs-on: ubuntu-latest
steps:
- uses: yanhao98/composite-actions/setup-node-environment@main
- name: 静态代码分析
run: pnpm lint
- name: Vue 类型检查
run: npx vue-tsc --build --force
- name: 修改 .npmrc
run: |
sed -i '/use-node-version/d' .npmrc;
sed -i '/node-mirror/d' .npmrc;
# https://github.com/vercel/examples/tree/main/ci-cd/github-actions
# cname-cn.vercel.com cname-china.vercel-dns.com
- name: 拉取 Vercel 环境信息
run: pnpm exec vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: 构建项目
run: pnpm exec vercel build --target=production --token=${{ secrets.VERCEL_TOKEN }}
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 部署到 Vercel
run: pnpm exec vercel deploy --prebuilt --target=production --token=${{ secrets.VERCEL_TOKEN }}
playwright: # act --job playwright --workflows .github/workflows/ci.yaml --pull=false --dryrun
runs-on: ubuntu-latest
container: yanhao98/runner-images:pnpm-with-playwright
steps:
- uses: yanhao98/composite-actions/setup-node-environment@13dd5d7bdbf6eb3ef88afa0ae8c524dc0ccf94ba
- run: pnpm run build-only
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 部署到 Surge
id: surge_deploy
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
uses: yanhao98/composite-actions/deploy-dist-to-surge@13dd5d7bdbf6eb3ef88afa0ae8c524dc0ccf94ba
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm exec playwright test
env:
BASE_URL: ${{ steps.surge_deploy.outputs.url }}
depcheck:
runs-on: ubuntu-latest
steps:
- uses: yanhao98/composite-actions/setup-node-environment@main
- run: npx depcheck || true
- run: node .depcheck.js
# surge-preview:
# runs-on: ubuntu-latest
# permissions:
# pull-requests: write # allow surge-preview to create/update PR comments
# steps:
# - uses: yanhao98/composite-actions/setup-node-environment@main
# - uses: afc163/surge-preview@v1
# id: surge_preview_step
# with:
# dist: dist
# build: pnpm run build-only
# - name: Get the preview_url
# run: echo "url => ${{ steps.surge_preview_step.outputs.preview_url }}"

36
.github/workflows/lint.yaml vendored Normal file
View File

@@ -0,0 +1,36 @@
# https://cn.vitejs.dev/guide/static-deploy
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
jobs:
lint-build-and-check:
runs-on: ubuntu-latest
steps:
- name: 🛠️ 设置Node环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
- name: 🔍 静态代码分析
run: pnpm run lint
- name: 📦 构建项目
run: pnpm run build-only
- name: 📊 计算构建大小
run: |
echo "📊 构建大小统计:"
echo "----------------------------------------"
echo "🔹 人类可读格式: $(du -sh dist | cut -f1)"
echo "🔹 以MB为单位: $(du -sm dist | cut -f1) MB"
echo "🔹 以KB为单位: $(du -sk dist | cut -f1) KB"
echo "🔹 文件总数: $(find dist -type f | wc -l) 个文件"
echo "----------------------------------------"
- name: ✅ 类型检查
run: pnpm run type-check # 要先 build保证 components.d.ts 存在
- name: 🔍 检查未使用的依赖
run: npx depcheck || true
- run: node .github/.depcheck.js
- run: npx taze --help
- run: npx taze
- run: npx taze -a
# - run: npx size-limit

60
.github/workflows/playwright.yaml vendored Normal file
View File

@@ -0,0 +1,60 @@
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
workflow_dispatch:
inputs:
run_cleanup:
description: '是否运行 Surge 清理 Job'
required: false
type: boolean
default: true
jobs:
surge:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.surge_deploy.outputs.url }}
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
- name: 🔨 构建项目
run: pnpm run build-only
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
- name: 🚀 部署到 Surge
id: surge_deploy
if: ${{ github.actor != 'nektos/act' }} # https://nektosact.com/usage/index.html#skipping-steps
uses: yanhao98/composite-actions/deploy-dist-to-surge@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
playwright:
needs: surge
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.55.0-noble
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
# - name: 📥 安装 Playwright 浏览器
# run: pnpm exec playwright install --with-deps
- name: ▶️ 运行 Playwright 测试
run: |
echo "BASE_URL: ${{ needs.surge.outputs.url }}"
echo "───────────────────────────────"
npx playwright test
env:
BASE_URL: ${{ needs.surge.outputs.url }}
cleanup_surge:
runs-on: ubuntu-latest
needs: [surge, playwright]
if: github.event_name == 'push' || github.event.inputs.run_cleanup == true
steps:
- name: 🧹 清理 Surge 部署
run: npx surge teardown ${{ needs.surge.outputs.url }} --token ${{ env.SURGE_TOKEN }}
env:
SURGE_TOKEN: d843de16b331c626f10771245c56ed93

33
.github/workflows/vercel.yaml vendored Normal file
View File

@@ -0,0 +1,33 @@
# https://cn.vitejs.dev/guide/static-deploy
# https://github.com/vercel/examples/tree/main/ci-cd/github-actions
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
branches: [main]
jobs:
build-and-deploy-to-vercel:
runs-on: ubuntu-latest
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
- name: 📥 拉取 Vercel 环境信息
run: pnpm dlx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
- name: 🏗️ 构建项目 # vite build
run: pnpm dlx vercel build --target=production --token=${{ secrets.VERCEL_TOKEN }}
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
__VUE_PROD_DEVTOOLS__: true
- name: 🧐 类型检查 # 要先 build保证 components.d.ts 存在
run: pnpm run type-check
# 🇨🇳: cname-cn.vercel.com cname-china.vercel-dns.com
- name: 🚀 部署到 Vercel
run: pnpm dlx vercel deploy --prebuilt --target=production --token=${{ secrets.VERCEL_TOKEN }} --confirm

3
.gitignore vendored
View File

@@ -11,6 +11,7 @@ node_modules
.DS_Store
dist
dist-ssr
dist.zip
coverage
*.local
@@ -26,10 +27,12 @@ coverage
*.njsproj
*.sln
*.sw?
.VSCodeCounter
*.tsbuildinfo
components.d.ts
auto-imports.d.ts
.vercel
/test-results/
/playwright-report/

20
.husky/README.md Normal file
View File

@@ -0,0 +1,20 @@
### Husky 遇到 command not found: husky
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
```shell
ln -s $(which pnpm) $HOME/.local/bin/pnpm
```
```
# if command -v pnpm >/dev/null 2>&1; then
# # 如果 pnpm 可用,直接使用它
# pnpm exec lint-staged
# else
# # 如果 pnpm 不可用,使用 $HOME/.local/bin/pnpm
# # ln -s $(which pnpm) $HOME/.local/bin/pnpm
# echo "找不到 pnpm使用 $HOME/.local/bin/pnpm"
# "$HOME"/.local/bin/pnpm exec lint-staged
# fi
```

6
.husky/commit-msg Normal file
View File

@@ -0,0 +1,6 @@
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
echo "当前提交的文件是:$1"
# npx --no -- commitlint --edit "$1"
pnpm exec commitlint --edit $1
echo "✅ [Commit-msg] commit-msg 钩子完成!"

4
.husky/post-merge Normal file
View File

@@ -0,0 +1,4 @@
# 此钩子在 git merge 或 git pull 成功完成后运行。
echo "🔗 [Post-merge] 正在安装依赖..."
pnpm install
echo "✅ [Post-merge] 依赖安装完成!"

View File

@@ -1,11 +1,4 @@
echo "Running pre-commit hook..."
if command -v pnpm >/dev/null 2>&1; then
# 如果 pnpm 可用,直接使用它
# 此钩子在执行 git commit 命令时,在创建提交之前运行。
echo "🧹 [Pre-commit] 正在运行 lint-staged..."
pnpm exec lint-staged
else
# 如果 pnpm 不可用,使用 $HOME/.local/bin/pnpm
# ln -s $(which pnpm) $HOME/.local/bin/pnpm
echo "pnpm not found, using $HOME/.local/bin/pnpm"
"$HOME"/.local/bin/pnpm exec lint-staged
fi
echo "✅ [Pre-commit] lint-staged 完成!"

12
.npmrc
View File

@@ -1,11 +1,13 @@
registry=https://registry.npmmirror.com/
registry=https://mirrors.cloud.tencent.com/npm/
registry=https://mirrors.huaweicloud.com/repository/npm/
registry=https://registry.npmjs.org/
registry=https://r-npm.oo1.dev
registry=https://registry.npmmirror.com
# registry=https://nexus.oo1.dev/repository/npm/
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
# use-node-version=20.17.0
node-mirror:release=https://npmmirror.com/mirrors/node/
use-node-version=22.18.0
node-mirror:release=https://npmmirror.com/mirrors/node/ # pnpm config set node-mirror:release=https://npmmirror.com/mirrors/node/
node-mirror:rc=https://npmmirror.com/mirrors/node-rc/
node-mirror:nightly=https://npmmirror.com/mirrors/node-nightly/
shamefully-hoist=true
# shamefully-hoist=true

14
.oxlintrc.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
// https://oxc.rs/docs/guide/usage/linter/rules/unicorn/no-new-array.html
// "rules": {
// "unicorn/no-useless-spread": "off"
// },
"ignorePatterns": [
"src/shadcn/**",
"src/components/InspiraUI/**",
"public/**",
"**/**.no-lint.ts",
"**/**.nolint.ts"
]
}

View File

@@ -1,2 +1,5 @@
/node_modules
/dist
/src/shadcn
/src/components/InspiraUI/
**/**.no-lint.ts
**/**.nolint.ts
**/**.noformat.json

1
.roorules Symbolic link
View File

@@ -0,0 +1 @@
.github/copilot-instructions.md

View File

@@ -4,6 +4,6 @@
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"simonhe.common-intellisense",
"antfu.file-nesting"
"oxc.oxc-vscode"
]
}

40
.vscode/settings.json vendored
View File

@@ -1,19 +1,35 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "explicit"
},
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// "editor.formatOnSaveMode": "modificationsIfAvailable", // 只格式化修改的部分
"editor.formatOnSaveMode": "file", // 格式化整个文件
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"],
// https://github.com/antfu/vscode-file-nesting-config
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"tsconfig.json": "tsconfig.*.json, env.d.ts",
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .prettier*, prettier*, .editorconfig"
}
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.oxc": "explicit",
"source.organizeImports": "never"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "never",
"source.fixAll.stylelint": "never",
"source.fixAll.oxc": "never",
"source.organizeImports": "never"
},
"editor.formatOnSave": false,
"oxc.enable": true,
"eslint.enable": true
}

33
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "🚀 dev",
"type": "shell",
"command": "pnpm run dev",
"isBackground": true,
"problemMatcher": {
"owner": "vite",
"pattern": {
"regexp": "."
},
"background": {
"activeOnStart": true,
"beginsPattern": ".*VITE.*",
"endsPattern": ".*ready in.*"
}
},
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "new"
},
"runOptions": {
"instanceLimit": 1
}
}
]
}

View File

@@ -9,25 +9,15 @@ curl -fsSL https://get-pnpm.oo1.dev/install.sh | sh -
iwr https://get-pnpm.oo1.dev/install.ps1 -useb | iex
# 安装依赖
pnpm install --registry=https://r-npm.oo1.dev
pnpm install --registry=https://nexus.oo1.dev/repository/npm
# 启动服务
pnpm run dev
```
## Tips
### Husky 遇到 command not found: husky
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
```shell
ln -s $(which pnpm) $HOME/.local/bin/pnpm
```
## Links
- https://github.com/hyoban-template/shadcn-vue-unocss-starter
- [Performance API优化页面性能](https://juejin.cn/post/7238779568478552122)
- [vitepress-theme-demoblock](https://www.npmjs.com/package/vitepress-theme-demoblock)
- [Vite PWA](https://vite-pwa-org-zh.netlify.app/guide/)
@@ -39,7 +29,12 @@ ln -s $(which pnpm) $HOME/.local/bin/pnpm
---
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
- https://biomejs.dev/zh-cn/internals/language-support/
- https://github.dev/antfu-collective/vitesse/
- [Vue3 入门指南与实战案例](https://vue3.chengpeiquan.com/)
- [如何建立一个最小重现](https://antfu.me/posts/why-reproductions-are-required-zh#如何建立一个最小重现)
---
- [primevue-scopedtokens](https://primevue.org/theming/styled/#scopedtokens)
+ https://www.npmjs.com/package/npkill

318
auto-imports.d.ts vendored
View File

@@ -1,318 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const computed: typeof import('vue')['computed']
const computedAsync: typeof import('@vueuse/core')['computedAsync']
const computedEager: typeof import('@vueuse/core')['computedEager']
const computedInject: typeof import('@vueuse/core')['computedInject']
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
const controlledRef: typeof import('@vueuse/core')['controlledRef']
const createApp: typeof import('vue')['createApp']
const createEventHook: typeof import('@vueuse/core')['createEventHook']
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
const createPinia: typeof import('pinia')['createPinia']
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
const customRef: typeof import('vue')['customRef']
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef']
const getActiveHead: typeof import('@unhead/vue')['getActiveHead']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
const inject: typeof import('vue')['inject']
const injectHead: typeof import('@unhead/vue')['injectHead']
const injectLocal: typeof import('@vueuse/core')['injectLocal']
const isDefined: typeof import('@vueuse/core')['isDefined']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
const onLongPress: typeof import('@vueuse/core')['onLongPress']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: typeof import('vue')['provide']
const provideLocal: typeof import('@vueuse/core')['provideLocal']
const reactify: typeof import('@vueuse/core')['reactify']
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
const reactive: typeof import('vue')['reactive']
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
const reactivePick: typeof import('@vueuse/core')['reactivePick']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
const refDebounced: typeof import('@vueuse/core')['refDebounced']
const refDefault: typeof import('@vueuse/core')['refDefault']
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const syncRef: typeof import('@vueuse/core')['syncRef']
const syncRefs: typeof import('@vueuse/core')['syncRefs']
const templateRef: typeof import('@vueuse/core')['templateRef']
const throttledRef: typeof import('@vueuse/core')['throttledRef']
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
const toRaw: typeof import('vue')['toRaw']
const toReactive: typeof import('@vueuse/core')['toReactive']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
const unref: typeof import('vue')['unref']
const unrefElement: typeof import('@vueuse/core')['unrefElement']
const until: typeof import('@vueuse/core')['until']
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
const useAnimate: typeof import('@vueuse/core')['useAnimate']
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
const useArraySome: typeof import('@vueuse/core')['useArraySome']
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
const useCached: typeof import('@vueuse/core')['useCached']
const useClipboard: typeof import('@vueuse/core')['useClipboard']
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
const useCloned: typeof import('@vueuse/core')['useCloned']
const useColorMode: typeof import('@vueuse/core')['useColorMode']
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
const useCountdown: typeof import('utils4u/vue-use')['useCountdown']
const useCounter: typeof import('@vueuse/core')['useCounter']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVar: typeof import('@vueuse/core')['useCssVar']
const useCssVars: typeof import('vue')['useCssVars']
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
const useCycleList: typeof import('@vueuse/core')['useCycleList']
const useDark: typeof import('@vueuse/core')['useDark']
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
const useDebounce: typeof import('@vueuse/core')['useDebounce']
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
const useDraggable: typeof import('@vueuse/core')['useDraggable']
const useDropZone: typeof import('@vueuse/core')['useDropZone']
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
const useElementHover: typeof import('@vueuse/core')['useElementHover']
const useElementSize: typeof import('@vueuse/core')['useElementSize']
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
const useEventBus: typeof import('@vueuse/core')['useEventBus']
const useEventListener: typeof import('@vueuse/core')['useEventListener']
const useEventSource: typeof import('@vueuse/core')['useEventSource']
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
const useFps: typeof import('@vueuse/core')['useFps']
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useHead: typeof import('@unhead/vue')['useHead']
const useHeadSafe: typeof import('@unhead/vue')['useHeadSafe']
const useId: typeof import('vue')['useId']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useModel: typeof import('vue')['useModel']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
const useParallax: typeof import('@vueuse/core')['useParallax']
const useParentElement: typeof import('@vueuse/core')['useParentElement']
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
const usePermission: typeof import('@vueuse/core')['usePermission']
const usePointer: typeof import('@vueuse/core')['usePointer']
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
const usePrevious: typeof import('@vueuse/core')['usePrevious']
const useRafFn: typeof import('@vueuse/core')['useRafFn']
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
const useScroll: typeof import('@vueuse/core')['useScroll']
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
const useSeoMeta: typeof import('@unhead/vue')['useSeoMeta']
const useServerHead: typeof import('@unhead/vue')['useServerHead']
const useServerHeadSafe: typeof import('@unhead/vue')['useServerHeadSafe']
const useServerSeoMeta: typeof import('@unhead/vue')['useServerSeoMeta']
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
const useShare: typeof import('@vueuse/core')['useShare']
const useSlots: typeof import('vue')['useSlots']
const useSorted: typeof import('@vueuse/core')['useSorted']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
const useTimeout: typeof import('@vueuse/core')['useTimeout']
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
const useTitle: typeof import('@vueuse/core')['useTitle']
const useToNumber: typeof import('@vueuse/core')['useToNumber']
const useToString: typeof import('@vueuse/core')['useToString']
const useToggle: typeof import('@vueuse/core')['useToggle']
const useTransition: typeof import('@vueuse/core')['useTransition']
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
const useVModel: typeof import('@vueuse/core')['useVModel']
const useVModels: typeof import('@vueuse/core')['useVModels']
const useVibrate: typeof import('@vueuse/core')['useVibrate']
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
const useVueCountdown: typeof import('utils4u/vue-use')['useCountdown']
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchDeep: typeof import('@vueuse/core')['watchDeep']
const watchEffect: typeof import('vue')['watchEffect']
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
const watchOnce: typeof import('@vueuse/core')['watchOnce']
const watchPausable: typeof import('@vueuse/core')['watchPausable']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

9
commitlint.config.ts Normal file
View File

@@ -0,0 +1,9 @@
import type { UserConfig } from '@commitlint/types';
const Configuration: UserConfig = {
extends: ['@commitlint/config-conventional'],
formatter: '@commitlint/format',
};
export default Configuration;

20
components.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/shadcn/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/shadcn/components",
"composables": "@/shadcn/composables",
"utils": "@/shadcn/lib/utils",
"ui": "@/shadcn/components/ui",
"lib": "@/shadcn/lib"
},
"iconLibrary": "lucide"
}

View File

@@ -1,7 +1,7 @@
import { test, expect } from '@playwright/test';
import { expect, test } from '@playwright/test';
test('接口请求页面', async ({ page }) => {
await page.goto('/api');
await page.goto('/Page/API');
// /fake-api 正常返回。
await expect(page.locator('pre')).toContainText('http://dummyimage.com/200x200');
// // /npm-registry-api 正常返回。

View File

@@ -0,0 +1,18 @@
import { expect, test } from '@playwright/test';
import { consola } from 'consola';
test('页面加载正常', async ({ page }, testInfo) => {
await page.goto('/Home');
// await expect(page.getByRole('link', { name: '中文-页面.page.vue' })).toBeVisible();
// 截图并将其附加到测试结果
await testInfo.attach('screenshot', { body: await page.screenshot({ fullPage: true }), contentType: 'image/png' });
// 查找包含"commit:"文本的元素
const commitElement = page.locator('text=commit:').first();
// 获取元素的文本内容
const innerText = await commitElement.textContent();
consola.debug(`Commit 文本内容: "${innerText}"`);
expect(innerText).toContain('commit:');
});

9
env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
/// <reference types="vitest" />
/// <reference types="vite-plugin-vue-layouts/client" />
/// <reference types="vite-plugin-vue-meta-layouts/client" />
/* /// <reference types="vite-plugin-pwa/client" /> */
/// <reference types="unplugin-vue-macros/macros-global" />
/// <reference types="unplugin-vue-router/client" />
/// <reference types="unplugin-icons/types/vue" />
/// <reference types="@intlify/unplugin-vue-i18n/messages" />

View File

@@ -1,43 +0,0 @@
import { includeIgnoreFile } from '@eslint/compat';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import vueTsEslintConfig from '@vue/eslint-config-typescript';
import pluginVue from 'eslint-plugin-vue';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const gitignorePath = path.resolve(__dirname, '.gitignore');
/**
* @type import('eslint').Linter.Config[]
*/
export default [
// --ignore-path .gitignore
includeIgnoreFile(gitignorePath),
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig({
supportedScriptLangs: {
ts: true,
tsx: true,
}
}),
skipFormatting,
{
rules: {
'vue/multi-word-component-names': 'off',
},
}
];

134
eslint.config.ts Normal file
View File

@@ -0,0 +1,134 @@
/*
MORE:
https://juejin.cn/post/7403244457263628300#heading-11
*/
import pluginVitest from '@vitest/eslint-plugin';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import { configureVueProject, defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
import { flatConfigs as eslintPluginImportX_flatConfigs } from 'eslint-plugin-import-x';
import pluginOxlint from 'eslint-plugin-oxlint';
import perfectionist from 'eslint-plugin-perfectionist';
import pluginPlaywright from 'eslint-plugin-playwright';
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
import pluginVue from 'eslint-plugin-vue';
// https://github.com/vuejs/eslint-config-typescript/#advanced-setup
configureVueProject({ scriptLangs: ['ts', 'tsx', 'js', 'jsx'] });
const _ignores = [
// >>>
(await import('@eslint/compat')).includeIgnoreFile(
// eslint-disable-next-line unicorn/import-style
(await import('node:path')).default.resolve(import.meta.dirname, '.gitignore'),
),
// <<<
// >>>
(await import('eslint/config')).globalIgnores([
'**/dist/**',
'**/dist-ssr/**',
'**/coverage/**',
'auto-imports.d.ts',
'components.d.ts',
'typed-router.d.ts',
// .oxlintrc.json > ignorePatterns >>>
'src/shadcn/**',
'src/components/InspiraUI/**',
'public/**',
'**/**.no-lint.ts',
'**/**.nolint.ts',
// <<< .oxlintrc.json
]),
// <<<
];
export default defineConfigWithVueTs(
// >>> create vue >>>
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
_ignores,
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
{
...pluginPlaywright.configs['flat/recommended'],
files: ['e2e/playwright/**/*.{test,spec}.{js,ts,jsx,tsx}'],
},
...pluginOxlint.configs['flat/recommended'],
skipFormatting,
// <<< create vue <<<
// region >> eslint-plugin-unicorn >>
eslintPluginUnicorn.configs.recommended,
{
rules: {
'unicorn/filename-case': 'off',
'unicorn/no-console-spaces': 'off',
'unicorn/no-null': 'off',
'unicorn/no-useless-spread': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/relative-url-style': 'off', // [plugin:vite:import-glob] Invalid glob: "imgs/*.png" (resolved: "imgs/*.png"). It must start with '/' or './'
'unicorn/no-await-expression-member': 'off',
},
},
// endregion <<< eslint-plugin-unicorn <<<
// region >>> eslint-plugin-import-x >>>
eslintPluginImportX_flatConfigs.recommended,
{
rules: {
'import-x/no-unresolved': 'off', // https://github.com/pzmosquito/eslint-import-resolver-vite/blob/67da5e259ee4c9da4c44d81b93364ae2777d00eb/index.js#L100
'import-x/newline-after-import': 'error',
'import-x/first': 'error',
'import-x/no-named-as-default': 'off',
'import-x/no-duplicates': 'off',
'import-x/named': 'off',
},
},
// endregion <<< eslint-plugin-import-x <<<
// region >>> eslint-plugin-perfectionist >>>
// https://perfectionist.dev/guide/getting-started
perfectionist.configs['recommended-natural'],
{
rules: {
'perfectionist/sort-classes': 'off',
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-imports': ['error'],
'perfectionist/sort-modules': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-enums': 'off',
'perfectionist/sort-union-types': 'off',
},
},
// endregion <<< eslint-plugin-perfectionist <<<
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'vue/block-order': [
'error',
{
order: ['script', 'template', 'style'],
},
],
'vue/define-macros-order': [
'error',
{
order: ['defineOptions', 'defineProps', 'defineEmits', 'defineSlots'],
},
],
'vue/multi-word-component-names': 'off',
},
},
);

26
fake/upload.fake.ts Normal file
View File

@@ -0,0 +1,26 @@
// fake/user.fake.ts
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
let fail = !false;
export default defineFakeRoute([
{
method: 'POST',
rawResponse(req, res) {
fail = !fail;
if (fail) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Upload failed' }));
} else {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ url: 'https://picsum.photos/200/300' }));
}
},
response: () => {
return {
url: 'https://picsum.photos/200/300',
};
},
timeout: 2000,
url: '/fake/upload',
},
]);

View File

@@ -1,34 +1,34 @@
import { faker } from '@faker-js/faker';
// fake/user.fake.ts
import Mock from 'mockjs';
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
import { faker } from '@faker-js/faker';
export default defineFakeRoute([
{
url: '/mock/get-user-info',
response: () => {
return Mock.mock({
id: '@guid',
username: '@first',
email: '@email',
avatar: '@image("200x200")',
email: '@email',
id: '@guid',
role: 'admin',
username: '@first',
});
},
url: '/mock/get-user-info',
},
{
url: '/fake/get-user-info',
response: () => {
return {
id: faker.string.uuid(),
avatar: faker.image.avatar(),
birthday: faker.date.birthdate(),
email: faker.internet.email(),
firstName: faker.person.firstName(),
id: faker.string.uuid(),
lastName: faker.person.lastName(),
sex: faker.person.sexType(),
role: 'admin',
sex: faker.person.sexType(),
};
},
url: '/fake/get-user-info',
},
]);

120
fake/xml.fake.ts Normal file
View File

@@ -0,0 +1,120 @@
import { defineFakeRoute } from 'vite-plugin-fake-server/client';
// 简单的实体转义,避免在 XML 中出现非法字符;先替换 & 再替换其他符号,防止二次转义
function esc(s: string) {
return s
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&apos;');
}
// 从原始 XML 文本中提取某个标签的文本内容(弱解析,仅用于演示)
function pickTag(xml: string, tag: string) {
// 说明使用非贪婪匹配并允许标签上带属性N.B. 未处理命名空间与嵌套同名标签的复杂情形
const re = new RegExp(`<${tag}(?:\n|\r|s|>|/)[^>]*>([sS]*?)</${tag}>`, 'i');
const m = xml.match(re);
return m?.[1]?.trim() ?? '';
}
// 通过 rawResponse 返回 XML 文本,便于在浏览器端演示 XML 解析
export default defineFakeRoute([
{
method: 'GET',
url: '/xml/sample',
rawResponse(req, res) {
// 通过查询参数自定义返回的 XML 字段
// 支持的参数to, from, heading, body, id, createdAt
const url = new URL(req.url!, 'http://localhost');
const q = url.searchParams;
const to = q.get('to') ?? 'George';
const from = q.get('from') ?? 'John';
const heading = q.get('heading') ?? 'Reminder';
const body = q.get('body') ?? "Don't forget the meeting at 3 PM today.";
const id = q.get('id') ?? '42';
const createdAt = q.get('createdAt') ?? '2025-09-02T10:00:00Z';
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>${esc(to)}</to>
<from>${esc(from)}</from>
<heading>${esc(heading)}</heading>
<body>${esc(body)}</body>
<meta>
<id>${esc(id)}</id>
<createdAt>${esc(createdAt)}</createdAt>
</meta>
</note>`;
res.writeHead(200, { 'Content-Type': 'application/xml; charset=UTF-8' });
res.end(xml);
},
},
{
method: 'POST',
url: '/xml/submit',
rawResponse(req, res) {
const chunks: Buffer[] = [];
req.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
req.on('end', () => {
const contentType = String(req.headers['content-type'] || '').toLowerCase();
const raw = Buffer.concat(chunks).toString('utf8');
// 默认值,与 GET 一致
let to = 'George';
let from = 'John';
let heading = 'Reminder';
let body = "Don't forget the meeting at 3 PM today.";
let id = '42';
let createdAt = new Date().toISOString();
try {
if (contentType.includes('application/json')) {
const data = JSON.parse(raw || '{}');
to = data.to ?? to;
from = data.from ?? from;
heading = data.heading ?? heading;
body = data.body ?? body;
id = data.id ?? id;
createdAt = data.createdAt ?? createdAt;
} else if (contentType.includes('application/xml') || contentType.includes('text/xml')) {
// 解析 XML 请求体中常用字段(演示用,未处理命名空间/CDATA 等复杂场景)
to = pickTag(raw, 'to') || to;
from = pickTag(raw, 'from') || from;
heading = pickTag(raw, 'heading') || heading;
body = pickTag(raw, 'body') || body;
id = pickTag(raw, 'id') || id;
createdAt = pickTag(raw, 'createdAt') || createdAt;
} else if (contentType.includes('application/x-www-form-urlencoded')) {
// 简单解析表单key=value&…;此处演示用途,未处理复杂编码场景
const params = new URLSearchParams(raw);
to = params.get('to') ?? to;
from = params.get('from') ?? from;
heading = params.get('heading') ?? heading;
body = params.get('body') ?? body;
id = params.get('id') ?? id;
createdAt = params.get('createdAt') ?? createdAt;
}
} catch {
// 忽略解析错误,保持默认值
}
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>${esc(to)}</to>
<from>${esc(from)}</from>
<heading>${esc(heading)}</heading>
<body>${esc(body)}</body>
<meta>
<id>${esc(id)}</id>
<createdAt>${esc(createdAt)}</createdAt>
</meta>
</note>`;
res.writeHead(200, { 'Content-Type': 'application/xml; charset=UTF-8' });
res.end(xml);
});
},
},
]);

View File

@@ -2,23 +2,54 @@
<html lang="zh-CN" data-build-time="%VITE_BUILD_TIME%" data-commit="%VITE_BUILD_COMMIT%">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<!-- <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> -->
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<title>vue-ts-example</title>
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/@unocss/reset/tailwind.min.css" />
<style type="text/css">
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
}
}
#app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
@supports (min-height: 100dvh) {
#app {
min-height: 100dvh;
}
html .min-h-screen {
min-height: 100dvh;
}
}
.page-wrapper {
flex-grow: 1;
}
</style>
</head>
<body ontouchstart ontouchend>
<!-- ontouchstart ontouchend -->
<body>
<div id="app">
<div style="display: flex; justify-content: center; align-items: center; height: 100vh">Loading...</div>
<div class="page-wrapper" style="display: flex; justify-content: center; align-items: center">Loading...</div>
</div>
<script type="module" src="/src/main.ts"></script>
<script src="https://fastly.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script>
<link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/nprogress/nprogress.css" />
<script src="https://testingcf.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script>
<!-- .min.js 是 jsDelivr 的特殊处理 -->
<!-- <script src="https://unpkg.luckincdn.com/@vant/touch-emulator@1.4.0/dist/index.js"></script> -->
</body>
<script>
(function (d) {
@@ -48,6 +79,6 @@
} catch (e) {}
};
s.parentNode.insertBefore(tk, s);
})(document);
}) /* (document) */;
</script>
</html>

View File

@@ -1,105 +1,181 @@
{
"packageManager": "pnpm@9.12.1",
"packageManager": "pnpm@10.15.1",
"name": "vue-ts-example",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --port 4730 --host",
"all": "run-p build-only format type-check lint",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
"preview": "vite preview --port 4173",
"lint-format": "run-p lint:oxlint lint:eslint format",
"format": "prettier --write src/",
"type-check": "vue-tsc --build",
"lint": "run-s lint:*",
"_oxlint_cfg": "oxlint . --fix --ignore-path=.gitignore --print-config",
"__oxlint_-D": "oxlint . --fix --deny=correctness",
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
"lint:eslint": "eslint . --fix",
"prepare": "husky",
"dev+preview": "bunx dev-and-preview@1.0.0",
"taze": "pnpx taze",
"test:unit": "vitest",
"playwright": "playwright test",
"playwright:headless": "HEADLESS=true playwright test",
"playwright:ui": "playwright test --ui",
"playwright:chromium": "playwright test --project=chromium",
"depcheck": "depcheck"
"dep:dedupe": "pnpm dedupe",
"dep:update": "pnpm dlx taze major --interactive",
"sizecheck:Treemap": "pnpm dlx vite-bundle-visualizer -t treemap",
"sizecheck:Sunburst": "pnpm dlx vite-bundle-visualizer -t sunburst",
"sizecheck:Network": "pnpm dlx vite-bundle-visualizer -t network",
"knip": "pnpm dlx knip"
},
"lint-staged": {
"src/**/*.{js,ts,vue}": [
"{src,e2e}/**/*.{js,jsx,ts,tsx,vue}": [
"prettier --write",
"eslint --fix"
"eslint --fix",
"oxlint --fix"
]
},
"pnpm": {
"patchedDependencies": {}
"overrides": {
"vite": "$vite",
"@primevue/auto-import-resolver": "$primevue"
}
},
"dependencies": {
"@alova/adapter-axios": "^2.0.7",
"@unhead/vue": "^1.11.7",
"@alova/adapter-axios": "^2.0.16",
"@ant-design/icons-vue": "^7.0.1",
"@formkit/auto-animate": "^0.8.4",
"@intlify/unplugin-vue-i18n": "^11.0.0",
"@pinia/colada": "^0.17.3",
"@primeuix/themes": "^1.2.3",
"@primevue/icons": "^4.3.8",
"@splinetool/runtime": "^1.10.53",
"@types/p5": "^1.7.6",
"@types/sortablejs": "^1.15.8",
"@unhead/vue": "^2.0.14",
"@vant/use": "^1.6.0",
"@vueuse/core": "^11.1.0",
"alova": "^3.0.20",
"axios": "^1.7.7",
"dayjs": "^1.11.13",
"jsencrypt": "^3.3.2",
"@vueuse/core": "^13.9.0",
"alova": "^3.3.4",
"ant-design-vue": "~4.2.6",
"axios": "^1.11.0",
"cesium": "^1.132.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"consola": "^3.4.2",
"dayjs": "^1.11.18",
"deep-freeze-es6": "^4.0.1",
"jsencrypt": "^3.5.4",
"lucide-vue-next": "^0.542.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"p5": "^2.0.5",
"page-stack-vue3": "^2.5.6",
"pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.1",
"radash": "^12.1.0",
"taze": "^0.17.2",
"tdesign-icons-vue-next": "^0.2.6",
"tdesign-mobile-vue": "^1.4.3",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"plotly.js-dist-min": "^3.1.0",
"primeicons": "^7.0.0",
"primelocale": "^2.1.7",
"primevue": "^4.3.8",
"radash": "^12.1.1",
"radix-vue": "^1.9.17",
"reka-ui": "^2.5.0",
"satellite.js": "^6.0.1",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.3.1",
"tdesign-icons-vue-next": "^0.3.7",
"three": "^0.179.1",
"ts-enum-util": "^4.1.0",
"utils4u": "^2.11.0",
"vant": "^4.9.7",
"vite-plugin-webfont-dl": "^3.9.5",
"vue": "^3.5.11",
"utils4u": "^4.2.3",
"vant": "^4.9.21",
"vite-plugin-image-optimizer": "^2.0.2",
"vue": "^3.5.20",
"vue-data-ui": "^3.0.15",
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^11.1.11",
"vue-page-stack": "^3.2.0",
"vue-router": "^4.4.5"
"vue-router": "^4.5.1"
},
"devDependencies": {
"@eslint/compat": "^1.2.0",
"@faker-js/faker": "^9.0.3",
"@iconify-json/carbon": "^1.2.2",
"@iconify/utils": "^2.1.33",
"@playwright/test": "^1.48.0",
"@tsconfig/node20": "^20.1.4",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@eslint/compat": "^1.3.2",
"@faker-js/faker": "^10.0.0",
"@iconify-json/carbon": "^1.2.13",
"@iconify-json/logos": "^1.2.9",
"@iconify-json/mdi": "^1.2.3",
"@iconify/utils": "^3.0.1",
"@playwright/test": "^1.55.0",
"@prettier/plugin-oxc": "^0.0.4",
"@primevue/auto-import-resolver": "^4.3.8",
"@primevue/metadata": "^4.3.8",
"@tsconfig/node22": "^22.0.2",
"@types/archiver": "^6.0.3",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.7.5",
"@types/mockjs": "^1.0.10",
"@types/node": "^24.3.0",
"@types/nprogress": "^0.2.3",
"@unocss/preset-attributify": "^0.63.4",
"@unocss/preset-rem-to-px": "^0.63.4",
"@unocss/reset": "^0.63.4",
"@vant/auto-import-resolver": "^1.2.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/eslint-config-prettier": "^10.0.0",
"@vue/eslint-config-typescript": "^14.0.1",
"@types/plotly.js-dist-min": "^2.3.4",
"@types/three": "^0.180.0",
"@unocss/preset-attributify": "^66.5.0",
"@unocss/reset": "^66.5.0",
"@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.1.1",
"@vitest/eslint-plugin": "^1.3.6",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.5.1",
"@vue/tsconfig": "^0.8.1",
"archiver": "^7.0.1",
"create-vue": "3.18.0",
"depcheck": "^1.4.7",
"eruda": "^3.4.0",
"eslint": "^9.12.0",
"eslint-plugin-vue": "^9.29.0",
"husky": "^9.1.6",
"jsdom": "^25.0.1",
"less": "^4.2.0",
"lint-staged": "^15.2.10",
"eruda": "^3.4.3",
"eslint": "^9.34.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-oxlint": "^1.14.0",
"eslint-plugin-perfectionist": "^4.15.0",
"eslint-plugin-playwright": "^2.2.2",
"eslint-plugin-unicorn": "^61.0.2",
"eslint-plugin-vue": "^10.4.0",
"husky": "^9.1.7",
"jiti": "^2.5.1",
"jsdom": "^26.1.0",
"less": "^4.4.1",
"lint-staged": "^16.1.6",
"mockjs": "^1.1.0",
"npm-run-all2": "^6.2.3",
"prettier": "^3.3.3",
"surge": "latest",
"typescript": "~5.6.0",
"unocss": "^0.63.4",
"unplugin-auto-import": "^0.18.3",
"unplugin-icons": "^0.19.3",
"unplugin-vue-components": "^0.27.4",
"unplugin-vue-macros": "^2.12.3",
"unplugin-vue-markdown": "^0.26.2",
"unplugin-vue-router": "^0.10.8",
"vercel": "latest",
"vite": "^5.4.8",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-fake-server": "^2.1.2",
"vite-plugin-vue-devtools": "^7.4.6",
"vue-tsc": "^2.1.6"
"naive-ui": "^2.42.0",
"npm-run-all2": "^8.0.4",
"oxlint": "~1.14.0",
"prettier": "3.6.2",
"spritesmith": "^3.5.1",
"typescript": "~5.9.2",
"unocss": "66.5.0",
"unocss-preset-animations": "^1.2.1",
"unocss-preset-chinese": "^0.3.3",
"unocss-preset-shadcn": "^0.5.0",
"unplugin-auto-import": "^20.1.0",
"unplugin-icons": "^22.2.0",
"unplugin-vue-components": "^29.0.0",
"unplugin-vue-macros": "^2.14.5",
"unplugin-vue-markdown": "^29.1.0",
"unplugin-vue-router": "^0.15.0",
"vfonts": "^0.0.3",
"vite": "npm:rolldown-vite@^7.1.5",
"vite-plugin-checker": "^0.10.3",
"vite-plugin-fake-server": "^2.2.0",
"vite-plugin-image-tools": "^3.0.0",
"vite-plugin-purgecss-updated-v5": "^1.2.6",
"vite-plugin-singlefile": "^2.3.0",
"vite-plugin-static-copy": "^3.1.2",
"vite-plugin-vue-devtools": "^8.0.1",
"vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-vue-meta-layouts": "^0.6.0",
"vite-plugin-webfont-dl": "^3.11.1",
"vitest": "^3.2.4",
"vue-component-type-helpers": "^3.0.6",
"vue-tsc": "^3.0.6"
}
}

View File

@@ -1,13 +0,0 @@
diff --git a/dist/vue-router.d.ts b/dist/vue-router.d.ts
index a66b6e0c627a9221a5394066666da49597891d07..19aa9d5c2d42a7221017265225d08c4467f1a69b 100644
--- a/dist/vue-router.d.ts
+++ b/dist/vue-router.d.ts
@@ -1751,7 +1751,7 @@ export { }
* NOTE: this used to be `@vue/runtime-core` but it should have been `vue` for a long time. Using both declaration at
* the same time breaks so using only one everywhere is the preferred way.
*/
-declare module 'vue' {
+declare module '@vue/runtime-core' {
export interface ComponentCustomOptions {
/**
* Guard called when the router is navigating to the route that is rendering

View File

@@ -1,79 +1,95 @@
import { defineConfig, devices } from '@playwright/test';
import process from 'node:process';
const runningInVSCode = process.env.TERM_PROGRAM === 'vscode';
const baseURL = runningInVSCode ? 'http://localhost:4173' : process.env.BASE_URL || 'https://vue-ts-example.oo1.dev';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
// require('dotenv').config();
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
/* Run tests in files in parallel */
fullyParallel: true,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 5000,
},
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: process.env.BASE_URL || 'https://vue-ts-example.oo1.dev',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// use: {
// channel: 'chrome',
// },
// },
],
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
testDir: './e2e/playwright',
/* Maximum time one test can run for. */
timeout: 30 * 1000,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 0,
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL,
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
/* Only on CI systems run the tests headless */
headless: !!process.env.CI || process.env.HEADLESS === 'true',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
// /* Run your local dev server before starting the tests */
webServer: runningInVSCode
? {
command: 'pnpm run build-only; pnpm run preview',
port: 4173,
reuseExistingServer: true,
}
: undefined,
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/',
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
});

12763
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,81 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>星座图的html页面</title>
</head>
<body>
<div id="planispherewidget-container"></div>
<script src="./lib/konva.2.4.2.min.js"></script>
<script src="./lib/jquery.3.6.0.min.js"></script>
<script src="./planispherewidget.js"></script>
<script>
window.planisphereWidgetInstance = null;
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
// 强烈建议在此处添加来源验证 (event.origin) 以提高安全性
// 例如: if (event.origin !== 'YOUR_VUE_APP_ORIGIN') return;
if (!event.data) return; // 如果没有数据,则忽略
// 处理初始化消息
if (event.data.type === 'INIT_WIDGET') {
console.log('HTML 页面:收到初始化指令');
if (!window.planisphereWidgetInstance) {
// 创建 PlanisphereWidget 实例,指定容器 ID
window.planisphereWidgetInstance = new PlanisphereWidget('planispherewidget-container', {
// 从 planispherewidget.js 获取的默认值
bg_color: '#27293D', // 背景色
fore_color: '#CCCCCC', // 前景色
coor_color: '#1B1B24', // 坐标颜色
border_color: '#CCCCCC', // 边框颜色
wave_color: '#1D8CF8', // 波形颜色
margin_left: 0, // 左边距
margin_top: 0, // 上边距
// 您可以在这里覆盖上面的默认值,或者从 event.data.options 获取配置
// ...event.data.options // 如果 Vue 组件传递了配置
});
console.log('HTML 页面PlanisphereWidget 已实例化');
} else {
console.log('HTML 页面PlanisphereWidget 已存在,无需重复实例化');
}
// 可选:通知父窗口已准备好接收数据
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'WIDGET_READY' }, '*'); // 使用更具体的 targetOrigin
// }
}
// 处理数据馈送消息
else if (event.data.type === 'FEED_DATA') {
if (window.planisphereWidgetInstance) {
console.log('HTML 页面:收到来自父窗口的数据:', event.data.payload);
if (Array.isArray(event.data.payload)) {
window.planisphereWidgetInstance.feed(event.data.payload);
} else {
console.error('HTML 页面:收到的数据格式无效:', event.data.payload);
}
} else {
console.warn('HTML 页面Widget 尚未初始化,无法处理 FEED_DATA');
}
}
});
</script>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body > div {
width: 100%;
height: 100%;
}
</style>
</html>

View File

@@ -0,0 +1,19 @@
## 这个代码没测试。
``javascript
// 如果需要明确设置模式(虽然默认就是星座图)
widget.setViewMode(0); // 0=星座图
widget.feed(testData);
// 如果想测试余晖效果
widget.setViewMode(3); // 3=余晖图
setInterval(() => {
const newData = [];
for (let i = 0; i < 20; i++) { // 每次添加少量新点
const x = Math.random() * 2 - 1;
const y = Math.random() * 2 - 1;
newData.push([x, y]);
}
widget.feed(newData); // 传入新数据以观察余晖
}, 500); // 每 500ms 更新一次
```

View File

@@ -0,0 +1,188 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>频谱图+瀑布图的html页面</title>
</head>
<body>
<div id="waterfall" style="width: 100%; height: 100%"></div>
<script src="./lib/konva.2.4.2.min.js"></script>
<script src="./lib/jquery.3.6.0.min.js"></script>
<script src="./lib/lodash@4.17.21.min.js"></script>
<script src="colormapwidget.js"></script>
<script src="waterfallwidget.js"></script>
<script>
// 定义默认配置对象
const payload = {
minFreq: 0,
maxFreq: 200000000,
rtmode: 0,
doppler: false,
adjShow: true,
adjMaxbw: 100000,
enableWfBuff: false,
unitId: 0,
coorFreqType: 0,
showFreqLable: true,
wfFreqPointLable: true,
wfFreqPointLableList: [],
showWhitelist: false,
waveSplite: 0.5,
};
const wfOption = {
wfRulerEnable: true,
wfTheme: 'default',
wfRulerGravity: true,
wfRulerLocation: -100,
wfMindB: -120,
wfMaxdB: 0,
showPeakMarker: true,
wfShowAvg: true,
wfShowMin: false,
wfShowMax: false,
wfInfoPos: 0,
wfCurLine: true,
specColor: '#00ff00',
specAvgColor: '#ffff00',
specMaxColor: '#ff0000',
specMinColor: '#0000ff',
afterGlowColor: '#00ffff',
wfAdjStepType: 0,
wfAdjStep: 1000,
};
const id = 'waterfall';
// 定义 label_manager
const pl = {
label_manager: {
labels: [
// 这里可以添加一些示例标签,或者保持为空
// { name: 'FM', id: 'id1', bg_color: '#2bffc6', fore_color: '#000', location: 80000000, bw: 100000 },
// { name: 'AM', id: 'id2', bg_color: '#2bffc6', fore_color: '#000', location: 90000000, bw: 10000 },
// { name: 'AM', id: 'id2', bg_color: '#ff0000', fore_color: '#ff0000', location: 90000000, bw: 10000 },
],
onclick: function (label) {
console.log('Label clicked:', label);
},
load: function (start, end) {
// 简单的 load 实现,可以根据需要扩展
console.log(`Loading labels for range: ${start} - ${end}`);
// return this.labels.filter(label => label.location >= start && label.location <= end);
return []; // 返回空数组以避免依赖 this.labels
},
},
};
// 全局变量,用于存储 waterfallwidget 实例
window.waterWidgetInstance = null;
// 监听来自父窗口的消息
window.addEventListener('message', (event) => {
// 强烈建议在此处添加来源验证 (event.origin) 以提高安全性
// 例如: if (event.origin !== 'YOUR_VUE_APP_ORIGIN') return;
if (!event.data) return; // 如果没有数据,则忽略
// 处理初始化消息
if (event.data.type === 'INIT_WIDGET') {
console.log('[🌐] HTML (频谱图): 收到初始化指令');
if (!window.waterWidgetInstance) {
// 合并 Vue 传递的配置(如果存在)
const mergedPayload = { ...payload, ...(event.data.config?.payload || {}) };
const mergedWfOption = { ...wfOption, ...(event.data.config?.wfOption || {}) };
// 创建 waterfallwidget 实例
window.waterWidgetInstance = new waterfallwidget(id, {
wfOption: mergedWfOption,
min_freq: mergedPayload.minFreq,
max_freq: mergedPayload.maxFreq,
min_band: 500,
max_band: 100000,
min_db: mergedWfOption.wfMindB,
max_db: mergedWfOption.wfMaxdB,
showPeakMarker: mergedWfOption.showPeakMarker,
showSpectrogramAvg: mergedWfOption.wfShowAvg,
showSpectrogramMin: mergedWfOption.wfShowMin,
showSpectrogramMax: mergedWfOption.wfShowMax,
spec_per: mergedPayload.waveSplite,
menuEnableHandle: null, // 在 iframe 中通常不需要此句柄
menuDisableText: '---',
menuDoppler: mergedPayload.doppler,
allowRunMode: mergedPayload.rtmode,
info_Position: mergedWfOption.wfInfoPos,
showCurLine: mergedWfOption.wfCurLine,
adj_maxbw: mergedPayload.adjMaxbw,
adj_show: mergedPayload.adjShow,
fucFFTBuff: mergedPayload.enableWfBuff ? this.fucFFTBuff.bind(this) : null, // 如果需要,保留 FFT 缓冲功能
unitBuffId: mergedPayload.unitId,
wave_color: mergedWfOption.specColor,
wave_color_avg: mergedWfOption.specAvgColor,
wave_color_max: mergedWfOption.specMaxColor,
wave_color_min: mergedWfOption.specMinColor,
waveAfterglow_color: mergedWfOption.afterGlowColor,
adj_step: mergedWfOption.wfAdjStepType == 1 ? 0 : mergedWfOption.wfAdjStep,
label_manager: pl.label_manager, // 保留 label_manager
coorFreqType: mergedPayload.coorFreqType,
showFreqLable: mergedPayload.showFreqLable,
wfFreqPointLable: mergedPayload.wfFreqPointLable,
wfFreqPointLableList: mergedPayload.wfFreqPointLableList,
showWhitelist: mergedPayload.showWhitelist,
});
console.log('[🌐] HTML (频谱图): waterfallwidget 已实例化');
// 监听用户框选范围变更事件 (移到初始化之后)
window.waterWidgetInstance.on('zoom-change', (e) => {
console.log('频率范围变更:', e.start, e.end);
// 可选:将此事件通知回 Vue 组件
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'ZOOM_CHANGE', payload: { start: e.start, end: e.end } }, '*');
// }
});
} else {
console.log('[🌐] HTML (频谱图): waterfallwidget 已存在,无需重复实例化');
}
// 可选:通知父窗口已准备好接收数据
// if (window.parent !== window) {
// window.parent.postMessage({ type: 'WIDGET_READY' }, '*'); // 使用更具体的 targetOrigin
// }
}
// 处理数据馈送消息
else if (event.data.type === 'FEED_DATA') {
if (window.waterWidgetInstance) {
console.log('[🌐] HTML (频谱图): 收到来自父窗口的数据:', event.data.payload);
if (Array.isArray(event.data.payload)) {
// waterfallwidget.addData 接受可选的第二个参数 time
// 如果 Vue 组件传递了时间戳,可以在这里使用
const time = event.data.time || new Date().toLocaleTimeString(); // 如果没有提供时间,使用当前时间
window.waterWidgetInstance.addData(event.data.payload, time);
} else {
console.error('[🌐] HTML (频谱图): 收到的数据格式无效:', event.data.payload);
}
} else {
console.warn('[🌐] HTML (频谱图): Widget 尚未初始化,无法处理 FEED_DATA');
}
}
});
</script>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body > div {
width: 100%;
height: 100%;
}
</style>
</html>

View File

@@ -0,0 +1,153 @@
// @ts-nocheck
colormapwidget.names = ['default', 'blue', 'gray', 'cooledit'];
colormapwidget.prototype.getGradientColors = function (name) {
if (name == 'blue') {
return ['#050525', '#000CFF', '#33FFFF', '#F9FFFF'];
} else if (name == 'gray') {
return ['#050525', '#FBFBFB'];
} else if (name == 'cooledit') {
return ['#010E19', '#720271', '#D7032C', '#FDBC5F', '#F8FFED'];
} else {
return ['#050525', '#0000CF', '#00FF20', '#EFFF00', '#FF007C'];
}
};
// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
colormapwidget.prototype.colorRgb = function (sColor) {
var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
var sColor = sColor.toLowerCase();
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
var sColorNew = '#';
for (var i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = sColorNew;
}
//处理六位的颜色值
var sColorChange = [];
for (var i = 1; i < 7; i += 2) {
sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)));
}
return sColorChange;
} else {
return sColor;
}
};
// 将rgb表示方式转换为hex表示方式
colormapwidget.prototype.colorHex = function (rgb) {
var reg = /(\d{1,3}),(\d{1,3}),(\d{1,3})/;
var arr = reg.exec(rgb);
function hex(x) {
return ('0' + parseInt(x).toString(16)).slice(-2);
}
var _hex = '#' + hex(arr[1]) + hex(arr[2]) + hex(arr[3]);
return _hex.toUpperCase();
};
//计算两个颜色的step个阶梯色
colormapwidget.prototype.gradientColor = function (startColor, endColor, step) {
var startRGB = this.colorRgb(startColor); //转换为rgb数组模式
var startR = startRGB[0];
var startG = startRGB[1];
var startB = startRGB[2];
var endRGB = this.colorRgb(endColor);
var endR = endRGB[0];
var endG = endRGB[1];
var endB = endRGB[2];
var sR = (endR - startR) / step; //总差值
var sG = (endG - startG) / step;
var sB = (endB - startB) / step;
var colorArr = [];
for (var i = 0; i < step; i++) {
//计算每一步的hex值
//var hex = this.colorHex('rgb('+parseInt((sR*i+startR))+','+parseInt((sG*i+startG))+','+parseInt((sB*i+startB))+')');
//colorArr.push(hex);
colorArr.push([parseInt(sR * i + startR), parseInt(sG * i + startG), parseInt(sB * i + startB)]);
}
return colorArr;
};
colormapwidget.prototype.setDbRange = function (count) {
this.count = count;
this.mapCount = count;
this.calColors();
};
colormapwidget.prototype.getColors = function () {
if (
this.name === this.buff_name &&
this.gravity === this.buff_gravity &&
this.gravity_value === this.buff_gravity_value &&
this.count === this.buff_count
)
return this.buff_colors;
return this.calColors();
};
colormapwidget.prototype.getMapColors = function () {
return this.buff_mapCountcolors;
};
colormapwidget.prototype.calColors = function () {
this.buff_name = this.name;
this.buff_gravity = this.gravity;
this.buff_gravity_value = this.gravity_value;
this.buff_count = this.count;
this.buff_colors = this._calColorsByCount(this.count);
this.buff_mapCountcolors = this._calColorsByCount(this.mapCount);
return this.buff_colors;
};
colormapwidget.prototype._calColorsByCount = function (count) {
//console.log("_calColorsByCount " + count)
var colors = this.getGradientColors(this.name);
var arr = [];
var stepSpan = colors.length - 1;
if (!this.gravity) {
var step = Math.ceil(count / stepSpan);
for (var i = 0; i < stepSpan; i++) {
var carr = this.gradientColor(colors[i], colors[i + 1], step);
arr = arr.concat(carr);
}
} else {
var pStart = this.gravity_value - 0.2;
pStart = pStart < 0 ? 0 : pStart;
var pEnd = this.gravity_value + 0.2;
pEnd = pEnd > 1 ? 1 : pEnd;
pStart = count * pStart;
pEnd = count * pEnd;
for (var i = 0; i < pStart; i++) {
arr.push(this.colorRgb(colors[0]));
}
var step = Math.ceil((pEnd - pStart + 1) / stepSpan);
for (var i = 0; i < stepSpan; i++) {
var carr = this.gradientColor(colors[i], colors[i + 1], step);
arr = arr.concat(carr);
}
for (var i = pEnd; i < count; i++) {
arr.push(this.colorRgb(colors[colors.length - 1]));
}
}
return arr;
};
function colormapwidget(option) {
this.name = option && option.name ? option.name : 'default';
this.gravity = option && option.gravity ? option.gravity : false;
this.gravity_value = option && option.gravity_value ? option.gravity_value : 0.5;
this.count = option && option.count ? option.count : 200;
this.mapCount = option && option.mapCount ? option.mapCount : 200;
this.getColors();
}

File diff suppressed because one or more lines are too long

7380
public/html-page/lib/konva.2.4.2.min.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
(function(){function n(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function t(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u<i;){var o=n[u];t(e,o,r(o),n)}return e}function r(n,t){for(var r=-1,e=null==n?0:n.length;++r<e&&t(n[r],r,n)!==!1;);return n}function e(n,t){for(var r=null==n?0:n.length;r--&&t(n[r],r,n)!==!1;);return n}function u(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(!t(n[r],r,n))return!1;
return!0}function i(n,t){for(var r=-1,e=null==n?0:n.length,u=0,i=[];++r<e;){var o=n[r];t(o,r,n)&&(i[u++]=o)}return i}function o(n,t){return!!(null==n?0:n.length)&&y(n,t,0)>-1}function f(n,t,r){for(var e=-1,u=null==n?0:n.length;++e<u;)if(r(t,n[e]))return!0;return!1}function c(n,t){for(var r=-1,e=null==n?0:n.length,u=Array(e);++r<e;)u[r]=t(n[r],r,n);return u}function a(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];return n}function l(n,t,r,e){var u=-1,i=null==n?0:n.length;for(e&&i&&(r=n[++u]);++u<i;)r=t(r,n[u],u,n);
return r}function s(n,t,r,e){var u=null==n?0:n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r}function h(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(t(n[r],r,n))return!0;return!1}function p(n){return n.split("")}function _(n){return n.match($t)||[]}function v(n,t,r){var e;return r(n,function(n,r,u){if(t(n,r,u))return e=r,!1}),e}function g(n,t,r,e){for(var u=n.length,i=r+(e?1:-1);e?i--:++i<u;)if(t(n[i],i,n))return i;return-1}function y(n,t,r){return t===t?Z(n,t,r):g(n,b,r)}function d(n,t,r,e){
for(var u=r-1,i=n.length;++u<i;)if(e(n[u],t))return u;return-1}function b(n){return n!==n}function w(n,t){var r=null==n?0:n.length;return r?k(n,t)/r:Cn}function m(n){return function(t){return null==t?X:t[n]}}function x(n){return function(t){return null==n?X:n[t]}}function j(n,t,r,e,u){return u(n,function(n,u,i){r=e?(e=!1,n):t(r,n,u,i)}),r}function A(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].value;return n}function k(n,t){for(var r,e=-1,u=n.length;++e<u;){var i=t(n[e]);i!==X&&(r=r===X?i:r+i);
}return r}function O(n,t){for(var r=-1,e=Array(n);++r<n;)e[r]=t(r);return e}function I(n,t){return c(t,function(t){return[t,n[t]]})}function R(n){return n?n.slice(0,H(n)+1).replace(Lt,""):n}function z(n){return function(t){return n(t)}}function E(n,t){return c(t,function(t){return n[t]})}function S(n,t){return n.has(t)}function W(n,t){for(var r=-1,e=n.length;++r<e&&y(t,n[r],0)>-1;);return r}function L(n,t){for(var r=n.length;r--&&y(t,n[r],0)>-1;);return r}function C(n,t){for(var r=n.length,e=0;r--;)n[r]===t&&++e;
return e}function U(n){return"\\"+Yr[n]}function B(n,t){return null==n?X:n[t]}function T(n){return Nr.test(n)}function $(n){return Pr.test(n)}function D(n){for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}function M(n){var t=-1,r=Array(n.size);return n.forEach(function(n,e){r[++t]=[e,n]}),r}function F(n,t){return function(r){return n(t(r))}}function N(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){var o=n[r];o!==t&&o!==cn||(n[r]=cn,i[u++]=r)}return i}function P(n){var t=-1,r=Array(n.size);
return n.forEach(function(n){r[++t]=n}),r}function q(n){var t=-1,r=Array(n.size);return n.forEach(function(n){r[++t]=[n,n]}),r}function Z(n,t,r){for(var e=r-1,u=n.length;++e<u;)if(n[e]===t)return e;return-1}function K(n,t,r){for(var e=r+1;e--;)if(n[e]===t)return e;return e}function V(n){return T(n)?J(n):_e(n)}function G(n){return T(n)?Y(n):p(n)}function H(n){for(var t=n.length;t--&&Ct.test(n.charAt(t)););return t}function J(n){for(var t=Mr.lastIndex=0;Mr.test(n);)++t;return t}function Y(n){return n.match(Mr)||[];
}function Q(n){return n.match(Fr)||[]}var X,nn="4.17.21",tn=200,rn="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",en="Expected a function",un="Invalid `variable` option passed into `_.template`",on="__lodash_hash_undefined__",fn=500,cn="__lodash_placeholder__",an=1,ln=2,sn=4,hn=1,pn=2,_n=1,vn=2,gn=4,yn=8,dn=16,bn=32,wn=64,mn=128,xn=256,jn=512,An=30,kn="...",On=800,In=16,Rn=1,zn=2,En=3,Sn=1/0,Wn=9007199254740991,Ln=1.7976931348623157e308,Cn=NaN,Un=4294967295,Bn=Un-1,Tn=Un>>>1,$n=[["ary",mn],["bind",_n],["bindKey",vn],["curry",yn],["curryRight",dn],["flip",jn],["partial",bn],["partialRight",wn],["rearg",xn]],Dn="[object Arguments]",Mn="[object Array]",Fn="[object AsyncFunction]",Nn="[object Boolean]",Pn="[object Date]",qn="[object DOMException]",Zn="[object Error]",Kn="[object Function]",Vn="[object GeneratorFunction]",Gn="[object Map]",Hn="[object Number]",Jn="[object Null]",Yn="[object Object]",Qn="[object Promise]",Xn="[object Proxy]",nt="[object RegExp]",tt="[object Set]",rt="[object String]",et="[object Symbol]",ut="[object Undefined]",it="[object WeakMap]",ot="[object WeakSet]",ft="[object ArrayBuffer]",ct="[object DataView]",at="[object Float32Array]",lt="[object Float64Array]",st="[object Int8Array]",ht="[object Int16Array]",pt="[object Int32Array]",_t="[object Uint8Array]",vt="[object Uint8ClampedArray]",gt="[object Uint16Array]",yt="[object Uint32Array]",dt=/\b__p \+= '';/g,bt=/\b(__p \+=) '' \+/g,wt=/(__e\(.*?\)|\b__t\)) \+\n'';/g,mt=/&(?:amp|lt|gt|quot|#39);/g,xt=/[&<>"']/g,jt=RegExp(mt.source),At=RegExp(xt.source),kt=/<%-([\s\S]+?)%>/g,Ot=/<%([\s\S]+?)%>/g,It=/<%=([\s\S]+?)%>/g,Rt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,zt=/^\w*$/,Et=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,St=/[\\^$.*+?()[\]{}|]/g,Wt=RegExp(St.source),Lt=/^\s+/,Ct=/\s/,Ut=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Bt=/\{\n\/\* \[wrapped with (.+)\] \*/,Tt=/,? & /,$t=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,Dt=/[()=,{}\[\]\/\s]/,Mt=/\\(\\)?/g,Ft=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,Nt=/\w*$/,Pt=/^[-+]0x[0-9a-f]+$/i,qt=/^0b[01]+$/i,Zt=/^\[object .+?Constructor\]$/,Kt=/^0o[0-7]+$/i,Vt=/^(?:0|[1-9]\d*)$/,Gt=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,Ht=/($^)/,Jt=/['\n\r\u2028\u2029\\]/g,Yt="\\ud800-\\udfff",Qt="\\u0300-\\u036f",Xt="\\ufe20-\\ufe2f",nr="\\u20d0-\\u20ff",tr=Qt+Xt+nr,rr="\\u2700-\\u27bf",er="a-z\\xdf-\\xf6\\xf8-\\xff",ur="\\xac\\xb1\\xd7\\xf7",ir="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",or="\\u2000-\\u206f",fr=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",cr="A-Z\\xc0-\\xd6\\xd8-\\xde",ar="\\ufe0e\\ufe0f",lr=ur+ir+or+fr,sr="['\u2019]",hr="["+Yt+"]",pr="["+lr+"]",_r="["+tr+"]",vr="\\d+",gr="["+rr+"]",yr="["+er+"]",dr="[^"+Yt+lr+vr+rr+er+cr+"]",br="\\ud83c[\\udffb-\\udfff]",wr="(?:"+_r+"|"+br+")",mr="[^"+Yt+"]",xr="(?:\\ud83c[\\udde6-\\uddff]){2}",jr="[\\ud800-\\udbff][\\udc00-\\udfff]",Ar="["+cr+"]",kr="\\u200d",Or="(?:"+yr+"|"+dr+")",Ir="(?:"+Ar+"|"+dr+")",Rr="(?:"+sr+"(?:d|ll|m|re|s|t|ve))?",zr="(?:"+sr+"(?:D|LL|M|RE|S|T|VE))?",Er=wr+"?",Sr="["+ar+"]?",Wr="(?:"+kr+"(?:"+[mr,xr,jr].join("|")+")"+Sr+Er+")*",Lr="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Cr="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",Ur=Sr+Er+Wr,Br="(?:"+[gr,xr,jr].join("|")+")"+Ur,Tr="(?:"+[mr+_r+"?",_r,xr,jr,hr].join("|")+")",$r=RegExp(sr,"g"),Dr=RegExp(_r,"g"),Mr=RegExp(br+"(?="+br+")|"+Tr+Ur,"g"),Fr=RegExp([Ar+"?"+yr+"+"+Rr+"(?="+[pr,Ar,"$"].join("|")+")",Ir+"+"+zr+"(?="+[pr,Ar+Or,"$"].join("|")+")",Ar+"?"+Or+"+"+Rr,Ar+"+"+zr,Cr,Lr,vr,Br].join("|"),"g"),Nr=RegExp("["+kr+Yt+tr+ar+"]"),Pr=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,qr=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],Zr=-1,Kr={};
Kr[at]=Kr[lt]=Kr[st]=Kr[ht]=Kr[pt]=Kr[_t]=Kr[vt]=Kr[gt]=Kr[yt]=!0,Kr[Dn]=Kr[Mn]=Kr[ft]=Kr[Nn]=Kr[ct]=Kr[Pn]=Kr[Zn]=Kr[Kn]=Kr[Gn]=Kr[Hn]=Kr[Yn]=Kr[nt]=Kr[tt]=Kr[rt]=Kr[it]=!1;var Vr={};Vr[Dn]=Vr[Mn]=Vr[ft]=Vr[ct]=Vr[Nn]=Vr[Pn]=Vr[at]=Vr[lt]=Vr[st]=Vr[ht]=Vr[pt]=Vr[Gn]=Vr[Hn]=Vr[Yn]=Vr[nt]=Vr[tt]=Vr[rt]=Vr[et]=Vr[_t]=Vr[vt]=Vr[gt]=Vr[yt]=!0,Vr[Zn]=Vr[Kn]=Vr[it]=!1;var Gr={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a",
"\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae",
"\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g",
"\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O",
"\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w",
"\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},Hr={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},Jr={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'"},Yr={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Qr=parseFloat,Xr=parseInt,ne="object"==typeof global&&global&&global.Object===Object&&global,te="object"==typeof self&&self&&self.Object===Object&&self,re=ne||te||Function("return this")(),ee="object"==typeof exports&&exports&&!exports.nodeType&&exports,ue=ee&&"object"==typeof module&&module&&!module.nodeType&&module,ie=ue&&ue.exports===ee,oe=ie&&ne.process,fe=function(){
try{var n=ue&&ue.require&&ue.require("util").types;return n?n:oe&&oe.binding&&oe.binding("util")}catch(n){}}(),ce=fe&&fe.isArrayBuffer,ae=fe&&fe.isDate,le=fe&&fe.isMap,se=fe&&fe.isRegExp,he=fe&&fe.isSet,pe=fe&&fe.isTypedArray,_e=m("length"),ve=x(Gr),ge=x(Hr),ye=x(Jr),de=function p(x){function Z(n){if(cc(n)&&!bh(n)&&!(n instanceof Ct)){if(n instanceof Y)return n;if(bl.call(n,"__wrapped__"))return eo(n)}return new Y(n)}function J(){}function Y(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t,
this.__index__=0,this.__values__=X}function Ct(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=Un,this.__views__=[]}function $t(){var n=new Ct(this.__wrapped__);return n.__actions__=Tu(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=Tu(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=Tu(this.__views__),n}function Yt(){if(this.__filtered__){var n=new Ct(this);n.__dir__=-1,
n.__filtered__=!0}else n=this.clone(),n.__dir__*=-1;return n}function Qt(){var n=this.__wrapped__.value(),t=this.__dir__,r=bh(n),e=t<0,u=r?n.length:0,i=Oi(0,u,this.__views__),o=i.start,f=i.end,c=f-o,a=e?f:o-1,l=this.__iteratees__,s=l.length,h=0,p=Hl(c,this.__takeCount__);if(!r||!e&&u==c&&p==c)return wu(n,this.__actions__);var _=[];n:for(;c--&&h<p;){a+=t;for(var v=-1,g=n[a];++v<s;){var y=l[v],d=y.iteratee,b=y.type,w=d(g);if(b==zn)g=w;else if(!w){if(b==Rn)continue n;break n}}_[h++]=g}return _}function Xt(n){
var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function nr(){this.__data__=is?is(null):{},this.size=0}function tr(n){var t=this.has(n)&&delete this.__data__[n];return this.size-=t?1:0,t}function rr(n){var t=this.__data__;if(is){var r=t[n];return r===on?X:r}return bl.call(t,n)?t[n]:X}function er(n){var t=this.__data__;return is?t[n]!==X:bl.call(t,n)}function ur(n,t){var r=this.__data__;return this.size+=this.has(n)?0:1,r[n]=is&&t===X?on:t,this}function ir(n){
var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function or(){this.__data__=[],this.size=0}function fr(n){var t=this.__data__,r=Wr(t,n);return!(r<0)&&(r==t.length-1?t.pop():Ll.call(t,r,1),--this.size,!0)}function cr(n){var t=this.__data__,r=Wr(t,n);return r<0?X:t[r][1]}function ar(n){return Wr(this.__data__,n)>-1}function lr(n,t){var r=this.__data__,e=Wr(r,n);return e<0?(++this.size,r.push([n,t])):r[e][1]=t,this}function sr(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){
var e=n[t];this.set(e[0],e[1])}}function hr(){this.size=0,this.__data__={hash:new Xt,map:new(ts||ir),string:new Xt}}function pr(n){var t=xi(this,n).delete(n);return this.size-=t?1:0,t}function _r(n){return xi(this,n).get(n)}function vr(n){return xi(this,n).has(n)}function gr(n,t){var r=xi(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this}function yr(n){var t=-1,r=null==n?0:n.length;for(this.__data__=new sr;++t<r;)this.add(n[t])}function dr(n){return this.__data__.set(n,on),this}function br(n){
return this.__data__.has(n)}function wr(n){this.size=(this.__data__=new ir(n)).size}function mr(){this.__data__=new ir,this.size=0}function xr(n){var t=this.__data__,r=t.delete(n);return this.size=t.size,r}function jr(n){return this.__data__.get(n)}function Ar(n){return this.__data__.has(n)}function kr(n,t){var r=this.__data__;if(r instanceof ir){var e=r.__data__;if(!ts||e.length<tn-1)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new sr(e)}return r.set(n,t),this.size=r.size,this}function Or(n,t){
var r=bh(n),e=!r&&dh(n),u=!r&&!e&&mh(n),i=!r&&!e&&!u&&Oh(n),o=r||e||u||i,f=o?O(n.length,hl):[],c=f.length;for(var a in n)!t&&!bl.call(n,a)||o&&("length"==a||u&&("offset"==a||"parent"==a)||i&&("buffer"==a||"byteLength"==a||"byteOffset"==a)||Ci(a,c))||f.push(a);return f}function Ir(n){var t=n.length;return t?n[tu(0,t-1)]:X}function Rr(n,t){return Xi(Tu(n),Mr(t,0,n.length))}function zr(n){return Xi(Tu(n))}function Er(n,t,r){(r===X||Gf(n[t],r))&&(r!==X||t in n)||Br(n,t,r)}function Sr(n,t,r){var e=n[t];
bl.call(n,t)&&Gf(e,r)&&(r!==X||t in n)||Br(n,t,r)}function Wr(n,t){for(var r=n.length;r--;)if(Gf(n[r][0],t))return r;return-1}function Lr(n,t,r,e){return ys(n,function(n,u,i){t(e,n,r(n),i)}),e}function Cr(n,t){return n&&$u(t,Pc(t),n)}function Ur(n,t){return n&&$u(t,qc(t),n)}function Br(n,t,r){"__proto__"==t&&Tl?Tl(n,t,{configurable:!0,enumerable:!0,value:r,writable:!0}):n[t]=r}function Tr(n,t){for(var r=-1,e=t.length,u=il(e),i=null==n;++r<e;)u[r]=i?X:Mc(n,t[r]);return u}function Mr(n,t,r){return n===n&&(r!==X&&(n=n<=r?n:r),
t!==X&&(n=n>=t?n:t)),n}function Fr(n,t,e,u,i,o){var f,c=t&an,a=t&ln,l=t&sn;if(e&&(f=i?e(n,u,i,o):e(n)),f!==X)return f;if(!fc(n))return n;var s=bh(n);if(s){if(f=zi(n),!c)return Tu(n,f)}else{var h=zs(n),p=h==Kn||h==Vn;if(mh(n))return Iu(n,c);if(h==Yn||h==Dn||p&&!i){if(f=a||p?{}:Ei(n),!c)return a?Mu(n,Ur(f,n)):Du(n,Cr(f,n))}else{if(!Vr[h])return i?n:{};f=Si(n,h,c)}}o||(o=new wr);var _=o.get(n);if(_)return _;o.set(n,f),kh(n)?n.forEach(function(r){f.add(Fr(r,t,e,r,n,o))}):jh(n)&&n.forEach(function(r,u){
f.set(u,Fr(r,t,e,u,n,o))});var v=l?a?di:yi:a?qc:Pc,g=s?X:v(n);return r(g||n,function(r,u){g&&(u=r,r=n[u]),Sr(f,u,Fr(r,t,e,u,n,o))}),f}function Nr(n){var t=Pc(n);return function(r){return Pr(r,n,t)}}function Pr(n,t,r){var e=r.length;if(null==n)return!e;for(n=ll(n);e--;){var u=r[e],i=t[u],o=n[u];if(o===X&&!(u in n)||!i(o))return!1}return!0}function Gr(n,t,r){if("function"!=typeof n)throw new pl(en);return Ws(function(){n.apply(X,r)},t)}function Hr(n,t,r,e){var u=-1,i=o,a=!0,l=n.length,s=[],h=t.length;
if(!l)return s;r&&(t=c(t,z(r))),e?(i=f,a=!1):t.length>=tn&&(i=S,a=!1,t=new yr(t));n:for(;++u<l;){var p=n[u],_=null==r?p:r(p);if(p=e||0!==p?p:0,a&&_===_){for(var v=h;v--;)if(t[v]===_)continue n;s.push(p)}else i(t,_,e)||s.push(p)}return s}function Jr(n,t){var r=!0;return ys(n,function(n,e,u){return r=!!t(n,e,u)}),r}function Yr(n,t,r){for(var e=-1,u=n.length;++e<u;){var i=n[e],o=t(i);if(null!=o&&(f===X?o===o&&!bc(o):r(o,f)))var f=o,c=i}return c}function ne(n,t,r,e){var u=n.length;for(r=kc(r),r<0&&(r=-r>u?0:u+r),
e=e===X||e>u?u:kc(e),e<0&&(e+=u),e=r>e?0:Oc(e);r<e;)n[r++]=t;return n}function te(n,t){var r=[];return ys(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function ee(n,t,r,e,u){var i=-1,o=n.length;for(r||(r=Li),u||(u=[]);++i<o;){var f=n[i];t>0&&r(f)?t>1?ee(f,t-1,r,e,u):a(u,f):e||(u[u.length]=f)}return u}function ue(n,t){return n&&bs(n,t,Pc)}function oe(n,t){return n&&ws(n,t,Pc)}function fe(n,t){return i(t,function(t){return uc(n[t])})}function _e(n,t){t=ku(t,n);for(var r=0,e=t.length;null!=n&&r<e;)n=n[no(t[r++])];
return r&&r==e?n:X}function de(n,t,r){var e=t(n);return bh(n)?e:a(e,r(n))}function we(n){return null==n?n===X?ut:Jn:Bl&&Bl in ll(n)?ki(n):Ki(n)}function me(n,t){return n>t}function xe(n,t){return null!=n&&bl.call(n,t)}function je(n,t){return null!=n&&t in ll(n)}function Ae(n,t,r){return n>=Hl(t,r)&&n<Gl(t,r)}function ke(n,t,r){for(var e=r?f:o,u=n[0].length,i=n.length,a=i,l=il(i),s=1/0,h=[];a--;){var p=n[a];a&&t&&(p=c(p,z(t))),s=Hl(p.length,s),l[a]=!r&&(t||u>=120&&p.length>=120)?new yr(a&&p):X}p=n[0];
var _=-1,v=l[0];n:for(;++_<u&&h.length<s;){var g=p[_],y=t?t(g):g;if(g=r||0!==g?g:0,!(v?S(v,y):e(h,y,r))){for(a=i;--a;){var d=l[a];if(!(d?S(d,y):e(n[a],y,r)))continue n}v&&v.push(y),h.push(g)}}return h}function Oe(n,t,r,e){return ue(n,function(n,u,i){t(e,r(n),u,i)}),e}function Ie(t,r,e){r=ku(r,t),t=Gi(t,r);var u=null==t?t:t[no(jo(r))];return null==u?X:n(u,t,e)}function Re(n){return cc(n)&&we(n)==Dn}function ze(n){return cc(n)&&we(n)==ft}function Ee(n){return cc(n)&&we(n)==Pn}function Se(n,t,r,e,u){
return n===t||(null==n||null==t||!cc(n)&&!cc(t)?n!==n&&t!==t:We(n,t,r,e,Se,u))}function We(n,t,r,e,u,i){var o=bh(n),f=bh(t),c=o?Mn:zs(n),a=f?Mn:zs(t);c=c==Dn?Yn:c,a=a==Dn?Yn:a;var l=c==Yn,s=a==Yn,h=c==a;if(h&&mh(n)){if(!mh(t))return!1;o=!0,l=!1}if(h&&!l)return i||(i=new wr),o||Oh(n)?pi(n,t,r,e,u,i):_i(n,t,c,r,e,u,i);if(!(r&hn)){var p=l&&bl.call(n,"__wrapped__"),_=s&&bl.call(t,"__wrapped__");if(p||_){var v=p?n.value():n,g=_?t.value():t;return i||(i=new wr),u(v,g,r,e,i)}}return!!h&&(i||(i=new wr),vi(n,t,r,e,u,i));
}function Le(n){return cc(n)&&zs(n)==Gn}function Ce(n,t,r,e){var u=r.length,i=u,o=!e;if(null==n)return!i;for(n=ll(n);u--;){var f=r[u];if(o&&f[2]?f[1]!==n[f[0]]:!(f[0]in n))return!1}for(;++u<i;){f=r[u];var c=f[0],a=n[c],l=f[1];if(o&&f[2]){if(a===X&&!(c in n))return!1}else{var s=new wr;if(e)var h=e(a,l,c,n,t,s);if(!(h===X?Se(l,a,hn|pn,e,s):h))return!1}}return!0}function Ue(n){return!(!fc(n)||Di(n))&&(uc(n)?kl:Zt).test(to(n))}function Be(n){return cc(n)&&we(n)==nt}function Te(n){return cc(n)&&zs(n)==tt;
}function $e(n){return cc(n)&&oc(n.length)&&!!Kr[we(n)]}function De(n){return"function"==typeof n?n:null==n?La:"object"==typeof n?bh(n)?Ze(n[0],n[1]):qe(n):Fa(n)}function Me(n){if(!Mi(n))return Vl(n);var t=[];for(var r in ll(n))bl.call(n,r)&&"constructor"!=r&&t.push(r);return t}function Fe(n){if(!fc(n))return Zi(n);var t=Mi(n),r=[];for(var e in n)("constructor"!=e||!t&&bl.call(n,e))&&r.push(e);return r}function Ne(n,t){return n<t}function Pe(n,t){var r=-1,e=Hf(n)?il(n.length):[];return ys(n,function(n,u,i){
e[++r]=t(n,u,i)}),e}function qe(n){var t=ji(n);return 1==t.length&&t[0][2]?Ni(t[0][0],t[0][1]):function(r){return r===n||Ce(r,n,t)}}function Ze(n,t){return Bi(n)&&Fi(t)?Ni(no(n),t):function(r){var e=Mc(r,n);return e===X&&e===t?Nc(r,n):Se(t,e,hn|pn)}}function Ke(n,t,r,e,u){n!==t&&bs(t,function(i,o){if(u||(u=new wr),fc(i))Ve(n,t,o,r,Ke,e,u);else{var f=e?e(Ji(n,o),i,o+"",n,t,u):X;f===X&&(f=i),Er(n,o,f)}},qc)}function Ve(n,t,r,e,u,i,o){var f=Ji(n,r),c=Ji(t,r),a=o.get(c);if(a)return Er(n,r,a),X;var l=i?i(f,c,r+"",n,t,o):X,s=l===X;
if(s){var h=bh(c),p=!h&&mh(c),_=!h&&!p&&Oh(c);l=c,h||p||_?bh(f)?l=f:Jf(f)?l=Tu(f):p?(s=!1,l=Iu(c,!0)):_?(s=!1,l=Wu(c,!0)):l=[]:gc(c)||dh(c)?(l=f,dh(f)?l=Rc(f):fc(f)&&!uc(f)||(l=Ei(c))):s=!1}s&&(o.set(c,l),u(l,c,e,i,o),o.delete(c)),Er(n,r,l)}function Ge(n,t){var r=n.length;if(r)return t+=t<0?r:0,Ci(t,r)?n[t]:X}function He(n,t,r){t=t.length?c(t,function(n){return bh(n)?function(t){return _e(t,1===n.length?n[0]:n)}:n}):[La];var e=-1;return t=c(t,z(mi())),A(Pe(n,function(n,r,u){return{criteria:c(t,function(t){
return t(n)}),index:++e,value:n}}),function(n,t){return Cu(n,t,r)})}function Je(n,t){return Ye(n,t,function(t,r){return Nc(n,r)})}function Ye(n,t,r){for(var e=-1,u=t.length,i={};++e<u;){var o=t[e],f=_e(n,o);r(f,o)&&fu(i,ku(o,n),f)}return i}function Qe(n){return function(t){return _e(t,n)}}function Xe(n,t,r,e){var u=e?d:y,i=-1,o=t.length,f=n;for(n===t&&(t=Tu(t)),r&&(f=c(n,z(r)));++i<o;)for(var a=0,l=t[i],s=r?r(l):l;(a=u(f,s,a,e))>-1;)f!==n&&Ll.call(f,a,1),Ll.call(n,a,1);return n}function nu(n,t){for(var r=n?t.length:0,e=r-1;r--;){
var u=t[r];if(r==e||u!==i){var i=u;Ci(u)?Ll.call(n,u,1):yu(n,u)}}return n}function tu(n,t){return n+Nl(Ql()*(t-n+1))}function ru(n,t,r,e){for(var u=-1,i=Gl(Fl((t-n)/(r||1)),0),o=il(i);i--;)o[e?i:++u]=n,n+=r;return o}function eu(n,t){var r="";if(!n||t<1||t>Wn)return r;do t%2&&(r+=n),t=Nl(t/2),t&&(n+=n);while(t);return r}function uu(n,t){return Ls(Vi(n,t,La),n+"")}function iu(n){return Ir(ra(n))}function ou(n,t){var r=ra(n);return Xi(r,Mr(t,0,r.length))}function fu(n,t,r,e){if(!fc(n))return n;t=ku(t,n);
for(var u=-1,i=t.length,o=i-1,f=n;null!=f&&++u<i;){var c=no(t[u]),a=r;if("__proto__"===c||"constructor"===c||"prototype"===c)return n;if(u!=o){var l=f[c];a=e?e(l,c,f):X,a===X&&(a=fc(l)?l:Ci(t[u+1])?[]:{})}Sr(f,c,a),f=f[c]}return n}function cu(n){return Xi(ra(n))}function au(n,t,r){var e=-1,u=n.length;t<0&&(t=-t>u?0:u+t),r=r>u?u:r,r<0&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0;for(var i=il(u);++e<u;)i[e]=n[e+t];return i}function lu(n,t){var r;return ys(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function su(n,t,r){
var e=0,u=null==n?e:n.length;if("number"==typeof t&&t===t&&u<=Tn){for(;e<u;){var i=e+u>>>1,o=n[i];null!==o&&!bc(o)&&(r?o<=t:o<t)?e=i+1:u=i}return u}return hu(n,t,La,r)}function hu(n,t,r,e){var u=0,i=null==n?0:n.length;if(0===i)return 0;t=r(t);for(var o=t!==t,f=null===t,c=bc(t),a=t===X;u<i;){var l=Nl((u+i)/2),s=r(n[l]),h=s!==X,p=null===s,_=s===s,v=bc(s);if(o)var g=e||_;else g=a?_&&(e||h):f?_&&h&&(e||!p):c?_&&h&&!p&&(e||!v):!p&&!v&&(e?s<=t:s<t);g?u=l+1:i=l}return Hl(i,Bn)}function pu(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){
var o=n[r],f=t?t(o):o;if(!r||!Gf(f,c)){var c=f;i[u++]=0===o?0:o}}return i}function _u(n){return"number"==typeof n?n:bc(n)?Cn:+n}function vu(n){if("string"==typeof n)return n;if(bh(n))return c(n,vu)+"";if(bc(n))return vs?vs.call(n):"";var t=n+"";return"0"==t&&1/n==-Sn?"-0":t}function gu(n,t,r){var e=-1,u=o,i=n.length,c=!0,a=[],l=a;if(r)c=!1,u=f;else if(i>=tn){var s=t?null:ks(n);if(s)return P(s);c=!1,u=S,l=new yr}else l=t?[]:a;n:for(;++e<i;){var h=n[e],p=t?t(h):h;if(h=r||0!==h?h:0,c&&p===p){for(var _=l.length;_--;)if(l[_]===p)continue n;
t&&l.push(p),a.push(h)}else u(l,p,r)||(l!==a&&l.push(p),a.push(h))}return a}function yu(n,t){return t=ku(t,n),n=Gi(n,t),null==n||delete n[no(jo(t))]}function du(n,t,r,e){return fu(n,t,r(_e(n,t)),e)}function bu(n,t,r,e){for(var u=n.length,i=e?u:-1;(e?i--:++i<u)&&t(n[i],i,n););return r?au(n,e?0:i,e?i+1:u):au(n,e?i+1:0,e?u:i)}function wu(n,t){var r=n;return r instanceof Ct&&(r=r.value()),l(t,function(n,t){return t.func.apply(t.thisArg,a([n],t.args))},r)}function mu(n,t,r){var e=n.length;if(e<2)return e?gu(n[0]):[];
for(var u=-1,i=il(e);++u<e;)for(var o=n[u],f=-1;++f<e;)f!=u&&(i[u]=Hr(i[u]||o,n[f],t,r));return gu(ee(i,1),t,r)}function xu(n,t,r){for(var e=-1,u=n.length,i=t.length,o={};++e<u;){r(o,n[e],e<i?t[e]:X)}return o}function ju(n){return Jf(n)?n:[]}function Au(n){return"function"==typeof n?n:La}function ku(n,t){return bh(n)?n:Bi(n,t)?[n]:Cs(Ec(n))}function Ou(n,t,r){var e=n.length;return r=r===X?e:r,!t&&r>=e?n:au(n,t,r)}function Iu(n,t){if(t)return n.slice();var r=n.length,e=zl?zl(r):new n.constructor(r);
return n.copy(e),e}function Ru(n){var t=new n.constructor(n.byteLength);return new Rl(t).set(new Rl(n)),t}function zu(n,t){return new n.constructor(t?Ru(n.buffer):n.buffer,n.byteOffset,n.byteLength)}function Eu(n){var t=new n.constructor(n.source,Nt.exec(n));return t.lastIndex=n.lastIndex,t}function Su(n){return _s?ll(_s.call(n)):{}}function Wu(n,t){return new n.constructor(t?Ru(n.buffer):n.buffer,n.byteOffset,n.length)}function Lu(n,t){if(n!==t){var r=n!==X,e=null===n,u=n===n,i=bc(n),o=t!==X,f=null===t,c=t===t,a=bc(t);
if(!f&&!a&&!i&&n>t||i&&o&&c&&!f&&!a||e&&o&&c||!r&&c||!u)return 1;if(!e&&!i&&!a&&n<t||a&&r&&u&&!e&&!i||f&&r&&u||!o&&u||!c)return-1}return 0}function Cu(n,t,r){for(var e=-1,u=n.criteria,i=t.criteria,o=u.length,f=r.length;++e<o;){var c=Lu(u[e],i[e]);if(c){if(e>=f)return c;return c*("desc"==r[e]?-1:1)}}return n.index-t.index}function Uu(n,t,r,e){for(var u=-1,i=n.length,o=r.length,f=-1,c=t.length,a=Gl(i-o,0),l=il(c+a),s=!e;++f<c;)l[f]=t[f];for(;++u<o;)(s||u<i)&&(l[r[u]]=n[u]);for(;a--;)l[f++]=n[u++];return l;
}function Bu(n,t,r,e){for(var u=-1,i=n.length,o=-1,f=r.length,c=-1,a=t.length,l=Gl(i-f,0),s=il(l+a),h=!e;++u<l;)s[u]=n[u];for(var p=u;++c<a;)s[p+c]=t[c];for(;++o<f;)(h||u<i)&&(s[p+r[o]]=n[u++]);return s}function Tu(n,t){var r=-1,e=n.length;for(t||(t=il(e));++r<e;)t[r]=n[r];return t}function $u(n,t,r,e){var u=!r;r||(r={});for(var i=-1,o=t.length;++i<o;){var f=t[i],c=e?e(r[f],n[f],f,r,n):X;c===X&&(c=n[f]),u?Br(r,f,c):Sr(r,f,c)}return r}function Du(n,t){return $u(n,Is(n),t)}function Mu(n,t){return $u(n,Rs(n),t);
}function Fu(n,r){return function(e,u){var i=bh(e)?t:Lr,o=r?r():{};return i(e,n,mi(u,2),o)}}function Nu(n){return uu(function(t,r){var e=-1,u=r.length,i=u>1?r[u-1]:X,o=u>2?r[2]:X;for(i=n.length>3&&"function"==typeof i?(u--,i):X,o&&Ui(r[0],r[1],o)&&(i=u<3?X:i,u=1),t=ll(t);++e<u;){var f=r[e];f&&n(t,f,e,i)}return t})}function Pu(n,t){return function(r,e){if(null==r)return r;if(!Hf(r))return n(r,e);for(var u=r.length,i=t?u:-1,o=ll(r);(t?i--:++i<u)&&e(o[i],i,o)!==!1;);return r}}function qu(n){return function(t,r,e){
for(var u=-1,i=ll(t),o=e(t),f=o.length;f--;){var c=o[n?f:++u];if(r(i[c],c,i)===!1)break}return t}}function Zu(n,t,r){function e(){return(this&&this!==re&&this instanceof e?i:n).apply(u?r:this,arguments)}var u=t&_n,i=Gu(n);return e}function Ku(n){return function(t){t=Ec(t);var r=T(t)?G(t):X,e=r?r[0]:t.charAt(0),u=r?Ou(r,1).join(""):t.slice(1);return e[n]()+u}}function Vu(n){return function(t){return l(Ra(ca(t).replace($r,"")),n,"")}}function Gu(n){return function(){var t=arguments;switch(t.length){
case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:return new n(t[0],t[1],t[2]);case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=gs(n.prototype),e=n.apply(r,t);return fc(e)?e:r}}function Hu(t,r,e){function u(){for(var o=arguments.length,f=il(o),c=o,a=wi(u);c--;)f[c]=arguments[c];var l=o<3&&f[0]!==a&&f[o-1]!==a?[]:N(f,a);
return o-=l.length,o<e?oi(t,r,Qu,u.placeholder,X,f,l,X,X,e-o):n(this&&this!==re&&this instanceof u?i:t,this,f)}var i=Gu(t);return u}function Ju(n){return function(t,r,e){var u=ll(t);if(!Hf(t)){var i=mi(r,3);t=Pc(t),r=function(n){return i(u[n],n,u)}}var o=n(t,r,e);return o>-1?u[i?t[o]:o]:X}}function Yu(n){return gi(function(t){var r=t.length,e=r,u=Y.prototype.thru;for(n&&t.reverse();e--;){var i=t[e];if("function"!=typeof i)throw new pl(en);if(u&&!o&&"wrapper"==bi(i))var o=new Y([],!0)}for(e=o?e:r;++e<r;){
i=t[e];var f=bi(i),c="wrapper"==f?Os(i):X;o=c&&$i(c[0])&&c[1]==(mn|yn|bn|xn)&&!c[4].length&&1==c[9]?o[bi(c[0])].apply(o,c[3]):1==i.length&&$i(i)?o[f]():o.thru(i)}return function(){var n=arguments,e=n[0];if(o&&1==n.length&&bh(e))return o.plant(e).value();for(var u=0,i=r?t[u].apply(this,n):e;++u<r;)i=t[u].call(this,i);return i}})}function Qu(n,t,r,e,u,i,o,f,c,a){function l(){for(var y=arguments.length,d=il(y),b=y;b--;)d[b]=arguments[b];if(_)var w=wi(l),m=C(d,w);if(e&&(d=Uu(d,e,u,_)),i&&(d=Bu(d,i,o,_)),
y-=m,_&&y<a){return oi(n,t,Qu,l.placeholder,r,d,N(d,w),f,c,a-y)}var x=h?r:this,j=p?x[n]:n;return y=d.length,f?d=Hi(d,f):v&&y>1&&d.reverse(),s&&c<y&&(d.length=c),this&&this!==re&&this instanceof l&&(j=g||Gu(j)),j.apply(x,d)}var s=t&mn,h=t&_n,p=t&vn,_=t&(yn|dn),v=t&jn,g=p?X:Gu(n);return l}function Xu(n,t){return function(r,e){return Oe(r,n,t(e),{})}}function ni(n,t){return function(r,e){var u;if(r===X&&e===X)return t;if(r!==X&&(u=r),e!==X){if(u===X)return e;"string"==typeof r||"string"==typeof e?(r=vu(r),
e=vu(e)):(r=_u(r),e=_u(e)),u=n(r,e)}return u}}function ti(t){return gi(function(r){return r=c(r,z(mi())),uu(function(e){var u=this;return t(r,function(t){return n(t,u,e)})})})}function ri(n,t){t=t===X?" ":vu(t);var r=t.length;if(r<2)return r?eu(t,n):t;var e=eu(t,Fl(n/V(t)));return T(t)?Ou(G(e),0,n).join(""):e.slice(0,n)}function ei(t,r,e,u){function i(){for(var r=-1,c=arguments.length,a=-1,l=u.length,s=il(l+c),h=this&&this!==re&&this instanceof i?f:t;++a<l;)s[a]=u[a];for(;c--;)s[a++]=arguments[++r];
return n(h,o?e:this,s)}var o=r&_n,f=Gu(t);return i}function ui(n){return function(t,r,e){return e&&"number"!=typeof e&&Ui(t,r,e)&&(r=e=X),t=Ac(t),r===X?(r=t,t=0):r=Ac(r),e=e===X?t<r?1:-1:Ac(e),ru(t,r,e,n)}}function ii(n){return function(t,r){return"string"==typeof t&&"string"==typeof r||(t=Ic(t),r=Ic(r)),n(t,r)}}function oi(n,t,r,e,u,i,o,f,c,a){var l=t&yn,s=l?o:X,h=l?X:o,p=l?i:X,_=l?X:i;t|=l?bn:wn,t&=~(l?wn:bn),t&gn||(t&=~(_n|vn));var v=[n,t,u,p,s,_,h,f,c,a],g=r.apply(X,v);return $i(n)&&Ss(g,v),g.placeholder=e,
Yi(g,n,t)}function fi(n){var t=al[n];return function(n,r){if(n=Ic(n),r=null==r?0:Hl(kc(r),292),r&&Zl(n)){var e=(Ec(n)+"e").split("e");return e=(Ec(t(e[0]+"e"+(+e[1]+r)))+"e").split("e"),+(e[0]+"e"+(+e[1]-r))}return t(n)}}function ci(n){return function(t){var r=zs(t);return r==Gn?M(t):r==tt?q(t):I(t,n(t))}}function ai(n,t,r,e,u,i,o,f){var c=t&vn;if(!c&&"function"!=typeof n)throw new pl(en);var a=e?e.length:0;if(a||(t&=~(bn|wn),e=u=X),o=o===X?o:Gl(kc(o),0),f=f===X?f:kc(f),a-=u?u.length:0,t&wn){var l=e,s=u;
e=u=X}var h=c?X:Os(n),p=[n,t,r,e,u,l,s,i,o,f];if(h&&qi(p,h),n=p[0],t=p[1],r=p[2],e=p[3],u=p[4],f=p[9]=p[9]===X?c?0:n.length:Gl(p[9]-a,0),!f&&t&(yn|dn)&&(t&=~(yn|dn)),t&&t!=_n)_=t==yn||t==dn?Hu(n,t,f):t!=bn&&t!=(_n|bn)||u.length?Qu.apply(X,p):ei(n,t,r,e);else var _=Zu(n,t,r);return Yi((h?ms:Ss)(_,p),n,t)}function li(n,t,r,e){return n===X||Gf(n,gl[r])&&!bl.call(e,r)?t:n}function si(n,t,r,e,u,i){return fc(n)&&fc(t)&&(i.set(t,n),Ke(n,t,X,si,i),i.delete(t)),n}function hi(n){return gc(n)?X:n}function pi(n,t,r,e,u,i){
var o=r&hn,f=n.length,c=t.length;if(f!=c&&!(o&&c>f))return!1;var a=i.get(n),l=i.get(t);if(a&&l)return a==t&&l==n;var s=-1,p=!0,_=r&pn?new yr:X;for(i.set(n,t),i.set(t,n);++s<f;){var v=n[s],g=t[s];if(e)var y=o?e(g,v,s,t,n,i):e(v,g,s,n,t,i);if(y!==X){if(y)continue;p=!1;break}if(_){if(!h(t,function(n,t){if(!S(_,t)&&(v===n||u(v,n,r,e,i)))return _.push(t)})){p=!1;break}}else if(v!==g&&!u(v,g,r,e,i)){p=!1;break}}return i.delete(n),i.delete(t),p}function _i(n,t,r,e,u,i,o){switch(r){case ct:if(n.byteLength!=t.byteLength||n.byteOffset!=t.byteOffset)return!1;
n=n.buffer,t=t.buffer;case ft:return!(n.byteLength!=t.byteLength||!i(new Rl(n),new Rl(t)));case Nn:case Pn:case Hn:return Gf(+n,+t);case Zn:return n.name==t.name&&n.message==t.message;case nt:case rt:return n==t+"";case Gn:var f=M;case tt:var c=e&hn;if(f||(f=P),n.size!=t.size&&!c)return!1;var a=o.get(n);if(a)return a==t;e|=pn,o.set(n,t);var l=pi(f(n),f(t),e,u,i,o);return o.delete(n),l;case et:if(_s)return _s.call(n)==_s.call(t)}return!1}function vi(n,t,r,e,u,i){var o=r&hn,f=yi(n),c=f.length;if(c!=yi(t).length&&!o)return!1;
for(var a=c;a--;){var l=f[a];if(!(o?l in t:bl.call(t,l)))return!1}var s=i.get(n),h=i.get(t);if(s&&h)return s==t&&h==n;var p=!0;i.set(n,t),i.set(t,n);for(var _=o;++a<c;){l=f[a];var v=n[l],g=t[l];if(e)var y=o?e(g,v,l,t,n,i):e(v,g,l,n,t,i);if(!(y===X?v===g||u(v,g,r,e,i):y)){p=!1;break}_||(_="constructor"==l)}if(p&&!_){var d=n.constructor,b=t.constructor;d!=b&&"constructor"in n&&"constructor"in t&&!("function"==typeof d&&d instanceof d&&"function"==typeof b&&b instanceof b)&&(p=!1)}return i.delete(n),
i.delete(t),p}function gi(n){return Ls(Vi(n,X,_o),n+"")}function yi(n){return de(n,Pc,Is)}function di(n){return de(n,qc,Rs)}function bi(n){for(var t=n.name+"",r=fs[t],e=bl.call(fs,t)?r.length:0;e--;){var u=r[e],i=u.func;if(null==i||i==n)return u.name}return t}function wi(n){return(bl.call(Z,"placeholder")?Z:n).placeholder}function mi(){var n=Z.iteratee||Ca;return n=n===Ca?De:n,arguments.length?n(arguments[0],arguments[1]):n}function xi(n,t){var r=n.__data__;return Ti(t)?r["string"==typeof t?"string":"hash"]:r.map;
}function ji(n){for(var t=Pc(n),r=t.length;r--;){var e=t[r],u=n[e];t[r]=[e,u,Fi(u)]}return t}function Ai(n,t){var r=B(n,t);return Ue(r)?r:X}function ki(n){var t=bl.call(n,Bl),r=n[Bl];try{n[Bl]=X;var e=!0}catch(n){}var u=xl.call(n);return e&&(t?n[Bl]=r:delete n[Bl]),u}function Oi(n,t,r){for(var e=-1,u=r.length;++e<u;){var i=r[e],o=i.size;switch(i.type){case"drop":n+=o;break;case"dropRight":t-=o;break;case"take":t=Hl(t,n+o);break;case"takeRight":n=Gl(n,t-o)}}return{start:n,end:t}}function Ii(n){var t=n.match(Bt);
return t?t[1].split(Tt):[]}function Ri(n,t,r){t=ku(t,n);for(var e=-1,u=t.length,i=!1;++e<u;){var o=no(t[e]);if(!(i=null!=n&&r(n,o)))break;n=n[o]}return i||++e!=u?i:(u=null==n?0:n.length,!!u&&oc(u)&&Ci(o,u)&&(bh(n)||dh(n)))}function zi(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&bl.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function Ei(n){return"function"!=typeof n.constructor||Mi(n)?{}:gs(El(n))}function Si(n,t,r){var e=n.constructor;switch(t){case ft:return Ru(n);
case Nn:case Pn:return new e(+n);case ct:return zu(n,r);case at:case lt:case st:case ht:case pt:case _t:case vt:case gt:case yt:return Wu(n,r);case Gn:return new e;case Hn:case rt:return new e(n);case nt:return Eu(n);case tt:return new e;case et:return Su(n)}}function Wi(n,t){var r=t.length;if(!r)return n;var e=r-1;return t[e]=(r>1?"& ":"")+t[e],t=t.join(r>2?", ":" "),n.replace(Ut,"{\n/* [wrapped with "+t+"] */\n")}function Li(n){return bh(n)||dh(n)||!!(Cl&&n&&n[Cl])}function Ci(n,t){var r=typeof n;
return t=null==t?Wn:t,!!t&&("number"==r||"symbol"!=r&&Vt.test(n))&&n>-1&&n%1==0&&n<t}function Ui(n,t,r){if(!fc(r))return!1;var e=typeof t;return!!("number"==e?Hf(r)&&Ci(t,r.length):"string"==e&&t in r)&&Gf(r[t],n)}function Bi(n,t){if(bh(n))return!1;var r=typeof n;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=n&&!bc(n))||(zt.test(n)||!Rt.test(n)||null!=t&&n in ll(t))}function Ti(n){var t=typeof n;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==n:null===n}function $i(n){
var t=bi(n),r=Z[t];if("function"!=typeof r||!(t in Ct.prototype))return!1;if(n===r)return!0;var e=Os(r);return!!e&&n===e[0]}function Di(n){return!!ml&&ml in n}function Mi(n){var t=n&&n.constructor;return n===("function"==typeof t&&t.prototype||gl)}function Fi(n){return n===n&&!fc(n)}function Ni(n,t){return function(r){return null!=r&&(r[n]===t&&(t!==X||n in ll(r)))}}function Pi(n){var t=Cf(n,function(n){return r.size===fn&&r.clear(),n}),r=t.cache;return t}function qi(n,t){var r=n[1],e=t[1],u=r|e,i=u<(_n|vn|mn),o=e==mn&&r==yn||e==mn&&r==xn&&n[7].length<=t[8]||e==(mn|xn)&&t[7].length<=t[8]&&r==yn;
if(!i&&!o)return n;e&_n&&(n[2]=t[2],u|=r&_n?0:gn);var f=t[3];if(f){var c=n[3];n[3]=c?Uu(c,f,t[4]):f,n[4]=c?N(n[3],cn):t[4]}return f=t[5],f&&(c=n[5],n[5]=c?Bu(c,f,t[6]):f,n[6]=c?N(n[5],cn):t[6]),f=t[7],f&&(n[7]=f),e&mn&&(n[8]=null==n[8]?t[8]:Hl(n[8],t[8])),null==n[9]&&(n[9]=t[9]),n[0]=t[0],n[1]=u,n}function Zi(n){var t=[];if(null!=n)for(var r in ll(n))t.push(r);return t}function Ki(n){return xl.call(n)}function Vi(t,r,e){return r=Gl(r===X?t.length-1:r,0),function(){for(var u=arguments,i=-1,o=Gl(u.length-r,0),f=il(o);++i<o;)f[i]=u[r+i];
i=-1;for(var c=il(r+1);++i<r;)c[i]=u[i];return c[r]=e(f),n(t,this,c)}}function Gi(n,t){return t.length<2?n:_e(n,au(t,0,-1))}function Hi(n,t){for(var r=n.length,e=Hl(t.length,r),u=Tu(n);e--;){var i=t[e];n[e]=Ci(i,r)?u[i]:X}return n}function Ji(n,t){if(("constructor"!==t||"function"!=typeof n[t])&&"__proto__"!=t)return n[t]}function Yi(n,t,r){var e=t+"";return Ls(n,Wi(e,ro(Ii(e),r)))}function Qi(n){var t=0,r=0;return function(){var e=Jl(),u=In-(e-r);if(r=e,u>0){if(++t>=On)return arguments[0]}else t=0;
return n.apply(X,arguments)}}function Xi(n,t){var r=-1,e=n.length,u=e-1;for(t=t===X?e:t;++r<t;){var i=tu(r,u),o=n[i];n[i]=n[r],n[r]=o}return n.length=t,n}function no(n){if("string"==typeof n||bc(n))return n;var t=n+"";return"0"==t&&1/n==-Sn?"-0":t}function to(n){if(null!=n){try{return dl.call(n)}catch(n){}try{return n+""}catch(n){}}return""}function ro(n,t){return r($n,function(r){var e="_."+r[0];t&r[1]&&!o(n,e)&&n.push(e)}),n.sort()}function eo(n){if(n instanceof Ct)return n.clone();var t=new Y(n.__wrapped__,n.__chain__);
return t.__actions__=Tu(n.__actions__),t.__index__=n.__index__,t.__values__=n.__values__,t}function uo(n,t,r){t=(r?Ui(n,t,r):t===X)?1:Gl(kc(t),0);var e=null==n?0:n.length;if(!e||t<1)return[];for(var u=0,i=0,o=il(Fl(e/t));u<e;)o[i++]=au(n,u,u+=t);return o}function io(n){for(var t=-1,r=null==n?0:n.length,e=0,u=[];++t<r;){var i=n[t];i&&(u[e++]=i)}return u}function oo(){var n=arguments.length;if(!n)return[];for(var t=il(n-1),r=arguments[0],e=n;e--;)t[e-1]=arguments[e];return a(bh(r)?Tu(r):[r],ee(t,1));
}function fo(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),au(n,t<0?0:t,e)):[]}function co(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),t=e-t,au(n,0,t<0?0:t)):[]}function ao(n,t){return n&&n.length?bu(n,mi(t,3),!0,!0):[]}function lo(n,t){return n&&n.length?bu(n,mi(t,3),!0):[]}function so(n,t,r,e){var u=null==n?0:n.length;return u?(r&&"number"!=typeof r&&Ui(n,t,r)&&(r=0,e=u),ne(n,t,r,e)):[]}function ho(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:kc(r);
return u<0&&(u=Gl(e+u,0)),g(n,mi(t,3),u)}function po(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e-1;return r!==X&&(u=kc(r),u=r<0?Gl(e+u,0):Hl(u,e-1)),g(n,mi(t,3),u,!0)}function _o(n){return(null==n?0:n.length)?ee(n,1):[]}function vo(n){return(null==n?0:n.length)?ee(n,Sn):[]}function go(n,t){return(null==n?0:n.length)?(t=t===X?1:kc(t),ee(n,t)):[]}function yo(n){for(var t=-1,r=null==n?0:n.length,e={};++t<r;){var u=n[t];e[u[0]]=u[1]}return e}function bo(n){return n&&n.length?n[0]:X}function wo(n,t,r){
var e=null==n?0:n.length;if(!e)return-1;var u=null==r?0:kc(r);return u<0&&(u=Gl(e+u,0)),y(n,t,u)}function mo(n){return(null==n?0:n.length)?au(n,0,-1):[]}function xo(n,t){return null==n?"":Kl.call(n,t)}function jo(n){var t=null==n?0:n.length;return t?n[t-1]:X}function Ao(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e;return r!==X&&(u=kc(r),u=u<0?Gl(e+u,0):Hl(u,e-1)),t===t?K(n,t,u):g(n,b,u,!0)}function ko(n,t){return n&&n.length?Ge(n,kc(t)):X}function Oo(n,t){return n&&n.length&&t&&t.length?Xe(n,t):n;
}function Io(n,t,r){return n&&n.length&&t&&t.length?Xe(n,t,mi(r,2)):n}function Ro(n,t,r){return n&&n.length&&t&&t.length?Xe(n,t,X,r):n}function zo(n,t){var r=[];if(!n||!n.length)return r;var e=-1,u=[],i=n.length;for(t=mi(t,3);++e<i;){var o=n[e];t(o,e,n)&&(r.push(o),u.push(e))}return nu(n,u),r}function Eo(n){return null==n?n:Xl.call(n)}function So(n,t,r){var e=null==n?0:n.length;return e?(r&&"number"!=typeof r&&Ui(n,t,r)?(t=0,r=e):(t=null==t?0:kc(t),r=r===X?e:kc(r)),au(n,t,r)):[]}function Wo(n,t){
return su(n,t)}function Lo(n,t,r){return hu(n,t,mi(r,2))}function Co(n,t){var r=null==n?0:n.length;if(r){var e=su(n,t);if(e<r&&Gf(n[e],t))return e}return-1}function Uo(n,t){return su(n,t,!0)}function Bo(n,t,r){return hu(n,t,mi(r,2),!0)}function To(n,t){if(null==n?0:n.length){var r=su(n,t,!0)-1;if(Gf(n[r],t))return r}return-1}function $o(n){return n&&n.length?pu(n):[]}function Do(n,t){return n&&n.length?pu(n,mi(t,2)):[]}function Mo(n){var t=null==n?0:n.length;return t?au(n,1,t):[]}function Fo(n,t,r){
return n&&n.length?(t=r||t===X?1:kc(t),au(n,0,t<0?0:t)):[]}function No(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===X?1:kc(t),t=e-t,au(n,t<0?0:t,e)):[]}function Po(n,t){return n&&n.length?bu(n,mi(t,3),!1,!0):[]}function qo(n,t){return n&&n.length?bu(n,mi(t,3)):[]}function Zo(n){return n&&n.length?gu(n):[]}function Ko(n,t){return n&&n.length?gu(n,mi(t,2)):[]}function Vo(n,t){return t="function"==typeof t?t:X,n&&n.length?gu(n,X,t):[]}function Go(n){if(!n||!n.length)return[];var t=0;return n=i(n,function(n){
if(Jf(n))return t=Gl(n.length,t),!0}),O(t,function(t){return c(n,m(t))})}function Ho(t,r){if(!t||!t.length)return[];var e=Go(t);return null==r?e:c(e,function(t){return n(r,X,t)})}function Jo(n,t){return xu(n||[],t||[],Sr)}function Yo(n,t){return xu(n||[],t||[],fu)}function Qo(n){var t=Z(n);return t.__chain__=!0,t}function Xo(n,t){return t(n),n}function nf(n,t){return t(n)}function tf(){return Qo(this)}function rf(){return new Y(this.value(),this.__chain__)}function ef(){this.__values__===X&&(this.__values__=jc(this.value()));
var n=this.__index__>=this.__values__.length;return{done:n,value:n?X:this.__values__[this.__index__++]}}function uf(){return this}function of(n){for(var t,r=this;r instanceof J;){var e=eo(r);e.__index__=0,e.__values__=X,t?u.__wrapped__=e:t=e;var u=e;r=r.__wrapped__}return u.__wrapped__=n,t}function ff(){var n=this.__wrapped__;if(n instanceof Ct){var t=n;return this.__actions__.length&&(t=new Ct(this)),t=t.reverse(),t.__actions__.push({func:nf,args:[Eo],thisArg:X}),new Y(t,this.__chain__)}return this.thru(Eo);
}function cf(){return wu(this.__wrapped__,this.__actions__)}function af(n,t,r){var e=bh(n)?u:Jr;return r&&Ui(n,t,r)&&(t=X),e(n,mi(t,3))}function lf(n,t){return(bh(n)?i:te)(n,mi(t,3))}function sf(n,t){return ee(yf(n,t),1)}function hf(n,t){return ee(yf(n,t),Sn)}function pf(n,t,r){return r=r===X?1:kc(r),ee(yf(n,t),r)}function _f(n,t){return(bh(n)?r:ys)(n,mi(t,3))}function vf(n,t){return(bh(n)?e:ds)(n,mi(t,3))}function gf(n,t,r,e){n=Hf(n)?n:ra(n),r=r&&!e?kc(r):0;var u=n.length;return r<0&&(r=Gl(u+r,0)),
dc(n)?r<=u&&n.indexOf(t,r)>-1:!!u&&y(n,t,r)>-1}function yf(n,t){return(bh(n)?c:Pe)(n,mi(t,3))}function df(n,t,r,e){return null==n?[]:(bh(t)||(t=null==t?[]:[t]),r=e?X:r,bh(r)||(r=null==r?[]:[r]),He(n,t,r))}function bf(n,t,r){var e=bh(n)?l:j,u=arguments.length<3;return e(n,mi(t,4),r,u,ys)}function wf(n,t,r){var e=bh(n)?s:j,u=arguments.length<3;return e(n,mi(t,4),r,u,ds)}function mf(n,t){return(bh(n)?i:te)(n,Uf(mi(t,3)))}function xf(n){return(bh(n)?Ir:iu)(n)}function jf(n,t,r){return t=(r?Ui(n,t,r):t===X)?1:kc(t),
(bh(n)?Rr:ou)(n,t)}function Af(n){return(bh(n)?zr:cu)(n)}function kf(n){if(null==n)return 0;if(Hf(n))return dc(n)?V(n):n.length;var t=zs(n);return t==Gn||t==tt?n.size:Me(n).length}function Of(n,t,r){var e=bh(n)?h:lu;return r&&Ui(n,t,r)&&(t=X),e(n,mi(t,3))}function If(n,t){if("function"!=typeof t)throw new pl(en);return n=kc(n),function(){if(--n<1)return t.apply(this,arguments)}}function Rf(n,t,r){return t=r?X:t,t=n&&null==t?n.length:t,ai(n,mn,X,X,X,X,t)}function zf(n,t){var r;if("function"!=typeof t)throw new pl(en);
return n=kc(n),function(){return--n>0&&(r=t.apply(this,arguments)),n<=1&&(t=X),r}}function Ef(n,t,r){t=r?X:t;var e=ai(n,yn,X,X,X,X,X,t);return e.placeholder=Ef.placeholder,e}function Sf(n,t,r){t=r?X:t;var e=ai(n,dn,X,X,X,X,X,t);return e.placeholder=Sf.placeholder,e}function Wf(n,t,r){function e(t){var r=h,e=p;return h=p=X,d=t,v=n.apply(e,r)}function u(n){return d=n,g=Ws(f,t),b?e(n):v}function i(n){var r=n-y,e=n-d,u=t-r;return w?Hl(u,_-e):u}function o(n){var r=n-y,e=n-d;return y===X||r>=t||r<0||w&&e>=_;
}function f(){var n=fh();return o(n)?c(n):(g=Ws(f,i(n)),X)}function c(n){return g=X,m&&h?e(n):(h=p=X,v)}function a(){g!==X&&As(g),d=0,h=y=p=g=X}function l(){return g===X?v:c(fh())}function s(){var n=fh(),r=o(n);if(h=arguments,p=this,y=n,r){if(g===X)return u(y);if(w)return As(g),g=Ws(f,t),e(y)}return g===X&&(g=Ws(f,t)),v}var h,p,_,v,g,y,d=0,b=!1,w=!1,m=!0;if("function"!=typeof n)throw new pl(en);return t=Ic(t)||0,fc(r)&&(b=!!r.leading,w="maxWait"in r,_=w?Gl(Ic(r.maxWait)||0,t):_,m="trailing"in r?!!r.trailing:m),
s.cancel=a,s.flush=l,s}function Lf(n){return ai(n,jn)}function Cf(n,t){if("function"!=typeof n||null!=t&&"function"!=typeof t)throw new pl(en);var r=function(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache;if(i.has(u))return i.get(u);var o=n.apply(this,e);return r.cache=i.set(u,o)||i,o};return r.cache=new(Cf.Cache||sr),r}function Uf(n){if("function"!=typeof n)throw new pl(en);return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:
return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}function Bf(n){return zf(2,n)}function Tf(n,t){if("function"!=typeof n)throw new pl(en);return t=t===X?t:kc(t),uu(n,t)}function $f(t,r){if("function"!=typeof t)throw new pl(en);return r=null==r?0:Gl(kc(r),0),uu(function(e){var u=e[r],i=Ou(e,0,r);return u&&a(i,u),n(t,this,i)})}function Df(n,t,r){var e=!0,u=!0;if("function"!=typeof n)throw new pl(en);return fc(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),
Wf(n,t,{leading:e,maxWait:t,trailing:u})}function Mf(n){return Rf(n,1)}function Ff(n,t){return ph(Au(t),n)}function Nf(){if(!arguments.length)return[];var n=arguments[0];return bh(n)?n:[n]}function Pf(n){return Fr(n,sn)}function qf(n,t){return t="function"==typeof t?t:X,Fr(n,sn,t)}function Zf(n){return Fr(n,an|sn)}function Kf(n,t){return t="function"==typeof t?t:X,Fr(n,an|sn,t)}function Vf(n,t){return null==t||Pr(n,t,Pc(t))}function Gf(n,t){return n===t||n!==n&&t!==t}function Hf(n){return null!=n&&oc(n.length)&&!uc(n);
}function Jf(n){return cc(n)&&Hf(n)}function Yf(n){return n===!0||n===!1||cc(n)&&we(n)==Nn}function Qf(n){return cc(n)&&1===n.nodeType&&!gc(n)}function Xf(n){if(null==n)return!0;if(Hf(n)&&(bh(n)||"string"==typeof n||"function"==typeof n.splice||mh(n)||Oh(n)||dh(n)))return!n.length;var t=zs(n);if(t==Gn||t==tt)return!n.size;if(Mi(n))return!Me(n).length;for(var r in n)if(bl.call(n,r))return!1;return!0}function nc(n,t){return Se(n,t)}function tc(n,t,r){r="function"==typeof r?r:X;var e=r?r(n,t):X;return e===X?Se(n,t,X,r):!!e;
}function rc(n){if(!cc(n))return!1;var t=we(n);return t==Zn||t==qn||"string"==typeof n.message&&"string"==typeof n.name&&!gc(n)}function ec(n){return"number"==typeof n&&Zl(n)}function uc(n){if(!fc(n))return!1;var t=we(n);return t==Kn||t==Vn||t==Fn||t==Xn}function ic(n){return"number"==typeof n&&n==kc(n)}function oc(n){return"number"==typeof n&&n>-1&&n%1==0&&n<=Wn}function fc(n){var t=typeof n;return null!=n&&("object"==t||"function"==t)}function cc(n){return null!=n&&"object"==typeof n}function ac(n,t){
return n===t||Ce(n,t,ji(t))}function lc(n,t,r){return r="function"==typeof r?r:X,Ce(n,t,ji(t),r)}function sc(n){return vc(n)&&n!=+n}function hc(n){if(Es(n))throw new fl(rn);return Ue(n)}function pc(n){return null===n}function _c(n){return null==n}function vc(n){return"number"==typeof n||cc(n)&&we(n)==Hn}function gc(n){if(!cc(n)||we(n)!=Yn)return!1;var t=El(n);if(null===t)return!0;var r=bl.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&dl.call(r)==jl}function yc(n){
return ic(n)&&n>=-Wn&&n<=Wn}function dc(n){return"string"==typeof n||!bh(n)&&cc(n)&&we(n)==rt}function bc(n){return"symbol"==typeof n||cc(n)&&we(n)==et}function wc(n){return n===X}function mc(n){return cc(n)&&zs(n)==it}function xc(n){return cc(n)&&we(n)==ot}function jc(n){if(!n)return[];if(Hf(n))return dc(n)?G(n):Tu(n);if(Ul&&n[Ul])return D(n[Ul]());var t=zs(n);return(t==Gn?M:t==tt?P:ra)(n)}function Ac(n){if(!n)return 0===n?n:0;if(n=Ic(n),n===Sn||n===-Sn){return(n<0?-1:1)*Ln}return n===n?n:0}function kc(n){
var t=Ac(n),r=t%1;return t===t?r?t-r:t:0}function Oc(n){return n?Mr(kc(n),0,Un):0}function Ic(n){if("number"==typeof n)return n;if(bc(n))return Cn;if(fc(n)){var t="function"==typeof n.valueOf?n.valueOf():n;n=fc(t)?t+"":t}if("string"!=typeof n)return 0===n?n:+n;n=R(n);var r=qt.test(n);return r||Kt.test(n)?Xr(n.slice(2),r?2:8):Pt.test(n)?Cn:+n}function Rc(n){return $u(n,qc(n))}function zc(n){return n?Mr(kc(n),-Wn,Wn):0===n?n:0}function Ec(n){return null==n?"":vu(n)}function Sc(n,t){var r=gs(n);return null==t?r:Cr(r,t);
}function Wc(n,t){return v(n,mi(t,3),ue)}function Lc(n,t){return v(n,mi(t,3),oe)}function Cc(n,t){return null==n?n:bs(n,mi(t,3),qc)}function Uc(n,t){return null==n?n:ws(n,mi(t,3),qc)}function Bc(n,t){return n&&ue(n,mi(t,3))}function Tc(n,t){return n&&oe(n,mi(t,3))}function $c(n){return null==n?[]:fe(n,Pc(n))}function Dc(n){return null==n?[]:fe(n,qc(n))}function Mc(n,t,r){var e=null==n?X:_e(n,t);return e===X?r:e}function Fc(n,t){return null!=n&&Ri(n,t,xe)}function Nc(n,t){return null!=n&&Ri(n,t,je);
}function Pc(n){return Hf(n)?Or(n):Me(n)}function qc(n){return Hf(n)?Or(n,!0):Fe(n)}function Zc(n,t){var r={};return t=mi(t,3),ue(n,function(n,e,u){Br(r,t(n,e,u),n)}),r}function Kc(n,t){var r={};return t=mi(t,3),ue(n,function(n,e,u){Br(r,e,t(n,e,u))}),r}function Vc(n,t){return Gc(n,Uf(mi(t)))}function Gc(n,t){if(null==n)return{};var r=c(di(n),function(n){return[n]});return t=mi(t),Ye(n,r,function(n,r){return t(n,r[0])})}function Hc(n,t,r){t=ku(t,n);var e=-1,u=t.length;for(u||(u=1,n=X);++e<u;){var i=null==n?X:n[no(t[e])];
i===X&&(e=u,i=r),n=uc(i)?i.call(n):i}return n}function Jc(n,t,r){return null==n?n:fu(n,t,r)}function Yc(n,t,r,e){return e="function"==typeof e?e:X,null==n?n:fu(n,t,r,e)}function Qc(n,t,e){var u=bh(n),i=u||mh(n)||Oh(n);if(t=mi(t,4),null==e){var o=n&&n.constructor;e=i?u?new o:[]:fc(n)&&uc(o)?gs(El(n)):{}}return(i?r:ue)(n,function(n,r,u){return t(e,n,r,u)}),e}function Xc(n,t){return null==n||yu(n,t)}function na(n,t,r){return null==n?n:du(n,t,Au(r))}function ta(n,t,r,e){return e="function"==typeof e?e:X,
null==n?n:du(n,t,Au(r),e)}function ra(n){return null==n?[]:E(n,Pc(n))}function ea(n){return null==n?[]:E(n,qc(n))}function ua(n,t,r){return r===X&&(r=t,t=X),r!==X&&(r=Ic(r),r=r===r?r:0),t!==X&&(t=Ic(t),t=t===t?t:0),Mr(Ic(n),t,r)}function ia(n,t,r){return t=Ac(t),r===X?(r=t,t=0):r=Ac(r),n=Ic(n),Ae(n,t,r)}function oa(n,t,r){if(r&&"boolean"!=typeof r&&Ui(n,t,r)&&(t=r=X),r===X&&("boolean"==typeof t?(r=t,t=X):"boolean"==typeof n&&(r=n,n=X)),n===X&&t===X?(n=0,t=1):(n=Ac(n),t===X?(t=n,n=0):t=Ac(t)),n>t){
var e=n;n=t,t=e}if(r||n%1||t%1){var u=Ql();return Hl(n+u*(t-n+Qr("1e-"+((u+"").length-1))),t)}return tu(n,t)}function fa(n){return Qh(Ec(n).toLowerCase())}function ca(n){return n=Ec(n),n&&n.replace(Gt,ve).replace(Dr,"")}function aa(n,t,r){n=Ec(n),t=vu(t);var e=n.length;r=r===X?e:Mr(kc(r),0,e);var u=r;return r-=t.length,r>=0&&n.slice(r,u)==t}function la(n){return n=Ec(n),n&&At.test(n)?n.replace(xt,ge):n}function sa(n){return n=Ec(n),n&&Wt.test(n)?n.replace(St,"\\$&"):n}function ha(n,t,r){n=Ec(n),t=kc(t);
var e=t?V(n):0;if(!t||e>=t)return n;var u=(t-e)/2;return ri(Nl(u),r)+n+ri(Fl(u),r)}function pa(n,t,r){n=Ec(n),t=kc(t);var e=t?V(n):0;return t&&e<t?n+ri(t-e,r):n}function _a(n,t,r){n=Ec(n),t=kc(t);var e=t?V(n):0;return t&&e<t?ri(t-e,r)+n:n}function va(n,t,r){return r||null==t?t=0:t&&(t=+t),Yl(Ec(n).replace(Lt,""),t||0)}function ga(n,t,r){return t=(r?Ui(n,t,r):t===X)?1:kc(t),eu(Ec(n),t)}function ya(){var n=arguments,t=Ec(n[0]);return n.length<3?t:t.replace(n[1],n[2])}function da(n,t,r){return r&&"number"!=typeof r&&Ui(n,t,r)&&(t=r=X),
(r=r===X?Un:r>>>0)?(n=Ec(n),n&&("string"==typeof t||null!=t&&!Ah(t))&&(t=vu(t),!t&&T(n))?Ou(G(n),0,r):n.split(t,r)):[]}function ba(n,t,r){return n=Ec(n),r=null==r?0:Mr(kc(r),0,n.length),t=vu(t),n.slice(r,r+t.length)==t}function wa(n,t,r){var e=Z.templateSettings;r&&Ui(n,t,r)&&(t=X),n=Ec(n),t=Sh({},t,e,li);var u,i,o=Sh({},t.imports,e.imports,li),f=Pc(o),c=E(o,f),a=0,l=t.interpolate||Ht,s="__p += '",h=sl((t.escape||Ht).source+"|"+l.source+"|"+(l===It?Ft:Ht).source+"|"+(t.evaluate||Ht).source+"|$","g"),p="//# sourceURL="+(bl.call(t,"sourceURL")?(t.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++Zr+"]")+"\n";
n.replace(h,function(t,r,e,o,f,c){return e||(e=o),s+=n.slice(a,c).replace(Jt,U),r&&(u=!0,s+="' +\n__e("+r+") +\n'"),f&&(i=!0,s+="';\n"+f+";\n__p += '"),e&&(s+="' +\n((__t = ("+e+")) == null ? '' : __t) +\n'"),a=c+t.length,t}),s+="';\n";var _=bl.call(t,"variable")&&t.variable;if(_){if(Dt.test(_))throw new fl(un)}else s="with (obj) {\n"+s+"\n}\n";s=(i?s.replace(dt,""):s).replace(bt,"$1").replace(wt,"$1;"),s="function("+(_||"obj")+") {\n"+(_?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(u?", __e = _.escape":"")+(i?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+s+"return __p\n}";
var v=Xh(function(){return cl(f,p+"return "+s).apply(X,c)});if(v.source=s,rc(v))throw v;return v}function ma(n){return Ec(n).toLowerCase()}function xa(n){return Ec(n).toUpperCase()}function ja(n,t,r){if(n=Ec(n),n&&(r||t===X))return R(n);if(!n||!(t=vu(t)))return n;var e=G(n),u=G(t);return Ou(e,W(e,u),L(e,u)+1).join("")}function Aa(n,t,r){if(n=Ec(n),n&&(r||t===X))return n.slice(0,H(n)+1);if(!n||!(t=vu(t)))return n;var e=G(n);return Ou(e,0,L(e,G(t))+1).join("")}function ka(n,t,r){if(n=Ec(n),n&&(r||t===X))return n.replace(Lt,"");
if(!n||!(t=vu(t)))return n;var e=G(n);return Ou(e,W(e,G(t))).join("")}function Oa(n,t){var r=An,e=kn;if(fc(t)){var u="separator"in t?t.separator:u;r="length"in t?kc(t.length):r,e="omission"in t?vu(t.omission):e}n=Ec(n);var i=n.length;if(T(n)){var o=G(n);i=o.length}if(r>=i)return n;var f=r-V(e);if(f<1)return e;var c=o?Ou(o,0,f).join(""):n.slice(0,f);if(u===X)return c+e;if(o&&(f+=c.length-f),Ah(u)){if(n.slice(f).search(u)){var a,l=c;for(u.global||(u=sl(u.source,Ec(Nt.exec(u))+"g")),u.lastIndex=0;a=u.exec(l);)var s=a.index;
c=c.slice(0,s===X?f:s)}}else if(n.indexOf(vu(u),f)!=f){var h=c.lastIndexOf(u);h>-1&&(c=c.slice(0,h))}return c+e}function Ia(n){return n=Ec(n),n&&jt.test(n)?n.replace(mt,ye):n}function Ra(n,t,r){return n=Ec(n),t=r?X:t,t===X?$(n)?Q(n):_(n):n.match(t)||[]}function za(t){var r=null==t?0:t.length,e=mi();return t=r?c(t,function(n){if("function"!=typeof n[1])throw new pl(en);return[e(n[0]),n[1]]}):[],uu(function(e){for(var u=-1;++u<r;){var i=t[u];if(n(i[0],this,e))return n(i[1],this,e)}})}function Ea(n){
return Nr(Fr(n,an))}function Sa(n){return function(){return n}}function Wa(n,t){return null==n||n!==n?t:n}function La(n){return n}function Ca(n){return De("function"==typeof n?n:Fr(n,an))}function Ua(n){return qe(Fr(n,an))}function Ba(n,t){return Ze(n,Fr(t,an))}function Ta(n,t,e){var u=Pc(t),i=fe(t,u);null!=e||fc(t)&&(i.length||!u.length)||(e=t,t=n,n=this,i=fe(t,Pc(t)));var o=!(fc(e)&&"chain"in e&&!e.chain),f=uc(n);return r(i,function(r){var e=t[r];n[r]=e,f&&(n.prototype[r]=function(){var t=this.__chain__;
if(o||t){var r=n(this.__wrapped__);return(r.__actions__=Tu(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,a([this.value()],arguments))})}),n}function $a(){return re._===this&&(re._=Al),this}function Da(){}function Ma(n){return n=kc(n),uu(function(t){return Ge(t,n)})}function Fa(n){return Bi(n)?m(no(n)):Qe(n)}function Na(n){return function(t){return null==n?X:_e(n,t)}}function Pa(){return[]}function qa(){return!1}function Za(){return{}}function Ka(){return"";
}function Va(){return!0}function Ga(n,t){if(n=kc(n),n<1||n>Wn)return[];var r=Un,e=Hl(n,Un);t=mi(t),n-=Un;for(var u=O(e,t);++r<n;)t(r);return u}function Ha(n){return bh(n)?c(n,no):bc(n)?[n]:Tu(Cs(Ec(n)))}function Ja(n){var t=++wl;return Ec(n)+t}function Ya(n){return n&&n.length?Yr(n,La,me):X}function Qa(n,t){return n&&n.length?Yr(n,mi(t,2),me):X}function Xa(n){return w(n,La)}function nl(n,t){return w(n,mi(t,2))}function tl(n){return n&&n.length?Yr(n,La,Ne):X}function rl(n,t){return n&&n.length?Yr(n,mi(t,2),Ne):X;
}function el(n){return n&&n.length?k(n,La):0}function ul(n,t){return n&&n.length?k(n,mi(t,2)):0}x=null==x?re:be.defaults(re.Object(),x,be.pick(re,qr));var il=x.Array,ol=x.Date,fl=x.Error,cl=x.Function,al=x.Math,ll=x.Object,sl=x.RegExp,hl=x.String,pl=x.TypeError,_l=il.prototype,vl=cl.prototype,gl=ll.prototype,yl=x["__core-js_shared__"],dl=vl.toString,bl=gl.hasOwnProperty,wl=0,ml=function(){var n=/[^.]+$/.exec(yl&&yl.keys&&yl.keys.IE_PROTO||"");return n?"Symbol(src)_1."+n:""}(),xl=gl.toString,jl=dl.call(ll),Al=re._,kl=sl("^"+dl.call(bl).replace(St,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Ol=ie?x.Buffer:X,Il=x.Symbol,Rl=x.Uint8Array,zl=Ol?Ol.allocUnsafe:X,El=F(ll.getPrototypeOf,ll),Sl=ll.create,Wl=gl.propertyIsEnumerable,Ll=_l.splice,Cl=Il?Il.isConcatSpreadable:X,Ul=Il?Il.iterator:X,Bl=Il?Il.toStringTag:X,Tl=function(){
try{var n=Ai(ll,"defineProperty");return n({},"",{}),n}catch(n){}}(),$l=x.clearTimeout!==re.clearTimeout&&x.clearTimeout,Dl=ol&&ol.now!==re.Date.now&&ol.now,Ml=x.setTimeout!==re.setTimeout&&x.setTimeout,Fl=al.ceil,Nl=al.floor,Pl=ll.getOwnPropertySymbols,ql=Ol?Ol.isBuffer:X,Zl=x.isFinite,Kl=_l.join,Vl=F(ll.keys,ll),Gl=al.max,Hl=al.min,Jl=ol.now,Yl=x.parseInt,Ql=al.random,Xl=_l.reverse,ns=Ai(x,"DataView"),ts=Ai(x,"Map"),rs=Ai(x,"Promise"),es=Ai(x,"Set"),us=Ai(x,"WeakMap"),is=Ai(ll,"create"),os=us&&new us,fs={},cs=to(ns),as=to(ts),ls=to(rs),ss=to(es),hs=to(us),ps=Il?Il.prototype:X,_s=ps?ps.valueOf:X,vs=ps?ps.toString:X,gs=function(){
function n(){}return function(t){if(!fc(t))return{};if(Sl)return Sl(t);n.prototype=t;var r=new n;return n.prototype=X,r}}();Z.templateSettings={escape:kt,evaluate:Ot,interpolate:It,variable:"",imports:{_:Z}},Z.prototype=J.prototype,Z.prototype.constructor=Z,Y.prototype=gs(J.prototype),Y.prototype.constructor=Y,Ct.prototype=gs(J.prototype),Ct.prototype.constructor=Ct,Xt.prototype.clear=nr,Xt.prototype.delete=tr,Xt.prototype.get=rr,Xt.prototype.has=er,Xt.prototype.set=ur,ir.prototype.clear=or,ir.prototype.delete=fr,
ir.prototype.get=cr,ir.prototype.has=ar,ir.prototype.set=lr,sr.prototype.clear=hr,sr.prototype.delete=pr,sr.prototype.get=_r,sr.prototype.has=vr,sr.prototype.set=gr,yr.prototype.add=yr.prototype.push=dr,yr.prototype.has=br,wr.prototype.clear=mr,wr.prototype.delete=xr,wr.prototype.get=jr,wr.prototype.has=Ar,wr.prototype.set=kr;var ys=Pu(ue),ds=Pu(oe,!0),bs=qu(),ws=qu(!0),ms=os?function(n,t){return os.set(n,t),n}:La,xs=Tl?function(n,t){return Tl(n,"toString",{configurable:!0,enumerable:!1,value:Sa(t),
writable:!0})}:La,js=uu,As=$l||function(n){return re.clearTimeout(n)},ks=es&&1/P(new es([,-0]))[1]==Sn?function(n){return new es(n)}:Da,Os=os?function(n){return os.get(n)}:Da,Is=Pl?function(n){return null==n?[]:(n=ll(n),i(Pl(n),function(t){return Wl.call(n,t)}))}:Pa,Rs=Pl?function(n){for(var t=[];n;)a(t,Is(n)),n=El(n);return t}:Pa,zs=we;(ns&&zs(new ns(new ArrayBuffer(1)))!=ct||ts&&zs(new ts)!=Gn||rs&&zs(rs.resolve())!=Qn||es&&zs(new es)!=tt||us&&zs(new us)!=it)&&(zs=function(n){var t=we(n),r=t==Yn?n.constructor:X,e=r?to(r):"";
if(e)switch(e){case cs:return ct;case as:return Gn;case ls:return Qn;case ss:return tt;case hs:return it}return t});var Es=yl?uc:qa,Ss=Qi(ms),Ws=Ml||function(n,t){return re.setTimeout(n,t)},Ls=Qi(xs),Cs=Pi(function(n){var t=[];return 46===n.charCodeAt(0)&&t.push(""),n.replace(Et,function(n,r,e,u){t.push(e?u.replace(Mt,"$1"):r||n)}),t}),Us=uu(function(n,t){return Jf(n)?Hr(n,ee(t,1,Jf,!0)):[]}),Bs=uu(function(n,t){var r=jo(t);return Jf(r)&&(r=X),Jf(n)?Hr(n,ee(t,1,Jf,!0),mi(r,2)):[]}),Ts=uu(function(n,t){
var r=jo(t);return Jf(r)&&(r=X),Jf(n)?Hr(n,ee(t,1,Jf,!0),X,r):[]}),$s=uu(function(n){var t=c(n,ju);return t.length&&t[0]===n[0]?ke(t):[]}),Ds=uu(function(n){var t=jo(n),r=c(n,ju);return t===jo(r)?t=X:r.pop(),r.length&&r[0]===n[0]?ke(r,mi(t,2)):[]}),Ms=uu(function(n){var t=jo(n),r=c(n,ju);return t="function"==typeof t?t:X,t&&r.pop(),r.length&&r[0]===n[0]?ke(r,X,t):[]}),Fs=uu(Oo),Ns=gi(function(n,t){var r=null==n?0:n.length,e=Tr(n,t);return nu(n,c(t,function(n){return Ci(n,r)?+n:n}).sort(Lu)),e}),Ps=uu(function(n){
return gu(ee(n,1,Jf,!0))}),qs=uu(function(n){var t=jo(n);return Jf(t)&&(t=X),gu(ee(n,1,Jf,!0),mi(t,2))}),Zs=uu(function(n){var t=jo(n);return t="function"==typeof t?t:X,gu(ee(n,1,Jf,!0),X,t)}),Ks=uu(function(n,t){return Jf(n)?Hr(n,t):[]}),Vs=uu(function(n){return mu(i(n,Jf))}),Gs=uu(function(n){var t=jo(n);return Jf(t)&&(t=X),mu(i(n,Jf),mi(t,2))}),Hs=uu(function(n){var t=jo(n);return t="function"==typeof t?t:X,mu(i(n,Jf),X,t)}),Js=uu(Go),Ys=uu(function(n){var t=n.length,r=t>1?n[t-1]:X;return r="function"==typeof r?(n.pop(),
r):X,Ho(n,r)}),Qs=gi(function(n){var t=n.length,r=t?n[0]:0,e=this.__wrapped__,u=function(t){return Tr(t,n)};return!(t>1||this.__actions__.length)&&e instanceof Ct&&Ci(r)?(e=e.slice(r,+r+(t?1:0)),e.__actions__.push({func:nf,args:[u],thisArg:X}),new Y(e,this.__chain__).thru(function(n){return t&&!n.length&&n.push(X),n})):this.thru(u)}),Xs=Fu(function(n,t,r){bl.call(n,r)?++n[r]:Br(n,r,1)}),nh=Ju(ho),th=Ju(po),rh=Fu(function(n,t,r){bl.call(n,r)?n[r].push(t):Br(n,r,[t])}),eh=uu(function(t,r,e){var u=-1,i="function"==typeof r,o=Hf(t)?il(t.length):[];
return ys(t,function(t){o[++u]=i?n(r,t,e):Ie(t,r,e)}),o}),uh=Fu(function(n,t,r){Br(n,r,t)}),ih=Fu(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),oh=uu(function(n,t){if(null==n)return[];var r=t.length;return r>1&&Ui(n,t[0],t[1])?t=[]:r>2&&Ui(t[0],t[1],t[2])&&(t=[t[0]]),He(n,ee(t,1),[])}),fh=Dl||function(){return re.Date.now()},ch=uu(function(n,t,r){var e=_n;if(r.length){var u=N(r,wi(ch));e|=bn}return ai(n,e,t,r,u)}),ah=uu(function(n,t,r){var e=_n|vn;if(r.length){var u=N(r,wi(ah));e|=bn;
}return ai(t,e,n,r,u)}),lh=uu(function(n,t){return Gr(n,1,t)}),sh=uu(function(n,t,r){return Gr(n,Ic(t)||0,r)});Cf.Cache=sr;var hh=js(function(t,r){r=1==r.length&&bh(r[0])?c(r[0],z(mi())):c(ee(r,1),z(mi()));var e=r.length;return uu(function(u){for(var i=-1,o=Hl(u.length,e);++i<o;)u[i]=r[i].call(this,u[i]);return n(t,this,u)})}),ph=uu(function(n,t){return ai(n,bn,X,t,N(t,wi(ph)))}),_h=uu(function(n,t){return ai(n,wn,X,t,N(t,wi(_h)))}),vh=gi(function(n,t){return ai(n,xn,X,X,X,t)}),gh=ii(me),yh=ii(function(n,t){
return n>=t}),dh=Re(function(){return arguments}())?Re:function(n){return cc(n)&&bl.call(n,"callee")&&!Wl.call(n,"callee")},bh=il.isArray,wh=ce?z(ce):ze,mh=ql||qa,xh=ae?z(ae):Ee,jh=le?z(le):Le,Ah=se?z(se):Be,kh=he?z(he):Te,Oh=pe?z(pe):$e,Ih=ii(Ne),Rh=ii(function(n,t){return n<=t}),zh=Nu(function(n,t){if(Mi(t)||Hf(t))return $u(t,Pc(t),n),X;for(var r in t)bl.call(t,r)&&Sr(n,r,t[r])}),Eh=Nu(function(n,t){$u(t,qc(t),n)}),Sh=Nu(function(n,t,r,e){$u(t,qc(t),n,e)}),Wh=Nu(function(n,t,r,e){$u(t,Pc(t),n,e);
}),Lh=gi(Tr),Ch=uu(function(n,t){n=ll(n);var r=-1,e=t.length,u=e>2?t[2]:X;for(u&&Ui(t[0],t[1],u)&&(e=1);++r<e;)for(var i=t[r],o=qc(i),f=-1,c=o.length;++f<c;){var a=o[f],l=n[a];(l===X||Gf(l,gl[a])&&!bl.call(n,a))&&(n[a]=i[a])}return n}),Uh=uu(function(t){return t.push(X,si),n(Mh,X,t)}),Bh=Xu(function(n,t,r){null!=t&&"function"!=typeof t.toString&&(t=xl.call(t)),n[t]=r},Sa(La)),Th=Xu(function(n,t,r){null!=t&&"function"!=typeof t.toString&&(t=xl.call(t)),bl.call(n,t)?n[t].push(r):n[t]=[r]},mi),$h=uu(Ie),Dh=Nu(function(n,t,r){
Ke(n,t,r)}),Mh=Nu(function(n,t,r,e){Ke(n,t,r,e)}),Fh=gi(function(n,t){var r={};if(null==n)return r;var e=!1;t=c(t,function(t){return t=ku(t,n),e||(e=t.length>1),t}),$u(n,di(n),r),e&&(r=Fr(r,an|ln|sn,hi));for(var u=t.length;u--;)yu(r,t[u]);return r}),Nh=gi(function(n,t){return null==n?{}:Je(n,t)}),Ph=ci(Pc),qh=ci(qc),Zh=Vu(function(n,t,r){return t=t.toLowerCase(),n+(r?fa(t):t)}),Kh=Vu(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Vh=Vu(function(n,t,r){return n+(r?" ":"")+t.toLowerCase()}),Gh=Ku("toLowerCase"),Hh=Vu(function(n,t,r){
return n+(r?"_":"")+t.toLowerCase()}),Jh=Vu(function(n,t,r){return n+(r?" ":"")+Qh(t)}),Yh=Vu(function(n,t,r){return n+(r?" ":"")+t.toUpperCase()}),Qh=Ku("toUpperCase"),Xh=uu(function(t,r){try{return n(t,X,r)}catch(n){return rc(n)?n:new fl(n)}}),np=gi(function(n,t){return r(t,function(t){t=no(t),Br(n,t,ch(n[t],n))}),n}),tp=Yu(),rp=Yu(!0),ep=uu(function(n,t){return function(r){return Ie(r,n,t)}}),up=uu(function(n,t){return function(r){return Ie(n,r,t)}}),ip=ti(c),op=ti(u),fp=ti(h),cp=ui(),ap=ui(!0),lp=ni(function(n,t){
return n+t},0),sp=fi("ceil"),hp=ni(function(n,t){return n/t},1),pp=fi("floor"),_p=ni(function(n,t){return n*t},1),vp=fi("round"),gp=ni(function(n,t){return n-t},0);return Z.after=If,Z.ary=Rf,Z.assign=zh,Z.assignIn=Eh,Z.assignInWith=Sh,Z.assignWith=Wh,Z.at=Lh,Z.before=zf,Z.bind=ch,Z.bindAll=np,Z.bindKey=ah,Z.castArray=Nf,Z.chain=Qo,Z.chunk=uo,Z.compact=io,Z.concat=oo,Z.cond=za,Z.conforms=Ea,Z.constant=Sa,Z.countBy=Xs,Z.create=Sc,Z.curry=Ef,Z.curryRight=Sf,Z.debounce=Wf,Z.defaults=Ch,Z.defaultsDeep=Uh,
Z.defer=lh,Z.delay=sh,Z.difference=Us,Z.differenceBy=Bs,Z.differenceWith=Ts,Z.drop=fo,Z.dropRight=co,Z.dropRightWhile=ao,Z.dropWhile=lo,Z.fill=so,Z.filter=lf,Z.flatMap=sf,Z.flatMapDeep=hf,Z.flatMapDepth=pf,Z.flatten=_o,Z.flattenDeep=vo,Z.flattenDepth=go,Z.flip=Lf,Z.flow=tp,Z.flowRight=rp,Z.fromPairs=yo,Z.functions=$c,Z.functionsIn=Dc,Z.groupBy=rh,Z.initial=mo,Z.intersection=$s,Z.intersectionBy=Ds,Z.intersectionWith=Ms,Z.invert=Bh,Z.invertBy=Th,Z.invokeMap=eh,Z.iteratee=Ca,Z.keyBy=uh,Z.keys=Pc,Z.keysIn=qc,
Z.map=yf,Z.mapKeys=Zc,Z.mapValues=Kc,Z.matches=Ua,Z.matchesProperty=Ba,Z.memoize=Cf,Z.merge=Dh,Z.mergeWith=Mh,Z.method=ep,Z.methodOf=up,Z.mixin=Ta,Z.negate=Uf,Z.nthArg=Ma,Z.omit=Fh,Z.omitBy=Vc,Z.once=Bf,Z.orderBy=df,Z.over=ip,Z.overArgs=hh,Z.overEvery=op,Z.overSome=fp,Z.partial=ph,Z.partialRight=_h,Z.partition=ih,Z.pick=Nh,Z.pickBy=Gc,Z.property=Fa,Z.propertyOf=Na,Z.pull=Fs,Z.pullAll=Oo,Z.pullAllBy=Io,Z.pullAllWith=Ro,Z.pullAt=Ns,Z.range=cp,Z.rangeRight=ap,Z.rearg=vh,Z.reject=mf,Z.remove=zo,Z.rest=Tf,
Z.reverse=Eo,Z.sampleSize=jf,Z.set=Jc,Z.setWith=Yc,Z.shuffle=Af,Z.slice=So,Z.sortBy=oh,Z.sortedUniq=$o,Z.sortedUniqBy=Do,Z.split=da,Z.spread=$f,Z.tail=Mo,Z.take=Fo,Z.takeRight=No,Z.takeRightWhile=Po,Z.takeWhile=qo,Z.tap=Xo,Z.throttle=Df,Z.thru=nf,Z.toArray=jc,Z.toPairs=Ph,Z.toPairsIn=qh,Z.toPath=Ha,Z.toPlainObject=Rc,Z.transform=Qc,Z.unary=Mf,Z.union=Ps,Z.unionBy=qs,Z.unionWith=Zs,Z.uniq=Zo,Z.uniqBy=Ko,Z.uniqWith=Vo,Z.unset=Xc,Z.unzip=Go,Z.unzipWith=Ho,Z.update=na,Z.updateWith=ta,Z.values=ra,Z.valuesIn=ea,
Z.without=Ks,Z.words=Ra,Z.wrap=Ff,Z.xor=Vs,Z.xorBy=Gs,Z.xorWith=Hs,Z.zip=Js,Z.zipObject=Jo,Z.zipObjectDeep=Yo,Z.zipWith=Ys,Z.entries=Ph,Z.entriesIn=qh,Z.extend=Eh,Z.extendWith=Sh,Ta(Z,Z),Z.add=lp,Z.attempt=Xh,Z.camelCase=Zh,Z.capitalize=fa,Z.ceil=sp,Z.clamp=ua,Z.clone=Pf,Z.cloneDeep=Zf,Z.cloneDeepWith=Kf,Z.cloneWith=qf,Z.conformsTo=Vf,Z.deburr=ca,Z.defaultTo=Wa,Z.divide=hp,Z.endsWith=aa,Z.eq=Gf,Z.escape=la,Z.escapeRegExp=sa,Z.every=af,Z.find=nh,Z.findIndex=ho,Z.findKey=Wc,Z.findLast=th,Z.findLastIndex=po,
Z.findLastKey=Lc,Z.floor=pp,Z.forEach=_f,Z.forEachRight=vf,Z.forIn=Cc,Z.forInRight=Uc,Z.forOwn=Bc,Z.forOwnRight=Tc,Z.get=Mc,Z.gt=gh,Z.gte=yh,Z.has=Fc,Z.hasIn=Nc,Z.head=bo,Z.identity=La,Z.includes=gf,Z.indexOf=wo,Z.inRange=ia,Z.invoke=$h,Z.isArguments=dh,Z.isArray=bh,Z.isArrayBuffer=wh,Z.isArrayLike=Hf,Z.isArrayLikeObject=Jf,Z.isBoolean=Yf,Z.isBuffer=mh,Z.isDate=xh,Z.isElement=Qf,Z.isEmpty=Xf,Z.isEqual=nc,Z.isEqualWith=tc,Z.isError=rc,Z.isFinite=ec,Z.isFunction=uc,Z.isInteger=ic,Z.isLength=oc,Z.isMap=jh,
Z.isMatch=ac,Z.isMatchWith=lc,Z.isNaN=sc,Z.isNative=hc,Z.isNil=_c,Z.isNull=pc,Z.isNumber=vc,Z.isObject=fc,Z.isObjectLike=cc,Z.isPlainObject=gc,Z.isRegExp=Ah,Z.isSafeInteger=yc,Z.isSet=kh,Z.isString=dc,Z.isSymbol=bc,Z.isTypedArray=Oh,Z.isUndefined=wc,Z.isWeakMap=mc,Z.isWeakSet=xc,Z.join=xo,Z.kebabCase=Kh,Z.last=jo,Z.lastIndexOf=Ao,Z.lowerCase=Vh,Z.lowerFirst=Gh,Z.lt=Ih,Z.lte=Rh,Z.max=Ya,Z.maxBy=Qa,Z.mean=Xa,Z.meanBy=nl,Z.min=tl,Z.minBy=rl,Z.stubArray=Pa,Z.stubFalse=qa,Z.stubObject=Za,Z.stubString=Ka,
Z.stubTrue=Va,Z.multiply=_p,Z.nth=ko,Z.noConflict=$a,Z.noop=Da,Z.now=fh,Z.pad=ha,Z.padEnd=pa,Z.padStart=_a,Z.parseInt=va,Z.random=oa,Z.reduce=bf,Z.reduceRight=wf,Z.repeat=ga,Z.replace=ya,Z.result=Hc,Z.round=vp,Z.runInContext=p,Z.sample=xf,Z.size=kf,Z.snakeCase=Hh,Z.some=Of,Z.sortedIndex=Wo,Z.sortedIndexBy=Lo,Z.sortedIndexOf=Co,Z.sortedLastIndex=Uo,Z.sortedLastIndexBy=Bo,Z.sortedLastIndexOf=To,Z.startCase=Jh,Z.startsWith=ba,Z.subtract=gp,Z.sum=el,Z.sumBy=ul,Z.template=wa,Z.times=Ga,Z.toFinite=Ac,Z.toInteger=kc,
Z.toLength=Oc,Z.toLower=ma,Z.toNumber=Ic,Z.toSafeInteger=zc,Z.toString=Ec,Z.toUpper=xa,Z.trim=ja,Z.trimEnd=Aa,Z.trimStart=ka,Z.truncate=Oa,Z.unescape=Ia,Z.uniqueId=Ja,Z.upperCase=Yh,Z.upperFirst=Qh,Z.each=_f,Z.eachRight=vf,Z.first=bo,Ta(Z,function(){var n={};return ue(Z,function(t,r){bl.call(Z.prototype,r)||(n[r]=t)}),n}(),{chain:!1}),Z.VERSION=nn,r(["bind","bindKey","curry","curryRight","partial","partialRight"],function(n){Z[n].placeholder=Z}),r(["drop","take"],function(n,t){Ct.prototype[n]=function(r){
r=r===X?1:Gl(kc(r),0);var e=this.__filtered__&&!t?new Ct(this):this.clone();return e.__filtered__?e.__takeCount__=Hl(r,e.__takeCount__):e.__views__.push({size:Hl(r,Un),type:n+(e.__dir__<0?"Right":"")}),e},Ct.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),r(["filter","map","takeWhile"],function(n,t){var r=t+1,e=r==Rn||r==En;Ct.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:mi(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),r(["head","last"],function(n,t){
var r="take"+(t?"Right":"");Ct.prototype[n]=function(){return this[r](1).value()[0]}}),r(["initial","tail"],function(n,t){var r="drop"+(t?"":"Right");Ct.prototype[n]=function(){return this.__filtered__?new Ct(this):this[r](1)}}),Ct.prototype.compact=function(){return this.filter(La)},Ct.prototype.find=function(n){return this.filter(n).head()},Ct.prototype.findLast=function(n){return this.reverse().find(n)},Ct.prototype.invokeMap=uu(function(n,t){return"function"==typeof n?new Ct(this):this.map(function(r){
return Ie(r,n,t)})}),Ct.prototype.reject=function(n){return this.filter(Uf(mi(n)))},Ct.prototype.slice=function(n,t){n=kc(n);var r=this;return r.__filtered__&&(n>0||t<0)?new Ct(r):(n<0?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==X&&(t=kc(t),r=t<0?r.dropRight(-t):r.take(t-n)),r)},Ct.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},Ct.prototype.toArray=function(){return this.take(Un)},ue(Ct.prototype,function(n,t){var r=/^(?:filter|find|map|reject)|While$/.test(t),e=/^(?:head|last)$/.test(t),u=Z[e?"take"+("last"==t?"Right":""):t],i=e||/^find/.test(t);
u&&(Z.prototype[t]=function(){var t=this.__wrapped__,o=e?[1]:arguments,f=t instanceof Ct,c=o[0],l=f||bh(t),s=function(n){var t=u.apply(Z,a([n],o));return e&&h?t[0]:t};l&&r&&"function"==typeof c&&1!=c.length&&(f=l=!1);var h=this.__chain__,p=!!this.__actions__.length,_=i&&!h,v=f&&!p;if(!i&&l){t=v?t:new Ct(this);var g=n.apply(t,o);return g.__actions__.push({func:nf,args:[s],thisArg:X}),new Y(g,h)}return _&&v?n.apply(this,o):(g=this.thru(s),_?e?g.value()[0]:g.value():g)})}),r(["pop","push","shift","sort","splice","unshift"],function(n){
var t=_l[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);Z.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(bh(u)?u:[],n)}return this[r](function(r){return t.apply(bh(r)?r:[],n)})}}),ue(Ct.prototype,function(n,t){var r=Z[t];if(r){var e=r.name+"";bl.call(fs,e)||(fs[e]=[]),fs[e].push({name:t,func:r})}}),fs[Qu(X,vn).name]=[{name:"wrapper",func:X}],Ct.prototype.clone=$t,Ct.prototype.reverse=Yt,Ct.prototype.value=Qt,Z.prototype.at=Qs,
Z.prototype.chain=tf,Z.prototype.commit=rf,Z.prototype.next=ef,Z.prototype.plant=of,Z.prototype.reverse=ff,Z.prototype.toJSON=Z.prototype.valueOf=Z.prototype.value=cf,Z.prototype.first=Z.prototype.head,Ul&&(Z.prototype[Ul]=uf),Z},be=de();"function"==typeof define&&"object"==typeof define.amd&&define.amd?(re._=be,define(function(){return be})):ue?((ue.exports=be)._=be,ee._=be):re._=be}).call(this);

View File

@@ -0,0 +1,428 @@
class PlanisphereWidget {
constructor(id, options) {
console.log("PlanisphereWidget " + id);
this.options = options;
this.container = document.getElementById(id);
this.bg_color = (options && options.bg_color) ? options.bg_color : '#27293D';
this.fore_color = (options && options.bg_color) ? options.bg_color : '#CCCCCC';
this.coor_color = (options && options.coor_color) ? options.coor_color : '#1B1B24';
this.border_color = (options && options.border_color) ? options.border_color : '#CCCCCC';
this.wave_color = (options && options.wave_color) ? options.wave_color : '#1D8CF8';
this.margin_left = (options && options.margin_left) ? options.margin_left : 0;
this.margin_top = (options && options.margin_top) ? options.margin_top : 0;
this.stage = new Konva.Stage({
container: id,
width: this.container.clientWidth || 500,
height: this.container.clientHeight || 500
});
this.layer_coor = new Konva.Layer();
this.layer_coor.hitGraphEnabled(false);
this.layer_wave = new Konva.Layer();
this.layer_wave.hitGraphEnabled(false);
this.stage.add(this.layer_coor);
this.stage.add(this.layer_wave);
this.canvas = document.createElement("canvas");
this.ctx_canvas = this.canvas.getContext("2d");
this.ctx_canvas.fillStyle = this.bg_color;
this.init();
this.initResize();
this.dAlpha = false;
this.d1 = null;
this.d2 = null;
this.d3 = null;
this.d4 = null;
}
init() {
if (this.container.clientHeight === 0) return;
this.stage.setWidth(this.container.clientWidth);
this.stage.setHeight(this.container.clientWidth);
this.area_x = this.margin_left;
this.area_y = this.margin_top;
this.area_width = this.stage.getWidth() - this.margin_left;
this.area_height = this.stage.getHeight() - this.margin_top;
this.drawCoor();
this.canvas.height = this.area_height;
this.canvas.width = this.area_width;
this.ctx_canvas.strokeStyle = this.wave_color;
this.ctx_canvas.lineWidth = 1;
this.pointer = new Konva.Circle({
x: 0,
y: 0,
radius: 1,
stroke: this.wave_color,
strokeWidth: 1
});
this.pointer.cache();
}
drawCoor() {
var layer = this.layer_coor;
layer.destroyChildren();
var len = this.area_width;
var rval = this.area_width / 2;
layer.add(new Konva.Rect({
x: this.area_x,
y: this.area_y,
width: this.area_width,
height: this.area_height,
stroke: this.border_color,
strokeWidth: 1
}));
layer.add(new Konva.Line({
points: [0, 0, this.area_width, this.area_height],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [this.area_width, 0, 0, this.area_height],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval, this.area_width, rval],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval / 2, this.area_width, rval / 2],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [0, rval / 2 * 3, this.area_width, rval / 2 * 3],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
///
layer.add(new Konva.Line({
points: [rval, 0, rval, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [rval / 2, 0, rval / 2, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Line({
points: [rval / 2 * 3, 0, rval / 2 * 3, len],
stroke: this.border_color,
strokeWidth: 1,
lineJoin: 'round',
dash: [1, 1]
}));
layer.add(new Konva.Circle({
x: rval,
y: rval,
radius: rval,
stroke: this.border_color,
strokeWidth: 1
}));
layer.add(new Konva.Circle({
x: rval,
y: rval,
radius: rval / 2,
stroke: this.border_color,
strokeWidth: 1
}));
layer.batchDraw();
}
setViewMode(vm) {
if (vm == 3) {//0=星座图3=余晖图。2021.03.31李伟添加;
this.dAlpha = true;
vm = 0;
} else {
this.dAlpha = false;
}
this.viewmodel = vm;
this.clear();
}
//星座图
feed(d, di, dq) {
if (!this.viewmodel || this.viewmodel == 0) {
this.drawWave_vector(d);
// this.drawWave_planis(d);
return;
}
}
//矢量图
feedVector(d) {
if (!this.viewmodel || this.viewmodel == 0) {
this.drawWave_vector(d);
return;
}
}
//眼图
feedIQ(di, dq) {
if (this.viewmodel == 1 && di != null) {
this.drawIQ(di);
}
else if (this.viewmodel == 2 && dq != null) {
this.drawIQ(dq);
}
}
drawIQ(IQBuff) {
this.clear();
for (var j = 0; j < IQBuff.length; j++) {
this.drawWave_eye(IQBuff[j]);
}
}
clear() {
this.imgData = null;
this.drawWave_planis([]);
this.drawWave_vector([]);
}
drawWave_eye(d) {
// console.log("in drawWave_eye" + d.length);
var layer = this.layer_wave;
var width = this.area_width;
var height = this.area_height;
var rwidth = width / 2;
var rHeight = height / 2;
var ctx_canvas = layer.getContext();
ctx_canvas.strokeStyle = this.wave_color;
ctx_canvas.lineWidth = 1;
ctx_canvas.beginPath();
for (var n = 0; n < d.length; n++) {
//var x = Math.floor(width*(1-1/1.4)/2+ n * width / d.length / 1.4);
var x = Math.floor( n * width / d.length );
var y = Math.floor(rHeight + d[n] * rHeight / 1.4);
if (n == 0) {
ctx_canvas.moveTo(x, y);
}
else {
ctx_canvas.lineTo(x, y);
}
}
ctx_canvas.stroke();
}
drawWave_planis(d) {
//console.log("in plan" + d.length);
var layer = this.layer_wave;
var width = this.getDrawWHByPixelRatio(this.area_width) ;
var height = this.getDrawWHByPixelRatio(this.area_height);
var rwidth = width / 2;
var rHeight = height / 2;
//不显示的时候不报错
if (!width || !height||width == 0 || height == 0) {
return;
}
var context = layer.getContext();
context.clearRect(0, 0, width, height);
var imgData = context.createImageData(width, height);
var dlen = imgData.data.length;
//2021.03.31 李伟增加余晖效果
if (this.dAlpha && d) {
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d4, 32);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d3, 32);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d2, 64);
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, this.d1, 128);
this.d4 = this.d3;
this.d3 = this.d2;
this.d2 = this.d1;
this.d1 = d;
}
this.drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, 255);
/*
for (var n = 0; n < d.length; n++) {
//console.log(rwidth+d[n][0] * rwidth);
var x = Math.floor(rwidth + d[n][0] * rwidth/1.4);
var y = Math.floor(rHeight+ d[n][1] * rHeight/1.4);
var p = ( y * width + x )* 4;
this.setWaveColor(imgData, p, dlen,width*4);
}
*/
context.putImageData(imgData, 0, 0);
}
drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, a) {
if (!d)
return;
for (var n = 0; n < d.length; n++) {
//console.log(rwidth+d[n][0] * rwidth);
var x = Math.floor(rwidth + d[n][0] * rwidth / 1.4);
var y = Math.floor(rHeight + d[n][1] * rHeight / 1.4);
var p = (y * width + x) * 4;
this.setWaveColor(imgData, p, dlen, width * 4, a);
}
}
drawWave_vector(d) {
var maxLine = 100;
var dlen = Math.min(maxLine, d.length);
var layer = this.layer_wave;
var width = this.area_width;
var height = this.area_height;
var rwidth = width / 2;
var rHeight = height / 2;
//不显示的时候不报错
if (!width || !height || width == 0 || height == 0) {
return;
}
var context = layer.getContext();
context.clearRect(0, 0, width, height);
var ctx_canvas = layer.getContext();
ctx_canvas.strokeStyle = this.wave_color;
ctx_canvas.lineWidth = 1;
ctx_canvas.beginPath();
for (var n = 0; n < dlen; n++) {
var x = Math.floor(rwidth + d[n][0] * rwidth / 1.2);
var y = Math.floor(rHeight + d[n][1] * rHeight / 1.2);
if (n == 0) {
ctx_canvas.moveTo(x, y);
}
else {
ctx_canvas.lineTo(x, y);
}
}
ctx_canvas.stroke();
}
/*像素单位转css单位,该函数返回当前浏览器缩放比例所影响的 x y width height 的绘图位置
仅适用putImageData getImageData等函数*/
getDrawWHByPixelRatio(xory) {
if (xory == 0) return 0;
if (!xory) return xory;
var ret = Math.ceil(window.devicePixelRatio * xory);
//console.log(`xory=${xory} ratio=${window.devicePixelRatio} ret=${ret}`);
return ret;
}
setWaveColor_thin(imgData, p, dlen, rowlen) {
var dp = p + rowlen;
if (p < 0) return;
if (dp + 11 >= dlen) return;
imgData.data[p] = 33;
imgData.data[p + 1] = 129;
imgData.data[p + 2] = 247;
imgData.data[p + 3] = 255;
}
setWaveColor(imgData, p, dlen, rowlen, a = 255) {//2021.03.31 李伟增加余晖效果 a=alpha
var dp = p + rowlen;
if (p < 0) return;
if (dp + 11 >= dlen) return;
var r = 33;
var g = 129;
var b = 247;
if (a != 255) {
r = 0;
g = 255;
b = 0;
}
//console.log(a);
imgData.data[p] = r;
imgData.data[p + 1] = g;
imgData.data[p + 2] = b;
imgData.data[p + 3] = a;//255;//2021.03.31 李伟增加余晖效果
imgData.data[p + 4] = r;
imgData.data[p + 5] = g;
imgData.data[p + 6] = b;
imgData.data[p + 7] = a;//255;
imgData.data[p + 8] = r;
imgData.data[p + 9] = g;
imgData.data[p + 10] = b;
imgData.data[p + 11] = a;//255;
imgData.data[dp] = r;
imgData.data[dp + 1] = g;
imgData.data[dp + 2] = b;
imgData.data[dp + 3] = a;//255;
imgData.data[dp + 4] = r;
imgData.data[dp + 5] = g;
imgData.data[dp + 6] = b;
imgData.data[dp + 7] = a;//255;
imgData.data[dp + 8] = r;
imgData.data[dp + 9] = g;
imgData.data[dp + 10] = b;
imgData.data[dp + 11] = a;//255;
}
destroy() {
this.imgData = null;
this.stage.destroy();
this.color_map = null;
}
dispose() {
console.log("dispose wfmin");
this.destroy();
}
initResize() {
var _self = this;
$(this.container).resize(function () {
var target = this;
if (target.resizeFlag) {
clearTimeout(target.resizeFlag);
}
target.resizeFlag = setTimeout(function () {
_self.init();
target.resizeFlag = null;
}, 100);
});
}
}

View File

@@ -0,0 +1,198 @@
# PlanisphereWidget 类文档
## 概述
`PlanisphereWidget` 是一个基于 JavaScript 的类,用于在网页中创建和展示星座图、矢量图和眼图等可视化内容。该类依赖于 Konva.js 库,通过 canvas 绘制图形,提供了一种灵活的方式来呈现复杂的波形数据和坐标系图形。它适用于需要动态展示信号处理结果或科学数据的场景。
Konva.js 是一个强大的 2D 绘图库,`PlanisphereWidget` 利用其舞台Stage和层Layer机制来管理绘图元素确保图形的高效渲染和交互性。
## 构造函数
```javascript
constructor(id, options)
```
- **参数**
- `id` (String): 指定容器的 ID`PlanisphereWidget` 将在这个容器内创建 Konva 舞台并绘制图形。
- `options` (Object): 可选的配置对象,用于自定义绘图样式和布局。
- `bg_color` (String): 背景颜色,默认值为 '#27293D'。
- `fore_color` (String): 前景颜色,默认值为 '#CCCCCC'。
- `coor_color` (String): 坐标系颜色,默认值为 '#1B1B24'。
- `border_color` (String): 边框颜色,默认值为 '#CCCCCC'。
- `wave_color` (String): 波形颜色,默认值为 '#1D8CF8'。
- `margin_left` (Number): 左边距,默认值为 0。
- `margin_top` (Number): 上边距,默认值为 0。
- **功能**:初始化 `PlanisphereWidget` 实例,设置容器、颜色配置和 Konva 舞台。构造函数会创建两个 Konva 层(`layer_coor` 用于坐标系,`layer_wave` 用于波形图),并调用 `init()``initResize()` 方法进行进一步的初始化。
## 属性
- `container`: 绘图所在的 DOM 元素,通过构造函数中的 `id` 参数获取。
- `stage`: Konva 舞台对象,用于管理绘图层和渲染。
- `layer_coor`: Konva 层对象,用于绘制坐标系,不启用命中检测(`hitGraphEnabled(false)`)。
- `layer_wave`: Konva 层对象,用于绘制波形图,不启用命中检测。
- `bg_color`: 背景颜色,用于填充绘图区域。
- `fore_color`: 前景颜色,目前与 `bg_color` 绑定,实际用途待定。
- `coor_color`: 坐标系颜色,目前未直接使用。
- `border_color`: 边框和坐标线颜色。
- `wave_color`: 波形和指针颜色。
- `margin_left`: 左边距,定义绘图区域的左边界。
- `margin_top`: 上边距,定义绘图区域的上边界。
- `area_x`: 绘图区域的 X 坐标,等于 `margin_left`
- `area_y`: 绘图区域的 Y 坐标,等于 `margin_top`
- `area_width`: 绘图区域的宽度,等于舞台宽度减去 `margin_left`
- `area_height`: 绘图区域的高度,等于舞台高度减去 `margin_top`
- `canvas`: 用于离屏绘制的临时 canvas 元素。
- `ctx_canvas`: 临时 canvas 的 2D 上下文,用于绘制波形数据。
- `pointer`: Konva 圆形对象,用于表示波形中的指针。
- `viewmodel`: 当前视图模式0 表示星座图1 和 2 表示眼图的不同模式。
- `dAlpha`: 布尔值,是否启用余晖效果。
- `d1`, `d2`, `d3`, `d4`: 用于存储历史数据以实现余晖效果。
## 方法
### `init()`
- **功能**:初始化舞台和绘图区域。如果容器高度为 0则不执行初始化。设置舞台的宽度和高度计算绘图区域的尺寸并调用 `drawCoor()` 绘制坐标系。初始化离屏 canvas 和指针对象。
- **参数**:无
- **返回值**:无
### `drawCoor()`
- **功能**:绘制坐标系,包括边框、网格线和圆形参考线。坐标系包括水平和垂直的虚线网格、对角线以及两个同心圆,中心位于绘图区域的中心。
- **参数**:无
- **返回值**:无
### `setViewMode(vm)`
- **功能**:设置视图模式,决定绘制星座图还是眼图。如果 `vm` 为 3则启用余晖效果并将模式设为 0星座图。调用 `clear()` 方法清除当前绘图内容。
- **参数**
- `vm` (Number): 视图模式0 表示星座图1 和 2 表示眼图3 表示带余晖效果的星座图。
- **返回值**:无
### `feed(d, di, dq)`
- **功能**:处理星座图数据。如果当前视图模式为 0星座图则调用 `drawWave_vector(d)` 绘制矢量图。
- **参数**
- `d` (Array): 星座图数据。
- `di` (Array): 眼图 I 通道数据,未使用。
- `dq` (Array): 眼图 Q 通道数据,未使用。
- **返回值**:无
### `feedVector(d)`
- **功能**:处理矢量图数据。如果当前视图模式为 0则调用 `drawWave_vector(d)` 绘制矢量图。
- **参数**
- `d` (Array): 矢量图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `feedIQ(di, dq)`
- **功能**:处理眼图数据。如果视图模式为 1则绘制 `di` 数据;如果为 2则绘制 `dq` 数据。调用 `drawIQ()` 方法进行实际绘制。
- **参数**
- `di` (Array): 眼图 I 通道数据。
- `dq` (Array): 眼图 Q 通道数据。
- **返回值**:无
### `drawIQ(IQBuff)`
- **功能**:绘制眼图数据。清除当前绘图内容,然后遍历 `IQBuff` 数组,调用 `drawWave_eye()` 绘制每个数据段。
- **参数**
- `IQBuff` (Array): 眼图数据数组,每个元素是一段波形数据。
- **返回值**:无
### `clear()`
- **功能**:清除当前绘图内容,重置图像数据,并调用 `drawWave_planis([])``drawWave_vector([])` 清空星座图和矢量图。
- **参数**:无
- **返回值**:无
### `drawWave_eye(d)`
- **功能**:绘制眼图波形。使用 Konva 层的上下文绘制折线图,横轴为数据索引,纵轴为数据值,缩放至绘图区域的一半高度。
- **参数**
- `d` (Array): 波形数据数组,每个元素是一个数值。
- **返回值**:无
### `drawWave_planis(d)`
- **功能**:绘制星座图。清除当前绘图内容,创建图像数据对象。如果启用余晖效果(`dAlpha` 为 true则使用历史数据`d1``d4`)绘制不同透明度的点,实现余晖效果。最后绘制当前数据 `d`
- **参数**
- `d` (Array): 星座图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `drawWave_vector(d)`
- **功能**:绘制矢量图。清除当前绘图内容,使用 Konva 层的上下文绘制折线图,连接数据点,最多处理前 100 个点以提高性能。
- **参数**
- `d` (Array): 矢量图数据,二维数组,每个元素包含 x 和 y 坐标。
- **返回值**:无
### `drawWave_plainsD(rwidth, rHeight, width, imgData, dlen, d, a)`
- **功能**:辅助方法,用于绘制星座图中的点,支持透明度设置。计算每个数据点的坐标位置,并设置图像数据中的颜色和透明度。
- **参数**
- `rwidth` (Number): 绘图区域宽度的一半。
- `rHeight` (Number): 绘图区域高度的一半。
- `width` (Number): 图像数据的宽度。
- `imgData` (ImageData): 图像数据对象。
- `dlen` (Number): 图像数据的长度。
- `d` (Array): 星座图数据,二维数组。
- `a` (Number): 透明度值,范围 0-255。
- **返回值**:无
### `getDrawWHByPixelRatio(xory)`
- **功能**:根据设备像素比计算绘图尺寸,用于处理高分辨率显示器上的绘图精度问题。
- **参数**
- `xory` (Number): 输入的宽度或高度值。
- **返回值**Number - 调整后的尺寸值。
### `setWaveColor(imgData, p, dlen, rowlen, a = 255)`
- **功能**:设置图像数据中指定位置的颜色和透明度,用于绘制星座图中的点。如果透明度不是 255则使用绿色否则使用蓝色。
- **参数**
- `imgData` (ImageData): 图像数据对象。
- `p` (Number): 像素位置索引。
- `dlen` (Number): 图像数据长度。
- `rowlen` (Number): 每行的字节数。
- `a` (Number): 透明度值,默认 255。
- **返回值**:无
### `destroy()`
- **功能**:销毁实例,清除图像数据并销毁 Konva 舞台。
- **参数**:无
- **返回值**:无
### `dispose()`
- **功能**:释放资源,调用 `destroy()` 方法。
- **参数**:无
- **返回值**:无
### `initResize()`
- **功能**:初始化窗口大小调整事件监听。使用 jQuery 的 `resize` 事件监听容器大小变化,延迟 100 毫秒后调用 `init()` 方法重新初始化绘图区域。
- **参数**:无
- **返回值**:无
## 使用示例
以下是一个简单的代码示例,展示如何创建 `PlanisphereWidget` 实例并绘制星座图:
```javascript
// HTML 中需要有一个容器元素
<div id="planisphereContainer" style="width: 500px; height: 500px;"></div>
// JavaScript 代码
const widget = new PlanisphereWidget('planisphereContainer', {
bg_color: '#27293D',
wave_color: '#1D8CF8',
border_color: '#CCCCCC',
margin_left: 10,
margin_top: 10
});
// 设置视图模式为星座图
widget.setViewMode(0);
// 提供星座图数据,格式为二维数组 [[x1, y1], [x2, y2], ...]
const data = [
[0.5, 0.5],
[-0.5, 0.5],
[-0.5, -0.5],
[0.5, -0.5]
];
widget.feed(data);
// 启用余晖效果
widget.setViewMode(3);
widget.feed(data);
```
## 注意事项
- 确保容器元素(通过 `id` 指定)在 DOM 中存在,并且具有明确的宽度和高度,否则初始化可能会失败。
- 数据格式必须符合方法的要求,例如 `feed()``feedVector()` 需要二维数组,`feedIQ()` 需要一维数组。
- 余晖效果会保存历史数据,可能会增加内存使用量,长时间使用后建议调用 `clear()` 清除历史数据。
- 绘图性能可能受数据量影响,`drawWave_vector()` 方法限制了最多绘制 100 个点以保证性能。
- 该类依赖于 Konva.js 和 jQuery 库,确保在页面中引入这些库。

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
# WaterfallWidget.js 文档
本文档为 `waterfallwidget.js` 文件提供详细说明,涵盖了 `waterfallwidget` 类的功能、用法和配置选项。该类用于创建频谱图和瀑布图可视化工具,广泛应用于信号处理和频谱分析领域。
## 概述
`waterfallwidget` 是一个功能强大的 JavaScript 类,用于在网页中绘制频谱图和瀑布图。它支持实时数据更新、频率缩放、用户交互、信号标注等多种功能,适用于无线电信号分析、频谱监控等场景。该类基于 Konva.js 库实现绘图功能,并提供了丰富的配置选项和事件处理机制。
## 初始化
要使用 `waterfallwidget`,需要通过构造函数创建一个实例,并传入容器 ID 和配置选项。以下是构造函数的定义:
```javascript
function waterfallwidget(id, options)
```
- **id**:字符串,指定容器的 DOM ID用于渲染瀑布图。
- **options**:对象,包含各种配置选项,用于自定义瀑布图的行为和外观。
### 配置选项
以下是常用的配置选项及其默认值:
- **min_db**:最小分贝值,默认为 -200。
- **max_db**:最大分贝值,默认为 0。
- **min_freq**:最小频率值,默认为 0。
- **max_freq**:最大频率值,默认为 40000。
- **showSpectrogramAvg**:是否显示频谱平均值,默认为 false。
- **showSpectrogramMin**:是否显示频谱最小值,默认为 false。
- **showSpectrogramMax**:是否显示频谱最大值,默认为 false。
- **showPeakMarker**:是否显示峰值标记,默认为 false。
- **allowRunMode**运行模式0 表示侦察模式1 表示实时模式,默认为 0。
- **disableZoom**:是否禁用缩放功能,默认为 false。
- **wfFreqPointLable**:是否启用频点标注,默认为 false。
- **bg_color**:背景颜色,默认为 '#37384B'。
- **fore_color**:前景颜色,默认为 '#CCCCFF'。
- **wave_color**:波形颜色,默认为 '#4BF3A7'。
更多配置选项请参见代码中的构造函数部分。
### 初始化示例
```javascript
var widget = new waterfallwidget('container', {
min_db: -150,
max_db: 0,
min_freq: 50000000,
max_freq: 100000000,
showPeakMarker: true,
allowRunMode: 1
});
widget.init();
```
## 主要方法
`waterfallwidget` 类提供了大量方法,用于数据处理、绘图和用户交互。以下是一些关键方法:
### addData(d, time)
添加新数据并更新瀑布图和频谱图。
- **d**:数组,包含信号强度数据。
- **time**:字符串,可选,数据的时间戳。
### drawWaterFall(bins)
绘制瀑布图的一行数据。
- **bins**:数组,包含信号强度数据。
### setFreqZoom(start, end)
设置频率缩放范围。
- **start**:数字,起始频率。
- **end**:数字,结束频率。
### changeDBRange(min_db, max_db, force)
更改分贝范围。
- **min_db**:数字,最小分贝值。
- **max_db**:数字,最大分贝值。
- **force**:布尔值,是否强制更新,默认为 false。
### drawAllSpectrogram(bins)
绘制完整的频谱图,包括峰值标记、最大/最小保持等。
- **bins**:数组,包含信号强度数据。
### setAdjCenter(freq, fireEvent)
设置调整中心频率。
- **freq**:数字,中心频率。
- **fireEvent**:布尔值,是否触发事件,默认为 false。
更多方法请参见代码中的详细实现。
## 属性
`waterfallwidget` 类包含大量属性,用于控制瀑布图的状态和外观。以下是一些重要属性:
- **min_db**:最小分贝值。
- **max_db**:最大分贝值。
- **freq_start**:当前显示的起始频率。
- **freq_end**:当前显示的结束频率。
- **showSpectrogramAvg**:是否显示频谱平均值。
- **showPeakMarker**:是否显示峰值标记。
- **adj_start**:调整起始频率。
- **adj_end**:调整结束频率。
- **bg_color**:背景颜色。
- **wave_color**:波形颜色。
## 事件处理
`waterfallwidget` 支持多种事件,允许开发者响应用户交互和状态变化。以下是一些常用事件:
- **zoom-change**:频率缩放变化时触发。
- **pointer-change**:指针位置变化时触发。
- **adj-change**:调整范围变化时触发。
- **label-change**:标签选择变化时触发。
- **context-click**:右键菜单点击时触发。
### 事件绑定示例
```javascript
widget.on('zoom-change', function(event) {
console.log('Zoom changed to:', event.start, event.end);
});
```
## 用户交互
`waterfallwidget` 支持丰富的用户交互功能,包括:
- **频率缩放**:通过鼠标滚轮或拖动选择区域进行缩放。
- **调整范围**:在实时模式下,可以拖动调整频率范围。
- **峰值标记**:显示信号峰值并标注频率和强度。
- **右键菜单**:提供上下文菜单,支持多种操作如侦察、数据采集等。
- **频点标注**:允许用户添加和编辑频率点标注。
## 绘图逻辑
`waterfallwidget` 将绘图区域分为频谱图和瀑布图两部分:
- **频谱图**:显示当前信号的强度分布,支持平均值、最大值、最小值等多种显示模式。
- **瀑布图**:随时间滚动显示历史信号数据,使用颜色映射表示信号强度。
绘图使用 Konva.js 库实现,通过多层 canvas 叠加实现复杂的视觉效果。
## 使用示例
以下是一个完整的示例,展示如何初始化和使用 `waterfallwidget`
```javascript
// 创建瀑布图实例
var widget = new waterfallwidget('waterfall-container', {
min_db: -150,
max_db: 0,
min_freq: 50000000,
max_freq: 100000000,
showPeakMarker: true,
showSpectrogramMax: true,
allowRunMode: 0
});
// 初始化
widget.init();
// 添加数据
setInterval(function() {
var data = Array.from({length: 2000}, () => Math.random() * (-50 + 150) - 150);
widget.addData(data, new Date().toLocaleTimeString());
}, 1000);
// 监听缩放事件
widget.on('zoom-change', function(event) {
console.log('Frequency range:', event.start, 'to', event.end);
});
```
## 注意事项
- **性能考虑**:对于大数据量和高频率更新,建议优化数据处理逻辑,避免频繁重绘。
- **浏览器兼容性**:依赖 Konva.js 库,确保在支持 HTML5 Canvas 的现代浏览器中使用。
- **配置选项**:初始化时需仔细配置频率和分贝范围,以适应具体的信号分析需求。
- **事件处理**:事件监听器应妥善管理,避免内存泄漏。
以上是 `waterfallwidget.js` 的完整文档,希望对您的使用和开发有所帮助。

View File

@@ -0,0 +1 @@
- [卫星星座图绘制原理](https://juejin.cn/post/6844904142448640007)

View File

@@ -0,0 +1,16 @@
```
给我说一下 语谱图spectrogram是什么
卫星领域有什么用处?
```
在卫星领域语谱图spectrogram也发挥着重要的作用主要应用于以下方面
- **信号分析与识别:**
- **信号干扰检测:** 通过观察语谱图可以快速发现是否存在对卫星信号的干扰例如人为的干扰jamming或意外的频率冲突。干扰信号通常在语谱图上会显示出异常的能量分布或特定的频率模式。
- **信号欺骗检测 (Spoofing):** 在卫星导航领域,语谱图可以帮助识别欺骗信号。欺骗信号可能会伪造卫星的定位信息,语谱图可以显示出与真实信号不同的特征。
- **雷达应用:**
- **空间目标监视 (Space Domain Awareness):** 雷达卫星或地面雷达站利用语谱图分析反射回来的信号,可以识别和跟踪空间中的物体,如废弃的卫星或碎片。通过分析信号的多普勒频移等信息,可以估计目标的速度和距离。
- 研究报告提到通过观察RHESSI卫星的观测数据生成的语谱图可以进行空间目标监视的微多普勒特征分析。
- **科学研究:**
- **粒子数据分析:** 在某些卫星任务中,例如分析太空中的粒子 flux流量可以将粒子能量随时间和空间例如 McIlwain L 参数)的变化绘制成二维彩色图,这种图也被称为语谱图。虽然这里的语谱图侧重于能量分布而非频率,但可视化的原理是相似的。

View File

@@ -0,0 +1,85 @@
<!--
Plotly 需要用 iframe 嵌套在页面中的原因:
最下面那个代码,在项目里渲染不正常。
但是新建一个 vue3 项目,直接在 App.vue 里那样子写,却能正常渲染。
-->
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Plotly 图表的 HTML 页面</title>
<!-- <script src="https://cdn.plot.ly/plotly-3.0.1.js"></script> -->
<script src="./plotly-3.0.1.js"></script>
<!-- https://github.com/plotly/plotly.js/blob/master/dist/README.md#to-include-localization -->
<script src="./plotly-locale-zh-cn.js"></script>
</head>
<body>
<script>
Plotly.setPlotConfig({ locale: 'zh-CN' });
</script>
<div id="PLOTLY_CHART_spectrogram" style="width: 100%; height: 100%"></div>
<div id="PLOTLY_CHART_waterfall" style="width: 100%; height: 100%"></div>
</body>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden; /* 防止滚动条出现 */
}
/* 确保容器占满整个 iframe */
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
</html>
<!--
<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue'
import Plotly from 'plotly.js-dist-min'
const plotRef = useTemplateRef<import('plotly.js-dist-min').PlotlyHTMLElement | null>('plotRef')
// 数据
const trace1 = {
x: [1, 2, 3, 4, 5],
y: [2, 4, 1, 5, 3],
type: 'scatter',
} satisfies import('plotly.js-dist-min').Data
const data = [trace1]
// 布局
const layout = {
title: '简单折线图',
xaxis: {
title: 'X 轴',
},
yaxis: {
title: 'Y 轴',
},
} satisfies Partial<import('plotly.js-dist-min').Layout>
onMounted(async () => {
// 绘制图表
Plotly.newPlot(plotRef.value!, data, layout)
plotRef.value?.on('plotly_relayout', (eventData) => {
console.debug(`eventData :>> `, eventData)
})
})
</script>
<template>
<div ref="plotRef" style="width: 600px; height: 400px"></div>
</template>
-->

257645
public/iframe/plotly-3.0.1.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
var locale={moduleType:"locale",name:"zh-CN",dictionary:{Autoscale:"\u81ea\u52a8\u7f29\u653e","Box Select":"\u77e9\u5f62\u6846\u9009","Click to enter Colorscale title":"\u70b9\u51fb\u8f93\u5165\u8272\u9636\u7684\u6807\u9898","Click to enter Component A title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6A\u7684\u6807\u9898","Click to enter Component B title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6B\u7684\u6807\u9898","Click to enter Component C title":"\u70b9\u51fb\u8f93\u5165\u7ec4\u4ef6C\u7684\u6807\u9898","Click to enter Plot title":"\u70b9\u51fb\u8f93\u5165\u56fe\u8868\u7684\u6807\u9898","Click to enter X axis title":"\u70b9\u51fb\u8f93\u5165X\u8f74\u7684\u6807\u9898","Click to enter Y axis title":"\u70b9\u51fb\u8f93\u5165Y\u8f74\u7684\u6807\u9898","Compare data on hover":"\u60ac\u505c\u65f6\u6bd4\u8f83\u6570\u636e","Double-click on legend to isolate one trace":"\u53cc\u51fb\u56fe\u4f8b\u6765\u7a81\u663e\u5bf9\u5e94\u8f68\u8ff9","Double-click to zoom back out":"\u53cc\u51fb\u8fd4\u56de\u7f29\u5c0f\u663e\u793a","Download plot as a png":"\u4e0b\u8f7d\u56fe\u8868\u4e3aPNG\u683c\u5f0f","Download plot":"\u4e0b\u8f7d\u56fe\u8868","Edit in Chart Studio":"\u5728Chart Studio\u4e2d\u7f16\u8f91","IE only supports svg. Changing format to svg.":"IE\u53ea\u652f\u6301SVG\u3002\u8f6c\u6362\u683c\u5f0f\u4e3aSVG\u3002","Lasso Select":"\u5957\u7d22\u9009\u62e9","Orbital rotation":"\u8f68\u9053\u65cb\u8f6c",Pan:"\u5e73\u79fb","Produced with Plotly.js":"\u7531Plotly.js\u751f\u6210",Reset:"\u91cd\u7f6e","Reset axes":"\u91cd\u7f6e\u8f74","Reset camera to default":"\u91cd\u7f6e\u955c\u5934\u89c6\u89d2\u4e3a\u9ed8\u8ba4\u72b6\u6001","Reset camera to last save":"\u91cd\u7f6e\u955c\u5934\u89c6\u89d2\u4e3a\u4e0a\u6b21\u4fdd\u5b58\u72b6\u6001","Reset view":"\u91cd\u7f6e\u89c6\u56fe","Reset views":"\u91cd\u7f6e\u89c6\u56fe","Show closest data on hover":"\u60ac\u505c\u65f6\u663e\u793a\u6700\u8fd1\u7684\u6570\u636e","Snapshot succeeded":"\u751f\u6210\u5feb\u7167\u6210\u529f","Sorry, there was a problem downloading your snapshot!":"\u62b1\u6b49\uff0c\u4e0b\u8f7d\u5feb\u7167\u51fa\u73b0\u95ee\u9898\uff01","Taking snapshot - this may take a few seconds":"\u6b63\u5728\u751f\u6210\u5feb\u7167 - \u53ef\u80fd\u9700\u8981\u51e0\u79d2\u949f",Zoom:"\u7f29\u653e","Zoom in":"\u653e\u5927","Zoom out":"\u7f29\u5c0f","close:":"\u5173\u95ed:",trace:"\u8e2a\u8ff9:","lat:":"\u7eac\u5ea6:","lon:":"\u7ecf\u5ea6:","q1:":"\u7b2c\u4e00\u56db\u5206\u4f4d\u6570:","q3:":"\u7b2c\u4e09\u56db\u5206\u4f4d\u6570:","source:":"\u6e90:","target:":"\u76ee\u6807:","lower fence:":"\u5185\u4fa7\u680f(lower fence):","upper fence:":"\u5916\u4fa7\u680f(upper fence):","max:":"\u6700\u5927\u503c:","mean \xb1 \u03c3:":"\u5e73\u5747\u6570 \xb1 \u6807\u51c6\u5dee\u03c3:","mean:":"\u5e73\u5747\u6570:","median:":"\u4e2d\u4f4d\u6570:","min:":"\u6700\u5c0f\u503c:","Turntable rotation":"\u65cb\u8f6c\u8f6c\u76d8:","Toggle Spike Lines":"\u5207\u6362\u663e\u793a\u6570\u636e\u70b9\u8f85\u52a9\u7ebf(Spike Lines)","open:":"\u6253\u5f00:","high:":"\u9ad8:","low:":"\u4f4e:","Toggle show closest data on hover":"\u5207\u6362\u60ac\u505c\u65f6\u663e\u793a\u6700\u8fd1\u7684\u6570\u636e\u70b9","incoming flow count:":"\u6d41\u5165\u6570\u91cf:","outgoing flow count:":"\u6d41\u51fa\u6570\u91cf:","kde:":"kde:","Click to enter radial axis title":"\u70b9\u51fb\u8f93\u5165\u5f84\u5411\u8f74\u6807\u9898","new text":"\u65b0\u5efa\u6587\u672c"},format:{days:["\u661f\u671f\u65e5","\u661f\u671f\u4e00","\u661f\u671f\u4e8c","\u661f\u671f\u4e09","\u661f\u671f\u56db","\u661f\u671f\u4e94","\u661f\u671f\u516d"],shortDays:["\u5468\u65e5","\u5468\u4e00","\u5468\u4e8c","\u5468\u4e09","\u5468\u56db","\u5468\u4e94","\u5468\u516d"],months:["\u4e00\u6708","\u4e8c\u6708","\u4e09\u6708","\u56db\u6708","\u4e94\u6708","\u516d\u6708","\u4e03\u6708","\u516b\u6708","\u4e5d\u6708","\u5341\u6708","\u5341\u4e00\u6708","\u5341\u4e8c\u6708"],shortMonths:["\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u5341\u4e00","\u5341\u4e8c"],date:"%Y-%m-%d"}};"undefined"==typeof Plotly?(window.PlotlyLocales=window.PlotlyLocales||[],window.PlotlyLocales.push(locale)):Plotly.register(locale);

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["https://git.1-h.cc/examples/renovate-example/raw/branch/main/default.json5", ":automergeMinor"]
"extends": ["https://git.1-h.cc/examples/renovate-example/raw/branch/main/default.json5", ":automergeMinor"],
"postUpdateOptions": ["pnpmDedupe"]
}

View File

@@ -1,11 +1,41 @@
<script setup lang="ts">
const VITE_BUILD_COMMIT = import.meta.env.VITE_BUILD_COMMIT;
import type { ThemeConfig } from 'ant-design-vue/es/config-provider/context';
import { theme } from 'ant-design-vue';
import { useLayout } from './layouts/sakai-vue/composables/layout';
const { isDarkTheme } = useLayout();
const themeConfig = computed(() => {
return {
algorithm: isDarkTheme.value ? theme.darkAlgorithm : theme.defaultAlgorithm,
} satisfies ThemeConfig;
});
</script>
<template>
<div fixed rounded-br-4 bottom-0 left-0 z-9999 px-4 py-2 bg-black text-white op-75>
commit: {{ VITE_BUILD_COMMIT }}
</div>
<div>$__DEV__: {{ $__DEV__ }}</div>
<n-config-provider preflight-style-disabled>
<a-config-provider :theme="themeConfig">
<RouterView />
</a-config-provider>
</n-config-provider>
<DynamicDialog /> <ConfirmDialog /> <Toast />
</template>
<style>
.layout-wrapper {
isolation: isolate;
}
@supports (height: 100dvh) {
.h-screen {
height: 100dvh;
}
}
@supports (width: 100dvw) {
.w-screen {
width: 100dvw;
}
}
</style>

View File

@@ -1,84 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
:root {
--h-base-font: lxgw-wenkai-mono-tc, alibaba-puhuiti, Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
}
:root:root {
--van-base-font: var(--h-base-font);
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family: var(--h-base-font);
font-weight: 400;
font-style: normal;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -1,24 +0,0 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
button:not(.van-button),
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
button:not(.van-button):hover,
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,316 @@
<template>
<div
ref="bubbleParentContainer"
class="relative h-72 w-full overflow-hidden"
>
<div ref="bubbleCanvasContainer"></div>
<div
:style="{
'--bubbles-blur': `${blur}px`,
}"
class="absolute inset-0 z-[2] size-full backdrop-blur-[--bubbles-blur]"
>
<slot />
</div>
</div>
</template>
<script setup lang="ts">
import {
ShaderMaterial,
SphereGeometry,
Vector3,
Color,
MathUtils,
Mesh,
Clock,
WebGLRenderer,
Scene,
PerspectiveCamera,
} from "three";
import { ref, onMounted, onBeforeUnmount } from "vue";
defineProps({
blur: {
type: Number,
default: 0,
},
});
const bubbleParentContainer = ref<HTMLElement | null>(null);
const bubbleCanvasContainer = ref<HTMLElement | null>(null);
let renderer: WebGLRenderer;
let scene: Scene;
let camera: PerspectiveCamera;
let clock: Clock;
const spheres: Mesh[] = [];
const BG_COLOR_BOTTOM_BLUISH = rgb(170, 215, 217);
const BG_COLOR_TOP_BLUISH = rgb(57, 167, 255);
const BG_COLOR_BOTTOM_ORANGISH = rgb(255, 160, 75);
const BG_COLOR_TOP_ORANGISH = rgb(239, 172, 53);
const SPHERE_COLOR_BOTTOM_BLUISH = rgb(120, 235, 124);
const SPHERE_COLOR_TOP_BLUISH = rgb(0, 167, 255);
const SPHERE_COLOR_BOTTOM_ORANGISH = rgb(235, 170, 0);
const SPHERE_COLOR_TOP_ORANGISH = rgb(255, 120, 0);
const SPHERE_COUNT = 250;
const SPHERE_SCALE_COEFF = 3;
const ORBIT_MIN = SPHERE_SCALE_COEFF + 2;
const ORBIT_MAX = ORBIT_MIN + 10;
const RAND_SEED = 898211544;
const rand = seededRandom(RAND_SEED);
const { PI, cos, sin } = Math;
const PI2 = PI * 2;
const sizes = new Array(SPHERE_COUNT).fill(0).map(() => randRange(1) * Math.pow(randRange(), 3));
const orbitRadii = new Array(SPHERE_COUNT)
.fill(0)
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const positions: [number, number, number][] = orbitRadii.map((rad, i) => [
rad * cos(thetas[i]!) * sin(phis[i]!),
rad * sin(thetas[i]!) * sin(phis[i]!),
rad * cos(phis[i]!),
]);
const sphereGeometry = new SphereGeometry(SPHERE_SCALE_COEFF);
const sphereMaterial = getGradientMaterial(
SPHERE_COLOR_BOTTOM_BLUISH,
SPHERE_COLOR_TOP_BLUISH,
SPHERE_COLOR_BOTTOM_ORANGISH,
SPHERE_COLOR_TOP_ORANGISH,
);
const bgGeometry = new SphereGeometry();
bgGeometry.scale(-1, 1, 1);
const bgMaterial = getGradientMaterial(
BG_COLOR_BOTTOM_BLUISH,
BG_COLOR_TOP_BLUISH,
BG_COLOR_BOTTOM_ORANGISH,
BG_COLOR_TOP_ORANGISH,
);
bgMaterial.uniforms.uTemperatureVariancePeriod!.value = new Vector3(0, 0, 0.1);
function seededRandom(a: number) {
return function () {
a |= 0;
a = (a + 0x9e3779b9) | 0;
var t = a ^ (a >>> 16);
t = Math.imul(t, 0x21f0aaad);
t = t ^ (t >>> 15);
t = Math.imul(t, 0x735a2d97);
return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296;
};
}
function randRange(n = 1) {
return rand() * n;
}
function rgb(r: number, g: number, b: number) {
return new Color(r / 255, g / 255, b / 255);
}
function getGradientMaterial(
colorBottomWarm: Color,
colorTopWarm: Color,
colorBottomCool: Color,
colorTopCool: Color,
) {
return new ShaderMaterial({
uniforms: {
colorBottomWarm: {
value: new Color().copy(colorBottomWarm),
},
colorTopWarm: {
value: new Color().copy(colorTopWarm),
},
colorBottomCool: {
value: new Color().copy(colorBottomCool),
},
colorTopCool: {
value: new Color().copy(colorTopCool),
},
uTemperature: {
value: 0.0,
},
uTemperatureVariancePeriod: {
value: new Vector3(0.08, 0.1, 0.2),
},
uElapsedTime: {
value: 0,
},
},
vertexShader: `
uniform vec4 uTemperatureVariancePeriod;
uniform float uTemperature;
uniform float uElapsedTime;
varying float topBottomMix;
varying float warmCoolMix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
topBottomMix = normal.y;
warmCoolMix = 0.6 * uTemperature +
0.4 * (sin(
(uElapsedTime + gl_Position.x) * uTemperatureVariancePeriod.x +
(uElapsedTime + gl_Position.y) * uTemperatureVariancePeriod.y +
(uElapsedTime + gl_Position.z) * uTemperatureVariancePeriod.z) * 0.5 + 0.5);
}
`,
fragmentShader: `
uniform vec3 colorBottomWarm;
uniform vec3 colorTopWarm;
uniform vec3 colorBottomCool;
uniform vec3 colorTopCool;
varying float topBottomMix;
varying float warmCoolMix;
void main() {
gl_FragColor = vec4(mix(
mix(colorTopCool, colorTopWarm, warmCoolMix),
mix(colorBottomCool, colorBottomWarm, warmCoolMix),
topBottomMix), 1.0);
}
`,
});
}
function createScene() {
const width = bubbleCanvasContainer.value?.clientWidth || 1;
const height = bubbleCanvasContainer.value?.clientHeight || 1;
// Set up the scene, camera, and renderer
scene = new Scene();
camera = new PerspectiveCamera(50, width / height, 1, 2000);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 23;
renderer = new WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(BG_COLOR_BOTTOM_BLUISH);
// Add these properties to allow overlap
sphereMaterial.depthWrite = false;
sphereMaterial.depthTest = true; // Keep this true for depth sorting
if (bubbleCanvasContainer.value) {
bubbleCanvasContainer.value.appendChild(renderer.domElement);
}
// Create the background mesh
const bgMesh = new Mesh(bgGeometry, bgMaterial);
// Position the background far behind everything
bgMesh.position.set(0, 0, -1); // Move the background far back
// Disable depth testing for the background to ensure it's always behind other objects
bgMesh.material.depthTest = false;
bgMesh.renderOrder = -1; // Ensure the background is rendered first
// Calculate the scale to ensure the background covers the full canvas
const distance = camera.position.z; // Distance from the camera
const aspect = camera.aspect;
const frustumHeight = 2 * distance * Math.tan(MathUtils.degToRad(camera.fov) / 2);
const frustumWidth = frustumHeight * aspect;
// Scale the background geometry to match the camera's frustum size
bgMesh.scale.set(
frustumWidth / bgGeometry.parameters.radius,
frustumHeight / bgGeometry.parameters.radius,
1,
);
scene.add(bgMesh); // Add the backgrou
// Create sphere meshes
const orbitRadii = new Array(SPHERE_COUNT)
.fill(0)
.map(() => MathUtils.lerp(ORBIT_MIN, ORBIT_MAX, randRange()));
const thetas = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const phis = new Array(SPHERE_COUNT).fill(0).map(() => randRange(PI2));
const positions = orbitRadii.map((rad, i) => [
rad * cos(thetas[i]!) * sin(phis[i]!),
rad * sin(thetas[i]!) * sin(phis[i]!),
rad * cos(phis[i]!),
]);
for (let i = 0; i < SPHERE_COUNT; i++) {
const sphere = new Mesh(sphereGeometry, sphereMaterial);
const [x, y, z] = positions[i]!;
const scaleVector = sizes[i];
sphere.scale.set(scaleVector!, scaleVector!, scaleVector!);
sphere.position.set(x!, y!, z!);
spheres.push(sphere);
scene.add(sphere);
}
clock = new Clock();
}
function animate() {
requestAnimationFrame(animate);
const elapsed = clock.getElapsedTime();
const temperature = sin(elapsed * 0.5) * 0.5 + 0.5;
bgMaterial.uniforms.uTemperature!.value = temperature;
bgMaterial.uniforms.uElapsedTime!.value = elapsed;
sphereMaterial.uniforms.uTemperature!.value = temperature;
sphereMaterial.uniforms.uElapsedTime!.value = elapsed;
// Floating effect for spheres
spheres.forEach((sphere, index) => {
const basePosition = positions[index];
const floatFactor = 2; // Adjust this value to control float intensity
const speed = 0.3; // Adjust this value to control float speed
const floatY = sin(elapsed * speed + index) * floatFactor;
sphere.position.y = basePosition![1] + floatY;
});
renderer.render(scene, camera);
}
function updateRendererSize() {
const width = bubbleParentContainer.value?.clientWidth || 1;
const height = bubbleParentContainer.value?.clientHeight || 1;
// Update renderer size and aspect ratio
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
// Recalculate background mesh scale
const distance = camera.position.z;
const frustumHeight = 2 * distance * Math.tan(MathUtils.degToRad(camera.fov) / 2);
const frustumWidth = frustumHeight * camera.aspect;
// Get the background mesh and update its scale
const bgMesh = scene.children.find(
(obj) => obj instanceof Mesh && obj.geometry === bgGeometry,
) as Mesh;
if (bgMesh) {
bgMesh.scale.set(
frustumWidth / bgGeometry.parameters.radius,
frustumHeight / bgGeometry.parameters.radius,
1,
);
}
}
onMounted(() => {
createScene();
updateRendererSize();
window.addEventListener("resize", updateRendererSize);
animate();
});
onBeforeUnmount(() => {
window.removeEventListener("resize", updateRendererSize); // Cleanup on component unmount
});
</script>

View File

@@ -0,0 +1 @@
export { default as BubblesBg } from "./BubblesBg.vue";

View File

@@ -0,0 +1,166 @@
<template>
<canvas
ref="starsCanvas"
:class="cn('absolute inset-0 w-full h-full', $props.class)"
></canvas>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { onMounted, ref } from "vue";
interface Star {
x: number;
y: number;
z: number;
speed: number;
}
const props = withDefaults(
defineProps<{
color?: string;
count?: number;
class?: string;
}>(),
{
color: "#FFF",
count: 200,
},
);
const starsCanvas = ref<HTMLCanvasElement | null>(null);
let perspective: number = 0;
let stars: Star[] = [];
let ctx: CanvasRenderingContext2D | null = null;
onMounted(() => {
const canvas = starsCanvas.value;
if (!canvas) return;
window.addEventListener("resize", resizeCanvas);
resizeCanvas(); // Call it initially to set correct size
perspective = canvas.width / 2;
stars = [];
// Initialize stars
for (let i = 0; i < props.count; i++) {
stars.push({
x: (Math.random() - 0.5) * 2 * canvas.width,
y: (Math.random() - 0.5) * 2 * canvas.height,
z: Math.random() * canvas.width,
speed: Math.random() * 5 + 2, // Speed for falling effect
});
}
animate(); // Start animation
});
function hexToRgb() {
let hex = props.color.replace(/^#/, "");
// If the hex code is 3 characters, expand it to 6 characters
if (hex.length === 3) {
hex = hex
.split("")
.map((char) => char + char)
.join("");
}
// Parse the r, g, b values from the hex string
const bigint = parseInt(hex, 16);
const r = (bigint >> 16) & 255; // Extract the red component
const g = (bigint >> 8) & 255; // Extract the green component
const b = bigint & 255; // Extract the blue component
// Return the RGB values as a string separated by spaces
return {
r,
g,
b,
};
}
// Function to draw a star with a sharp line and blurred trail
function drawStar(star: Star) {
const canvas = starsCanvas.value;
if (!canvas) return;
ctx = canvas.getContext("2d");
if (!ctx) return;
const scale = perspective / (perspective + star.z); // 3D perspective scale
const x2d = canvas.width / 2 + star.x * scale;
const y2d = canvas.height / 2 + star.y * scale;
const size = Math.max(scale * 3, 0.5); // Size based on perspective
// Previous position for a trail effect
const prevScale = perspective / (perspective + star.z + star.speed * 15); // Longer trail distance
const xPrev = canvas.width / 2 + star.x * prevScale;
const yPrev = canvas.height / 2 + star.y * prevScale;
const rgb = hexToRgb();
// Draw blurred trail (longer, with low opacity)
ctx.save(); // Save current context state for restoring later
ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`;
ctx.lineWidth = size * 2.5; // Thicker trail for a blur effect
ctx.shadowBlur = 35; // Add blur to the trail
ctx.shadowColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`;
ctx.beginPath();
ctx.moveTo(x2d, y2d);
ctx.lineTo(xPrev, yPrev); // Longer trail
ctx.stroke();
ctx.restore(); // Restore context state to remove blur from the main line
// Draw sharp line (no blur)
ctx.strokeStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.6)`;
ctx.lineWidth = size; // The line width is the same as the star's size
ctx.beginPath();
ctx.moveTo(x2d, y2d);
ctx.lineTo(xPrev, yPrev); // Sharp trail
ctx.stroke();
// Draw the actual star (dot)
ctx.fillStyle = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 1)`;
ctx.beginPath();
ctx.arc(x2d, y2d, size / 4, 0, Math.PI * 2); // Dot with size matching the width
ctx.fill();
}
// Function to animate the stars
function animate() {
const canvas = starsCanvas.value;
if (!canvas) return;
ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear canvas for each frame
stars.forEach((star) => {
drawStar(star);
// Move star towards the screen (decrease z)
star.z -= star.speed;
// Reset star when it reaches the viewer (z = 0)
if (star.z <= 0) {
star.z = canvas.width;
star.x = (Math.random() - 0.5) * 2 * canvas.width;
star.y = (Math.random() - 0.5) * 2 * canvas.height;
}
});
requestAnimationFrame(animate); // Continue animation
}
// Set canvas to full screen
function resizeCanvas() {
const canvas = starsCanvas.value;
if (!canvas) return;
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
}
</script>

View File

@@ -0,0 +1 @@
export { default as FallingStarsBg } from "./FallingStarsBg.vue";

View File

@@ -0,0 +1,65 @@
<template>
<div
:class="
cn(
'border-beam',
'pointer-events-none absolute inset-0 rounded-[inherit] [border:calc(var(--border-width)*1px)_solid_transparent]',
'![mask-clip:padding-box,border-box] ![mask-composite:intersect] [mask:linear-gradient(transparent,transparent),linear-gradient(white,white)]',
'after:absolute after:aspect-square after:w-[calc(var(--size)*1px)] animate-border-beam after:[animation-delay:var(--delay)] after:[background:linear-gradient(to_left,var(--color-from),var(--color-to),transparent)] after:[offset-anchor:calc(var(--anchor)*1%)_50%] after:[offset-path:rect(0_auto_auto_0_round_calc(var(--size)*1px))]',
props.class,
)
"
></div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { computed } from "vue";
interface BorderBeamProps {
class?: string;
size?: number;
duration?: number;
borderWidth?: number;
anchor?: number;
colorFrom?: string;
colorTo?: string;
delay?: number;
}
const props = withDefaults(defineProps<BorderBeamProps>(), {
size: 200,
duration: 15000,
anchor: 90,
borderWidth: 1.5,
colorFrom: "#ffaa40",
colorTo: "#9c40ff",
delay: 0,
});
const durationInSeconds = computed(() => `${props.duration}s`);
const delayInSeconds = computed(() => `${props.delay}s`);
</script>
<style scoped>
.border-beam {
--size: v-bind(size);
--duration: v-bind(durationInSeconds);
--anchor: v-bind(anchor);
--border-width: v-bind(borderWidth);
--color-from: v-bind(colorFrom);
--color-to: v-bind(colorTo);
--delay: v-bind(delayInSeconds);
}
.animate-border-beam::after {
content: "";
animation: border-beam-anim var(--duration) infinite linear;
}
@keyframes border-beam-anim {
to {
offset-distance: 100%;
}
}
</style>

View File

@@ -0,0 +1 @@
export { default as BorderBeam } from "./BorderBeam.vue";

View File

@@ -0,0 +1,71 @@
<template>
<div
:class="[
'group relative flex size-full overflow-hidden rounded-xl border bg-neutral-100 text-black dark:bg-neutral-900 dark:text-white',
$props.class,
]"
@mousemove="handleMouseMove"
@mouseleave="handleMouseLeave"
>
<div :class="cn('relative z-10', props.slotClass)">
<slot></slot>
</div>
<div
class="pointer-events-none absolute inset-0 rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
:style="{
background: backgroundStyle,
opacity: gradientOpacity,
}"
></div>
</div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { ref, computed, onMounted, type HTMLAttributes } from "vue";
const props = withDefaults(
defineProps<{
class?: HTMLAttributes["class"];
slotClass?: HTMLAttributes["class"];
gradientSize?: number;
gradientColor?: string;
gradientOpacity?: number;
}>(),
{
class: "",
slotClass: "",
gradientSize: 200,
gradientColor: "#262626",
gradientOpacity: 0.8,
},
);
const mouseX = ref(-props.gradientSize * 10);
const mouseY = ref(-props.gradientSize * 10);
function handleMouseMove(e: MouseEvent) {
const target = e.currentTarget as HTMLElement;
const rect = target.getBoundingClientRect();
mouseX.value = e.clientX - rect.left;
mouseY.value = e.clientY - rect.top;
}
function handleMouseLeave() {
mouseX.value = -props.gradientSize * 10;
mouseY.value = -props.gradientSize * 10;
}
onMounted(() => {
mouseX.value = -props.gradientSize * 10;
mouseY.value = -props.gradientSize * 10;
});
const backgroundStyle = computed(() => {
return `radial-gradient(
circle at ${mouseX.value}px ${mouseY.value}px,
${props.gradientColor} 0%,
rgba(0, 0, 0, 0) 70%
)`;
});
</script>

View File

@@ -0,0 +1 @@
export { default as CardSpotlight } from "./CardSpotlight.vue";

View File

@@ -0,0 +1,76 @@
<template>
<div
ref="dockRef"
:class="
cn(
'supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max rounded-2xl border p-2 backdrop-blur-md transition-all gap-4',
orientation === 'vertical' && 'flex-col w-[58px] h-max',
props.class,
dockClass,
)
"
@mousemove="onMouseMove"
@mouseleave="onMouseLeave"
>
<slot />
</div>
</template>
<script setup lang="ts">
import type { DataOrientation, Direction } from "./types";
import {
MOUSE_X_INJECTION_KEY,
MOUSE_Y_INJECTION_KEY,
MAGNIFICATION_INJECTION_KEY,
DISTANCE_INJECTION_KEY,
ORIENTATION_INJECTION_KEY,
} from "./injectionKeys";
import { cn } from "@/shadcn/lib/utils";
import type { HTMLAttributes } from "vue";
interface DockProps {
class?: HTMLAttributes["class"];
magnification?: number;
distance?: number;
direction?: Direction;
orientation?: DataOrientation;
}
const props = withDefaults(defineProps<DockProps>(), {
magnification: 60,
distance: 140,
direction: "middle",
orientation: "horizontal",
});
const dockRef = ref<HTMLElement | null>(null);
const mouseX = ref(Infinity);
const mouseY = ref(Infinity);
const magnification = computed(() => props.magnification);
const distance = computed(() => props.distance);
const dockClass = computed(() => ({
"items-start": props.direction === "top",
"items-center": props.direction === "middle",
"items-end": props.direction === "bottom",
}));
function onMouseMove(e: MouseEvent) {
requestAnimationFrame(() => {
mouseX.value = e.pageX;
mouseY.value = e.pageY;
});
}
function onMouseLeave() {
requestAnimationFrame(() => {
mouseX.value = Infinity;
mouseY.value = Infinity;
});
}
provide(MOUSE_X_INJECTION_KEY, mouseX);
provide(MOUSE_Y_INJECTION_KEY, mouseY);
provide(ORIENTATION_INJECTION_KEY, props.orientation);
provide(MAGNIFICATION_INJECTION_KEY, magnification);
provide(DISTANCE_INJECTION_KEY, distance);
</script>

View File

@@ -0,0 +1,61 @@
<template>
<div
ref="iconRef"
class="flex aspect-square cursor-pointer items-center justify-center rounded-full transition-all duration-200 ease-out"
:style="{
width: `${iconWidth}px`,
height: `${iconWidth}px`,
}"
:hovered="{
marginLeft: margin,
marginRight: margin,
}"
>
<slot />
</div>
</template>
<script setup lang="ts">
import {
MOUSE_X_INJECTION_KEY,
MOUSE_Y_INJECTION_KEY,
MAGNIFICATION_INJECTION_KEY,
DISTANCE_INJECTION_KEY,
ORIENTATION_INJECTION_KEY,
} from "./injectionKeys";
const iconRef = ref<HTMLDivElement | null>(null);
const mouseX = inject(MOUSE_X_INJECTION_KEY, ref(Infinity));
const mouseY = inject(MOUSE_Y_INJECTION_KEY, ref(Infinity));
const distance = inject(DISTANCE_INJECTION_KEY);
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
const magnification = inject(MAGNIFICATION_INJECTION_KEY);
const isVertical = computed(() => orientation === "vertical");
const margin = ref(0);
function calculateDistance(val: number) {
if (isVertical.value) {
const bounds = iconRef.value?.getBoundingClientRect() || {
y: 0,
height: 0,
};
return val - bounds.y - bounds.height / 2;
}
const bounds = iconRef.value?.getBoundingClientRect() || { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
}
const iconWidth = computed(() => {
const distanceCalc = isVertical.value
? calculateDistance(mouseY.value)
: calculateDistance(mouseX.value);
if (!distance?.value || !magnification?.value) return 40;
if (Math.abs(distanceCalc) < distance?.value) {
return (1 - Math.abs(distanceCalc) / distance?.value) * magnification?.value + 40;
}
return 40;
});
</script>

View File

@@ -0,0 +1,14 @@
<template>
<div
:class="
cn('relative block bg-secondary', orientation === 'vertical' ? 'w-4/5 h-0.5' : 'h-4/5 w-0.5')
"
></div>
</template>
<script setup lang="ts">
import { cn } from "@/shadcn/lib/utils";
import { ORIENTATION_INJECTION_KEY } from "./injectionKeys";
const orientation = inject(ORIENTATION_INJECTION_KEY, "vertical");
</script>

View File

@@ -0,0 +1,5 @@
export { default as Dock } from "./Dock.vue";
export { default as DockIcon } from "./DockIcon.vue";
export { default as DockSeparator } from "./DockSeparator.vue";
export type DataOrientation = "vertical" | "horizontal";

View File

@@ -0,0 +1,11 @@
import type { Ref, InjectionKey, ComputedRef } from "vue";
import type { DataOrientation } from "./types";
export const MOUSE_X_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
export const MOUSE_Y_INJECTION_KEY = Symbol() as InjectionKey<Ref<number>>;
export const MAGNIFICATION_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
export const DISTANCE_INJECTION_KEY = Symbol() as InjectionKey<ComputedRef<number>>;
export const ORIENTATION_INJECTION_KEY = Symbol() as InjectionKey<DataOrientation>;

View File

@@ -0,0 +1,2 @@
export type DataOrientation = "vertical" | "horizontal";
export type Direction = "top" | "middle" | "bottom";

View File

@@ -0,0 +1,83 @@
<template>
<button
:class="
cn(
'relative flex items-center justify-center min-w-28 min-h-10 overflow-hidden before:absolute before:-inset-[200%] animate-rainbow rainbow-btn',
props.class,
)
"
>
<span class="btn-content inline-flex size-full items-center justify-center px-4 py-2">
<slot />
</span>
</button>
</template>
<script lang="ts" setup>
import { cn } from "@/shadcn/lib/utils";
import { computed } from "vue";
interface GradientButtonProps {
borderWidth?: number;
colors?: string[];
duration?: number;
borderRadius?: number;
blur?: number;
class?: string;
bgColor?: string;
}
const props = withDefaults(defineProps<GradientButtonProps>(), {
colors: () => [
"#FF0000",
"#FFA500",
"#FFFF00",
"#008000",
"#0000FF",
"#4B0082",
"#EE82EE",
"#FF0000",
],
duration: 2500,
borderWidth: 2,
borderRadius: 8,
blur: 4,
bgColor: "#000",
});
const durationInMilliseconds = computed(() => `${props.duration}ms`);
const allColors = computed(() => props.colors.join(", "));
const borderWidthInPx = computed(() => `${props.borderWidth}px`);
const borderRadiusInPx = computed(() => `${props.borderRadius}px`);
const blurPx = computed(() => `${props.blur}px`);
</script>
<style scoped>
.animate-rainbow::before {
content: "";
background: conic-gradient(v-bind(allColors));
animation: rotate-rainbow v-bind(durationInMilliseconds) linear infinite;
filter: blur(v-bind(blurPx));
padding: v-bind(borderWidthInPx);
}
.rainbow-btn {
padding: v-bind(borderWidthInPx);
border-radius: v-bind(borderRadiusInPx);
}
.btn-content {
border-radius: v-bind(borderRadiusInPx);
background-color: v-bind(bgColor);
z-index: 0;
}
@keyframes rotate-rainbow {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1 @@
export { default as GradientButton } from "./GradientButton.vue";

View File

@@ -0,0 +1,60 @@
<template>
<button
ref="buttonRef"
:class="
cn(
'group relative w-auto cursor-pointer overflow-hidden rounded-full border bg-background p-2 px-6 text-center font-semibold',
props.class,
)
"
>
<div class="flex items-center gap-2">
<div
class="size-2 scale-100 rounded-lg bg-primary transition-all duration-300 group-hover:scale-[100.8]"
></div>
<span
class="inline-block whitespace-nowrap transition-all duration-300 group-hover:translate-x-12 group-hover:opacity-0"
>
{{ text }}
</span>
</div>
<div
class="absolute top-0 z-10 flex size-full translate-x-12 items-center justify-center gap-2 text-primary-foreground opacity-0 transition-all duration-300 group-hover:-translate-x-5 group-hover:opacity-100"
>
<span class="whitespace-nowrap">{{ text }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-arrow-right"
>
<path d="M5 12h14" />
<path d="m12 5 7 7-7 7" />
</svg>
</div>
</button>
</template>
<script lang="ts" setup>
import { cn } from "@/shadcn/lib/utils";
import { ref } from "vue";
interface Props {
text?: string;
class?: string;
}
const props = withDefaults(defineProps<Props>(), {
text: "Button",
});
const buttonRef = ref<HTMLButtonElement>();
</script>
<style></style>

View File

@@ -0,0 +1 @@
export { default as InteractiveHoverButton } from "./InteractiveHoverButton.vue";

View File

@@ -0,0 +1,138 @@
<template>
<div
:class="
cn(patternBackgroundVariants({ variant, size }), ` ${animate ? 'move move-' + direction : ''} `, props.class)
"
>
<div
:class="
cn(
'absolute pointer-events-none inset-0 flex items-center justify-center',
patternBackgroundMaskVariants({ mask }),
)
"
></div>
<slot />
</div>
</template>
<script setup lang="ts">
import { cn } from '@/shadcn/lib/utils';
import type { BaseProps as Props } from '.';
import {
PATTERN_BACKGROUND_DIRECTION,
PATTERN_BACKGROUND_SPEED,
PATTERN_BACKGROUND_VARIANT,
patternBackgroundMaskVariants,
patternBackgroundVariants,
} from '.';
import { computed } from 'vue';
const props = withDefaults(defineProps<Props>(), {
direction: () => PATTERN_BACKGROUND_DIRECTION.Top,
variant: () => PATTERN_BACKGROUND_VARIANT.Grid,
speed: () => PATTERN_BACKGROUND_SPEED.Default,
size: undefined,
mask: undefined,
});
const durationFormSpeed = computed(() => `${props.speed}ms`);
</script>
<style scoped>
@keyframes to-top {
0% {
background-position: 0 100%;
}
100% {
background-position: 0 0;
}
}
@keyframes to-bottom {
0% {
background-position: 0 0;
}
100% {
background-position: 0 100%;
}
}
@keyframes to-right {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 0;
}
}
@keyframes to-left {
0% {
background-position: 100% 0;
}
100% {
background-position: 0 0;
}
}
@keyframes to-top-right {
0% {
background-position: 0 100%;
}
100% {
background-position: 100% 0;
}
}
@keyframes to-top-left {
0% {
background-position: 100% 100%;
}
100% {
background-position: 0 0;
}
}
@keyframes to-bottom-right {
0% {
background-position: 0 0;
}
100% {
background-position: 100% 100%;
}
}
@keyframes to-bottom-left {
0% {
background-position: 100% 0;
}
100% {
background-position: 0 100%;
}
}
.move {
animation-duration: v-bind(durationFormSpeed);
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.move-top {
animation-name: to-top;
}
.move-bottom {
animation-name: to-bottom;
}
.move-right {
animation-name: to-right;
}
.move-left {
animation-name: to-left;
}
.move-top-right {
animation-name: to-top-right;
}
.move-top-left {
animation-name: to-top-left;
}
.move-bottom-right {
animation-name: to-bottom-right;
}
.move-bottom-left {
animation-name: to-bottom-left;
}
</style>

View File

@@ -0,0 +1,87 @@
import { cva, type VariantProps } from "class-variance-authority";
import type { HTMLAttributes } from "vue";
type ObjectValues<T> = T[keyof T];
export const PATTERN_BACKGROUND_DIRECTION = {
Top: "top",
Bottom: "bottom",
Left: "left",
Right: "right",
TopLeft: "top-left",
TopRight: "top-right",
BottomLeft: "bottom-left",
BottomRight: "bottom-right",
} as const;
export type PatternBackgroundDirection = ObjectValues<typeof PATTERN_BACKGROUND_DIRECTION>;
export interface BaseProps {
class?: HTMLAttributes["class"];
animate?: boolean;
direction?: PatternBackgroundDirection;
variant?: PatternBackgroundVariants["variant"];
size?: PatternBackgroundVariants["size"];
mask?: PatternBackgroundMaskVariants["mask"];
speed?: ObjectValues<typeof PATTERN_BACKGROUND_SPEED>;
}
export const PATTERN_BACKGROUND_VARIANT = {
Grid: "grid",
Dot: "dot",
BigDot: "big-dot",
} as const;
export const PATTERN_BACKGROUND_SPEED = {
Default: 10000,
Slow: 25000,
Fast: 5000,
} as const;
export const patternBackgroundVariants = cva("relative text-clip", {
variants: {
variant: {
[PATTERN_BACKGROUND_VARIANT.Grid]:
"bg-[linear-gradient(to_right,hsl(var(--foreground)/0.3)_1px,transparent_1px),linear-gradient(to_bottom,hsl(var(--foreground)/0.3)_1px,transparent_1px)]",
[PATTERN_BACKGROUND_VARIANT.Dot]:
"bg-[radial-gradient(hsl(var(--foreground)/0.3)_1px,transparent_1px)]",
[PATTERN_BACKGROUND_VARIANT.BigDot]:
"bg-[radial-gradient(hsl(var(--foreground)/0.3)_3px,transparent_3px)]",
},
size: {
xs: "bg-[size:8px_8px]",
sm: "bg-[size:16px_16px]",
md: "bg-[size:24px_24px]",
lg: "bg-[size:32px_32px]",
},
},
defaultVariants: {
variant: "grid",
size: "md",
},
});
export type PatternBackgroundVariants = VariantProps<typeof patternBackgroundVariants>;
export const PATTERN_BACKGROUND_MASK = {
Ellipse: "ellipse",
EllipseTop: "ellipse-top",
} as const;
export const patternBackgroundMaskVariants = cva("bg-background", {
variants: {
mask: {
[PATTERN_BACKGROUND_MASK.Ellipse]:
"[mask-image:radial-gradient(ellipse_at_center,transparent,black_80%)]",
[PATTERN_BACKGROUND_MASK.EllipseTop]:
"[mask-image:radial-gradient(ellipse_at_top,transparent,black_80%)]",
},
},
defaultVariants: {
mask: "ellipse",
},
});
export type PatternBackgroundMaskVariants = VariantProps<typeof patternBackgroundMaskVariants>;
export { default as PatternBackground } from "./PatternBackground.vue";

View File

@@ -0,0 +1,107 @@
<template>
<button
:class="
cn(
'group relative z-0 flex cursor-pointer items-center justify-center overflow-hidden whitespace-nowrap border border-white/10 px-6 py-3 text-white [background:var(--bg)] [border-radius:var(--radius)] dark:text-black',
'transform-gpu transition-transform duration-300 ease-in-out active:translate-y-px',
$props.class,
)
"
:style="{
'--spread': '90deg',
'--shimmer-color': shimmerColor,
'--radius': borderRadius,
'--speed': shimmerDuration,
'--cut': shimmerSize,
'--bg': background,
}"
>
<div :class="cn('-z-30 blur-[2px]', 'absolute inset-0 overflow-visible [container-type:size]')">
<div
class="animate-shimmer-btn-shimmer-slide absolute inset-0 h-[100cqh] [aspect-ratio:1] [border-radius:0] [mask:none]"
>
<div
class="animate-shimmer-btn-spin-around absolute -inset-full w-auto rotate-0 [background:conic-gradient(from_calc(270deg-(var(--spread)*0.5)),transparent_0,var(--shimmer-color)_var(--spread),transparent_var(--spread))] [translate:0_0]"
/>
</div>
</div>
<slot />
<div
:class="
cn(
'insert-0 absolute size-full',
'rounded-2xl px-4 py-1.5 text-sm font-medium shadow-[inset_0_-8px_10px_#ffffff1f]',
// transition
'transform-gpu transition-all duration-300 ease-in-out',
// on hover
'group-hover:shadow-[inset_0_-6px_10px_#ffffff3f]',
// on click
'group-active:shadow-[inset_0_-10px_10px_#ffffff3f]',
)
"
/>
<div
class="absolute -z-20 [background:var(--bg)] [border-radius:var(--radius)] [inset:var(--cut)]"
/>
</button>
</template>
<script lang="ts" setup>
import { cn } from '@/shadcn/lib/utils';
type ShimmerButtonProps = {
shimmerColor?: string;
shimmerSize?: string;
borderRadius?: string;
shimmerDuration?: string;
background?: string;
class?: string;
};
withDefaults(defineProps<ShimmerButtonProps>(), {
shimmerColor: "#ffffff",
shimmerSize: "0.05em",
shimmerDuration: "3s",
borderRadius: "100px",
background: "rgba(0, 0, 0, 1)",
});
</script>
<style scoped>
@keyframes shimmer-btn-shimmer-slide {
to {
transform: translate(calc(100cqw - 100%), 0);
}
}
@keyframes shimmer-btn-spin-around {
0% {
transform: translateZ(0) rotate(0);
}
15%,
35% {
transform: translateZ(0) rotate(90deg);
}
65%,
85% {
transform: translateZ(0) rotate(270deg);
}
100% {
transform: translateZ(0) rotate(360deg);
}
}
.animate-shimmer-btn-shimmer-slide {
animation: shimmer-btn-shimmer-slide var(--speed) ease-in-out infinite alternate;
}
.animate-shimmer-btn-spin-around {
animation: shimmer-btn-spin-around calc(var(--speed) * 2) infinite linear;
}
</style>

View File

@@ -0,0 +1 @@
export { default as ShimmerButton } from "./ShimmerButton.vue";

View File

@@ -0,0 +1,159 @@
<template>
<div
ref="containerRef"
class="relative size-full overflow-hidden will-change-transform"
:style="{ background }"
>
<canvas
ref="canvasRef"
class="absolute inset-0 size-full"
/>
</div>
</template>
<script setup lang="ts">
import { useRafFn, templateRef } from "@vueuse/core";
import { ref, onMounted, onBeforeUnmount } from "vue";
interface Props {
background?: string;
particleColor?: string;
minSize?: number;
maxSize?: number;
speed?: number;
particleDensity?: number;
}
interface Particle {
x: number;
y: number;
size: number;
opacity: number;
vx: number;
vy: number;
phase: number;
phaseSpeed: number;
}
const props = withDefaults(defineProps<Props>(), {
background: "#0d47a1",
particleColor: "#ffffff",
minSize: 1,
maxSize: 3,
speed: 4,
particleDensity: 120,
});
const containerRef = templateRef<HTMLElement | null>("containerRef");
const canvasRef = templateRef<HTMLCanvasElement | null>("canvasRef");
const particles = ref<Particle[]>([]);
const ctx = ref<CanvasRenderingContext2D | null>(null);
// Adjust canvas size on mount and resize
function resizeCanvas() {
if (!canvasRef.value || !containerRef.value) return;
const dpr = window.devicePixelRatio || 1;
const rect = containerRef.value.getBoundingClientRect();
canvasRef.value.width = rect.width * dpr;
canvasRef.value.height = rect.height * dpr;
if (ctx.value) {
ctx.value.scale(dpr, dpr);
}
}
function generateParticles(): void {
const newParticles: Particle[] = [];
const count = props.particleDensity;
for (let i = 0; i < count; i++) {
const baseSpeed = 0.05;
const speedVariance = Math.random() * 0.3 + 0.7;
newParticles.push({
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * (props.maxSize - props.minSize) + props.minSize,
opacity: Math.random() * 0.5 + 0.3,
vx: (Math.random() - 0.5) * baseSpeed * speedVariance * props.speed,
vy: ((Math.random() - 0.5) * baseSpeed - baseSpeed * 0.3) * speedVariance * props.speed,
phase: Math.random() * Math.PI * 2,
phaseSpeed: 0.015,
});
}
particles.value = newParticles;
}
function updateAndDrawParticles() {
if (!ctx.value || !canvasRef.value) return;
const canvas = canvasRef.value;
ctx.value.clearRect(0, 0, canvas.width, canvas.height);
particles.value = particles.value.map((particle) => {
let newX = particle.x + particle.vx;
let newY = particle.y + particle.vy;
if (newX < -2) newX = 102;
if (newX > 102) newX = -2;
if (newY < -2) newY = 102;
if (newY > 102) newY = -2;
const newPhase = (particle.phase + particle.phaseSpeed) % (Math.PI * 2);
const opacity = 0.3 + (Math.sin(newPhase) * 0.3 + 0.3);
// Draw particle
ctx.value!.beginPath();
ctx.value!.arc(
(newX * canvas.width) / 100,
(newY * canvas.height) / 100,
particle.size,
0,
Math.PI * 2,
);
ctx.value!.fillStyle = `${props.particleColor}${Math.floor(opacity * 255)
.toString(16)
.padStart(2, "0")}`;
ctx.value!.fill();
return {
...particle,
x: newX,
y: newY,
phase: newPhase,
opacity,
};
});
}
const { pause, resume } = useRafFn(updateAndDrawParticles, { immediate: false });
// Handle window resize
let resizeObserver: ResizeObserver | undefined;
onMounted(() => {
if (!canvasRef.value) return;
ctx.value = canvasRef.value.getContext("2d");
resizeCanvas();
generateParticles();
// Set up resize observer
resizeObserver = new ResizeObserver(resizeCanvas);
if (containerRef.value) {
resizeObserver.observe(containerRef.value);
}
resume();
});
onBeforeUnmount(() => {
pause();
if (resizeObserver && containerRef.value) {
resizeObserver.unobserve(containerRef.value);
}
});
</script>

View File

@@ -0,0 +1 @@
export { default as Sparkles } from "./Sparkles.vue";

View File

@@ -0,0 +1,80 @@
<!-- ParentSize.vue -->
<template>
<div ref="target" :style="mergedStyles" :class="cn('w-full h-full', props.class)" v-bind="attrsWithoutClassAndStyle">
<slot />
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, useAttrs } from 'vue';
import { useDebounceFn, useResizeObserver } from '@vueuse/core';
import { cn } from '@/shadcn/lib/utils';
const props = defineProps({
class: String,
debounceTime: {
type: Number,
default: 300,
},
ignoreDimensions: {
type: [Array, String],
default: () => [],
},
parentSizeStyles: Object,
enableDebounceLeadingCall: {
type: Boolean,
default: true,
},
});
const attrs = useAttrs();
const target = ref<HTMLElement | null>(null);
const state = reactive({
width: 0,
height: 0,
top: 0,
left: 0,
});
const mergedStyles = computed(() => ({
...props.parentSizeStyles,
...(attrs.style as object),
}));
const mergedClass = computed(() => ['w-full h-full', props.class]);
const attrsWithoutClassAndStyle = computed(() => {
const { class: _, style: __, ...rest } = attrs;
return rest;
});
const normalizedIgnore = computed(() =>
Array.isArray(props.ignoreDimensions) ? props.ignoreDimensions : [props.ignoreDimensions],
);
function updateDimensions(rect: DOMRectReadOnly) {
const { width, height, top, left } = rect;
const newState = { width, height, top, left };
const hasChange = Object.keys(newState).some(
(key) => state[key as keyof typeof state] !== newState[key as keyof typeof state],
);
if (!hasChange) return;
const shouldUpdate = !Object.keys(newState).every((key) =>
normalizedIgnore.value.includes(key as keyof typeof state),
);
if (shouldUpdate) {
Object.assign(state, newState);
}
}
const debouncedUpdate = useDebounceFn(updateDimensions, props.debounceTime);
useResizeObserver(target, (entries) => {
const entry = entries[0];
if (entry) debouncedUpdate(entry.contentRect);
});
</script>

View File

@@ -0,0 +1,112 @@
<!-- Spline.vue -->
<template>
<ParentSize
:parent-size-styles="parentSizeStyles"
:debounce-time="50"
v-bind="$attrs"
>
<template #default>
<canvas
ref="canvasRef"
:style="canvasStyle"
/>
<slot v-if="isLoading" />
</template>
</ParentSize>
</template>
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ref, onMounted, onUnmounted, computed } from "vue";
import { Application, type SplineEventName } from "@splinetool/runtime";
import { useDebounceFn } from "@vueuse/core";
import ParentSize from "./ParentSize.vue";
const props = defineProps({
scene: {
type: String,
required: true,
},
onLoad: Function,
renderOnDemand: {
type: Boolean,
default: true,
},
style: Object,
});
let cleanUpFns: any[] = [];
const emit = defineEmits([
"error",
...[
"spline-mouse-down",
"spline-mouse-up",
"spline-mouse-hover",
"spline-key-down",
"spline-key-up",
"spline-start",
"spline-look-at",
"spline-follow",
"spline-scroll",
],
]);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const isLoading = ref(true);
const splineApp = ref<Application | null>(null);
const parentSizeStyles = computed(() => ({
overflow: "hidden",
...props.style,
}));
const canvasStyle = computed(() => ({
display: isLoading.value ? "none" : "block",
width: "100%",
height: "100%",
}));
function eventHandler(name: SplineEventName, handler?: (e: any) => void) {
if (!handler || !splineApp.value) return;
const debouncedHandler = useDebounceFn(handler, 50, {
maxWait: 100,
});
splineApp.value.addEventListener(name, debouncedHandler);
return () => splineApp.value?.removeEventListener(name, debouncedHandler);
}
onMounted(async () => {
if (!canvasRef.value) return;
try {
splineApp.value = new Application(canvasRef.value, {
renderOnDemand: props.renderOnDemand,
});
await splineApp.value.load(props.scene);
cleanUpFns = [
eventHandler("mouseDown", (e: any) => emit("spline-mouse-down", e)),
eventHandler("mouseUp", (e: any) => emit("spline-mouse-up", e)),
eventHandler("mouseHover", (e: any) => emit("spline-mouse-hover", e)),
eventHandler("keyDown", (e: any) => emit("spline-key-down", e)),
eventHandler("keyUp", (e: any) => emit("spline-key-up", e)),
eventHandler("start", (e: any) => emit("spline-start", e)),
eventHandler("lookAt", (e: any) => emit("spline-look-at", e)),
eventHandler("follow", (e: any) => emit("spline-follow", e)),
eventHandler("scroll", (e: any) => emit("spline-scroll", e)),
].filter(Boolean);
isLoading.value = false;
props.onLoad?.(splineApp.value);
} catch (err) {
emit("error", err);
}
});
onUnmounted(() => {
cleanUpFns.forEach((fn) => fn?.());
splineApp.value?.dispose();
});
</script>

View File

@@ -0,0 +1,2 @@
export { default as Spline } from "./Spline.vue";
export { default as ParentSize } from "./ParentSize.vue";

View 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 = '+/-',
}

View 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; /* 推荐 */
}

View 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>

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Some files were not shown because too many files have changed in this diff Show More