Compare commits

..

317 Commits

Author SHA1 Message Date
renovatebot fcef8af513 chore(deps): update unplugin packages
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m22s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m28s
2026-02-17 06:24:55 +08:00
renovatebot 7934872e5f chore(deps): update dependency vite-plugin-webfont-dl to v3.12.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m14s
测试最新依赖 / build-and-test (push) Failing after 1m44s
测试最新依赖 / playwright (push) Failing after 2m23s
2026-02-17 02:27:10 +08:00
renovatebot ea20a081c6 chore(deps): update dependency vite-plugin-vue-devtools to v8.0.6
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m8s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m10s
测试最新依赖 / playwright (push) Failing after 1m49s
测试最新依赖 / build-and-test (push) Failing after 1m55s
2026-02-07 20:13:58 +08:00
renovatebot 3d2699a499 chore(deps): update vite packages
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
测试最新依赖 / playwright (push) Failing after 1m41s
测试最新依赖 / build-and-test (push) Failing after 2m8s
2026-02-05 20:17:40 +08:00
renovatebot 457a57a588 chore(deps): update dependency prettier to v3.8.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m57s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m15s
测试最新依赖 / build-and-test (push) Failing after 1m38s
测试最新依赖 / playwright (push) Failing after 2m23s
2026-02-05 02:05:33 +08:00
renovatebot 2ae5c50997 chore(deps): update dependency @primeuix/themes to v2.0.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
测试最新依赖 / playwright (push) Failing after 1m54s
测试最新依赖 / build-and-test (push) Failing after 2m12s
2026-02-02 23:28:33 +08:00
renovatebot 4507a5a7d0 chore(deps): update dependency prettier to v3.8.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m9s
测试最新依赖 / build-and-test (push) Failing after 1m41s
测试最新依赖 / playwright (push) Failing after 2m18s
2026-01-29 10:04:56 +08:00
renovatebot 17730cfb10 chore(deps): update dependency @pinia/colada to ^0.21.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
测试最新依赖 / playwright (push) Failing after 1m54s
测试最新依赖 / build-and-test (push) Failing after 2m10s
2026-01-21 00:26:04 +08:00
renovatebot c4b93140f0 chore(deps): update dependency vite to v7.3.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m13s
测试最新依赖 / build-and-test (push) Failing after 1m29s
测试最新依赖 / playwright (push) Successful in 2m23s
2026-01-10 16:06:22 +08:00
renovatebot 1a9257249f chore(deps): update dependency @pinia/colada to ^0.20.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
测试最新依赖 / build-and-test (push) Successful in 1m32s
测试最新依赖 / playwright (push) Successful in 1m57s
2026-01-07 01:48:38 +08:00
严浩 66280dc6a1 chore(devcontainer): remove AI setup configurations
CI/CD Pipeline / playwright (push) Successful in 1m55s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
测试最新依赖 / build-and-test (push) Successful in 1m44s
测试最新依赖 / playwright (push) Successful in 1m49s
2026-01-04 23:31:37 +08:00
renovatebot 16c93a82a2 chore(deps): replace dependency @tsconfig/node22 with @tsconfig/node24
renovate/stability-days Updates have not met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
测试最新依赖 / build-and-test (push) Successful in 1m28s
测试最新依赖 / playwright (push) Successful in 1m53s
2026-01-02 01:06:25 +08:00
renovatebot 16d4b3f749 chore(deps): update primevue pkgs to v4.5.4
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
测试最新依赖 / build-and-test (push) Successful in 1m43s
测试最新依赖 / playwright (push) Successful in 1m54s
2026-01-01 22:28:04 +08:00
renovatebot 42f63f7134 chore(deps): update dependency unplugin-vue-router to v0.19.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
2026-01-01 19:08:32 +08:00
renovatebot 9d12927793 chore(deps): update dependency @pinia/colada to ^0.19.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m59s
测试最新依赖 / playwright (push) Successful in 1m59s
测试最新依赖 / build-and-test (push) Successful in 2m7s
2025-12-31 06:25:53 +08:00
renovatebot bb82a6ab83 chore(deps): update dependency @vitejs/plugin-vue-jsx to v5.1.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
测试最新依赖 / playwright (push) Successful in 1m41s
测试最新依赖 / build-and-test (push) Successful in 1m51s
2025-12-27 12:15:40 +08:00
renovatebot 76d2a45015 chore(deps): update primevue pkgs to v4.5.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
测试最新依赖 / playwright (push) Failing after 1m2s
测试最新依赖 / build-and-test (push) Failing after 1m28s
2025-12-24 23:32:51 +08:00
严浩 935251ee53 chore(devcontainer): update configuration and lifecycle scripts
CI/CD Pipeline / playwright (push) Successful in 1m43s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m14s
测试最新依赖 / playwright (push) Successful in 1m46s
测试最新依赖 / build-and-test (push) Successful in 2m6s
2025-12-24 00:19:05 +08:00
renovatebot 5472f1c9b9 chore(deps): update dependency eslint-plugin-perfectionist to v5
CI/CD Pipeline / playwright (push) Successful in 2m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
测试最新依赖 / build-and-test (push) Successful in 1m54s
测试最新依赖 / playwright (push) Successful in 1m58s
2025-12-23 01:06:20 +08:00
renovatebot 744703811a chore(deps): update primevue pkgs to v4.5.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
2025-12-22 19:25:58 +08:00
renovatebot be3d59691b chore(deps): update dependency primelocale to v2.2.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
测试最新依赖 / build-and-test (push) Successful in 1m35s
测试最新依赖 / playwright (push) Successful in 1m46s
2025-12-19 04:51:01 +08:00
renovatebot f6bccd7536 chore(deps): update dependency unplugin-vue-router to v0.19.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
测试最新依赖 / build-and-test (push) Successful in 1m59s
测试最新依赖 / playwright (push) Successful in 2m28s
2025-12-18 23:35:22 +08:00
renovatebot 4673d622b6 chore(deps): update dependency vite to v7.3.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m18s
2025-12-18 20:56:11 +08:00
renovatebot 0e467a1e9d chore(deps): update primevue pkgs to v4.5.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m15s
测试最新依赖 / build-and-test (push) Successful in 1m28s
测试最新依赖 / playwright (push) Successful in 2m16s
2025-12-17 23:51:43 +08:00
renovatebot 7a5610c0dc chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
测试最新依赖 / playwright (push) Successful in 1m42s
测试最新依赖 / build-and-test (push) Successful in 2m9s
2025-12-17 03:32:37 +08:00
renovatebot 80d989f6d0 chore(deps): update dependency @pinia/colada to ^0.18.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
2025-12-17 00:53:21 +08:00
renovatebot 6097d39752 chore(deps): update dependency @primeuix/themes to v2
CI/CD Pipeline / playwright (push) Successful in 2m28s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
2025-12-16 23:17:01 +08:00
renovatebot 4bc62221b6 chore(deps): update dependency @vitejs/plugin-vue to v6.0.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
测试最新依赖 / build-and-test (push) Successful in 1m33s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-15 17:45:16 +08:00
renovatebot c405bc9481 chore(deps): update dependency @intlify/unplugin-vue-i18n to v11.0.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m44s
测试最新依赖 / build-and-test (push) Successful in 1m44s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-14 21:56:20 +08:00
renovatebot 54cc46b33a chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m58s
测试最新依赖 / build-and-test (push) Successful in 1m29s
测试最新依赖 / playwright (push) Successful in 2m26s
2025-12-12 23:14:48 +08:00
renovatebot 65ee7cccb3 chore(deps): update dependency happy-dom to v20.0.11
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m12s
2025-12-12 09:52:28 +08:00
renovatebot 6da6e59b6d chore(deps): update primevue pkgs to v4.5.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
2025-12-12 03:56:01 +08:00
renovatebot 8e6d7488f4 chore(deps): update dependency vite-plugin-checker to ^0.12.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
测试最新依赖 / build-and-test (push) Successful in 1m32s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-12 01:36:17 +08:00
严浩 9e050306bb feat: refactor Vite plugin loading mechanism and improve plugin management
CI/CD Pipeline / playwright (push) Successful in 3m3s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m6s
2025-12-12 00:09:53 +08:00
gitea_1-h.cc 7f1811098f 更新 .github/workflows/测试最新依赖.yaml
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
2025-12-11 21:04:48 +08:00
renovatebot 40b3686f2e chore(deps): update dependency @playwright/test to v1.57.0
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-12-11 21:03:49 +08:00
gitea_1-h.cc dfc64ecece 更新 .github/workflows/ci-cd.yaml
CI/CD Pipeline / playwright (push) Failing after 2m0s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-12-11 20:58:32 +08:00
renovatebot 1df5110034 chore(deps): update dependency @prettier/plugin-oxc to ^0.1.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
2025-12-11 17:44:41 +08:00
renovatebot 731a3da55d chore(deps): update dependency @vueuse/core to v14.1.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m48s
2025-12-11 15:08:52 +08:00
renovatebot 4e5b3cb96c chore(deps): update dependency vite to v7.2.7
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m56s
2025-12-11 11:47:59 +08:00
renovatebot 5e8df2572f chore(deps): update dependency @cloudflare/vite-plugin to v1.15.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
测试最新依赖 / build-and-test (push) Successful in 1m41s
测试最新依赖 / playwright (push) Failing after 2m12s
2025-12-11 01:01:20 +08:00
严浩 7d6474c92b refactor: update loadPlugin to use async/await and improve vueRouter configuration
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m40s
2025-12-11 00:05:04 +08:00
严浩 86920da611 refactor: update component names to PascalCase and improve structure
CI/CD Pipeline / playwright (push) Successful in 2m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
- Changed component names from kebab-case to PascalCase in various files for consistency.
- Updated `<router-view>` and `<transition>` to `<RouterView>` and `<Transition>` respectively in App.vue and base-layout.vue.
- Refactored AppNaiveUIProvider.vue to use PascalCase for Naive UI providers.
- Adjusted language and theme switch buttons to use PascalCase for icon components.
- Updated button components in demo pages to use PascalCase for Naive UI buttons.
- Modified ESLint rules in route message files to use perfectionist/sort-objects for better key sorting.
- Enhanced Vite plugin files to export loadPlugin functions for better plugin management.
- Improved plugin loading logic to handle errors and warnings more effectively.
2025-12-10 22:52:23 +08:00
renovatebot 3828f12a2d chore(deps): update dependency vue-i18n to v11.2.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
2025-12-10 14:06:40 +08:00
renovatebot f269a25968 chore(deps): update dependency vitest to v4.0.14
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m41s
测试最新依赖 / playwright (push) Failing after 1m58s
测试最新依赖 / build-and-test (push) Successful in 2m12s
2025-12-09 23:45:38 +08:00
renovatebot 4a97eca056 chore(deps): update yanhao98/composite-actions digest to faab20a
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
2025-12-09 07:24:47 +08:00
renovatebot e91476f55c chore(deps): update dependency @pinia/colada to v0.17.9
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m41s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m2s
测试最新依赖 / build-and-test (push) Failing after 2m3s
测试最新依赖 / playwright (push) Failing after 2m25s
2025-12-08 20:33:22 +08:00
renovatebot afd08f05fc chore(deps): update dependency @iconify-json/material-symbols to v1.2.48
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
2025-12-08 15:17:29 +08:00
renovatebot b2f9b9d3b3 chore(deps): update dependency unplugin-vue-router to ^0.19.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m3s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
测试最新依赖 / playwright (push) Failing after 1m47s
测试最新依赖 / build-and-test (push) Failing after 1m56s
2025-12-08 03:37:20 +08:00
严浩 9cb70b7827 chore: remove unused h-setup-ai-tools command and update vue-devtools plugin logic
CI/CD Pipeline / playwright (push) Successful in 2m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
2025-12-07 16:38:52 +08:00
renovatebot 52b1e4fb6b chore(deps): update all non-major dependencies to v3.1.5
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m48s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m40s
2025-12-07 05:42:51 +08:00
renovatebot f101237792 chore(deps): update dependency vite to v7.2.6
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m41s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
测试最新依赖 / build-and-test (push) Successful in 1m36s
测试最新依赖 / playwright (push) Failing after 2m10s
2025-12-04 15:17:40 +08:00
renovatebot c84380836a chore(deps): update dependency unplugin-vue-router to ^0.18.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m7s
测试最新依赖 / playwright (push) Failing after 1m39s
测试最新依赖 / build-and-test (push) Successful in 2m16s
2025-12-01 21:04:41 +08:00
renovatebot aca2c29946 chore(deps): update dependency unplugin-vue-router to v0.17.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m52s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m39s
测试最新依赖 / build-and-test (push) Successful in 1m35s
测试最新依赖 / playwright (push) Failing after 2m29s
2025-12-01 02:12:49 +08:00
renovatebot 9d1374d53c chore(deps): update dependency unplugin-auto-import to v20.3.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m42s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
2025-11-30 12:22:47 +08:00
严浩 814130af34 refactor: devcontainer
CI/CD Pipeline / playwright (push) Successful in 2m40s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
测试最新依赖 / build-and-test (push) Successful in 1m30s
测试最新依赖 / playwright (push) Failing after 2m12s
2025-11-23 00:00:26 +08:00
renovatebot 4a5ae8d2de chore(deps): update vite packages
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m53s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
2025-11-22 10:52:13 +08:00
renovatebot 18fd9cb226 chore(deps): update dependency rollup to v4.53.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m51s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
2025-11-22 06:03:12 +08:00
renovatebot 188c813447 chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m6s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m11s
测试最新依赖 / playwright (push) Successful in 2m5s
测试最新依赖 / build-and-test (push) Successful in 2m9s
2025-11-22 00:48:21 +08:00
renovatebot 737da9c585 chore(deps): update dependency @pinia/colada to v0.17.8
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
2025-11-21 19:01:10 +08:00
renovatebot 80090fdb90 chore(deps): update dependency @cloudflare/vite-plugin to v1.14.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
2025-11-21 06:43:22 +08:00
renovatebot abcf706acc chore(deps): update dependency vite-plugin-vue-devtools to v8.0.5
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m56s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m4s
2025-11-21 03:52:52 +08:00
renovatebot 760b75083d chore(deps): update dependency sharp to v0.34.5
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
测试最新依赖 / build-and-test (push) Successful in 1m32s
测试最新依赖 / playwright (push) Successful in 2m7s
2025-11-20 23:58:52 +08:00
renovatebot 0b4575b181 chore(deps): update dependency pinia to v3.0.4
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m52s
测试最新依赖 / build-and-test (push) Successful in 1m48s
测试最新依赖 / playwright (push) Successful in 1m51s
2025-11-19 18:59:22 +08:00
renovatebot 57fb6d5256 chore(deps): update dependency unplugin-vue-router to ^0.17.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m40s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
测试最新依赖 / playwright (push) Successful in 1m38s
测试最新依赖 / build-and-test (push) Successful in 1m48s
2025-11-19 01:15:14 +08:00
renovatebot e516dd4ba6 chore(deps): update dependency @cloudflare/vite-plugin to v1.13.19
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m7s
2025-11-18 22:50:15 +08:00
renovatebot 6fc619315a chore(deps): update dependency vite-plugin-fake-server to v2.2.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Failing after 2m21s
2025-11-18 20:01:02 +08:00
renovatebot a5082ef4e4 chore(deps): update dependency unplugin-vue-router to v0.16.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Failing after 1m49s
2025-11-18 17:31:38 +08:00
renovatebot 9be884be20 chore(deps): update dependency @pinia/colada to v0.17.7
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m57s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
2025-11-18 14:33:36 +08:00
Yanhao-MBP bbec2a3ff9 refactor(devcontainer): 合并配置脚本为单一环境设置脚本
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m48s
2025-11-18 13:41:14 +08:00
Yanhao aa851bb920 chore(devcontainer): update image tag to main in devcontainer configuration
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m58s
2025-11-18 10:48:41 +08:00
Yanhao 5610d3ea20 fix(.gitignore): add .pnpm-store to ignore list 2025-11-18 10:43:15 +08:00
renovatebot 7a20a45035 chore(deps): update yanhao98/composite-actions digest to 94776ed
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m49s
2025-11-18 06:58:54 +08:00
renovatebot db3f77d6d3 chore(deps): update all non-major dependencies to v3.1.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
2025-11-18 03:39:30 +08:00
严浩 e6731afff5 chore: update devcontainer configuration and add setup scripts
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
测试最新依赖 / build-and-test (push) Successful in 1m31s
测试最新依赖 / playwright (push) Successful in 2m26s
2025-11-18 00:47:42 +08:00
renovatebot 3affb21d99 chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m59s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
测试最新依赖 / playwright (push) Successful in 1m50s
测试最新依赖 / build-and-test (push) Successful in 2m6s
2025-11-15 17:32:55 +08:00
严浩 2585b70f6b fix(Dockerfile): comment out VS Code shell integration line in zsh configuration
CI/CD Pipeline / playwright (push) Successful in 1m59s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
2025-11-15 16:20:56 +08:00
严浩 a4e162cab0 build(devcontainer): configure pnpm cache and workspace settings
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m43s
测试最新依赖 / playwright (push) Successful in 1m49s
测试最新依赖 / build-and-test (push) Successful in 1m55s
2025-11-13 23:14:49 +08:00
严浩 88cc365935 build(devcontainer): adjust pnpm store directory creation 2025-11-13 22:51:55 +08:00
严浩 33801ca211 style(eslint): enforce padding between blocks in vue components
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m56s
2025-11-13 22:16:40 +08:00
严浩 4f79426206 build(devcontainer): 添加 VS Code shell 集成配置
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
2025-11-13 13:52:38 +08:00
严浩 1185843982 feat(.devcontainer): 调整 VS Code 扩展配置与 Copilot 相关设置
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m39s
2025-11-13 13:37:04 +08:00
renovatebot 0620651b12 chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
测试最新依赖 / build-and-test (push) Successful in 1m48s
测试最新依赖 / playwright (push) Successful in 2m27s
2025-11-13 01:07:40 +08:00
严浩 a82190d4aa chore(devcontainer): configure chat tool auto-approval
CI/CD Pipeline / playwright (push) Successful in 1m55s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m45s
2025-11-13 00:27:35 +08:00
严浩 d0a7afd5df chore(devcontainer): refine configuration and simplify tasks
CI/CD Pipeline / playwright (push) Successful in 1m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m12s
2025-11-12 23:56:59 +08:00
严浩 10fcb40d1d docs: 更新开发服务器部分,添加关于 Playwright 测试的说明
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
2025-11-12 22:37:44 +08:00
严浩 c7dccb0664 build(tooling): configure pnpm workspaces and update dev tooling
CI/CD Pipeline / playwright (push) Failing after 1m13s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-11-12 14:17:15 +08:00
严浩 1bcecabf7b chore(devcontainer): add todo-tree vscode extension
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
2025-11-12 11:31:45 +08:00
严浩 b7b508af08 chore(devcontainer): add gcmp vscode extension
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-11-12 11:29:47 +08:00
严浩 917b3c7324 fix(devcontainer): 优化端口转发配置并添加 iflow 权限修复脚本
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-11-12 11:19:07 +08:00
严浩 6a73a274bd chore(vscode): enable i18n-ally editing and configure json formatting
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m58s
2025-11-12 11:09:09 +08:00
严浩 82897ea690 build(devcontainer): add iflow-cli support
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-11-12 11:05:57 +08:00
严浩 e04310c2c9 style(devcontainer): format JSON configuration and add reference link 2025-11-12 11:05:57 +08:00
renovatebot d2a21581ff chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m32s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
测试最新依赖 / build-and-test (push) Successful in 1m41s
测试最新依赖 / playwright (push) Successful in 1m52s
2025-11-11 16:39:11 +08:00
严浩 a2c49cf600 refactor: 删除 use-safe-n-form-auto-imports.tsx 文件
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m5s
2025-11-11 12:42:15 +08:00
严浩 3429c75600 build(devcontainer): 添加容器名称配置
CI/CD Pipeline / playwright (push) Successful in 1m56s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m17s
测试最新依赖 / playwright (push) Successful in 1m59s
测试最新依赖 / build-and-test (push) Successful in 2m13s
2025-11-10 22:42:49 +08:00
renovatebot 0f57624cdf chore(deps): update dependency vite to v7.2.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m59s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m2s
2025-11-10 18:35:56 +08:00
严浩 68fc6ad1a3 fix(i18n-auto-imports): 修复菜单翻译缺失时的提示信息
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
2025-11-10 15:52:43 +08:00
严浩 b4727a1946 feat(devcontainer): 添加 antfu.unocss 插件支持
CI/CD Pipeline / playwright (push) Successful in 1m31s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m44s
2025-11-10 13:44:17 +08:00
严浩 f757b70174 build(devcontainer): use dynamic workspace folder name
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m39s
2025-11-10 12:53:33 +08:00
严浩 3033a8a707 build(devcontainer): 将PNPM环境变量从ARG改为ENV声明
CI/CD Pipeline / playwright (push) Successful in 1m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m8s
将Dockerfile中的PNPM_HOME和PATH从构建参数(ARG)改为环境变量(ENV)声明,
确保在容器运行时这些变量仍然有效。
2025-11-10 11:23:43 +08:00
严浩 fa77b772be build(devcontainer): 添加 host.docker.internal 作为主机网关
CI/CD Pipeline / playwright (push) Successful in 1m32s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m43s
2025-11-10 10:53:18 +08:00
严浩 2440605fce feat(vscode): 配置开发任务在文件夹打开时自动运行 [skip ci] 2025-11-10 00:46:18 +08:00
严浩 386b65cd05 Revert "fix(devcontainer): 修改 pnpm-store 卷挂载路径 [skip ci]"
This reverts commit b6fd7c698c.
2025-11-10 00:21:01 +08:00
严浩 b6fd7c698c fix(devcontainer): 修改 pnpm-store 卷挂载路径 [skip ci] 2025-11-10 00:11:14 +08:00
严浩 9376f8e68e feat(demos): 移除 Naive UI Demo 中的 SafeNForm 示例 [skip ci] 2025-11-10 00:07:22 +08:00
严浩 3a3e9aead5 docs(AGENTS.md): 移除关于 Naive UI 表单布局的冗余内容 2025-11-10 00:05:04 +08:00
严浩 97009158e8 refactor(scripts): 调整 all 脚本命令中任务执行顺序 [skip ci] 2025-11-10 00:04:34 +08:00
严浩 a9b916221c refactor(devcontainer): 调整 Dockerfile 和 devcontainer 配置
CI/CD Pipeline / playwright (push) Successful in 1m46s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m44s
测试最新依赖 / build-and-test (push) Successful in 2m13s
测试最新依赖 / playwright (push) Successful in 2m31s
将 DEVCONTAINER 和 SANDBOX 环境变量从 Dockerfile 移至 devcontainer.json,
以提升配置的灵活性和一致性。同时,更新注释内容以准确反映当前安装脚本的功能。
2025-11-09 22:26:03 +08:00
严浩 ac7cb6a31c build(devcontainer): 调整 pnpm LTS 版本配置时机
将 `pnpm env use lts --global` 命令从 Dockerfile 和 postCreateCommand 中移除,
并在 devcontainer.json 的 onCreateCommand 阶段添加该命令

[skip ci]
2025-11-09 22:20:34 +08:00
严浩 fdf928028c chore(pnpm-workspace): 清理工作区配置文件
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m44s
移除不必要的包路径配置和注释项
2025-11-09 21:58:19 +08:00
严浩 3f31d188f6 build(devcontainer): 更新 pnpm 配置为全局设置存储目录 [skip ci] 2025-11-09 21:53:19 +08:00
renovatebot c200206487 chore(deps): update dependency vite to v7.2.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m40s
2025-11-09 13:46:09 +08:00
严浩 b7aab0a6a5 build(devcontainer): 添加SANDBOX环境变量配置
CI/CD Pipeline / playwright (push) Successful in 1m46s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m41s
测试最新依赖 / build-and-test (push) Successful in 1m47s
测试最新依赖 / playwright (push) Successful in 1m56s
2025-11-09 02:49:47 +08:00
严浩 37a0d59f13 docs(package.json): 更新 devEngines.runtime 文档链接位置
CI/CD Pipeline / playwright (push) Successful in 1m34s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m42s
2025-11-09 02:28:26 +08:00
严浩 163af5e80c build(devcontainer): 优化 zsh 配置与 pnpm 环境设置
CI/CD Pipeline / playwright (push) Failing after 1m58s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-11-09 00:55:03 +08:00
renovatebot 8e148c6408 chore(deps): update dependency vite to v7.2.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m43s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
2025-11-08 20:35:14 +08:00
严浩 1f78af72fc refactor(devcontainer): 修改开发容器名称
CI/CD Pipeline / playwright (push) Successful in 1m56s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m3s
2025-11-08 19:07:00 +08:00
renovatebot 169e0984a4 chore(deps): update yanhao98/composite-actions digest to 336b588
CI/CD Pipeline / playwright (push) Successful in 2m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m2s
2025-11-08 18:46:53 +08:00
严浩 be9093fa77 feat(devcontainer): 调整挂载卷命名并优化容器命令执行流程
CI/CD Pipeline / playwright (push) Successful in 3m20s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m38s
2025-11-08 17:14:23 +08:00
严浩 5a6fdc0efd feat: 更新 Dockerfile 和 devcontainer.json,修改工作目录路径为 /wrkspc
CI/CD Pipeline / playwright (push) Successful in 2m53s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m57s
2025-11-08 16:22:54 +08:00
renovatebot 7551eed484 chore(deps): update dependency vue-tsc to v3.1.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m37s
2025-11-08 11:33:24 +08:00
严浩 a4068df0be feat: 更新 devcontainer.json,添加终端配置图标
CI/CD Pipeline / playwright (push) Successful in 2m22s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m26s
测试最新依赖 / build-and-test (push) Successful in 1m40s
测试最新依赖 / playwright (push) Successful in 2m35s
2025-11-08 03:00:56 +08:00
严浩 ccf61ee6c3 feat: 更新 Dockerfile 和 devcontainer.json
CI/CD Pipeline / playwright (push) Successful in 2m25s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m32s
2025-11-08 02:51:05 +08:00
严浩 3170fe889b chore: 清理 .npmrc 文件,更新 pnpm-workspace.yaml 配置
CI/CD Pipeline / playwright (push) Successful in 3m12s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m40s
2025-11-08 00:41:56 +08:00
严浩 aa55508ed7 feat: 更新 Dockerfile 和 devcontainer.json,添加别名和配置目录
CI/CD Pipeline / playwright (push) Successful in 2m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m13s
2025-11-07 11:44:59 +00:00
严浩 8d052bcac8 feat: 更新 Dockerfile 和 devcontainer.json,优化开发环境配置和扩展推荐
CI/CD Pipeline / playwright (push) Successful in 2m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m26s
2025-11-07 08:18:45 +00:00
严浩 ce2f5338de feat: 更新 VSCode 扩展推荐列表,添加 stylelint 和 lokalise.i18n-ally 2025-11-07 02:11:50 +00:00
renovatebot 966c40a338 chore(deps): update dependency @cloudflare/vite-plugin to v1.13.15
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m27s
测试最新依赖 / build-and-test (push) Successful in 1m50s
测试最新依赖 / playwright (push) Successful in 2m47s
2025-11-07 03:23:09 +08:00
严浩 fd42a4d6d5 chore(devcontainer): update forwarded ports to 4730 and 4731
CI/CD Pipeline / playwright (push) Successful in 3m2s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m13s
2025-11-06 17:43:14 +00:00
严浩 f883e0bec1 feat: 更新 pnpm 配置以设置全局存储目录 2025-11-07 01:38:48 +08:00
严浩 17bdbce8c8 feat: 添加 Dockerfile 和 devcontainer.json 文件以支持开发环境配置
CI/CD Pipeline / playwright (push) Successful in 2m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m48s
2025-11-07 01:34:21 +08:00
严浩 61e86e731d chore(husky): 移除时间命令以简化 commit-msg、post-merge 和 pre-commit 钩子
CI/CD Pipeline / playwright (push) Successful in 2m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m17s
2025-11-06 13:39:02 +08:00
严浩 ff790c7a3a feat: 添加 @intlify/eslint-plugin-vue-i18n 支持并更新相关配置
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-11-06 13:37:29 +08:00
renovatebot 076b8f3407 chore(deps): update dependency vitest to v4
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m48s
2025-11-06 00:46:19 +08:00
renovatebot 269d93187c chore(deps): update dependency @vueuse/core to v14
CI/CD Pipeline / playwright (push) Successful in 4m19s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m4s
测试最新依赖 / playwright (push) Successful in 2m24s
测试最新依赖 / build-and-test (push) Successful in 2m34s
2025-11-05 23:50:27 +08:00
renovatebot 33676dcc8b chore(deps): update dependency unplugin-vue-components to v30
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-11-05 23:50:18 +08:00
严浩 290aed74f2 fix(App): 调整 Toast 组件 z-index 并优化国际化缺失提示
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
- 为 Toast 组件添加 style="z-index: 5000" 以确保其层级高于其他 UI 元素
- 在开发模式下,当菜单翻译缺失时,通过 ToastService 弹出警告提示
- 更新 app-config 插件中的 __DEV__ 注释,明确其来源于 vite.config.ts 的 define 配置
- 提高 PrimeVue 的 zIndex 配置值,避免与自定义组件层级冲突
2025-11-05 23:23:28 +08:00
严浩 fd0543dcc2 build(package.json): 调整脚本执行顺序并增强格式化与检查命令
CI/CD Pipeline / playwright (push) Successful in 3m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m3s
2025-11-05 23:09:08 +08:00
严浩 fcd840d4fe feat(primevue): 配置PrimeVue的zIndex和类型约束
CI/CD Pipeline / playwright (push) Successful in 3m47s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m4s
2025-11-05 14:01:20 +08:00
严浩 2ba453bbed feat(app-store): 添加主题模式类型定义和初始值配置
CI/CD Pipeline / playwright (push) Successful in 2m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m31s
更新 useColorMode 钩子的类型参数,明确指定为 BasicColorSchema 类型,
并设置 initialValue 为 'auto',确保主题模式的类型安全和默认行为。
同时添加 attribute 配置项以支持 class 属性切换。
2025-11-05 13:45:15 +08:00
严浩 4b645bb916 feat(auto-imports): 添加 routeI18nT 类型声明
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-11-05 13:41:46 +08:00
renovatebot 0a8f1f8248 chore(deps): update dependency happy-dom to v20.0.8
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m40s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m0s
2025-11-05 08:29:46 +08:00
renovatebot eaafbef793 chore(deps): update dependency primelocale to v2.2.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 4m3s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m29s
2025-11-05 06:03:43 +08:00
renovatebot 9f57c8fe0b chore(deps): update dependency @cloudflare/vite-plugin to v1.13.14
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m3s
测试最新依赖 / build-and-test (push) Successful in 1m50s
测试最新依赖 / playwright (push) Successful in 2m3s
2025-11-04 22:48:37 +08:00
严浩 e912b2c710 feat(i18n): 导出路由国际化实例的翻译函数
CI/CD Pipeline / playwright (push) Successful in 5m15s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m39s
2025-11-04 11:15:25 +08:00
严浩 c0c5e9763e feat(i18n): 优化国际化路由标题设置逻辑并修复响应式问题
CI/CD Pipeline / playwright (push) Successful in 4m3s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-11-04 11:09:39 +08:00
严浩 6e915a5977 feat(eslint): 添加 jsonc 插件并启用 JSON 文件键排序规则
CI/CD Pipeline / playwright (push) Successful in 4m53s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m55s
测试最新依赖 / playwright (push) Successful in 2m1s
测试最新依赖 / build-and-test (push) Successful in 2m35s
新增 `eslint-plugin-jsonc` 插件,并在 `.json` 文件中启用 `jsonc/sort-keys` 规则,
以确保本地化文件中的键名按字母顺序排列。此举有助于减少多人协作时的合并冲突,
同时提升代码一致性和可维护性。

此外,调整了 VS Code 配置项顺序以优化读写逻辑,并更新相关依赖版本。
2025-11-04 01:28:01 +08:00
严浩 acd7c0db13 feat(i18n): 强制路由标题键名按字母顺序排序
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m41s
2025-11-03 23:41:36 +08:00
renovatebot 1ad46a62fd chore(deps): update dependency vite-plugin-image-optimizer to v2.0.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m47s
2025-11-03 20:47:17 +08:00
renovatebot bb02b796aa chore(deps): update dependency unplugin-vue-router to v0.16.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m26s
2025-11-03 17:45:06 +08:00
严浩 a4ea7ce56e fix(useMetaLayoutsMenuOptions): 移除调试日志输出
CI/CD Pipeline / playwright (push) Successful in 2m16s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m40s
2025-11-03 16:42:40 +08:00
严浩 166d76d980 feat(router): 支持通过 activeMenuName 指定菜单高亮路径
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m24s
2025-11-03 15:54:11 +08:00
严浩 f9f82e4d29 feat(i18n): 引入 routeI18nInstance 以支持路由菜单标题的多语言处理
CI/CD Pipeline / playwright (push) Successful in 5m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m48s
2025-11-03 15:03:22 +08:00
严浩 b4fcde324d feat(auth): 添加用户认证模块与登录页面
CI/CD Pipeline / playwright (push) Successful in 4m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m38s
2025-11-03 13:46:47 +08:00
严浩 b669889bb0 refactor(plugins): 重构插件自动加载逻辑
CI/CD Pipeline / playwright (push) Failing after 7m17s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-11-03 11:52:54 +08:00
严浩 8a3b9e03fd feat(router): 添加路由就绪等待逻辑
CI/CD Pipeline / playwright (push) Failing after 7m9s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-11-03 11:41:35 +08:00
renovatebot ec02658ede chore(deps): update dependency happy-dom to v20.0.7
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m23s
2025-11-03 07:18:20 +08:00
严浩 bd9acc06a8 feat(vite-plugins): 添加 vant-touch-emulator 和 eruda 脚本支持
CI/CD Pipeline / playwright (push) Successful in 4m20s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m21s
测试最新依赖 / build-and-test (push) Successful in 2m10s
测试最新依赖 / playwright (push) Successful in 2m15s
2025-11-03 01:16:27 +08:00
严浩 f790691d5a fix(useMetaLayoutsNMenuOptions): 优化路由标题设置逻辑
CI/CD Pipeline / playwright (push) Successful in 4m4s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m39s
2025-11-02 23:05:20 +08:00
严浩 838d5cfb6e fix(useMetaLayoutsMenuOptions): 调整路由标题设置逻辑以支持多语言
CI/CD Pipeline / playwright (push) Successful in 4m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m27s
2025-11-02 22:33:06 +08:00
renovatebot ac544a8ff5 chore(deps): update dependency wrangler to v4.45.2
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m18s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m25s
2025-11-02 18:20:03 +08:00
严浩 e7a4a7aff9 fix: 优化404页面返回按钮逻辑
CI/CD Pipeline / playwright (push) Successful in 3m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m27s
2025-11-02 17:19:16 +08:00
严浩 0d1a20d88d build(ci-cd): 添加构建时间环境变量支持
CI/CD Pipeline / playwright (push) Successful in 3m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m3s
2025-11-02 16:50:22 +08:00
renovatebot 19aaddc4e2 chore(deps): update dependency unplugin-icons to v22.5.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m42s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m20s
2025-11-02 12:19:18 +08:00
renovatebot 1e24193b84 chore(deps): update dependency primelocale to v2.2.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m41s
2025-11-02 09:14:20 +08:00
renovatebot cdec0c1a23 chore(deps): update dependency @types/node to v22.18.13
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m49s
2025-11-02 06:36:37 +08:00
renovatebot 94cdecb075 chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 4m6s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m24s
2025-11-02 04:01:43 +08:00
严浩 7f0cf5dd8f feat(package): 添加 sharp 依赖,版本为 0.34.4
CI/CD Pipeline / playwright (push) Successful in 5m0s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m17s
测试最新依赖 / build-and-test (push) Successful in 1m58s
测试最新依赖 / playwright (push) Successful in 2m39s
2025-10-31 19:15:38 +08:00
严浩 c8b8a3caa4 fix(use-safe-n-form): 更改 formRef 为 formInst
CI/CD Pipeline / playwright (push) Successful in 3m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m46s
2025-10-31 13:43:27 +08:00
严浩 48eb653f1a feat(demo): 添加 Naive UI 组件表单演示功能
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-31 13:40:23 +08:00
严浩 b3fbfe2d9d feat(i18n): 添加语言变更时更新html lang属性的功能
CI/CD Pipeline / playwright (push) Successful in 3m30s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m2s
测试最新依赖 / build-and-test (push) Successful in 2m5s
测试最新依赖 / playwright (push) Successful in 2m27s
2025-10-30 22:43:38 +08:00
严浩 da80cad976 chore(AppNaiveUIProvider): 移除 BUILD_TIME 常量声明
CI/CD Pipeline / playwright (push) Successful in 2m0s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m55s
2025-10-30 22:34:32 +08:00
严浩 d6e05a8b44 feat(vite.config): 添加 CI 环境变量日志输出
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-30 22:32:53 +08:00
renovatebot 5a196564d0 chore(deps): update dependency vite to v7.1.12
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m43s
2025-10-30 18:14:05 +08:00
renovatebot 7ec1751ba6 chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m59s
2025-10-30 15:24:10 +08:00
严浩 270a838185 feat(AppNaiveUIProvider): 添加日期本地化支持
CI/CD Pipeline / playwright (push) Successful in 4m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m23s
2025-10-30 14:32:12 +08:00
严浩 5b54fe5182 feat(naive-ui): 添加 Modal 命令式 API 支持并完善类型声明
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m16s
2025-10-30 14:18:06 +08:00
严浩 21676c11ff Revert "feat(AppNaiveUIProvider): 添加对 NModal 的支持"
CI/CD Pipeline / playwright (push) Successful in 3m58s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m21s
This reverts commit 75f461df0f.
2025-10-30 14:05:19 +08:00
严浩 75f461df0f feat(AppNaiveUIProvider): 添加对 NModal 的支持
CI/CD Pipeline / playwright (push) Successful in 4m6s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-10-30 14:00:43 +08:00
严浩 b623365e38 feat(demos): 添加 Naive UI 组件演示页面
CI/CD Pipeline / playwright (push) Successful in 4m2s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m6s
2025-10-30 13:37:31 +08:00
严浩 8130915d0e feat(env): 添加构建时间和提交信息的环境变量
CI/CD Pipeline / playwright (push) Successful in 5m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m42s
2025-10-30 12:08:58 +08:00
严浩 eeb72b24b5 feat(i18n): 更新 i18n-ally 配置
CI/CD Pipeline / playwright (push) Successful in 3m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m19s
2025-10-30 10:27:29 +08:00
严浩 68e320637e feat(main): 将 import.meta.glob 的 eager 选项添加注释说明,
CI/CD Pipeline / playwright (push) Successful in 2m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m34s
2025-10-30 09:35:51 +08:00
严浩 f81c7614be feat(store): 重构应用状态管理,移除旧的 app-store 并引入 app-store-auto-imports
CI/CD Pipeline / playwright (push) Successful in 4m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m33s
测试最新依赖 / playwright (push) Successful in 2m16s
测试最新依赖 / build-and-test (push) Failing after 2m26s
2025-10-29 23:37:31 +08:00
严浩 2874fdfaa7 feat(locales): 避免冲突 src/locales/demo
CI/CD Pipeline / playwright (push) Successful in 3m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m24s
2025-10-29 19:07:06 +08:00
严浩 7dd7ce73bc refactor(i18n): 重构国际化模块结构
CI/CD Pipeline / playwright (push) Successful in 4m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m39s
2025-10-29 13:16:18 +08:00
严浩 94d09d0bdd feat(dependencies): 添加 svgo 依赖项以优化图像处理
CI/CD Pipeline / playwright (push) Successful in 4m31s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m25s
2025-10-29 10:56:36 +08:00
严浩 b0b65b454c feat(toast): 添加一键打开所有 Toast 消息功能并优化显示时间
CI/CD Pipeline / playwright (push) Successful in 4m6s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m22s
测试最新依赖 / playwright (push) Successful in 1m50s
测试最新依赖 / build-and-test (push) Failing after 2m24s
2025-10-29 00:13:08 +08:00
严浩 c490cb1c8e feat: 添加 PrimeVue 组件演示页面
CI/CD Pipeline / playwright (push) Successful in 2m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m51s
2025-10-28 23:50:40 +08:00
严浩 33e8a4a5d6 refactor(env): 统一环境变量命名规范
CI/CD Pipeline / playwright (push) Successful in 3m46s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m5s
2025-10-28 19:18:00 +08:00
严浩 35640a2ade feat(vite-config): 扩展primevue库匹配规则
CI/CD Pipeline / playwright (push) Successful in 4m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m0s
2025-10-28 16:10:42 +08:00
严浩 8db7ded1b5 feat(dependencies): 添加 vue-component-type-helpers 依赖项 [skip ci] 2025-10-28 11:36:32 +08:00
严浩 aa8e3467d3 fix(scripts): 修复 _all 脚本的并行执行方式 [skip ci] 2025-10-28 11:20:53 +08:00
严浩 8ed289a917 fix(stylelint): 允许非 kebab-case 的类选择器 [skip ci] 2025-10-28 10:55:52 +08:00
严浩 88ca601d07 feat(i18n): 实现应用语言本地存储功能
CI/CD Pipeline / playwright (push) Successful in 3m46s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m6s
2025-10-28 09:55:28 +08:00
严浩 50911ada4e build(vite): 注释掉 layouts 手动分块逻辑
CI/CD Pipeline / playwright (push) Successful in 4m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m34s
测试最新依赖 / playwright (push) Successful in 1m51s
测试最新依赖 / build-and-test (push) Failing after 2m25s
2025-10-27 22:42:50 +08:00
严浩 d065c90e71 feat(dependencies): 添加 vue-memoize-dict 依赖项
CI/CD Pipeline / playwright (push) Successful in 4m24s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m29s
2025-10-27 22:09:24 +08:00
严浩 ad0a50d8c7 refactor(eslint): 添加 vue/attributes-order 规则以规范属性排序 2025-10-27 22:05:59 +08:00
严浩 d4d9620db2 refactor(useMetaLayoutsMenuOptions): 优化路由过滤与排序逻辑
CI/CD Pipeline / playwright (push) Successful in 4m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m27s
2025-10-27 14:04:58 +08:00
严浩 267bf75bc1 chore(useMetaLayoutsMenuOptions): 优化调试日志输出格式
CI/CD Pipeline / playwright (push) Successful in 3m55s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m11s
2025-10-27 13:46:06 +08:00
严浩 09ec2c7d12 feat(auto-import): 更新图标引入方式并升级相关依赖
CI/CD Pipeline / playwright (push) Successful in 2m8s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m3s
2025-10-27 12:46:08 +08:00
严浩 ad0df6b140 docs(agents): 更新 TypeScript/TSX 文件中图标自动导入的说明
CI/CD Pipeline / playwright (push) Successful in 4m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m22s
2025-10-27 12:18:51 +08:00
严浩 3269b10bfd fix(vite): 修复 rollup 手动分块逻辑
CI/CD Pipeline / playwright (push) Successful in 3m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m7s
2025-10-27 09:42:45 +08:00
严浩 047632b75f chore: lint format
CI/CD Pipeline / playwright (push) Failing after 8m18s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
测试最新依赖 / build-and-test (push) Failing after 1m44s
测试最新依赖 / playwright (push) Failing after 6m28s
2025-10-27 02:31:48 +08:00
严浩 7830ad0ebb Merge branch 'eslint-plugin-import' 2025-10-27 02:29:54 +08:00
严浩 a61a22569d fix(vite-config): 移除对 index.page.vue 文件的特殊处理逻辑 2025-10-27 02:29:25 +08:00
严浩 c27c8544de feat(vite-plugins): 更新cloudflare插件环境变量加载方式 2025-10-27 02:25:33 +08:00
严浩 aadf03edd0 chore: lint
CI/CD Pipeline / playwright (push) Failing after 8m43s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-27 02:01:08 +08:00
严浩 effb89c4dc feat(eslint): 添加 eslint-plugin-import 插件并配置导入规则 2025-10-27 01:54:35 +08:00
严浩 62a792b1da chore(tsconfig): 添加 unocss 和 commitlint 配置文件到编译排除列表
CI/CD Pipeline / playwright (push) Failing after 6m35s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-27 00:45:45 +08:00
严浩 2ec6f6c4ba docs(locales-4-route): 添加路由标题国际化机制说明文档
CI/CD Pipeline / playwright (push) Failing after 8m15s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-26 21:28:25 +08:00
严浩 a601f2bc55 feat(locales): 动态导入路由本地化消息文件
CI/CD Pipeline / playwright (push) Failing after 8m31s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-26 20:33:04 +08:00
严浩 5b0c0716fd feat(config): 添加 VITE_CLOUDFLARE_SERVER_ENABLED 环境变量支持
CI/CD Pipeline / playwright (push) Failing after 8m16s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-26 20:15:16 +08:00
严浩 463dcb269a fix(vite-config): 移除调试日志输出
CI/CD Pipeline / playwright (push) Failing after 8m40s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-26 18:18:00 +08:00
严浩 5fdc233dbc refactor(config): 重命名vite配置文件
CI/CD Pipeline / playwright (push) Failing after 8m20s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-10-26 17:28:29 +08:00
严浩 9a4ca844f7 feat(vite): 提取插件加载逻辑到独立模块
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-26 01:05:52 +08:00
严浩 0c45e2289f refactor(vite): 提取 rollup 配置到独立文件
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-26 01:01:12 +08:00
严浩 e251efff65 feat(vite-plugin-vue-devtools): 优化Vue DevTools插件加载逻辑 2025-10-26 00:42:22 +08:00
严浩 faef37f457 fix(materials): 修复移动端侧边栏样式问题,添加宽度为0的类名
CI/CD Pipeline / playwright (push) Successful in 2m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m33s
2025-10-25 23:41:25 +08:00
严浩 25b3658576 fix(build): set meta-layouts importMode to sync
CI/CD Pipeline / playwright (push) Successful in 3m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m7s
2025-10-25 23:27:30 +08:00
严浩 a1160bc394 refactor(layout): 重构基础布局头部组件结构 2025-10-25 23:24:50 +08:00
严浩 cc49ce2beb feat(menu): 支持菜单项显示排序编号并优化构建配置
CI/CD Pipeline / playwright (push) Successful in 2m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m23s
2025-10-25 19:06:05 +08:00
严浩 63556f7e82 ci(workflow): 调整单元测试与构建大小统计的执行顺序
CI/CD Pipeline / playwright (push) Successful in 3m48s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m4s
2025-10-25 18:35:51 +08:00
严浩 4f55acb3bf feat(vite): 优化开发插件加载逻辑与构建配置
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-25 18:34:34 +08:00
严浩 fb3c15aead feat(vite-plugins): 使用 consola 替换 console.log 以增强日志输出
CI/CD Pipeline / playwright (push) Successful in 3m57s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m18s
测试最新依赖 / build-and-test (push) Failing after 2m17s
测试最新依赖 / playwright (push) Successful in 2m25s
2025-10-25 04:59:11 +08:00
严浩 8a4733c7d2 feat(vite-plugins): 更新unplugin-auto-import插件配置
CI/CD Pipeline / playwright (push) Successful in 2m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m33s
2025-10-25 01:03:41 +08:00
严浩 d0f1ad0702 feat(auto-import): 添加 naive-ui 组件自动导入支持
CI/CD Pipeline / playwright (push) Successful in 4m8s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m35s
2025-10-25 00:47:07 +08:00
严浩 699b68990e feat(vite-plugins): 整合自动导入与组件解析插件功能
CI/CD Pipeline / playwright (push) Successful in 2m38s
CI/CD Pipeline / build-and-deploy (push) Failing after 4m0s
2025-10-25 00:25:24 +08:00
严浩 33ee02bce6 feat(demos): 添加函数式组件(TSX)示例并引入 highlight.js 2025-10-25 00:18:26 +08:00
严浩 83c5e14789 feat(eslint): 更新Vue宏定义顺序规则 2025-10-24 22:28:08 +08:00
严浩 a61d3cfe35 fix(settings): 忽略 SCSS、CSS 和 LESS 的未知规则警告 2025-10-24 22:14:34 +08:00
严浩 6018321d7e refactor(locales-4-route): 重命名消息自动导入文件 [skip ci] 2025-10-24 22:11:04 +08:00
严浩 91abfda3b2 fix(agents): 优化 NGrid 表单布局使用建议
CI/CD Pipeline / playwright (push) Successful in 4m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m15s
2025-10-24 18:57:11 +08:00
严浩 3fe6045e01 feat(locales): locales-4-route
CI/CD Pipeline / playwright (push) Successful in 4m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m40s
2025-10-24 12:52:39 +08:00
严浩 8d4e822e29 feat(vite-plugins): 为 fake-server 和 cloudflare 插件添加测试模式禁用逻辑
CI/CD Pipeline / playwright (push) Successful in 4m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m1s
2025-10-24 12:31:42 +08:00
严浩 6fcd0c54e8 fix(menu): 修复菜单展开逻辑,确保选中元素正确显示 2025-10-24 12:31:23 +08:00
严浩 c11473edb5 feat(composables): 提取菜单选项生成逻辑到独立 composables
CI/CD Pipeline / playwright (push) Successful in 4m0s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m33s
2025-10-24 12:14:46 +08:00
严浩 990f2811ae feat(router): 优化路由排序逻辑并添加菜单排序权重支持
CI/CD Pipeline / playwright (push) Successful in 2m8s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m21s
2025-10-24 11:08:54 +08:00
严浩 07eae7cab7 fix(layout): 调整 GlobalTab 样式以确保内容居中
CI/CD Pipeline / playwright (push) Successful in 4m31s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m6s
2025-10-24 09:46:45 +08:00
严浩 3c08e72f8d feat(dependencies): 添加 @iconify-json/clarity 依赖
CI/CD Pipeline / playwright (push) Successful in 4m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m6s
2025-10-24 09:43:08 +08:00
严浩 d816110b4b feat(layout): 添加语言切换功能并优化模板结构
CI/CD Pipeline / playwright (push) Successful in 2m42s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m56s
测试最新依赖 / build-and-test (push) Failing after 1m38s
测试最新依赖 / playwright (push) Successful in 2m31s
2025-10-23 23:56:58 +08:00
严浩 c9b75f55cc fix(demos): 更新示例页面显示文本
CI/CD Pipeline / playwright (push) Successful in 4m1s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m15s
2025-10-23 23:31:31 +08:00
严浩 063fb2641c refactor(router): 重命名路由插件文件并更新类型定义
CI/CD Pipeline / playwright (push) Successful in 2m6s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-10-23 23:27:10 +08:00
严浩 f222641f00 build(vite-plugins): 移除未使用的环境变量注释
CI/CD Pipeline / playwright (push) Successful in 2m14s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m35s
2025-10-23 23:10:40 +08:00
严浩 7d2d4675fc feat(layout): 调整基础布局结构与样式
CI/CD Pipeline / playwright (push) Successful in 4m30s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-10-23 23:06:37 +08:00
严浩 51ae1ad017 build(vite-plugins): 优化 vue-i18n 插件配置
CI/CD Pipeline / playwright (push) Successful in 3m51s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m34s
2025-10-23 22:41:28 +08:00
严浩 9aba215fd0 fix(01.unplugin-vue-i18n): 修正 i18n 资源文件路径配置
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-23 22:39:24 +08:00
严浩 a09b1da78d refactor(vite-plugins): 重新命名插件文件以优化加载顺序
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-10-23 22:38:16 +08:00
严浩 29d3000b50 feat(App): 添加 naive-ui 主题覆盖配置
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m23s
2025-10-23 18:25:22 +08:00
严浩 93ecda7617 feat(layout): 菜单支持国际化
CI/CD Pipeline / playwright (push) Successful in 3m42s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m34s
2025-10-23 18:15:45 +08:00
严浩 cec32dceb2 build(pnpm): 更新 onlyBuiltDependencies 配置 [skip ci] 2025-10-23 16:33:31 +08:00
严浩 4e43500084 feat: 根据环境变量控制是否显示 /demos 开头的路由
CI/CD Pipeline / playwright (push) Successful in 4m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m20s
2025-10-23 13:38:45 +08:00
严浩 a8b340403d fix(vite): 修正 locale 消息资源预编译选项的路径 2025-10-23 13:38:21 +08:00
严浩 768d3f8a88 fix: format
CI/CD Pipeline / playwright (push) Successful in 4m22s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m13s
2025-10-23 12:30:47 +08:00
严浩 7b9dee68cc refactor(vite): 重构 Vite 插件加载机制 2025-10-23 12:30:24 +08:00
renovatebot f1536ed24c chore(deps): update dependency @unhead/vue to v2.0.19
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 3m43s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m24s
2025-10-23 02:59:07 +08:00
严浩 5f62bebcc3 refactor(vite.config.ts): 修复未使用的参数命名
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m7s
测试最新依赖 / playwright (push) Successful in 2m45s
测试最新依赖 / build-and-test (push) Failing after 2m57s
2025-10-23 01:28:55 +08:00
严浩 a326c455ed feat(vite): 调整构建输出路径并增强 manualChunks 分包逻辑
CI/CD Pipeline / playwright (push) Successful in 4m20s
CI/CD Pipeline / build-and-deploy (push) Failing after 3m12s
2025-10-23 01:07:22 +08:00
严浩 e94c5fc372 docs(readme): 添加 vitesse 项目链接
CI/CD Pipeline / playwright (push) Successful in 4m16s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m20s
2025-10-22 22:28:51 +08:00
renovatebot 864e3dbb23 chore(deps): update pnpm to v10.18.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 4m41s
CI/CD Pipeline / playwright (push) Successful in 5m26s
2025-10-22 18:46:20 +08:00
严浩 0ac7636c74 feat(i18n): 集成 vue-i18n 并配置多语言支持
CI/CD Pipeline / playwright (push) Successful in 4m24s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m33s
(cherry picked from commit 15c9509318dd7d48c081c4ae4846598d9affb344)
2025-10-22 18:31:44 +08:00
严浩 917301dea6 feat: refactor sidebar management and routing metadata
CI/CD Pipeline / playwright (push) Successful in 2m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m39s
2025-10-22 14:38:31 +08:00
严浩 e95d883c23 feat(layout): 优化侧边栏菜单生成逻辑并增强类型安全
CI/CD Pipeline / playwright (push) Successful in 2m11s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m15s
2025-10-22 13:22:04 +08:00
严浩 4949e1c957 feat(menu): 为页面添加标题和布局配置支持 2025-10-22 11:29:27 +08:00
renovatebot 18360ac41d chore(deps): update all non-major dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m28s
CI/CD Pipeline / playwright (push) Successful in 3m54s
2025-10-22 05:57:42 +08:00
renovatebot 8000a39382 chore(deps): update primevue pkgs to v4.4.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 4m46s
CI/CD Pipeline / playwright (push) Successful in 5m28s
测试最新依赖 / build-and-test (push) Failing after 1m41s
测试最新依赖 / playwright (push) Successful in 2m1s
2025-10-22 03:17:25 +08:00
严浩 8d80a24203 fix(husky): 在钩子中添加时间跟踪以优化性能
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m9s
2025-10-22 00:48:33 +08:00
严浩 26f3348b28 feat(layout): base-layout 的 RouterView 添加 Transition 2025-10-22 00:45:28 +08:00
严浩 1afb28cb2b fix(layout): 更新移动侧边栏样式以支持深色模式
CI/CD Pipeline / playwright (push) Successful in 4m22s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m23s
2025-10-21 23:09:59 +08:00
严浩 e54272b68b feat(layout): 调整侧边栏菜单过滤逻辑并优化布局结构
CI/CD Pipeline / playwright (push) Successful in 6m22s
CI/CD Pipeline / build-and-deploy (push) Successful in 6m31s
2025-10-21 19:18:27 +08:00
严浩 642cac1493 docs(README): 更新参考资料链接 [skip ci]
添加 soybean-admin 2.0 预览地址链接到 README 文件中,方便用户快速访问相关资源。
2025-10-21 18:44:14 +08:00
严浩 437727fdd4 feat(menu): base-layout-sider.vue
CI/CD Pipeline / build-and-deploy (push) Successful in 5m1s
CI/CD Pipeline / playwright (push) Successful in 5m36s
2025-10-21 18:07:51 +08:00
renovatebot 7892c1f019 chore(deps): update dependency @types/node to v22.18.12
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 5m32s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m43s
2025-10-21 10:39:49 +08:00
严浩 cf48052b82 fix(router-plugin): 修复热更新处理逻辑的位置
CI/CD Pipeline / build-and-deploy (push) Successful in 2m29s
CI/CD Pipeline / playwright (push) Successful in 3m57s
2025-10-21 10:04:46 +08:00
renovatebot a324856c78 chore(deps): update lint dependencies
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 4m30s
CI/CD Pipeline / playwright (push) Successful in 5m6s
测试最新依赖 / playwright (push) Successful in 2m26s
测试最新依赖 / build-and-test (push) Failing after 2m33s
2025-10-21 03:17:33 +08:00
renovatebot fc4bcd99d2 chore(deps): update dependency unplugin-vue-router to ^0.16.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m26s
2025-10-20 23:31:08 +08:00
renovatebot a62577d5e4 chore(deps): update dependency vite-plugin-vue-devtools to v8.0.3
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m30s
CI/CD Pipeline / playwright (push) Successful in 4m2s
2025-10-20 21:03:42 +08:00
严浩 79fb40da5f build(pnpm): 更新 vite-plugin-vue-meta-layouts 版本至 0.6.1
CI/CD Pipeline / playwright (push) Successful in 5m28s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m52s
移除对该包的 patch 依赖,直接使用官方发布的新版本。
2025-10-20 19:23:49 +08:00
严浩 64296e1b3e fix(pnpm): 为 vite-plugin-vue-meta-layouts 应用补丁
CI/CD Pipeline / build-and-deploy (push) Failing after 2s
CI/CD Pipeline / playwright (push) Successful in 2m32s
2025-10-20 17:07:11 +08:00
renovatebot aa1618bf55 chore(deps): update dependency @playwright/test to v1.56.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 8m6s
CI/CD Pipeline / build-and-deploy (push) Successful in 8m37s
2025-10-20 10:45:37 +08:00
严浩 73c700e25b feat(index): 添加移动端防缩放与点击计数器功能
CI/CD Pipeline / playwright (push) Successful in 5m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m44s
测试最新依赖 / build-and-test (push) Failing after 1m56s
测试最新依赖 / playwright (push) Successful in 2m5s
2025-10-20 00:19:28 +08:00
严浩 9c5d9083ad ci(github-actions): 禁用主分支推送触发的测试工作流 [skip ci[
CI/CD Pipeline / build-and-deploy (push) Successful in 5m1s
CI/CD Pipeline / playwright (push) Successful in 5m36s
2025-10-19 23:35:32 +08:00
严浩 532dbaa5ee ci(workflow): 更新单元测试命令
测试最新依赖 / build-and-test (push) Failing after 1m36s
测试最新依赖 / playwright (push) Successful in 2m26s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m34s
CI/CD Pipeline / playwright (push) Successful in 5m19s
2025-10-19 23:29:20 +08:00
严浩 4421bdc864 build(workflows): 添加测试最新依赖的工作流配置
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
测试最新依赖 / build-and-test (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
测试最新依赖 / playwright (push) Has been cancelled
2025-10-19 23:28:19 +08:00
严浩 ebad5aec08 build(worker): 更新 Wrangler 生成的类型定义文件
/ build-and-test (push) Failing after 1m25s
CI/CD Pipeline / playwright (push) Has been cancelled
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
/ playwright (push) Has been cancelled
2025-10-19 23:26:48 +08:00
renovatebot 5ca4edc5d9 chore(deps): update dependency eslint-plugin-vue to v10.5.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m22s
CI/CD Pipeline / playwright (push) Successful in 3m50s
/ playwright (push) Successful in 2m38s
/ build-and-test (push) Failing after 1m24s
2025-10-18 20:22:25 +08:00
renovatebot 6158442e0d chore(deps): update dependency wrangler to v4.43.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 4m28s
CI/CD Pipeline / playwright (push) Successful in 5m20s
/ playwright (push) Successful in 1m58s
/ build-and-test (push) Failing after 1m35s
2025-10-18 03:22:35 +08:00
renovatebot 6697afba91 chore(deps): update dependency @vitest/eslint-plugin to v1.3.20
renovate/stability-days Updates have met minimum release age requirement
/ build-and-test (push) Failing after 1m28s
/ playwright (push) Successful in 1m47s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m25s
CI/CD Pipeline / playwright (push) Successful in 4m8s
2025-10-18 01:58:51 +08:00
renovatebot b9a29c2fb4 chore(deps): update dependency vite to v7.1.10
renovate/stability-days Updates have met minimum release age requirement
/ playwright (push) Successful in 1m57s
/ build-and-test (push) Failing after 1m47s
CI/CD Pipeline / playwright (push) Successful in 4m27s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m15s
2025-10-17 23:08:49 +08:00
renovatebot 816bacfe4e chore(deps): update mcr.microsoft.com/playwright docker tag to v1.56.1
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m26s
/ build-and-test (push) Failing after 1m37s
/ playwright (push) Successful in 3m16s
CI/CD Pipeline / playwright (push) Successful in 4m30s
2025-10-17 17:04:30 +08:00
renovatebot ad069bcec8 chore(deps): update dependency @types/node to v22.18.11
renovate/stability-days Updates have met minimum release age requirement
/ playwright (push) Successful in 2m3s
/ build-and-test (push) Failing after 1m35s
CI/CD Pipeline / playwright (push) Successful in 4m26s
CI/CD Pipeline / build-and-deploy (push) Successful in 5m29s
2025-10-17 11:58:49 +08:00
renovatebot b8fb2e9718 chore(deps): update dependency @vitest/eslint-plugin to v1.3.18
renovate/stability-days Updates have met minimum release age requirement
/ playwright (push) Successful in 1m54s
/ build-and-test (push) Failing after 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m23s
CI/CD Pipeline / playwright (push) Successful in 3m59s
2025-10-17 05:44:15 +08:00
renovatebot 68b0070ac2 chore(deps): update lint dependencies to ~1.23.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 4m26s
CI/CD Pipeline / playwright (push) Successful in 5m7s
/ playwright (push) Successful in 1m39s
/ build-and-test (push) Failing after 2m1s
2025-10-17 02:25:30 +08:00
renovatebot 69f4bf63ab chore(deps): update dependency wrangler to v4.42.2
renovate/stability-days Updates have met minimum release age requirement
/ build-and-test (push) Failing after 1m26s
/ playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m27s
CI/CD Pipeline / playwright (push) Successful in 4m8s
2025-10-16 21:17:52 +08:00
严浩 8b316c85e9 build(stylelint): 更新配置文件导出方式
CI/CD Pipeline / playwright (push) Successful in 2m12s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m47s
/ playwright (push) Successful in 2m41s
/ build-and-test (push) Failing after 1m29s
将 module.exports 更改为 export default 以支持 TypeScript 配置文件
2025-10-16 18:40:39 +08:00
严浩 137139af5f fix(stylelint): 引入 TypeScript 支持并优化配置结构
/ playwright (push) Successful in 1m38s
/ build-and-test (push) Failing after 59s
CI/CD Pipeline / build-and-deploy (push) Failing after 4m4s
CI/CD Pipeline / playwright (push) Successful in 4m36s
2025-10-16 17:10:06 +08:00
严浩 985c6b53c8 docs(stylelint): 添加配置文件注释和类型支持
CI/CD Pipeline / build-and-deploy (push) Successful in 2m11s
/ build-and-test (push) Failing after 1m26s
/ playwright (push) Successful in 1m52s
CI/CD Pipeline / playwright (push) Successful in 3m23s
2025-10-16 16:58:07 +08:00
严浩 a0f39d401a ci(ci-cd): 更新单元测试命令
/ playwright (push) Successful in 2m4s
/ build-and-test (push) Failing after 1m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m8s
CI/CD Pipeline / playwright (push) Successful in 4m32s
2025-10-16 16:51:40 +08:00
严浩 7b6f7ad2fb feat(vscode): 增强开发调试配置支持
/ build-and-test (push) Failing after 1m24s
/ playwright (push) Successful in 1m46s
CI/CD Pipeline / build-and-deploy (push) Failing after 2m2s
CI/CD Pipeline / playwright (push) Successful in 4m5s
2025-10-16 16:37:40 +08:00
严浩 4e31d8b80c refactor(server): 修改 WebSocket 关闭事件处理逻辑
/ playwright (push) Successful in 1m55s
/ build-and-test (push) Failing after 1m43s
CI/CD Pipeline / playwright (push) Successful in 3m48s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m46s
2025-10-16 15:06:39 +08:00
严浩 9b09413010 fix(server): 优化WebSocket连接关闭时的日志记录
CI/CD Pipeline / build-and-deploy (push) Successful in 2m11s
/ build-and-test (push) Failing after 1m40s
/ playwright (push) Successful in 2m17s
CI/CD Pipeline / playwright (push) Successful in 3m31s
2025-10-16 14:25:24 +08:00
严浩 c5b8321ac4 feat(src/pages/index.page.vue): 添加 WebSocket 事件日志记录功能
/ playwright (push) Successful in 2m7s
/ build-and-test (push) Failing after 1m53s
CI/CD Pipeline / playwright (push) Successful in 3m42s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m50s
2025-10-16 14:11:47 +08:00
严浩 2aef0739ce fix(server): 修复WebSocket连接关闭事件处理 2025-10-16 14:10:27 +08:00
严浩 f16a36680b refactor(vite.config.optimizeDeps): entries
/ playwright (push) Successful in 1m53s
/ build-and-test (push) Failing after 1m58s
CI/CD Pipeline / playwright (push) Successful in 4m4s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m40s
2025-10-16 13:39:13 +08:00
严浩 75ada37079 chore(lint-staged): 更新lint-staged配置以包含server目录
CI/CD Pipeline / playwright (push) Successful in 1m56s
/ playwright (push) Successful in 1m56s
/ build-and-test (push) Failing after 2m2s
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
2025-10-16 13:36:29 +08:00
严浩 39cc8354cc feat(server): 优化 WebSocket 连接逻辑 2025-10-16 13:36:06 +08:00
严浩 d3d5ebc66b fix(index.page): 修复消息输入框宽度问题
/ build-and-test (push) Failing after 1m59s
/ playwright (push) Successful in 2m15s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m12s
CI/CD Pipeline / playwright (push) Successful in 4m37s
为消息输入框添加 `w-full` 类,确保其在父容器中正确占满宽度。
2025-10-16 12:29:14 +08:00
严浩 f9748b78a3 fix(index.page): 优化WebSocket错误处理
CI/CD Pipeline / playwright (push) Successful in 2m2s
/ playwright (push) Successful in 2m9s
/ build-and-test (push) Failing after 1m48s
CI/CD Pipeline / build-and-deploy (push) Successful in 3m30s
2025-10-16 11:46:45 +08:00
renovatebot 4fcd3bdcdb chore(deps): update dependency @vitest/eslint-plugin to v1.3.17
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m14s
CI/CD Pipeline / playwright (push) Successful in 3m20s
/ playwright (push) Successful in 1m50s
/ build-and-test (push) Failing after 1m51s
2025-10-16 02:41:34 +08:00
严浩 ac572c6844 build(stylelint): 将配置文件从 TypeScript 转换为 ESM 模块
CI/CD Pipeline / build-and-deploy (push) Successful in 2m16s
CI/CD Pipeline / playwright (push) Successful in 3m29s
/ playwright (push) Successful in 1m54s
/ build-and-test (push) Failing after 1m54s
将 stylelint 配置文件从 `.ts` 重命名为 `.mjs`,
2025-10-16 01:53:48 +08:00
严浩 48d38950ae feat(base-layout): 调整全局布局组件背景透明度
/ build-and-test (push) Failing after 1m48s
/ playwright (push) Successful in 2m9s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m13s
CI/CD Pipeline / playwright (push) Successful in 3m28s
2025-10-16 01:46:34 +08:00
严浩 4733e59d1a feat: 添加 WebSocket 支持和连接管理功能 2025-10-16 01:45:36 +08:00
严浩 4d721dfb5b build(husky): 优化 commit-msg 钩子脚本
/ build-and-test (push) Failing after 2m11s
/ playwright (push) Successful in 2m1s
CI/CD Pipeline / build-and-deploy (push) Failing after 3m5s
CI/CD Pipeline / playwright (push) Successful in 4m27s
2025-10-16 00:13:48 +08:00
renovatebot a005f3f27c chore(deps): update primevue pkgs to v4.4.0
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / build-and-deploy (push) Successful in 2m20s
CI/CD Pipeline / playwright (push) Successful in 3m24s
/ playwright (push) Successful in 2m1s
/ build-and-test (push) Failing after 1m40s
2025-10-15 21:40:15 +08:00
renovatebot 264800ad94 chore(deps): update dependency jiti to v2.6.1
renovate/stability-days Updates have met minimum release age requirement
/ build-and-test (push) Failing after 1m55s
/ playwright (push) Successful in 3m2s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m15s
CI/CD Pipeline / playwright (push) Successful in 4m32s
2025-10-15 16:43:51 +08:00
严浩 e50d699a2a chore: initial commit
/ playwright (push) Successful in 1m33s
/ build-and-test (push) Failing after 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m16s
CI/CD Pipeline / playwright (push) Successful in 3m26s
2025-10-15 16:27:39 +08:00
137 changed files with 13748 additions and 4659 deletions
+39
View File
@@ -0,0 +1,39 @@
{
"image": "ghcr.io/yanhao98/h-devcontainer:main",
"runArgs": [
"--hostname=devcontainer-host",
"--name=${localWorkspaceFolderBasename}-devcontainer"
],
"forwardPorts": [4730, 4731, 5901],
"portsAttributes": {
"4730": { "label": "开发服务器端口", "onAutoForward": "notify" },
"4731": { "label": "预览服务器端口", "onAutoForward": "notify" }
},
"containerEnv": {
"TZ": "${localEnv:TZ:Asia/Shanghai}"
},
"customizations": {
"vscode": {
"extensions": ["prettier.prettier-vscode", "vue.volar"],
"settings": {
// "tasks": { "version": "2.0.0", "tasks": [] },
"github.copilot.chat.codeGeneration.instructions": [
{
"text": "This dev container includes a lightweight Fluxbox based desktop that can be accessed using a VNC viewer or the web. GUI-based commands executed from the built-in VS Code terminal will open on the desktop automatically."
}
]
}
}
},
"mounts": [
"source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
"source=${localWorkspaceFolder}/.devcontainer/lifecycle-scripts.d,target=/usr/local/etc/lifecycle-scripts.d,type=bind,consistency=delegated"
],
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=delegated",
"initializeCommand": "docker pull ghcr.io/yanhao98/h-devcontainer:main;",
"onCreateCommand": "/usr/local/bin/run-lifecycle-scripts.sh onCreateCommand",
"updateContentCommand": "/usr/local/bin/run-lifecycle-scripts.sh updateContentCommand",
"postCreateCommand": "/usr/local/bin/run-lifecycle-scripts.sh postCreateCommand",
"postStartCommand": "/usr/local/bin/run-lifecycle-scripts.sh postStartCommand",
"postAttachCommand": "/usr/local/bin/run-lifecycle-scripts.sh postAttachCommand"
}
+14
View File
@@ -0,0 +1,14 @@
#!/bin/zsh -eu
h-setup-zh-locale
h-setup-bun-bin
h-setup-pnpm-bin
h-setup-chromium
h-setup-desktop-lite
h-install-node-modules
echo "-----------------------------"
echo "开发容器已启动并配置完成!"
echo "-----------------------------"
+14 -3
View File
@@ -1,4 +1,15 @@
VITE_BASE=/ VITE_APP_BUILD_TIME=NOT_SET
VITE_APP_BUILD_COMMIT=NOT_SET
VITE_BUILD_SOURCE_MAP=true VITE_BUILD_SOURCE_MAP=true
VITE_BUILD_COMMIT= VITE_BUILD_MINIFY=true
VITE_BUILD_TIME= VITE_CLOUDFLARE_SERVER_ENABLED=true
VITE_APP_TITLE=vue-ts-example-2025
VITE_APP_BASE=/
VITE_APP_ENABLE_VUE_DEVTOOLS=true
VITE_APP_MENU_SHOW_DEMOS=true
VITE_APP_MENU_SHOW_ORDER=true
VITE_APP_ENABLE_ROUTER_LOG_GUARD=true
VITE_APP_API_URL=/API
VITE_APP_PROXY=[["/API","https://jsonplaceholder.typicode.com"]]
+24 -33
View File
@@ -12,24 +12,37 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
lint-build-and-typecheck: playwright:
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.57.0-noble
steps:
- name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@faab20ac2f9c85dfce1a4147fca493bf632bd744
# - name: 📥 安装 Playwright 浏览器
# run: pnpm exec playwright install --with-deps
- name: 📦 构建项目
run: pnpm run build-only
- name: ▶️ 运行 Playwright 测试
run: pnpm exec playwright test
build-and-deploy:
needs: playwright
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: gitea/runner-images:ubuntu-latest-slim # https://github.com/cloudflare/wrangler-action/issues/329#issuecomment-3046747722 container: gitea/runner-images:ubuntu-latest-slim # https://github.com/cloudflare/wrangler-action/issues/329#issuecomment-3046747722
steps: steps:
- name: 🛠️ 设置Node环境 - name: 🛠️ 设置Node环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8 uses: yanhao98/composite-actions/setup-node-environment@faab20ac2f9c85dfce1a4147fca493bf632bd744
- name: 🔍 静态代码分析 - name: 🔍 静态代码分析
run: pnpm run lint run: pnpm run lint
- name: 📦 构建项目 - name: 📦 构建项目
run: pnpm run build-only run: |
export VITE_APP_BUILD_TIME=$(date +"%Y-%m-%d %H:%M:%S")
pnpm run build-only
env: env:
VITE_BUILD_COMMIT: ${{ github.sha }} VITE_APP_BUILD_COMMIT: ${{ github.sha }}
- name: 🧪 单元测试
run: pnpm run test:unit
- name: 📊 计算构建大小 - name: 📊 计算构建大小
run: | run: |
@@ -41,36 +54,14 @@ jobs:
echo "🔹 文件总数: $(find dist -type f | wc -l) 个文件" echo "🔹 文件总数: $(find dist -type f | wc -l) 个文件"
echo "----------------------------------------" echo "----------------------------------------"
- name: 🧪 单元测试
run: pnpm exec vitest run
- name: ✅ 类型检查 - name: ✅ 类型检查
run: pnpm run type-check # 要先 build,保证 components.d.ts 存在 run: pnpm run type-check # 要先 build,保证 components.d.ts 存在
deploy:
runs-on: ubuntu-latest
needs: lint-build-and-typecheck
if: github.ref == 'refs/heads/main'
# https://github.com/cloudflare/wrangler-action/issues/329#issuecomment-3046747722
container:
image: gitea/runner-images:ubuntu-latest-slim
steps:
- name: 🛠️ 设置Node环境
uses: yanhao98/composite-actions/setup-node-environment@25eb4dc0c134cc9df2b7c569aa54140a366b45a8
- name: 📦 构建项目
run: pnpm run build-only
env:
VITE_BUILD_COMMIT: ${{ github.sha }}
# - name: 🚀 上传版本到 Cloudflare
# uses: cloudflare/wrangler-action@v3
# id: upload
# with:
# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# command: versions upload --tag "${{ github.sha }}" --message "Deploy commit ${{ github.sha }} from ${{ github.ref_name }}"
- name: 🚀 部署到 Cloudflare - name: 🚀 部署到 Cloudflare
if: github.ref == 'refs/heads/main'
uses: cloudflare/wrangler-action@v3 uses: cloudflare/wrangler-action@v3
with: with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
-26
View File
@@ -1,26 +0,0 @@
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
push:
workflow_dispatch:
jobs:
playwright:
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: 🔄 更新依赖
run: pnpm update --latest
- name: 📦 构建项目
run: pnpm run build-only
- name: ▶️ 运行 Playwright 测试
run: pnpm exec playwright test
+86
View File
@@ -0,0 +1,86 @@
name: 测试最新依赖
defaults:
run:
shell: bash
env:
TZ: Asia/Shanghai
on:
# push:
# branches: [main]
schedule:
- cron: "30 22 * * *" # 22:30 UTC = 6:30 CST
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
container: gitea/runner-images:ubuntu-latest-slim # https://github.com/cloudflare/wrangler-action/issues/329#issuecomment-3046747722
steps:
- uses: actions/checkout@main
with:
# fetch-depth: 0 # 0 代表完整检出,semantic-release 需要
filter: blob:none # 我们不需要所有 blob,只需要完整的树
show-progress: false
- run: rm pnpm-lock.yaml
- uses: pnpm/action-setup@master # https://github.com/pnpm/action-setup?tab=readme-ov-file#inputs
- uses: actions/setup-node@main # https://github.com/actions/setup-node?tab=readme-ov-file#usage
with:
cache: ""
- run: pnpm up --latest
- run: pnpm outdated
- name: 🔍 静态代码分析
run: pnpm run lint
- name: 📦 构建项目
run: pnpm run build-only
env:
VITE_APP_BUILD_COMMIT: ${{ github.sha }}
- 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 exec vitest run
- name: ✅ 类型检查
run: pnpm run type-check # 要先 build,保证 components.d.ts 存在
playwright:
runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.57.0-noble
steps:
- uses: actions/checkout@main
with:
# fetch-depth: 0 # 0 代表完整检出,semantic-release 需要
filter: blob:none # 我们不需要所有 blob,只需要完整的树
show-progress: false
- run: rm pnpm-lock.yaml
- uses: pnpm/action-setup@master # https://github.com/pnpm/action-setup?tab=readme-ov-file#inputs
- uses: actions/setup-node@main # https://github.com/actions/setup-node?tab=readme-ov-file#usage
with:
cache: ""
- run: pnpm up --latest
- run: pnpm outdated
- name: 📦 构建项目
run: pnpm run build-only
- name: ▶️ 运行 Playwright 测试
run: pnpm exec playwright test
+2 -1
View File
@@ -7,6 +7,7 @@ yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
.pnpm-store
node_modules node_modules
.DS_Store .DS_Store
dist dist
@@ -27,7 +28,7 @@ coverage
*.tsbuildinfo *.tsbuildinfo
test-results/ playwright-test-results/
playwright-report/ playwright-report/
# wrangler files # wrangler files
+11 -11
View File
@@ -3,18 +3,18 @@
- https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found - https://typicode.github.io/husky/zh/troubleshoot.html#找不到命令-command-not-found
- https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui - https://typicode.github.io/husky/zh/how-to.html#node-版本管理器和-gui
```shell ```bash
ln -s $(which pnpm) $HOME/.local/bin/pnpm ln -s $(which pnpm) $HOME/.local/bin/pnpm
``` ```
```bash
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
``` ```
# 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
```
+11 -3
View File
@@ -1,5 +1,13 @@
# 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。 # 此钩子在 pre-commit 钩子成功完成后,用于检查提交消息。
echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..." echo "📝 [Commit-msg] 正在运行 commit-msg 钩子..."
echo "检查提交消息:$1"
pnpm exec commitlint --edit $1 echo "🟢 检查提交消息:$1"
echo "✅ [Commit-msg] commit-msg 钩子完成!" cat $1
# node -v
echo "🟢 [Commit-msg] Node 版本:$(node -v)"
# pnpm exec commitlint --edit $1
node node_modules/@commitlint/cli/cli.js --edit $1
echo "📝 [Commit-msg] commit-msg 钩子完成!"
+1 -1
View File
@@ -1,4 +1,4 @@
# 此钩子在 git merge 或 git pull 成功完成后运行。 # 此钩子在 git merge 或 git pull 成功完成后运行。
echo "🔗 [Post-merge] 正在安装依赖..." echo "🔗 [Post-merge] 正在安装依赖..."
pnpm install pnpm install
echo " [Post-merge] 依赖安装完成!" echo "🔗 [Post-merge] 依赖安装完成!"
+3 -1
View File
@@ -1,4 +1,6 @@
# 此钩子在执行 git commit 命令时,在创建提交之前运行。 # 此钩子在执行 git commit 命令时,在创建提交之前运行。
echo "🧹 [Pre-commit] 正在运行 lint-staged..." echo "🧹 [Pre-commit] 正在运行 lint-staged..."
pnpm exec lint-staged pnpm exec lint-staged
echo "✅ [Pre-commit] lint-staged 完成!" pnpm run lint:vue-i18n-extract
# pnpm run type-check
echo "🧹 [Pre-commit] lint-staged 完成!"
+3
View File
@@ -0,0 +1,3 @@
# VS Code 搜索忽略
pnpm-lock.yaml
+1 -9
View File
@@ -1,9 +1 @@
registry=https://registry.npmmirror.com/ # registry=https://registry.npmmirror.com/
# https://pnpm.io/zh/npmrc#node-mirrorltreleasedir
use-node-version=24.7.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
+5 -2
View File
@@ -1,6 +1,9 @@
{ {
"$schema": "https://json.schemastore.org/prettierrc", "$schema": "https://json.schemastore.org/prettierrc",
"semi": false, "semi": true,
"tabWidth": 2,
"singleQuote": true, "singleQuote": true,
"printWidth": 100 "printWidth": 100,
"trailingComma": "all",
"plugins": ["@prettier/plugin-oxc"]
} }
+3 -2
View File
@@ -2,10 +2,11 @@
"recommendations": [ "recommendations": [
"Vue.volar", "Vue.volar",
"vitest.explorer", "vitest.explorer",
"ms-playwright.playwright",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig", "EditorConfig.EditorConfig",
"oxc.oxc-vscode", "oxc.oxc-vscode",
"esbenp.prettier-vscode" "prettier.prettier-vscode",
"stylelint.vscode-stylelint",
"lokalise.i18n-ally"
] ]
} }
+12 -1
View File
@@ -8,7 +8,18 @@
"url": "http://localhost:4730/", "url": "http://localhost:4730/",
"webRoot": "${workspaceFolder}", "webRoot": "${workspaceFolder}",
"firefoxExecutable": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox", "firefoxExecutable": "/Applications/Firefox Nightly.app/Contents/MacOS/firefox",
"preLaunchTask": "🚀 dev" "preLaunchTask": "🚀 dev",
"pathMappings": [
{
"url": "http://localhost:4730",
"path": "${workspaceFolder}"
}
],
"reAttach": true,
"reloadOnChange": {
"watch": "${workspaceFolder}/src/**/*.{js,jsx,ts,tsx,vue}",
"ignore": "**/node_modules/**"
}
} }
] ]
} }
+61 -3
View File
@@ -1,7 +1,65 @@
{ {
"editor.codeActionsOnSave": { "typescript.tsdk": "node_modules/typescript/lib",
"source.fixAll": "explicit" "typescript.preferences.autoImportFileExcludePatterns": [
"vue-router$",
"**/src/composables/**",
"**/*-auto-imports.ts",
"**/*-auto-imports.types.ts"
],
"search.exclude": {
"public/report-ui-dist": true
}, },
// ============================================================
// 代码格式化相关配置
// ============================================================
"prettier.enable": true,
"files.readonlyInclude": {
"dist/**": true
},
"eslint.enable": true,
"oxc.enable": true,
"stylelint.enable": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.oxc": "explicit",
"source.fixAll.stylelint": "explicit",
"source.organizeImports": "explicit"
},
"stylelint.validate": ["css", "less", "postcss", "scss", "vue"],
"scss.lint.unknownAtRules": "ignore",
"css.lint.unknownAtRules": "ignore",
"less.lint.unknownAtRules": "ignore",
"editor.defaultFormatter": "prettier.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "prettier.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "prettier.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "prettier.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "prettier.prettier-vscode"
},
// ============================================================
// i18n-ally 配置
// ============================================================
// >>>>>
"i18n-ally.readonly": false,
"i18n-ally.namespace": false /* @intlify/unplugin-vue-i18n */,
"i18n-ally.localesPaths": ["src/locales/demo", "src/locales"],
// https://github.com/lokalise/i18n-ally/wiki/Path-Matcher
// 默认: 🗃 Path Matcher Regex: /^(?<locale>[\w-_]+)(?:.*\/|^).*\.(?<ext>json|ya?ml|json5)$/
"i18n-ally.pathMatcher": "{locale}.json",
"i18n-ally.enabledParsers": ["json"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sourceLanguage": "zh-CN", // 翻译源语言 (源文件) 根据此语言文件翻译其他语言文件的变量和内容
"i18n-ally.displayLanguage": "zh-CN" // 显示语言 (显示文件/翻译文件)
// <<<<<
// "iconify.customCollectionJsonPaths": ["https://example.com/my-icons.json", "./local/icons.json"],
} }
+26 -22
View File
@@ -2,32 +2,36 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"type": "npm",
"script": "dev",
"label": "🚀 dev", "label": "🚀 dev",
"type": "shell", "detail": "启动开发服务器",
"command": "pnpm run dev", "problemMatcher": [],
"isBackground": true, "isBackground": true,
"problemMatcher": {
"owner": "vite",
"pattern": {
"regexp": "."
},
"background": {
"activeOnStart": true,
"beginsPattern": ".*VITE.*",
"endsPattern": ".*ready in.*"
}
},
"group": {
"kind": "build",
"isDefault": true
},
"presentation": { "presentation": {
"reveal": "always", "reveal": "always", // 控制运行任务的终端是否显示。可按选项 "revealProblems" 进行替代。默认设置为“始终”。
"panel": "new" "panel": "dedicated" // dedicated:一个任务独占一个终端,方便查看特定任务的日志,不会被其他任务干扰。
}, },
"runOptions": { "group": { "kind": "build", "isDefault": false }
"instanceLimit": 1 },
} {
"type": "npm",
"script": "build-only",
"label": "🔨 build-only",
"detail": "" /* VSCode 使 package.json scripts[scriptName] detail */,
"presentation": { "reveal": "always", "panel": "shared" },
"group": { "kind": "none", "isDefault": false }
},
{
"type": "npm",
"script": "wrangler:dev",
"label": "☁️ wrangler:dev",
"detail": "启动 Cloudflare Workers 开发服务器,相当于预览",
"dependsOn": ["🔨 build-only"],
"problemMatcher": [],
"isBackground": true,
"presentation": { "reveal": "always", "panel": "dedicated" },
"group": { "kind": "build", "isDefault": false }
} }
] ]
} }
+1
View File
@@ -0,0 +1 @@
- **vite-plugin-fake-server**: Mock API under `/fake-api` (dev only) from `fake/` directory
+74
View File
@@ -0,0 +1,74 @@
# AGENTS.md
This file provides guidance to AI when working with code in this repository.
## Project Overview
Vue 3 TypeScript application with Vite.
## 开发服务器
- **不要启动开发服务器**: 开发服务器通常已经由用户启动。除非特别要求,否则不要执行 `pnpm dev` 之类的命令。
- **不要执行 Playwright 测试**: 除非用户明确要求,否则不要运行 Playwright 端到端测试。
### Routing & Layouts
- **File-based routing**: Uses `unplugin-vue-router` with `.page.vue` and `.page.md` extensions in `src/pages/`
- **Route naming**: Converts to PascalCase (e.g., `user-profile.page.vue``UserProfile`)
- **Layouts**: `vite-plugin-vue-meta-layouts` with default layout `base-layout/base-layout`
### Auto-Import Configuration
Multiple auto-import systems are active:
- **Vue APIs**: Core Vue, VueUse, Pinia, Vue Router, vue-i18n
- **Components**: Auto-registered from multiple UI libraries (Naive UI, PrimeVue)
- **Icons**: Uses `unplugin-icons` with `icon-` prefix; custom SVGs from `src/assets/icons/svgs/` available via `icon-svg-filename`
**IMPORTANT - Auto-Import Limitations**:
- **Dynamic components**: Auto-imported components cannot be used with `<component :is="..." />` syntax
- ❌ Avoid: `<component :is="`icon-${name}`" />`
- ✅ Use: `<icon-foo v-if="condition" />` with `v-if`/`v-else-if`/`v-else` directives
- **Icons in TypeScript/TSX files**: Auto-import for icons does NOT work. You must explicitly import them using the `~icons/` prefix:
```tsx
import IconMenuRounded from '~icons/material-symbols/menu-rounded';
// Then use in JSX/TSX
const menuOption = {
icon: () => <IconMenuRounded />,
};
```
### UI Component Libraries
Project has multiple UI frameworks configured:
- **Naive UI**
- **PrimeVue**:
### Styling
- **UnoCSS**: Wind preset
- **SCSS**: Modern compiler API with global imports from `@/styles/scss/global.scss`
### State Management
Pinia stores
### Cloudflare Workers Integration
- **Server entry**: `server/index.ts` handles `/api/*` routes with KV storage
- **KV binding**: Named `KV`
### Vite Plugins (notable)
- **vue-macros**: Enhanced Vue features
- **unplugin-vue-markdown**: `.md` files as Vue components with frontmatter
### Path Aliases
- `@/` maps to `src/` directory
+73
View File
@@ -0,0 +1,73 @@
## 依赖管理
```shell
pnpm dedupe
```
去除重复的依赖包。当你的项目中存在多个版本的同一个包时,pnpm dedupe 会尝试将它们合并成尽可能少的版本,从而减少 node_modules 的体积。
```bash
pnpm dlx taze major --interactive
```
交互式地将项目依赖升级到最新的主要版本,可以逐个选择要升级哪些包。
```bash
pnpm dlx knip
```
检测项目中未使用的依赖、导出和文件,帮助清理冗余代码。
## Playwright
```bash
playwright test
```
- `HEADLESS=true`:强制无头模式
- `--ui`:启动 Playwright 的图形用户界面,方便调试测试用例
- `--project=chromium`:指定使用 Chromium 浏览器进行测试
- `--quiet`:减少输出信息,只显示必要的内容
## Oxlint
```bash
oxlint . --fix --ignore-path=.gitignore --print-config
```
```bash
oxlint . --fix --deny=correctness
```
## Wrangler
### Pages
```bash
wrangler pages deploy dist --project-name=vue-ts-example-2025 --branch=preview
```
```bash
wrangler pages deploy dist --project-name=vue-ts-example-2025
```
### Workers
```bash
wrangler deploy
```
```bash
wrangler versions upload
```
## 拆包体积分析
- https://github.com/nonzzz/vite-bundle-analyzer
- https://github.com/KusStar/vite-bundle-visualizer
```bash
pnpm dlx vite-bundle-visualizer -t treemap
pnpm dlx vite-bundle-visualizer -t sunburst
pnpm dlx vite-bundle-visualizer -t network
```
+4 -61
View File
@@ -1,64 +1,7 @@
# vue-ts-example-2025 # vue-ts-example-2025
This template should help get you started developing with Vue 3 in Vite. ## 参考资料
## Recommended IDE Setup - [soybean-admin (GitHub)](https://github.com/soybeanjs/soybean-admin) — 2.0 预览: [预览地址](https://v2-0.soybean-admin-df1.pages.dev/home)
- [Vite:静态部署指南(中文)](https://vitejs.cn/vite3-cn/guide/static-deploy.html) — Vite 官方中文文档中关于静态站点部署的说明。
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). - [vitesse (GitHub)](https://github.com/antfu-collective/vitesse) — Anthony Fu 的 Vite + Vue 3 快速启动模板
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
pnpm install
```
### Compile and Hot-Reload for Development
```sh
pnpm dev
```
### Type-Check, Compile and Minify for Production
```sh
pnpm build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
pnpm test:unit
```
### Run End-to-End Tests with [Playwright](https://playwright.dev)
```sh
# Install browsers for the first run
npx playwright install
# When testing on CI, must build the project first
pnpm build
# Runs the end-to-end tests
pnpm test:e2e
# Runs the tests only on Chromium
pnpm test:e2e --project=chromium
# Runs the tests of a specific file
pnpm test:e2e tests/example.spec.ts
# Runs the tests in debug mode
pnpm test:e2e --debug
```
### Lint with [ESLint](https://eslint.org/)
```sh
pnpm lint
```
+345 -320
View File
@@ -6,330 +6,344 @@
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'] const ConfirmationService: typeof import('utils4u/primevue').ConfirmationService
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const DialogService: typeof import('utils4u/primevue').DialogService
const arrayToTree: typeof import('utils4u/array')['arrayToTree'] const EffectScope: typeof import('vue').EffectScope
const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const ToastService: typeof import('utils4u/primevue').ToastService
const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
const computed: typeof import('vue')['computed'] const arrayToTree: typeof import('utils4u/array').arrayToTree
const computedAsync: typeof import('@vueuse/core')['computedAsync'] const asyncComputed: typeof import('@vueuse/core').asyncComputed
const computedEager: typeof import('@vueuse/core')['computedEager'] const autoResetRef: typeof import('@vueuse/core').autoResetRef
const computedInject: typeof import('@vueuse/core')['computedInject'] const computed: typeof import('vue').computed
const computedWithControl: typeof import('@vueuse/core')['computedWithControl'] const computedAsync: typeof import('@vueuse/core').computedAsync
const consola: typeof import('consola/browser')['consola'] const computedEager: typeof import('@vueuse/core').computedEager
const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] const computedInject: typeof import('@vueuse/core').computedInject
const controlledRef: typeof import('@vueuse/core')['controlledRef'] const computedWithControl: typeof import('@vueuse/core').computedWithControl
const convertFileToBase64: typeof import('utils4u/browser')['convertFileToBase64'] const consola: typeof import('consola/browser').consola
const createApp: typeof import('vue')['createApp'] const controlledComputed: typeof import('@vueuse/core').controlledComputed
const createEventHook: typeof import('@vueuse/core')['createEventHook'] const controlledRef: typeof import('@vueuse/core').controlledRef
const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] const convertFileToBase64: typeof import('utils4u/browser').convertFileToBase64
const createInjectionState: typeof import('@vueuse/core')['createInjectionState'] const createApp: typeof import('vue').createApp
const createLogGuard: typeof import('utils4u/vue-router')['createLogGuard'] const createEventHook: typeof import('@vueuse/core').createEventHook
const createNProgressGuard: typeof import('utils4u/vue-router')['createNProgressGuard'] const createGlobalState: typeof import('@vueuse/core').createGlobalState
const createPinia: typeof import('pinia')['createPinia'] const createInjectionState: typeof import('@vueuse/core').createInjectionState
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] const createLogGuard: typeof import('utils4u/vue-router').createLogGuard
const createRef: typeof import('@vueuse/core')['createRef'] const createNProgressGuard: typeof import('utils4u/vue-router').createNProgressGuard
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate'] const createPinia: typeof import('pinia').createPinia
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] const createReactiveFn: typeof import('@vueuse/core').createReactiveFn
const createStackGuard: typeof import('utils4u/vue-router')['createStackGuard'] const createRef: typeof import('@vueuse/core').createRef
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise'] const createReusableTemplate: typeof import('@vueuse/core').createReusableTemplate
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] const createSharedComposable: typeof import('@vueuse/core').createSharedComposable
const customRef: typeof import('vue')['customRef'] const createStackGuard: typeof import('utils4u/vue-router').createStackGuard
const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] const createTemplatePromise: typeof import('@vueuse/core').createTemplatePromise
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] const createUnrefFn: typeof import('@vueuse/core').createUnrefFn
const deepFreeze: typeof import('deep-freeze-es6')['default'] const customRef: typeof import('vue').customRef
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const debouncedRef: typeof import('@vueuse/core').debouncedRef
const defineComponent: typeof import('vue')['defineComponent'] const debouncedWatch: typeof import('@vueuse/core').debouncedWatch
const defineStore: typeof import('pinia')['defineStore'] const deepFreeze: typeof import('deep-freeze-es6').default
const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const effectScope: typeof import('vue')['effectScope'] const defineBasicLoader: typeof import('unplugin-vue-router/data-loaders/basic').defineBasicLoader
const extendRef: typeof import('@vueuse/core')['extendRef'] const defineComponent: typeof import('vue').defineComponent
const getActivePinia: typeof import('pinia')['getActivePinia'] const defineStore: typeof import('pinia').defineStore
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const eagerComputed: typeof import('@vueuse/core').eagerComputed
const getCurrentScope: typeof import('vue')['getCurrentScope'] const effectScope: typeof import('vue').effectScope
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher'] const extendRef: typeof import('@vueuse/core').extendRef
const h: typeof import('vue')['h'] const getActivePinia: typeof import('pinia').getActivePinia
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] const getCurrentInstance: typeof import('vue').getCurrentInstance
const inject: typeof import('vue')['inject'] const getCurrentScope: typeof import('vue').getCurrentScope
const injectLocal: typeof import('@vueuse/core')['injectLocal'] const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const isDefined: typeof import('@vueuse/core')['isDefined'] const h: typeof import('vue').h
const isProxy: typeof import('vue')['isProxy'] const i18nInstance: typeof import('./src/locales-utils/i18n-auto-imports').i18nInstance
const isReactive: typeof import('vue')['isReactive'] const i18nRouteMessages: typeof import('./src/locales-utils/route-messages/route-messages-auto-imports').i18nRouteMessages
const isReadonly: typeof import('vue')['isReadonly'] const ignorableWatch: typeof import('@vueuse/core').ignorableWatch
const isRef: typeof import('vue')['isRef'] const inject: typeof import('vue').inject
const isShallow: typeof import('vue')['isShallow'] const injectLocal: typeof import('@vueuse/core').injectLocal
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] const isDefined: typeof import('@vueuse/core').isDefined
const mapActions: typeof import('pinia')['mapActions'] const isProxy: typeof import('vue').isProxy
const mapGetters: typeof import('pinia')['mapGetters'] const isReactive: typeof import('vue').isReactive
const mapState: typeof import('pinia')['mapState'] const isReadonly: typeof import('vue').isReadonly
const mapStores: typeof import('pinia')['mapStores'] const isRef: typeof import('vue').isRef
const mapWritableState: typeof import('pinia')['mapWritableState'] const isShallow: typeof import('vue').isShallow
const markRaw: typeof import('vue')['markRaw'] const makeDestructurable: typeof import('@vueuse/core').makeDestructurable
const nextTick: typeof import('vue')['nextTick'] const mapActions: typeof import('pinia').mapActions
const onActivated: typeof import('vue')['onActivated'] const mapGetters: typeof import('pinia').mapGetters
const onBeforeMount: typeof import('vue')['onBeforeMount'] const mapState: typeof import('pinia').mapState
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] const mapStores: typeof import('pinia').mapStores
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] const mapWritableState: typeof import('pinia').mapWritableState
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] const markRaw: typeof import('vue').markRaw
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] const nextTick: typeof import('vue').nextTick
const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] const onActivated: typeof import('vue').onActivated
const onDeactivated: typeof import('vue')['onDeactivated'] const onBeforeMount: typeof import('vue').onBeforeMount
const onElementRemoval: typeof import('@vueuse/core')['onElementRemoval'] const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
const onErrorCaptured: typeof import('vue')['onErrorCaptured'] const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onLongPress: typeof import('@vueuse/core')['onLongPress'] const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onMounted: typeof import('vue')['onMounted'] const onClickOutside: typeof import('@vueuse/core').onClickOutside
const onRenderTracked: typeof import('vue')['onRenderTracked'] const onDeactivated: typeof import('vue').onDeactivated
const onRenderTriggered: typeof import('vue')['onRenderTriggered'] const onElementRemoval: typeof import('@vueuse/core').onElementRemoval
const onScopeDispose: typeof import('vue')['onScopeDispose'] const onErrorCaptured: typeof import('vue').onErrorCaptured
const onServerPrefetch: typeof import('vue')['onServerPrefetch'] const onKeyStroke: typeof import('@vueuse/core').onKeyStroke
const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] const onLongPress: typeof import('@vueuse/core').onLongPress
const onUnmounted: typeof import('vue')['onUnmounted'] const onMounted: typeof import('vue').onMounted
const onUpdated: typeof import('vue')['onUpdated'] const onRenderTracked: typeof import('vue').onRenderTracked
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] const onRenderTriggered: typeof import('vue').onRenderTriggered
const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] const onScopeDispose: typeof import('vue').onScopeDispose
const provide: typeof import('vue')['provide'] const onServerPrefetch: typeof import('vue').onServerPrefetch
const provideLocal: typeof import('@vueuse/core')['provideLocal'] const onStartTyping: typeof import('@vueuse/core').onStartTyping
const reactify: typeof import('@vueuse/core')['reactify'] const onUnmounted: typeof import('vue').onUnmounted
const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] const onUpdated: typeof import('vue').onUpdated
const reactive: typeof import('vue')['reactive'] const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed'] const pausableWatch: typeof import('@vueuse/core').pausableWatch
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit'] const provide: typeof import('vue').provide
const reactivePick: typeof import('@vueuse/core')['reactivePick'] const provideLocal: typeof import('@vueuse/core').provideLocal
const readonly: typeof import('vue')['readonly'] const reactify: typeof import('@vueuse/core').reactify
const ref: typeof import('vue')['ref'] const reactifyObject: typeof import('@vueuse/core').reactifyObject
const refAutoReset: typeof import('@vueuse/core')['refAutoReset'] const reactive: typeof import('vue').reactive
const refDebounced: typeof import('@vueuse/core')['refDebounced'] const reactiveComputed: typeof import('@vueuse/core').reactiveComputed
const refDefault: typeof import('@vueuse/core')['refDefault'] const reactiveOmit: typeof import('@vueuse/core').reactiveOmit
const refThrottled: typeof import('@vueuse/core')['refThrottled'] const reactivePick: typeof import('@vueuse/core').reactivePick
const refWithControl: typeof import('@vueuse/core')['refWithControl'] const readonly: typeof import('vue').readonly
const resolveComponent: typeof import('vue')['resolveComponent'] const ref: typeof import('vue').ref
const resolveRef: typeof import('@vueuse/core')['resolveRef'] const refAutoReset: typeof import('@vueuse/core').refAutoReset
const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] const refDebounced: typeof import('@vueuse/core').refDebounced
const setActivePinia: typeof import('pinia')['setActivePinia'] const refDefault: typeof import('@vueuse/core').refDefault
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix'] const refManualReset: typeof import('@vueuse/core').refManualReset
const setViewportCSSVars: typeof import('utils4u/browser')['setViewportCSSVars'] const refThrottled: typeof import('@vueuse/core').refThrottled
const shallowReactive: typeof import('vue')['shallowReactive'] const refWithControl: typeof import('@vueuse/core').refWithControl
const shallowReadonly: typeof import('vue')['shallowReadonly'] const resolveComponent: typeof import('vue').resolveComponent
const shallowRef: typeof import('vue')['shallowRef'] const resolveRef: typeof import('@vueuse/core').resolveRef
const showOpenFilePicker: typeof import('utils4u/browser')['showOpenFilePicker'] const routeI18nInstance: typeof import('./src/locales-utils/i18n-auto-imports').routeI18nInstance
const storeToRefs: typeof import('pinia')['storeToRefs'] const routeI18nT: typeof import('./src/locales-utils/i18n-auto-imports').routeI18nT
const syncRef: typeof import('@vueuse/core')['syncRef'] const setActivePinia: typeof import('pinia').setActivePinia
const syncRefs: typeof import('@vueuse/core')['syncRefs'] const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
const templateRef: typeof import('@vueuse/core')['templateRef'] const setViewportCSSVars: typeof import('utils4u/browser').setViewportCSSVars
const throttledRef: typeof import('@vueuse/core')['throttledRef'] const shallowReactive: typeof import('vue').shallowReactive
const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] const shallowReadonly: typeof import('vue').shallowReadonly
const toRaw: typeof import('vue')['toRaw'] const shallowRef: typeof import('vue').shallowRef
const toReactive: typeof import('@vueuse/core')['toReactive'] const showOpenFilePicker: typeof import('utils4u/browser').showOpenFilePicker
const toRef: typeof import('vue')['toRef'] const storeToRefs: typeof import('pinia').storeToRefs
const toRefs: typeof import('vue')['toRefs'] const syncRef: typeof import('@vueuse/core').syncRef
const toValue: typeof import('vue')['toValue'] const syncRefs: typeof import('@vueuse/core').syncRefs
const triggerRef: typeof import('vue')['triggerRef'] const templateRef: typeof import('@vueuse/core').templateRef
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount'] const throttledRef: typeof import('@vueuse/core').throttledRef
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] const throttledWatch: typeof import('@vueuse/core').throttledWatch
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] const toRaw: typeof import('vue').toRaw
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] const toReactive: typeof import('@vueuse/core').toReactive
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] const toRef: typeof import('vue').toRef
const unref: typeof import('vue')['unref'] const toRefs: typeof import('vue').toRefs
const unrefElement: typeof import('@vueuse/core')['unrefElement'] const toValue: typeof import('vue').toValue
const until: typeof import('@vueuse/core')['until'] const triggerRef: typeof import('vue').triggerRef
const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] const tryOnBeforeMount: typeof import('@vueuse/core').tryOnBeforeMount
const useAnimate: typeof import('@vueuse/core')['useAnimate'] const tryOnBeforeUnmount: typeof import('@vueuse/core').tryOnBeforeUnmount
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference'] const tryOnMounted: typeof import('@vueuse/core').tryOnMounted
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery'] const tryOnScopeDispose: typeof import('@vueuse/core').tryOnScopeDispose
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter'] const tryOnUnmounted: typeof import('@vueuse/core').tryOnUnmounted
const useArrayFind: typeof import('@vueuse/core')['useArrayFind'] const unref: typeof import('vue').unref
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex'] const unrefElement: typeof import('@vueuse/core').unrefElement
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast'] const until: typeof import('@vueuse/core').until
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes'] const useActiveElement: typeof import('@vueuse/core').useActiveElement
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin'] const useAnimate: typeof import('@vueuse/core').useAnimate
const useArrayMap: typeof import('@vueuse/core')['useArrayMap'] const useAppStore: typeof import('./src/stores/app-store-auto-imports').useAppStore
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce'] const useArrayDifference: typeof import('@vueuse/core').useArrayDifference
const useArraySome: typeof import('@vueuse/core')['useArraySome'] const useArrayEvery: typeof import('@vueuse/core').useArrayEvery
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique'] const useArrayFilter: typeof import('@vueuse/core').useArrayFilter
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] const useArrayFind: typeof import('@vueuse/core').useArrayFind
const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] const useArrayFindIndex: typeof import('@vueuse/core').useArrayFindIndex
const useAttrs: typeof import('vue')['useAttrs'] const useArrayFindLast: typeof import('@vueuse/core').useArrayFindLast
const useBase64: typeof import('@vueuse/core')['useBase64'] const useArrayIncludes: typeof import('@vueuse/core').useArrayIncludes
const useBattery: typeof import('@vueuse/core')['useBattery'] const useArrayJoin: typeof import('@vueuse/core').useArrayJoin
const useBluetooth: typeof import('@vueuse/core')['useBluetooth'] const useArrayMap: typeof import('@vueuse/core').useArrayMap
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] const useArrayReduce: typeof import('@vueuse/core').useArrayReduce
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel'] const useArraySome: typeof import('@vueuse/core').useArraySome
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] const useArrayUnique: typeof import('@vueuse/core').useArrayUnique
const useCached: typeof import('@vueuse/core')['useCached'] const useAsyncQueue: typeof import('@vueuse/core').useAsyncQueue
const useClipboard: typeof import('@vueuse/core')['useClipboard'] const useAsyncState: typeof import('@vueuse/core').useAsyncState
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems'] const useAttrs: typeof import('vue').useAttrs
const useCloned: typeof import('@vueuse/core')['useCloned'] const useAuthStore: typeof import('./src/stores/auth-store-auto-imports').useAuthStore
const useColorMode: typeof import('@vueuse/core')['useColorMode'] const useBase64: typeof import('@vueuse/core').useBase64
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] const useBattery: typeof import('@vueuse/core').useBattery
const useCountdown: typeof import('@vueuse/core')['useCountdown'] const useBluetooth: typeof import('@vueuse/core').useBluetooth
const useCounter: typeof import('@vueuse/core')['useCounter'] const useBreakpoints: typeof import('@vueuse/core').useBreakpoints
const useCssModule: typeof import('vue')['useCssModule'] const useBroadcastChannel: typeof import('@vueuse/core').useBroadcastChannel
const useCssVar: typeof import('@vueuse/core')['useCssVar'] const useBrowserLocation: typeof import('@vueuse/core').useBrowserLocation
const useCssVars: typeof import('vue')['useCssVars'] const useCached: typeof import('@vueuse/core').useCached
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement'] const useClipboard: typeof import('@vueuse/core').useClipboard
const useCycleList: typeof import('@vueuse/core')['useCycleList'] const useClipboardItems: typeof import('@vueuse/core').useClipboardItems
const useDark: typeof import('@vueuse/core')['useDark'] const useCloned: typeof import('@vueuse/core').useCloned
const useDateFormat: typeof import('@vueuse/core')['useDateFormat'] const useColorMode: typeof import('@vueuse/core').useColorMode
const useDebounce: typeof import('@vueuse/core')['useDebounce'] const useConfirmDialog: typeof import('@vueuse/core').useConfirmDialog
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] const useCountdown: typeof import('@vueuse/core').useCountdown
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] const useCounter: typeof import('@vueuse/core').useCounter
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] const useCssModule: typeof import('vue').useCssModule
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] const useCssVar: typeof import('@vueuse/core').useCssVar
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] const useCssVars: typeof import('vue').useCssVars
const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] const useCurrentElement: typeof import('@vueuse/core').useCurrentElement
const useDialog: typeof import('naive-ui')['useDialog'] const useCycleList: typeof import('@vueuse/core').useCycleList
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] const useDark: typeof import('@vueuse/core').useDark
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] const useDateFormat: typeof import('@vueuse/core').useDateFormat
const useDraggable: typeof import('@vueuse/core')['useDraggable'] const useDebounce: typeof import('@vueuse/core').useDebounce
const useDropZone: typeof import('@vueuse/core')['useDropZone'] const useDebounceFn: typeof import('@vueuse/core').useDebounceFn
const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] const useDebouncedRefHistory: typeof import('@vueuse/core').useDebouncedRefHistory
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] const useDeviceMotion: typeof import('@vueuse/core').useDeviceMotion
const useElementHover: typeof import('@vueuse/core')['useElementHover'] const useDeviceOrientation: typeof import('@vueuse/core').useDeviceOrientation
const useElementSize: typeof import('@vueuse/core')['useElementSize'] const useDevicePixelRatio: typeof import('@vueuse/core').useDevicePixelRatio
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] const useDevicesList: typeof import('@vueuse/core').useDevicesList
const useEventBus: typeof import('@vueuse/core')['useEventBus'] const useDialog: typeof import('naive-ui').useDialog
const useEventListener: typeof import('@vueuse/core')['useEventListener'] const useDisplayMedia: typeof import('@vueuse/core').useDisplayMedia
const useEventSource: typeof import('@vueuse/core')['useEventSource'] const useDocumentVisibility: typeof import('@vueuse/core').useDocumentVisibility
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] const useDraggable: typeof import('@vueuse/core').useDraggable
const useFavicon: typeof import('@vueuse/core')['useFavicon'] const useDropZone: typeof import('@vueuse/core').useDropZone
const useFetch: typeof import('@vueuse/core')['useFetch'] const useElementBounding: typeof import('@vueuse/core').useElementBounding
const useFileDialog: typeof import('@vueuse/core')['useFileDialog'] const useElementByPoint: typeof import('@vueuse/core').useElementByPoint
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess'] const useElementHover: typeof import('@vueuse/core').useElementHover
const useFocus: typeof import('@vueuse/core')['useFocus'] const useElementSize: typeof import('@vueuse/core').useElementSize
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] const useElementVisibility: typeof import('@vueuse/core').useElementVisibility
const useFps: typeof import('@vueuse/core')['useFps'] const useEventBus: typeof import('@vueuse/core').useEventBus
const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] const useEventListener: typeof import('@vueuse/core').useEventListener
const useGamepad: typeof import('@vueuse/core')['useGamepad'] const useEventSource: typeof import('@vueuse/core').useEventSource
const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] const useEyeDropper: typeof import('@vueuse/core').useEyeDropper
const useI18n: typeof import('vue-i18n')['useI18n'] const useFavicon: typeof import('@vueuse/core').useFavicon
const useId: typeof import('vue')['useId'] const useFetch: typeof import('@vueuse/core').useFetch
const useIdle: typeof import('@vueuse/core')['useIdle'] const useFileDialog: typeof import('@vueuse/core').useFileDialog
const useImage: typeof import('@vueuse/core')['useImage'] const useFileSystemAccess: typeof import('@vueuse/core').useFileSystemAccess
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] const useFocus: typeof import('@vueuse/core').useFocus
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] const useFocusWithin: typeof import('@vueuse/core').useFocusWithin
const useInterval: typeof import('@vueuse/core')['useInterval'] const useFps: typeof import('@vueuse/core').useFps
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] const useFullscreen: typeof import('@vueuse/core').useFullscreen
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] const useGamepad: typeof import('@vueuse/core').useGamepad
const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] const useGeolocation: typeof import('@vueuse/core').useGeolocation
const useLink: typeof import('vue-router/auto')['useLink'] const useI18n: typeof import('vue-i18n').useI18n
const useLoadingBar: typeof import('naive-ui')['useLoadingBar'] const useId: typeof import('vue').useId
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] const useIdle: typeof import('@vueuse/core').useIdle
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] const useImage: typeof import('@vueuse/core').useImage
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] const useInfiniteScroll: typeof import('@vueuse/core').useInfiniteScroll
const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] const useIntersectionObserver: typeof import('@vueuse/core').useIntersectionObserver
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] const useInterval: typeof import('@vueuse/core').useInterval
const useMemoize: typeof import('@vueuse/core')['useMemoize'] const useIntervalFn: typeof import('@vueuse/core').useIntervalFn
const useMemory: typeof import('@vueuse/core')['useMemory'] const useKeyModifier: typeof import('@vueuse/core').useKeyModifier
const useMessage: typeof import('naive-ui')['useMessage'] const useLastChanged: typeof import('@vueuse/core').useLastChanged
const useModal: typeof import('naive-ui')['useModal'] const useLoadingBar: typeof import('naive-ui').useLoadingBar
const useModel: typeof import('vue')['useModel'] const useLocalStorage: typeof import('@vueuse/core').useLocalStorage
const useMounted: typeof import('@vueuse/core')['useMounted'] const useMagicKeys: typeof import('@vueuse/core').useMagicKeys
const useMouse: typeof import('@vueuse/core')['useMouse'] const useManualRefHistory: typeof import('@vueuse/core').useManualRefHistory
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] const useMediaControls: typeof import('@vueuse/core').useMediaControls
const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] const useMediaQuery: typeof import('@vueuse/core').useMediaQuery
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] const useMemoize: typeof import('@vueuse/core').useMemoize
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage'] const useMemory: typeof import('@vueuse/core').useMemory
const useNetwork: typeof import('@vueuse/core')['useNetwork'] const useMessage: typeof import('naive-ui').useMessage
const useNotification: typeof import('naive-ui')['useNotification'] const useMetaLayoutsNMenuOptions: typeof import('./src/composables/useMetaLayoutsMenuOptions').useMetaLayoutsNMenuOptions
const useNow: typeof import('@vueuse/core')['useNow'] const useModal: typeof import('naive-ui').useModal
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl'] const useModel: typeof import('vue').useModel
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination'] const useMounted: typeof import('@vueuse/core').useMounted
const useOnline: typeof import('@vueuse/core')['useOnline'] const useMouse: typeof import('@vueuse/core').useMouse
const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] const useMouseInElement: typeof import('@vueuse/core').useMouseInElement
const useParallax: typeof import('@vueuse/core')['useParallax'] const useMousePressed: typeof import('@vueuse/core').useMousePressed
const useParentElement: typeof import('@vueuse/core')['useParentElement'] const useMutationObserver: typeof import('@vueuse/core').useMutationObserver
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver'] const useNavigatorLanguage: typeof import('@vueuse/core').useNavigatorLanguage
const usePermission: typeof import('@vueuse/core')['usePermission'] const useNetwork: typeof import('@vueuse/core').useNetwork
const usePointer: typeof import('@vueuse/core')['usePointer'] const useNotification: typeof import('naive-ui').useNotification
const usePointerLock: typeof import('@vueuse/core')['usePointerLock'] const useNow: typeof import('@vueuse/core').useNow
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] const useObjectUrl: typeof import('@vueuse/core').useObjectUrl
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] const useOffsetPagination: typeof import('@vueuse/core').useOffsetPagination
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast'] const useOnline: typeof import('@vueuse/core').useOnline
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] const usePageLeave: typeof import('@vueuse/core').usePageLeave
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] const useParallax: typeof import('@vueuse/core').useParallax
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion'] const useParentElement: typeof import('@vueuse/core').useParentElement
const usePreferredReducedTransparency: typeof import('@vueuse/core')['usePreferredReducedTransparency'] const usePerformanceObserver: typeof import('@vueuse/core').usePerformanceObserver
const usePrevious: typeof import('@vueuse/core')['usePrevious'] const usePermission: typeof import('@vueuse/core').usePermission
const useRafFn: typeof import('@vueuse/core')['useRafFn'] const usePointer: typeof import('@vueuse/core').usePointer
const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] const usePointerLock: typeof import('@vueuse/core').usePointerLock
const useRefs: typeof import('utils4u/vue-use')['useRefs'] const usePointerSwipe: typeof import('@vueuse/core').usePointerSwipe
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] const usePreferredColorScheme: typeof import('@vueuse/core').usePreferredColorScheme
const useRoute: typeof import('vue-router')['useRoute'] const usePreferredContrast: typeof import('@vueuse/core').usePreferredContrast
const useRouter: typeof import('vue-router')['useRouter'] const usePreferredDark: typeof import('@vueuse/core').usePreferredDark
const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] const usePreferredLanguages: typeof import('@vueuse/core').usePreferredLanguages
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] const usePreferredReducedMotion: typeof import('@vueuse/core').usePreferredReducedMotion
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] const usePreferredReducedTransparency: typeof import('@vueuse/core').usePreferredReducedTransparency
const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] const usePrevious: typeof import('@vueuse/core').usePrevious
const useScroll: typeof import('@vueuse/core')['useScroll'] const usePrimevueDialogRef: typeof import('utils4u/primevue').usePrimevueDialogRef
const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] const useRafFn: typeof import('@vueuse/core').useRafFn
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] const useRefHistory: typeof import('@vueuse/core').useRefHistory
const useShare: typeof import('@vueuse/core')['useShare'] const useRefs: typeof import('utils4u/vue-use').useRefs
const useSlots: typeof import('vue')['useSlots'] const useResizeObserver: typeof import('@vueuse/core').useResizeObserver
const useSorted: typeof import('@vueuse/core')['useSorted'] const useRoute: typeof import('vue-router').useRoute
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] const useRouter: typeof import('vue-router').useRouter
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] const useSSRWidth: typeof import('@vueuse/core').useSSRWidth
const useStepper: typeof import('@vueuse/core')['useStepper'] const useScreenOrientation: typeof import('@vueuse/core').useScreenOrientation
const useStorage: typeof import('@vueuse/core')['useStorage'] const useScreenSafeArea: typeof import('@vueuse/core').useScreenSafeArea
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] const useScriptTag: typeof import('@vueuse/core').useScriptTag
const useStyleTag: typeof import('@vueuse/core')['useStyleTag'] const useScroll: typeof import('@vueuse/core').useScroll
const useSupported: typeof import('@vueuse/core')['useSupported'] const useScrollLock: typeof import('@vueuse/core').useScrollLock
const useSwipe: typeof import('@vueuse/core')['useSwipe'] const useSessionStorage: typeof import('@vueuse/core').useSessionStorage
const useTemplateRef: typeof import('vue')['useTemplateRef'] const useShare: typeof import('@vueuse/core').useShare
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] const useSlots: typeof import('vue').useSlots
const useTextDirection: typeof import('@vueuse/core')['useTextDirection'] const useSorted: typeof import('@vueuse/core').useSorted
const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] const useSpeechRecognition: typeof import('@vueuse/core').useSpeechRecognition
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize'] const useSpeechSynthesis: typeof import('@vueuse/core').useSpeechSynthesis
const useThrottle: typeof import('@vueuse/core')['useThrottle'] const useStepper: typeof import('@vueuse/core').useStepper
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] const useStorage: typeof import('@vueuse/core').useStorage
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] const useStorageAsync: typeof import('@vueuse/core').useStorageAsync
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] const useStyleTag: typeof import('@vueuse/core').useStyleTag
const useTimeAgoIntl: typeof import('@vueuse/core')['useTimeAgoIntl'] const useSupported: typeof import('@vueuse/core').useSupported
const useTimeout: typeof import('@vueuse/core')['useTimeout'] const useSwipe: typeof import('@vueuse/core').useSwipe
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] const useTemplateRef: typeof import('vue').useTemplateRef
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll'] const useTemplateRefsList: typeof import('@vueuse/core').useTemplateRefsList
const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] const useTextDirection: typeof import('@vueuse/core').useTextDirection
const useTitle: typeof import('@vueuse/core')['useTitle'] const useTextSelection: typeof import('@vueuse/core').useTextSelection
const useToNumber: typeof import('@vueuse/core')['useToNumber'] const useTextareaAutosize: typeof import('@vueuse/core').useTextareaAutosize
const useToString: typeof import('@vueuse/core')['useToString'] const useThrottle: typeof import('@vueuse/core').useThrottle
const useToggle: typeof import('@vueuse/core')['useToggle'] const useThrottleFn: typeof import('@vueuse/core').useThrottleFn
const useTransition: typeof import('@vueuse/core')['useTransition'] const useThrottledRefHistory: typeof import('@vueuse/core').useThrottledRefHistory
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] const useTimeAgo: typeof import('@vueuse/core').useTimeAgo
const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] const useTimeAgoIntl: typeof import('@vueuse/core').useTimeAgoIntl
const useVModel: typeof import('@vueuse/core')['useVModel'] const useTimeout: typeof import('@vueuse/core').useTimeout
const useVModels: typeof import('@vueuse/core')['useVModels'] const useTimeoutFn: typeof import('@vueuse/core').useTimeoutFn
const useVibrate: typeof import('@vueuse/core')['useVibrate'] const useTimeoutPoll: typeof import('@vueuse/core').useTimeoutPoll
const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] const useTimestamp: typeof import('@vueuse/core').useTimestamp
const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] const useTitle: typeof import('@vueuse/core').useTitle
const useWebNotification: typeof import('@vueuse/core')['useWebNotification'] const useToNumber: typeof import('@vueuse/core').useToNumber
const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] const useToString: typeof import('@vueuse/core').useToString
const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] const useToggle: typeof import('@vueuse/core').useToggle
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] const useTransition: typeof import('@vueuse/core').useTransition
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] const useUrlSearchParams: typeof import('@vueuse/core').useUrlSearchParams
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] const useUserMedia: typeof import('@vueuse/core').useUserMedia
const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] const useVModel: typeof import('@vueuse/core').useVModel
const watch: typeof import('vue')['watch'] const useVModels: typeof import('@vueuse/core').useVModels
const watchArray: typeof import('@vueuse/core')['watchArray'] const useVibrate: typeof import('@vueuse/core').useVibrate
const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] const useVirtualList: typeof import('@vueuse/core').useVirtualList
const watchDebounced: typeof import('@vueuse/core')['watchDebounced'] const useWakeLock: typeof import('@vueuse/core').useWakeLock
const watchDeep: typeof import('@vueuse/core')['watchDeep'] const useWebNotification: typeof import('@vueuse/core').useWebNotification
const watchEffect: typeof import('vue')['watchEffect'] const useWebSocket: typeof import('@vueuse/core').useWebSocket
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable'] const useWebWorker: typeof import('@vueuse/core').useWebWorker
const watchImmediate: typeof import('@vueuse/core')['watchImmediate'] const useWebWorkerFn: typeof import('@vueuse/core').useWebWorkerFn
const watchOnce: typeof import('@vueuse/core')['watchOnce'] const useWindowFocus: typeof import('@vueuse/core').useWindowFocus
const watchPausable: typeof import('@vueuse/core')['watchPausable'] const useWindowScroll: typeof import('@vueuse/core').useWindowScroll
const watchPostEffect: typeof import('vue')['watchPostEffect'] const useWindowSize: typeof import('@vueuse/core').useWindowSize
const watchSyncEffect: typeof import('vue')['watchSyncEffect'] const watch: typeof import('vue').watch
const watchThrottled: typeof import('@vueuse/core')['watchThrottled'] const watchArray: typeof import('@vueuse/core').watchArray
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable'] const watchAtMost: typeof import('@vueuse/core').watchAtMost
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] const watchDebounced: typeof import('@vueuse/core').watchDebounced
const whenever: typeof import('@vueuse/core')['whenever'] 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 // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue') import('vue')
// @ts-ignore
export type { AppThemeMode } from './src/stores/app-store-auto-imports'
import('./src/stores/app-store-auto-imports')
} }
// for vue template auto import // for vue template auto import
@@ -337,7 +351,10 @@ import { UnwrapRef } from 'vue'
declare module 'vue' { declare module 'vue' {
interface GlobalComponents {} interface GlobalComponents {}
interface ComponentCustomProperties { interface ComponentCustomProperties {
readonly ConfirmationService: UnwrapRef<typeof import('utils4u/primevue')['ConfirmationService']>
readonly DialogService: UnwrapRef<typeof import('utils4u/primevue')['DialogService']>
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']> readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ToastService: UnwrapRef<typeof import('utils4u/primevue')['ToastService']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']> readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly arrayToTree: UnwrapRef<typeof import('utils4u/array')['arrayToTree']> readonly arrayToTree: UnwrapRef<typeof import('utils4u/array')['arrayToTree']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']> readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
@@ -370,6 +387,7 @@ declare module 'vue' {
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']> readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly deepFreeze: UnwrapRef<typeof import('deep-freeze-es6')['default']> readonly deepFreeze: UnwrapRef<typeof import('deep-freeze-es6')['default']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']> readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineBasicLoader: UnwrapRef<typeof import('unplugin-vue-router/data-loaders/basic')['defineBasicLoader']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']> readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']> readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']> readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
@@ -380,6 +398,8 @@ declare module 'vue' {
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']> readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']> readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
readonly h: UnwrapRef<typeof import('vue')['h']> readonly h: UnwrapRef<typeof import('vue')['h']>
readonly i18nInstance: UnwrapRef<typeof import('./src/locales-utils/i18n-auto-imports')['i18nInstance']>
readonly i18nRouteMessages: UnwrapRef<typeof import('./src/locales-utils/route-messages/route-messages-auto-imports')['i18nRouteMessages']>
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']> readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
readonly inject: UnwrapRef<typeof import('vue')['inject']> readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']> readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
@@ -432,11 +452,13 @@ declare module 'vue' {
readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']> readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']>
readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']> readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']>
readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']> readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
readonly refManualReset: UnwrapRef<typeof import('@vueuse/core')['refManualReset']>
readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']> readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']> readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']> readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']> readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']> readonly routeI18nInstance: UnwrapRef<typeof import('./src/locales-utils/i18n-auto-imports')['routeI18nInstance']>
readonly routeI18nT: UnwrapRef<typeof import('./src/locales-utils/i18n-auto-imports')['routeI18nT']>
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']> readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']> readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly setViewportCSSVars: UnwrapRef<typeof import('utils4u/browser')['setViewportCSSVars']> readonly setViewportCSSVars: UnwrapRef<typeof import('utils4u/browser')['setViewportCSSVars']>
@@ -466,6 +488,7 @@ declare module 'vue' {
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']> readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']> readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']> readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']>
readonly useAppStore: UnwrapRef<typeof import('./src/stores/app-store-auto-imports')['useAppStore']>
readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']> readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']>
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']> readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']> readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
@@ -481,6 +504,7 @@ declare module 'vue' {
readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']> readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']>
readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']> readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']> readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useAuthStore: UnwrapRef<typeof import('./src/stores/auth-store-auto-imports')['useAuthStore']>
readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']> readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']>
readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']> readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']>
readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']> readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']>
@@ -543,7 +567,6 @@ declare module 'vue' {
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']> readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']> readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']> readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
readonly useLoadingBar: UnwrapRef<typeof import('naive-ui')['useLoadingBar']> readonly useLoadingBar: UnwrapRef<typeof import('naive-ui')['useLoadingBar']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']> readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']> readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
@@ -553,6 +576,7 @@ declare module 'vue' {
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']> readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']> readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
readonly useMessage: UnwrapRef<typeof import('naive-ui')['useMessage']> readonly useMessage: UnwrapRef<typeof import('naive-ui')['useMessage']>
readonly useMetaLayoutsNMenuOptions: UnwrapRef<typeof import('./src/composables/useMetaLayoutsMenuOptions')['useMetaLayoutsNMenuOptions']>
readonly useModal: UnwrapRef<typeof import('naive-ui')['useModal']> readonly useModal: UnwrapRef<typeof import('naive-ui')['useModal']>
readonly useModel: UnwrapRef<typeof import('vue')['useModel']> readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']> readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
@@ -582,6 +606,7 @@ declare module 'vue' {
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']> readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
readonly usePreferredReducedTransparency: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedTransparency']> readonly usePreferredReducedTransparency: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedTransparency']>
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']> readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
readonly usePrimevueDialogRef: UnwrapRef<typeof import('utils4u/primevue')['usePrimevueDialogRef']>
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']> readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']> readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
readonly useRefs: UnwrapRef<typeof import('utils4u/vue-use')['useRefs']> readonly useRefs: UnwrapRef<typeof import('utils4u/vue-use')['useRefs']>
+3 -1
View File
@@ -1,9 +1,11 @@
import type { UserConfig } from '@commitlint/types'; import type { UserConfig } from '@commitlint/types';
const Configuration: UserConfig = { const Configuration: UserConfig = {
extends: ['@commitlint/config-conventional'], extends: ['@commitlint/config-conventional'],
formatter: '@commitlint/format', formatter: '@commitlint/format',
rules: {
'body-max-line-length': [2, 'always', 500],
},
}; };
export default Configuration; export default Configuration;
+8 -56
View File
@@ -1,60 +1,12 @@
import { test, expect } from '@playwright/test' import { test, expect } from '@playwright/test';
test.describe('Vue App', () => { test.describe('Vue App', () => {
test('visits the app root url', async ({ page }) => { test('app renders correctly', async ({ page }) => {
await page.goto('/') await page.goto('/');
await expect(page.locator('h1')).toHaveText('You did it!')
})
test('displays Vue documentation link', async ({ page }) => { const app = page.locator('#app');
await page.goto('/') await expect(app).toBeVisible();
const link = page.locator('a[href="https://vuejs.org/"]')
await expect(link).toBeVisible()
await expect(link).toHaveText('vuejs.org')
await expect(link).toHaveAttribute('target', '_blank')
await expect(link).toHaveAttribute('rel', 'noopener')
})
test('displays button with initial name state', async ({ page }) => { await page.locator('.app-loading').waitFor({ state: 'detached' });
await page.goto('/') });
const button = page.locator('button[aria-label="get name"]') });
await expect(button).toBeVisible()
await expect(button).toHaveText('Name from API is: Unknown')
})
test('button click triggers API call', async ({ page }) => {
await page.goto('/')
await page.route('/api/', async (route) => {
await route.fulfill({
contentType: 'application/json',
body: JSON.stringify({ name: 'Test User' }),
})
})
const button = page.locator('button[aria-label="get name"]')
await button.click()
await expect(button).toHaveText('Name from API is: Test User')
})
test('handles API error gracefully', async ({ page }) => {
await page.goto('/')
await page.route('/api/', async (route) => {
await route.abort('failed')
})
const button = page.locator('button[aria-label="get name"]')
await button.click()
await expect(button).toHaveText('Name from API is: Unknown')
})
test('app-layout is present', async ({ page }) => {
await page.goto('/')
const appLayout = page.locator('.app-layout')
await expect(appLayout).toBeVisible()
await expect(appLayout).toContainText('AppLayout')
})
})
+1 -1
View File
@@ -1,4 +1,4 @@
{ {
"extends": "@tsconfig/node22/tsconfig.json", "extends": "@tsconfig/node24/tsconfig.json",
"include": ["./**/*"] "include": ["./**/*"]
} }
+96 -16
View File
@@ -1,14 +1,22 @@
import { globalIgnores } from 'eslint/config' import vueI18n from '@intlify/eslint-plugin-vue-i18n';
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' // import stylistic from '@stylistic/eslint-plugin';
import pluginVue from 'eslint-plugin-vue' import pluginVitest from '@vitest/eslint-plugin';
import pluginVitest from '@vitest/eslint-plugin' import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
import pluginPlaywright from 'eslint-plugin-playwright' import {
import pluginOxlint from 'eslint-plugin-oxlint' configureVueProject,
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' defineConfigWithVueTs,
vueTsConfigs,
} from '@vue/eslint-config-typescript';
import pluginImport from 'eslint-plugin-import';
import pluginJsonc from 'eslint-plugin-jsonc';
import pluginOxlint from 'eslint-plugin-oxlint';
import pluginPerfectionist from 'eslint-plugin-perfectionist';
import pluginPlaywright from 'eslint-plugin-playwright';
import pluginVue from 'eslint-plugin-vue';
import { globalIgnores } from 'eslint/config';
import jsoncParser from 'jsonc-eslint-parser';
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: configureVueProject({ scriptLangs: ['ts', 'tsx'] });
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup // More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs( export default defineConfigWithVueTs(
@@ -17,7 +25,14 @@ export default defineConfigWithVueTs(
files: ['**/*.{ts,mts,tsx,vue}'], files: ['**/*.{ts,mts,tsx,vue}'],
}, },
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), globalIgnores([
'worker-configuration.d.ts',
'**/dist/**',
'**/dist-ssr/**',
'**/coverage/**',
'**/public/**',
'**/-----TEMP-----/**',
]),
pluginVue.configs['flat/essential'], pluginVue.configs['flat/essential'],
vueTsConfigs.recommended, vueTsConfigs.recommended,
@@ -32,9 +47,61 @@ export default defineConfigWithVueTs(
files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'], files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'],
}, },
...pluginOxlint.configs['flat/recommended'], ...pluginOxlint.configs['flat/recommended'],
skipFormatting,
// https://eslint-plugin-vue-i18n.intlify.dev/started.html#getting-started
...vueI18n.configs.recommended,
{
rules: {
'@intlify/vue-i18n/no-raw-text': 'off',
'@intlify/vue-i18n/no-unused-keys': [
'error',
{
src: './src',
extensions: ['.js', '.ts', '.tsx', '.vue'],
},
],
},
settings: {
'vue-i18n': {
localeDir: './src/locales/**/*.json',
messageSyntaxVersion: '^11.0.0',
},
},
},
{
plugins: { import: pluginImport },
rules: {
'import/first': 'error',
'import/no-duplicates': 'error',
'import/newline-after-import': 'error',
'import/no-mutable-exports': 'error',
'import/no-named-default': 'error',
'import/no-self-import': 'error',
'import/no-unresolved': 'off',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
},
},
{ plugins: { perfectionist: pluginPerfectionist } },
{ {
/**
* 启用 sort-keys 规则以强制对象键按字母顺序排序
* 原因:
* 1. 减少多人协作时的合并冲突
* 2. 保持代码一致性,提高可维护性
*/
files: ['src/locales/**/*.json'],
languageOptions: { parser: jsoncParser },
plugins: { jsonc: pluginJsonc },
rules: { 'jsonc/sort-keys': 'error' },
},
{
// plugins: {
// '@stylistic': stylistic,
// },
rules: { rules: {
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
'vue/block-order': [ 'vue/block-order': [
@@ -45,11 +112,24 @@ export default defineConfigWithVueTs(
], ],
'vue/define-macros-order': [ 'vue/define-macros-order': [
'error', 'error',
{ { order: ['defineOptions', 'defineModel', 'defineProps', 'defineEmits', 'defineSlots'] },
order: ['defineOptions', 'defineProps', 'defineEmits', 'defineSlots'],
},
], ],
'vue/attributes-order': 'error',
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'vue/padding-line-between-blocks': ['error', 'always'],
'vue/component-name-in-template-casing': [
'error',
'PascalCase',
{ registeredComponentsOnly: false, ignores: [] },
],
// '@stylistic/padding-line-between-statements': [
// 'error',
// { blankLine: 'always', prev: '*', next: ['const', 'let', 'var'] },
// { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
// { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] },
// ],
}, },
}, },
)
skipFormatting,
);
+129 -51
View File
@@ -1,88 +1,166 @@
<!doctype html> <!doctype html>
<html lang="zh-CN" data-build-time="%VITE_BUILD_TIME%" data-commit="%VITE_BUILD_COMMIT%"> <html lang="zh-CN" data-build-time="%VITE_APP_BUILD_TIME%" data-commit="%VITE_APP_BUILD_COMMIT%">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<!-- <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> --> <!-- <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> -->
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<!-- viewport-fit=cover, -->
<meta <meta
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover, user-scalable=no" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/> />
<meta name="color-scheme" content="light dark" />
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<title>vue-ts-example-2025</title>
<title>%VITE_APP_TITLE%</title>
<style type="text/css"> <style type="text/css">
*,
::before,
::after {
box-sizing: border-box;
}
html,
body,
#app {
height: 100%;
}
html,
body {
margin: 0;
padding: 0;
}
body { body {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
margin: 0;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #000;
color: #fff;
}
} }
</style>
<style type="text/css">
#app { #app {
min-height: 100vh; min-height: 100vh;
display: flex;
flex-direction: column;
} }
@supports (min-height: 100dvh) { @supports (min-height: 100dvh) {
#app { #app {
min-height: 100dvh; min-height: 100dvh;
} }
html .min-h-screen {
min-height: 100dvh;
}
}
.page-wrapper {
flex-grow: 1;
} }
</style> </style>
</head> </head>
<!-- ontouchstart ontouchend -->
<body> <body>
<div id="app"> <div id="app">
<div class="page-wrapper" style="display: flex; justify-content: center; align-items: center"> <style type="text/css">
Loading... @keyframes spin {
to {
transform: rotate(360deg);
}
}
.app-loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(0, 0, 0, 0.08);
border-top-color: #333;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #0f0f0f;
}
.app-loading-spinner {
border: 3px solid rgba(255, 255, 255, 0.1);
border-top-color: #e5e5e5;
}
}
</style>
<div
class="app-loading"
style="
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
min-height: 100dvh;
"
>
<div class="app-loading-spinner"></div>
</div> </div>
</div> </div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
<!-- <script src="https://testingcf.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script> --> <!-- <script src="https://testingcf.jsdelivr.net/npm/@vant/touch-emulator/dist/index.min.js"></script> -->
<!-- .min.js 是 jsDelivr 的特殊处理 --> <!-- .min.js 是 jsDelivr 的特殊处理 -->
<!-- <script src="https://unpkg.luckincdn.com/@vant/touch-emulator@1.4.0/dist/index.js"></script> --> <!-- <script src="https://unpkg.luckincdn.com/@vant/touch-emulator@1.4.0/dist/index.js"></script> -->
<script>
window.addEventListener('DOMContentLoaded', function () {
window.ontouchstart = function () {};
window.ontouchend = function () {};
});
window.onloadX = function () {
// 禁止双指缩放
document.addEventListener('touchstart', function (event) {
if (event.touches.length > 1) {
event.preventDefault();
}
});
// 禁止双击放大
var lastTouchEnd = 0;
document.addEventListener(
'touchend',
function (event) {
var now = new Date().getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
},
false,
);
// 禁止手势事件
document.addEventListener('gesturestart', function (event) {
event.preventDefault();
});
};
</script>
<script>
(function (d) {
var config = {
kitId: 'whk2tto',
scriptTimeout: 3000,
async: true,
},
h = d.documentElement,
t = setTimeout(function () {
h.className = h.className.replace(/\bwf-loading\b/g, '') + ' wf-inactive';
}, config.scriptTimeout),
tk = d.createElement('script'),
f = false,
s = d.getElementsByTagName('script')[0],
a;
h.className += ' wf-loading';
tk.src = 'https://use.typekit.net/' + config.kitId + '.js';
tk.async = true;
tk.onload = tk.onreadystatechange = function () {
a = this.readyState;
if (f || (a && a != 'complete' && a != 'loaded')) return;
f = true;
clearTimeout(t);
try {
Typekit.load(config);
} catch (e) {}
};
s.parentNode.insertBefore(tk, s);
}); /* (document) */
</script>
</body> </body>
<script>
;(function (d) {
var config = {
kitId: 'whk2tto',
scriptTimeout: 3000,
async: true,
},
h = d.documentElement,
t = setTimeout(function () {
h.className = h.className.replace(/\bwf-loading\b/g, '') + ' wf-inactive'
}, config.scriptTimeout),
tk = d.createElement('script'),
f = false,
s = d.getElementsByTagName('script')[0],
a
h.className += ' wf-loading'
tk.src = 'https://use.typekit.net/' + config.kitId + '.js'
tk.async = true
tk.onload = tk.onreadystatechange = function () {
a = this.readyState
if (f || (a && a != 'complete' && a != 'loaded')) return
f = true
clearTimeout(t)
try {
Typekit.load(config)
} catch (e) {}
}
s.parentNode.insertBefore(tk, s)
}) /* (document) */
</script>
</html> </html>
+129 -90
View File
@@ -1,124 +1,163 @@
{ {
"packageManager": "pnpm@10.15.1", "packageManager": "pnpm@10.25.0",
"name": "vue-ts-example-2025", "name": "vue-ts-example-2025",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"engines": { "_devenginesruntime_docs": "https://pnpm.io/zh/package_json#devenginesruntime",
"node": "^20.19.0 || >=22.12.0" "devEngines": {
"runtime": {
"name": "node",
"version": "^24.11.1",
"onFail": "download"
}
}, },
"scripts": { "scripts": {
"_all": "run-p build-only format type-check lint", "all": "run-s lint format:prettier build-only type-check test:unit:DisableWatch",
"dev": "vite --port 4730 --host --strictPort", "dev": "nodemon --delay 280ms --watch vite-plugins --ext ts -x vite -- --port 4730 --host --strictPort",
"build": "run-p type-check \"build-only {@}\" --", "build": "run-p type-check \"build-only {@}\" --",
"build-only": "vite build", "build-only": "vite build",
"preview": "vite preview", "preview": "vite preview --port 4731 --host --strictPort",
"preview:wrangler": "pnpm run build && wrangler dev", "wrangler:dev": "wrangler dev --port 4732",
"test:unit": "vitest", "format:prettier": "prettier --config=.prettierrc.json --cache --write --log-level=warn src/",
"test:playwright": "playwright test",
"test:playwright:headless": "HEADLESS=true playwright test",
"test:playwright:ui": "playwright test --ui",
"test:playwright:chromium": "playwright test --project=chromium",
"type-check": "vue-tsc --build", "type-check": "vue-tsc --build",
"lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
"lint:eslint": "eslint . --fix",
"_oxlint_cfg": "oxlint . --fix --ignore-path=.gitignore --print-config",
"__oxlint_-D": "oxlint . --fix --deny=correctness",
"lint": "run-s lint:*", "lint": "run-s lint:*",
"format": "prettier --write src/", "lint:vue-i18n-extract": "vue-i18n-extract report --vueFiles './src/**/*.?(ts|tsx|vue)' --languageFiles './src/locales/**/*.?(json|yml|yaml|js)' --ci",
"-wrangler:pages:deploy:preview": "wrangler pages deploy dist --project-name=vue-ts-example-2025 --branch=preview", "lint:stylelint": "stylelint --config=stylelint.config.mjs --cache --output-file=node_modules/.cache/stylelint/stylelint-report.json --cache-location=node_modules/.cache/stylelint/.stylelintcache --fix --ignore-path=.gitignore '**/*.{css,less,scss,vue}'",
"-wrangler:pages:deploy:prod": "wrangler pages deploy dist --project-name=vue-ts-example-2025", "lint:oxlint": "oxlint . --fix -D correctness --ignore-path .gitignore",
"-deploy:preview": "run-s build-only wrangler:pages:deploy:preview", "lint:eslint": "eslint . --fix --config=eslint.config.ts --concurrency=auto --env-info --cache --cache-location=node_modules/.cache/eslint/.eslintcache",
"-deploy:prod": "run-s build-only wrangler:pages:deploy:prod", "test:unit:DisableWatch": "vitest --run",
"wrangler:deploy": "pnpm run build && wrangler deploy", "test:playwright:headless": "HEADLESS=true playwright test --quiet",
"wrangler:versions:upload": "pnpm run build && wrangler versions upload", "_stylelint-config": "stylelint --config=stylelint.config.mjs --print-config src/styles/scss/global.scss",
"cf-typegen": "wrangler types", "postinstall": "wrangler types",
"_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",
"prepare": "husky" "prepare": "husky"
}, },
"lint-staged": { "lint-staged": {
"{src,e2e}/**/*.{js,jsx,ts,tsx,vue}": [ "{src,e2e}/**/*.{js,jsx,ts,tsx,vue}": [
"prettier --write",
"eslint --fix", "eslint --fix",
"oxlint --fix" "oxlint --fix",
] "prettier --write"
],
"{src,packages}/**/*.{css,less,scss,vue}": [
"stylelint --fix"
],
"{src/locales-utils,src/locales}/**/*": "node scripts/type-check-for-lint-staged.mjs"
}, },
"pnpm": { "pnpm": {
"overrides": { "onlyBuiltDependencies": [
"vite": "$vite" "@parcel/watcher",
} "esbuild",
"oxc-resolver",
"sharp",
"vue-demi",
"workerd"
]
}, },
"dependencies": { "dependencies": {
"@commitlint/cli": "^19.8.1", "@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^19.8.1", "@commitlint/config-conventional": "^20.0.0",
"@formkit/auto-animate": "^0.9.0", "@formkit/auto-animate": "^0.9.0",
"@pinia/colada": "^0.17.4", "@pinia/colada": "^0.21.0",
"@primeuix/themes": "^1.2.3", "@primeuix/themes": "^2.0.0",
"@unhead/vue": "^2.0.14", "@sa/materials": "workspace:*",
"@vueuse/core": "^13.9.0", "@unhead/vue": "^2.0.19",
"pinia": "^3.0.3", "@vueuse/core": "^14.0.0",
"pinia-plugin-persistedstate": "^4.5.0", "highlight.js": "^11.11.1",
"primelocale": "^2.1.7", "jsonc-eslint-parser": "^2.4.1",
"primevue": "^4.3.9", "lodash-es": "^4.17.21",
"utils4u": "^4.2.3", "naive-ui": "^2.43.2",
"vue": "^3.5.21", "pinia": "^3.0.4",
"vue-i18n": "^11.1.12", "primeicons": "^7.0.0",
"vue-router": "^4.5.1" "primelocale": "^2.2.2",
"primevue": "^4.4.1",
"ts-enum-util": "^4.1.0",
"utils4u": "^5",
"vue": "^3.5.24",
"vue-i18n": "^11.2.1",
"vue-memoize-dict": "^1.1.3",
"vue-router": "^4.6.3"
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/vite-plugin": "^1.12.4", "@cloudflare/vite-plugin": "^1.15.2",
"@commitlint/types": "^19.8.1", "@commitlint/types": "^20.0.0",
"@intlify/unplugin-vue-i18n": "^11.0.0", "@iconify-json/carbon": "^1.2.14",
"@playwright/test": "^1.55.0", "@iconify-json/clarity": "^1.2.4",
"@prettier/plugin-oxc": "^0.0.4", "@iconify-json/line-md": "^1.2.11",
"@primevue/auto-import-resolver": "^4.3.9", "@iconify-json/material-symbols": "^1.2.47",
"@primevue/metadata": "^4.3.9", "@intlify/eslint-plugin-vue-i18n": "^4.1.0",
"@tsconfig/node22": "^22.0.2", "@intlify/unplugin-vue-i18n": "^11.0.1",
"@types/jsdom": "^21.1.7", "@playwright/test": "^1.57.0",
"@types/node": "^22.18.1", "@prettier/plugin-oxc": "^0.1.3",
"@primevue/auto-import-resolver": "^4.4.1",
"@primevue/metadata": "^4.4.1",
"@stylelint-types/stylelint-order": "^7.0.0",
"@stylelint-types/stylelint-scss": "^6.11.0",
"@stylistic/eslint-plugin": "^5.6.1",
"@tsconfig/node24": "^24.0.0",
"@types/html-minifier-terser": "^7.0.2",
"@types/jsdom": "^27.0.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.10.1",
"@vant/auto-import-resolver": "^1.3.0", "@vant/auto-import-resolver": "^1.3.0",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.2",
"@vitejs/plugin-vue-jsx": "^5.1.1", "@vitejs/plugin-vue-jsx": "^5.1.2",
"@vitest/eslint-plugin": "^1.3.9", "@vitest/eslint-plugin": "^1.4.3",
"@vue/eslint-config-prettier": "^10.2.0", "@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0", "@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6", "@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"eslint": "^9.35.0", "boxen": "^8.0.1",
"eslint-plugin-oxlint": "~1.14.0", "consola": "^3.4.2",
"eslint-plugin-playwright": "^2.2.2", "eslint": "^9.39.1",
"eslint-plugin-vue": "~10.4.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-oxlint": "~1.32.0",
"eslint-plugin-perfectionist": "^5.0.0",
"eslint-plugin-playwright": "^2.3.0",
"eslint-plugin-vue": "~10.6.0",
"happy-dom": "^20.0.10",
"html-minifier-terser": "^7.2.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jiti": "^2.5.1", "jsdom": "^27.2.0",
"jsdom": "^26.1.0", "lint-staged": "^16.2.7",
"lint-staged": "^16.1.6", "nodemon": "^3.1.11",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"oxlint": "~1.14.0", "oxlint": "~1.29.0",
"prettier": "3.6.2", "postcss-html": "^1.8.0",
"typescript": "~5.9.2", "prettier": "3.8.1",
"unocss": "^66.5.1", "rollup": "^4.53.3",
"unocss-preset-animations": "^1.2.1", "sass-embedded": "^1.93.3",
"unplugin-auto-import": "^20.1.0", "sharp": "^0.34.5",
"unplugin-icons": "^22.2.0", "stylelint": "^16.26.0",
"unplugin-vue-components": "^29.0.0", "stylelint-config-recess-order": "^7.4.0",
"unplugin-vue-markdown": "^29.1.0", "stylelint-config-standard": "^39.0.1",
"unplugin-vue-router": "^0.15.0", "stylelint-config-standard-scss": "^16.0.0",
"vite": "npm:rolldown-vite@^7.1.9", "stylelint-config-standard-vue": "^1.0.0",
"vite-plugin-checker": "^0.10.3", "stylelint-define-config": "^16.24.0",
"vite-plugin-fake-server": "^2.2.0", "svgo": "^4.0.0",
"vite-plugin-image-optimizer": "^2.0.2", "tinyglobby": "^0.2.15",
"vite-plugin-vue-devtools": "^8.0.1", "type-fest": "^5.2.0",
"vite-plugin-vue-meta-layouts": "^0.6.0", "typescript": "~5.9.3",
"unocss": "^66.5.9",
"unocss-preset-animations": "^1.3.0",
"unplugin-auto-import": "^21.0.0",
"unplugin-icons": "^23.0.0",
"unplugin-vue-components": "^31.0.0",
"unplugin-vue-markdown": "^29.2.0",
"unplugin-vue-router": "^0.19.0",
"vite": "^7.2.4",
"vite-plugin-checker": "^0.12.0",
"vite-plugin-fake-server": "^2.2.2",
"vite-plugin-image-optimizer": "^2.0.3",
"vite-plugin-vue-devtools": "^8.0.5",
"vite-plugin-vue-meta-layouts": "^0.6.1",
"vite-plugin-webfont-dl": "^3.11.1", "vite-plugin-webfont-dl": "^3.11.1",
"vitest": "^3.2.4", "vitest": "^4.0.13",
"vue-macros": "3.0.0-beta.23", "vue-component-type-helpers": "^3.1.4",
"vue-tsc": "^3.0.6", "vue-i18n-extract": "^2.0.7",
"wrangler": "^4.35.0" "vue-macros": "3.1.1",
"vue-tsc": "^3.1.8",
"wrangler": "^4.50.0"
} }
} }
+20
View File
@@ -0,0 +1,20 @@
{
"name": "@sa/materials",
"version": "1.3.15",
"exports": {
".": "./src/index.ts"
},
"typesVersions": {
"*": {
"*": [
"./src/*"
]
}
},
"scripts": {
"gen:css-types": "bunx --bun typed-css-modules src --pattern '**/*.module.css'"
},
"dependencies": {
"simplebar-vue": "2.4.2"
}
}
+6
View File
@@ -0,0 +1,6 @@
import AdminLayout, { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './libs/admin-layout';
import SimpleScrollbar from './libs/simple-scrollbar';
export { AdminLayout, LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX, SimpleScrollbar };
export * from './types';
@@ -0,0 +1,63 @@
/* @type */
.layout-header,
.layout-header-placement {
height: var(--soy-header-height);
}
.layout-header {
z-index: var(--soy-header-z-index);
}
.layout-tab {
top: var(--soy-header-height);
z-index: var(--soy-tab-z-index);
height: var(--soy-tab-height);
}
.layout-tab-placement {
height: var(--soy-tab-height);
}
.layout-sider {
z-index: var(--soy-sider-z-index);
width: var(--soy-sider-width);
}
.layout-mobile-sider {
z-index: var(--soy-sider-z-index);
}
.layout-mobile-sider-mask {
z-index: var(--soy-mobile-sider-z-index);
}
.layout-sider-collapsed {
z-index: var(--soy-sider-z-index);
width: var(--soy-sider-collapsed-width);
}
.layout-footer,
.layout-footer-placement {
height: var(--soy-footer-height);
}
.layout-footer {
z-index: var(--soy-footer-z-index);
}
.left-gap {
padding-left: var(--soy-sider-width);
}
.left-gap-collapsed {
padding-left: var(--soy-sider-collapsed-width);
}
.sider-padding-top {
padding-top: var(--soy-header-height);
}
.sider-padding-bottom {
padding-bottom: var(--soy-footer-height);
}
@@ -0,0 +1,17 @@
declare const styles: {
readonly 'layout-footer': string;
readonly 'layout-footer-placement': string;
readonly 'layout-header': string;
readonly 'layout-header-placement': string;
readonly 'layout-mobile-sider': string;
readonly 'layout-mobile-sider-mask': string;
readonly 'layout-sider': string;
readonly 'layout-sider-collapsed': string;
readonly 'layout-tab': string;
readonly 'layout-tab-placement': string;
readonly 'left-gap': string;
readonly 'left-gap-collapsed': string;
readonly 'sider-padding-bottom': string;
readonly 'sider-padding-top': string;
};
export = styles;
@@ -0,0 +1,5 @@
import AdminLayout from './index.vue';
import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID } from './shared';
export default AdminLayout;
export { LAYOUT_SCROLL_EL_ID, LAYOUT_MAX_Z_INDEX };
@@ -0,0 +1,238 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { AdminLayoutProps } from '../../types';
import { LAYOUT_MAX_Z_INDEX, LAYOUT_SCROLL_EL_ID, createLayoutCssVars } from './shared';
import style from './index.module.css';
defineOptions({
name: 'AdminLayout',
});
const props = withDefaults(defineProps<AdminLayoutProps>(), {
mode: 'vertical',
scrollMode: 'content',
scrollElId: LAYOUT_SCROLL_EL_ID,
commonClass: 'transition-all-300',
fixedTop: true,
maxZIndex: LAYOUT_MAX_Z_INDEX,
headerVisible: true,
headerHeight: 56,
tabVisible: true,
tabHeight: 48,
siderVisible: true,
siderCollapse: false,
siderWidth: 220,
siderCollapsedWidth: 64,
footerVisible: true,
footerHeight: 48,
rightFooter: false,
});
const emit = defineEmits<Emits>();
const slots = defineSlots<Slots>();
interface Emits {
/** Update siderCollapse */
(e: 'update:siderCollapse', collapse: boolean): void;
}
type SlotFn = (props?: Record<string, unknown>) => any;
type Slots = {
/** Main */
default?: SlotFn;
/** Header */
header?: SlotFn;
/** Tab */
tab?: SlotFn;
/** Sider */
sider?: SlotFn;
/** Footer */
footer?: SlotFn;
};
const cssVars = computed(() => createLayoutCssVars(props));
// config visible
const showHeader = computed(() => Boolean(slots.header) && props.headerVisible);
const showTab = computed(() => Boolean(slots.tab) && props.tabVisible);
const showSider = computed(() => !props.isMobile && Boolean(slots.sider) && props.siderVisible);
const showMobileSider = computed(
() => props.isMobile && Boolean(slots.sider) && props.siderVisible,
);
const showFooter = computed(() => Boolean(slots.footer) && props.footerVisible);
// scroll mode
const isWrapperScroll = computed(() => props.scrollMode === 'wrapper');
const isContentScroll = computed(() => props.scrollMode === 'content');
// layout direction
const isVertical = computed(() => props.mode === 'vertical');
const isHorizontal = computed(() => props.mode === 'horizontal');
const fixedHeaderAndTab = computed(
() => props.fixedTop || (isHorizontal.value && isWrapperScroll.value),
);
// css
const leftGapClass = computed(() => {
if (!props.fullContent && showSider.value) {
return props.siderCollapse ? style['left-gap-collapsed'] : style['left-gap'];
}
return '';
});
const headerLeftGapClass = computed(() => (isVertical.value ? leftGapClass.value : ''));
const footerLeftGapClass = computed(() => {
const condition1 = isVertical.value;
const condition2 = isHorizontal.value && isWrapperScroll.value && !props.fixedFooter;
const condition3 = Boolean(isHorizontal.value && props.rightFooter);
if (condition1 || condition2 || condition3) {
return leftGapClass.value;
}
return '';
});
const siderPaddingClass = computed(() => {
let cls = '';
if (showHeader.value && !headerLeftGapClass.value) {
cls += style['sider-padding-top'];
}
if (showFooter.value && !footerLeftGapClass.value) {
cls += ` ${style['sider-padding-bottom']}`;
}
return cls;
});
function handleClickMask() {
emit('update:siderCollapse', true);
}
</script>
<template>
<div class="relative h-full" :class="[commonClass]" :style="cssVars">
<div
:id="isWrapperScroll ? scrollElId : undefined"
class="h-full flex flex-col"
:class="[commonClass, scrollWrapperClass, { 'overflow-y-auto': isWrapperScroll }]"
>
<!-- Header -->
<template v-if="showHeader">
<header
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-header'],
commonClass,
headerLeftGapClass,
{ 'absolute top-0 left-0 w-full': fixedHeaderAndTab },
]"
>
<slot name="header"></slot>
</header>
<div
v-show="!fullContent && fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-header-placement']]"
></div>
</template>
<!-- Tab -->
<template v-if="showTab">
<div
class="flex-shrink-0 overflow-hidden"
:class="[
style['layout-tab'],
commonClass,
tabClass,
{ 'top-0!': fullContent || !showHeader },
leftGapClass,
{ 'absolute left-0 w-full': fixedHeaderAndTab },
]"
>
<slot name="tab"></slot>
</div>
<div
v-show="fullContent || fixedHeaderAndTab"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-tab-placement']]"
></div>
</template>
<!-- Sider -->
<template v-if="showSider">
<aside
v-show="!fullContent"
class="absolute left-0 top-0 h-full"
:class="[
commonClass,
siderClass,
siderPaddingClass,
siderCollapse ? style['layout-sider-collapsed'] : style['layout-sider'],
]"
>
<slot name="sider"></slot>
</aside>
</template>
<!-- Mobile Sider -->
<template v-if="showMobileSider">
<aside
class="absolute left-0 top-0 h-full w-0 bg-white dark:bg-gray-800"
:class="[
commonClass,
mobileSiderClass,
style['layout-mobile-sider'],
siderCollapse ? 'overflow-hidden' : style['layout-sider'],
]"
>
<slot name="sider"></slot>
</aside>
<div
v-show="!siderCollapse"
class="absolute left-0 top-0 h-full w-full bg-[rgba(0,0,0,0.2)]"
:class="[style['layout-mobile-sider-mask']]"
@click="handleClickMask"
></div>
</template>
<!-- Main Content -->
<main
:id="isContentScroll ? scrollElId : undefined"
class="flex flex-col flex-grow"
:class="[commonClass, contentClass, leftGapClass, { 'overflow-y-auto': isContentScroll }]"
>
<slot></slot>
</main>
<!-- Footer -->
<template v-if="showFooter">
<footer
v-show="!fullContent"
class="flex-shrink-0"
:class="[
style['layout-footer'],
commonClass,
footerClass,
footerLeftGapClass,
{ 'absolute left-0 bottom-0 w-full': fixedFooter },
]"
>
<slot name="footer"></slot>
</footer>
<div
v-show="!fullContent && fixedFooter"
class="flex-shrink-0 overflow-hidden"
:class="[style['layout-footer-placement']]"
></div>
</template>
</div>
</div>
</template>
@@ -0,0 +1,68 @@
import type { AdminLayoutProps, LayoutCssVars, LayoutCssVarsProps } from '../../types';
/** The id of the scroll element of the layout */
export const LAYOUT_SCROLL_EL_ID = '__SCROLL_EL_ID__';
/** The max z-index of the layout */
export const LAYOUT_MAX_Z_INDEX = 100;
/**
* Create layout css vars by css vars props
*
* @param props Css vars props
*/
function createLayoutCssVarsByCssVarsProps(props: LayoutCssVarsProps) {
const cssVars: LayoutCssVars = {
'--soy-header-height': `${props.headerHeight}px`,
'--soy-header-z-index': props.headerZIndex,
'--soy-tab-height': `${props.tabHeight}px`,
'--soy-tab-z-index': props.tabZIndex,
'--soy-sider-width': `${props.siderWidth}px`,
'--soy-sider-collapsed-width': `${props.siderCollapsedWidth}px`,
'--soy-sider-z-index': props.siderZIndex,
'--soy-mobile-sider-z-index': props.mobileSiderZIndex,
'--soy-footer-height': `${props.footerHeight}px`,
'--soy-footer-z-index': props.footerZIndex
};
return cssVars;
}
/**
* Create layout css vars
*
* @param props
*/
export function createLayoutCssVars(props: AdminLayoutProps) {
const {
mode,
isMobile,
maxZIndex = LAYOUT_MAX_Z_INDEX,
headerHeight,
tabHeight,
siderWidth,
siderCollapsedWidth,
footerHeight
} = props;
const headerZIndex = maxZIndex - 3;
const tabZIndex = maxZIndex - 5;
const siderZIndex = mode === 'vertical' || isMobile ? maxZIndex - 1 : maxZIndex - 4;
const mobileSiderZIndex = isMobile ? maxZIndex - 2 : 0;
const footerZIndex = maxZIndex - 5;
const cssProps: LayoutCssVarsProps = {
headerHeight,
headerZIndex,
tabHeight,
tabZIndex,
siderWidth,
siderZIndex,
mobileSiderZIndex,
siderCollapsedWidth,
footerHeight,
footerZIndex
};
return createLayoutCssVarsByCssVarsProps(cssProps);
}
@@ -0,0 +1,3 @@
import SimpleScrollbar from './index.vue';
export default SimpleScrollbar;
@@ -0,0 +1,16 @@
<script setup lang="ts">
import Simplebar from 'simplebar-vue';
import 'simplebar-vue/dist/simplebar.min.css';
defineOptions({
name: 'SimpleScrollbar',
});
</script>
<template>
<div class="h-full flex-1-hidden">
<Simplebar class="h-full">
<slot />
</Simplebar>
</div>
</template>
+288
View File
@@ -0,0 +1,288 @@
/** Header config */
interface AdminLayoutHeaderConfig {
/**
* Whether header is visible
*
* @default true
*/
headerVisible?: boolean;
/**
* Header height
*
* @default 56px
*/
headerHeight?: number;
}
/** Tab config */
interface AdminLayoutTabConfig {
/**
* Whether tab is visible
*
* @default true
*/
tabVisible?: boolean;
/**
* Tab class
*
* @default ''
*/
tabClass?: string;
/**
* Tab height
*
* @default 48px
*/
tabHeight?: number;
}
/** Sider config */
interface AdminLayoutSiderConfig {
/**
* Whether sider is visible
*
* @default true
*/
siderVisible?: boolean;
/**
* Sider class
*
* @default ''
*/
siderClass?: string;
/**
* Mobile sider class
*
* @default ''
*/
mobileSiderClass?: string;
/**
* Sider collapse status
*
* @default false
*/
siderCollapse?: boolean;
/**
* Sider width when collapse is false
*
* @default '220px'
*/
siderWidth?: number;
/**
* Sider width when collapse is true
*
* @default '64px'
*/
siderCollapsedWidth?: number;
}
/** Content config */
export interface AdminLayoutContentConfig {
/**
* Content class
*
* @default ''
*/
contentClass?: string;
/**
* Whether content is full the page
*
* If true, other elements will be hidden by `display: none`
*/
fullContent?: boolean;
}
/** Footer config */
export interface AdminLayoutFooterConfig {
/**
* Whether footer is visible
*
* @default true
*/
footerVisible?: boolean;
/**
* Whether footer is fixed
*
* @default true
*/
fixedFooter?: boolean;
/**
* Footer class
*
* @default ''
*/
footerClass?: string;
/**
* Footer height
*
* @default 48px
*/
footerHeight?: number;
/**
* Whether footer is on the right side
*
* When the layout is vertical, the footer is on the right side
*/
rightFooter?: boolean;
}
/**
* Layout mode
*
* - Horizontal
* - Vertical
*/
export type LayoutMode = 'horizontal' | 'vertical';
/**
* The scroll mode when content overflow
*
* - Wrapper: the layout component's wrapper element has a scrollbar
* - Content: the layout component's content element has a scrollbar
*
* @default 'wrapper'
*/
export type LayoutScrollMode = 'wrapper' | 'content';
/** Admin layout props */
export interface AdminLayoutProps
extends AdminLayoutHeaderConfig,
AdminLayoutTabConfig,
AdminLayoutSiderConfig,
AdminLayoutContentConfig,
AdminLayoutFooterConfig {
/**
* Layout mode
*
* - {@link LayoutMode}
*/
mode?: LayoutMode;
/** Is mobile layout */
isMobile?: boolean;
/**
* Scroll mode
*
* - {@link ScrollMode}
*/
scrollMode?: LayoutScrollMode;
/**
* The id of the scroll element of the layout
*
* It can be used to get the corresponding Dom and scroll it
*
* @example
* use the default id by import
* ```ts
* import { adminLayoutScrollElId } from '@sa/vue-materials';
* ```
*
* @default
* ```ts
* const adminLayoutScrollElId = '__ADMIN_LAYOUT_SCROLL_EL_ID__'
* ```
*/
scrollElId?: string;
/** The class of the scroll element */
scrollElClass?: string;
/** The class of the scroll wrapper element */
scrollWrapperClass?: string;
/**
* The common class of the layout
*
* Is can be used to configure the transition animation
*
* @default 'transition-all-300'
*/
commonClass?: string;
/**
* Whether fix the header and tab
*
* @default true
*/
fixedTop?: boolean;
/**
* The max z-index of the layout
*
* The z-index of Header,Tab,Sider and Footer will not exceed this value
*/
maxZIndex?: number;
}
type Kebab<S extends string> = S extends Uncapitalize<S> ? S : `-${Uncapitalize<S>}`;
type KebabCase<S extends string> = S extends `${infer Start}${infer End}`
? `${Uncapitalize<Start>}${KebabCase<Kebab<End>>}`
: S;
type Prefix = '--soy-';
export type LayoutCssVarsProps = Pick<
AdminLayoutProps,
'headerHeight' | 'tabHeight' | 'siderWidth' | 'siderCollapsedWidth' | 'footerHeight'
> & {
headerZIndex?: number;
tabZIndex?: number;
siderZIndex?: number;
mobileSiderZIndex?: number;
footerZIndex?: number;
};
export type LayoutCssVars = {
[K in keyof LayoutCssVarsProps as `${Prefix}${KebabCase<K>}`]: string | number;
};
/**
* The mode of the tab
*
* - Button: button style
* - Chrome: chrome style
*
* @default chrome
*/
export type PageTabMode = 'button' | 'chrome';
export interface PageTabProps {
/** Whether is dark mode */
darkMode?: boolean;
/**
* The mode of the tab
*
* - {@link TabMode}
*/
mode?: PageTabMode;
/**
* The common class of the layout
*
* Is can be used to configure the transition animation
*
* @default 'transition-all-300'
*/
commonClass?: string;
/** The class of the button tab */
buttonClass?: string;
/** The class of the chrome tab */
chromeClass?: string;
/** Whether the tab is active */
active?: boolean;
/** The color of the active tab */
activeColor?: string;
/**
* Whether the tab is closable
*
* Show the close icon when true
*/
closable?: boolean;
}
export type PageTabCssVarsProps = {
primaryColor: string;
primaryColor1: string;
primaryColor2: string;
primaryColorOpacity1: string;
primaryColorOpacity2: string;
primaryColorOpacity3: string;
};
export type PageTabCssVars = {
[K in keyof PageTabCssVarsProps as `${Prefix}${KebabCase<K>}`]: string | number;
};
+20
View File
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
+5 -5
View File
@@ -1,8 +1,8 @@
import { defineConfig, devices } from '@playwright/test' import { defineConfig, devices } from '@playwright/test';
import process from 'node:process' import process from 'node:process';
// const runningInVSCode = process.env.TERM_PROGRAM === 'vscode' // const runningInVSCode = process.env.TERM_PROGRAM === 'vscode'
const baseURL = 'http://localhost:4173' const baseURL = 'http://localhost:4731';
/** /**
* Read environment variables from file. * Read environment variables from file.
@@ -97,7 +97,7 @@ export default defineConfig({
], ],
/* Folder for test artifacts such as screenshots, videos, traces, etc. */ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
// outputDir: 'test-results/', outputDir: 'playwright-test-results/',
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
webServer: { webServer: {
@@ -110,4 +110,4 @@ export default defineConfig({
port: Number(new URL(baseURL).port), port: Number(new URL(baseURL).port),
reuseExistingServer: true /* !process.env.CI */, reuseExistingServer: true /* !process.env.CI */,
}, },
}) });
+6425 -2888
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
packages:
- 'packages/*'
# shamefullyHoist: false # https://pnpm.io/zh/settings#shamefullyhoist
overrides:
vue-tsc: $vue-tsc
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env node
/**
* Type check script for lint-staged
* This script ignores file arguments passed by lint-staged and runs type-check on the entire project
*/
import { spawnSync } from 'child_process';
const result = spawnSync('pnpm', ['run', 'type-check'], {
stdio: 'inherit',
shell: process.platform === 'win32',
});
process.exit(result.status ?? 1);
+81 -16
View File
@@ -1,24 +1,89 @@
export default { export default {
async fetch(request, env) { async fetch(request, env) {
const url = new URL(request.url) const url = new URL(request.url);
// 本地开发环境延迟处理
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
await new Promise((r) => setTimeout(r, 250));
}
// API 路由处理
if (url.pathname.startsWith('/api/')) { if (url.pathname.startsWith('/api/')) {
// write a key-value pair await env.KV.put(
await env.KV.put('KEY', 'VALUE') 'events:api:last-call',
// read a key-value pair `${new Date().toISOString()} ${request.method} ${url.pathname}`,
const value = await env.KV.get('KEY') );
// list all key-value pairs
const allKeys = await env.KV.list() // 获取所有可用的键名
// delete a key-value pair const availableKeys = [
await env.KV.delete('KEY') 'events:api:last-call',
'events:ws:connection',
'events:ws:message',
'events:ws:disconnection',
];
return Response.json({ return Response.json({
name: 'Cloudflare', timestamp: Date.now(),
value, lastApiCall: await env.KV.get('events:api:last-call'),
valueAfterDelete: await env.KV.get('KEY'), availableKeys: availableKeys,
allKeys, storedData: {
}) apiLastCall: await env.KV.get('events:api:last-call'),
wsConnection: await env.KV.get('events:ws:connection'),
wsMessage: await env.KV.get('events:ws:message'),
wsDisconnection: await env.KV.get('events:ws:disconnection'),
},
});
} }
return new Response(null, { status: 404 })
// WebSocket 连接处理
if (url.pathname === '/ws') {
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader !== 'websocket') {
return new Response('Expected websocket', { status: 400 });
}
const [client, server] = Object.values(new WebSocketPair());
// 处理服务器端WebSocket消息
server.accept();
env.KV.put('events:ws:connection', `${new Date().toISOString()} ${url.pathname}`);
// accept 后立即发送欢迎消息
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
await new Promise((r) => setTimeout(r, 250));
}
server.send(
`欢迎连接到WebSocket服务器!连接时间: ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`,
);
server.addEventListener('message', async (event) => {
console.log('收到客户端消息:', event.data);
await env.KV.put('events:ws:message', `${new Date().toISOString()} ${event.data}`);
// 回复消息给客户端
if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
await new Promise((r) => setTimeout(r, 250));
}
server.send(
`服务器收到: ${event.data} (时间: ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })})`,
);
});
server.addEventListener('close', (event) => {
console.log('WebSocket连接关闭');
console.log('[close] event :>> ', event);
env.KV.put('events:ws:disconnection', `${new Date().toISOString()} ${url.pathname}`);
server.close(event.code, `连接关闭: ${event.reason}`);
});
return new Response(null, {
status: 101,
webSocket: client,
});
}
return new Response(null, { status: 404 });
}, },
} satisfies ExportedHandler<Env> } satisfies ExportedHandler<Env>;
+34
View File
@@ -0,0 +1,34 @@
/**
* @vitest-environment happy-dom
*/
/*
* https://pinia.vuejs.org/zh/cookbook/testing.html#unit-testing-components
*/
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import { createMemoryHistory, createRouter } from 'vue-router';
import App from './App.vue';
const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/',
component: {
template: 'Welcome to the blogging app',
},
},
],
});
describe('App', () => {
it('renders RouterView', async () => {
router.push('/');
await router.isReady();
const wrapper = mount(App, { global: { plugins: [router, createPinia()] } });
expect(wrapper.text()).toContain('Welcome to the blogging app');
});
});
+12 -23
View File
@@ -1,29 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { RouterView } from 'vue-router';
import AppNaiveUIProvider from './AppNaiveUIProvider.vue';
const name = ref('Unknown')
const getName = async () => {
const res = await fetch('/api/')
const data = await res.json()
name.value = data.name
}
</script> </script>
<template> <template>
<div> <DynamicDialog />
<h1>You did it!</h1> <ConfirmDialog />
<p> <Toast style="z-index: 5000" />
Visit <a href="https://vuejs.org/" target="_blank" rel="noopener">vuejs.org</a> to read the
documentation
</p>
<button class="green" @click="getName" aria-label="get name">
Name from API is: {{ name }}
</button>
</div>
<DynamicDialog /> <ConfirmDialog /> <Toast /> <AppNaiveUIProvider>
<RouterView /> <RouterView v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<component :is="Component" />
</Transition>
</RouterView>
</AppNaiveUIProvider>
</template> </template>
<style scoped></style>
+58
View File
@@ -0,0 +1,58 @@
<script setup lang="ts">
import type { GlobalThemeOverrides } from 'naive-ui';
import { darkTheme, dateZhCN, zhCN } from 'naive-ui';
import type { FunctionalComponent } from 'vue';
import { createTextVNode } from 'vue';
const appStore = useAppStore();
// https://www.naiveui.com/zh-CN/light/docs/customize-theme
const themeOverrides: GlobalThemeOverrides = {
common: {},
};
const ContextHolder: FunctionalComponent = () => {
window.$nLoadingBar = useLoadingBar();
window.$nModal = useModal();
window.$nDialog = useDialog();
window.$nMessage = useMessage();
window.$nNotification = useNotification();
return createTextVNode();
};
</script>
<script lang="ts">
declare global {
export interface Window {
$nLoadingBar?: import('naive-ui').LoadingBarProviderInst;
$nModal?: import('naive-ui').ModalProviderInst;
$nDialog?: import('naive-ui').DialogProviderInst;
$nMessage?: import('naive-ui').MessageProviderInst;
$nNotification?: import('naive-ui').NotificationProviderInst;
}
}
</script>
<template>
<NConfigProvider
:locale="zhCN"
:date-locale="dateZhCN"
:theme-overrides
preflight-style-disabled
:theme="appStore.isDark ? darkTheme : null"
abstract
>
<NLoadingBarProvider>
<NMessageProvider>
<NNotificationProvider>
<NModalProvider>
<NDialogProvider>
<slot></slot>
<ContextHolder />
</NDialogProvider>
</NModalProvider>
</NNotificationProvider>
</NMessageProvider>
</NLoadingBarProvider>
</NConfigProvider>
</template>
-11
View File
@@ -1,11 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import App from '../App.vue'
describe('App', () => {
it('mounts renders properly', () => {
const wrapper = mount(App)
expect(wrapper.text()).toContain('You did it!')
})
})
+16
View File
@@ -0,0 +1,16 @@
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<!-- 中心圆形 -->
<circle cx="100" cy="100" r="35" fill="#FDB813"/>
<!-- 光芒 -->
<g stroke="#FDB813" stroke-width="8" stroke-linecap="round">
<line x1="100" y1="30" x2="100" y2="50"/>
<line x1="141" y1="41" x2="129" y2="59"/>
<line x1="170" y1="100" x2="150" y2="100"/>
<line x1="141" y1="159" x2="129" y2="141"/>
<line x1="100" y1="170" x2="100" y2="150"/>
<line x1="59" y1="159" x2="71" y2="141"/>
<line x1="30" y1="100" x2="50" y2="100"/>
<line x1="59" y1="41" x2="71" y2="59"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 608 B

@@ -0,0 +1,184 @@
import type { MenuInst, MenuOption } from 'naive-ui';
import { createGetRoutes } from 'virtual:meta-layouts';
import type { Ref } from 'vue';
import type { RouteRecordRaw } from 'vue-router';
import { RouterLink } from 'vue-router';
import IconMenuRounded from '~icons/material-symbols/menu-rounded';
export function useMetaLayoutsNMenuOptions({ menuInstRef }: { menuInstRef: Ref<MenuInst | null> }) {
const router = useRouter();
const { t, te } = routeI18nInstance.global;
// 获取路由表但是不包含布局路由
const routes = createGetRoutes(router)();
const options = computed(() => convertRoutesToNMenuOptions(routes));
const selectedKey = ref('');
watch(
() => router.currentRoute.value,
(route) => {
// 优先使用 activeMenuName(通过路由名称解析为路径),如果没有则使用当前路径
const activeMenuPath = route.meta.activeMenuName
? router.resolve({ name: route.meta.activeMenuName }).path
: route.path;
selectedKey.value = activeMenuPath;
menuInstRef.value?.showOption(activeMenuPath); // 展开菜单,确保设定的元素被显示
},
{ immediate: true },
);
// 路由转换为菜单树的辅助函数
function convertRoutesToNMenuOptions(routes: Readonly<RouteRecordRaw[]>): MenuOption[] {
const orderMaxLength = routes.reduce((max, route) => {
const order = route.meta?.order;
if (order !== undefined) {
const orderLength = String(order).length;
return orderLength > max ? orderLength : max;
}
return max;
}, 0);
const menuMap = new Map<string, MenuOption>();
const rootMenus: MenuOption[] = [];
// 过滤路由
const validRoutes = routes.filter((route) => {
// 过滤掉不需要显示的路由
if (route.meta?.hideInMenu === true || route.meta?.layout === false) {
return false;
}
// 过滤掉通配符路径
if (route.path.includes('*')) {
return false;
}
// 根据环境变量判断是否显示 /demos 开头的路由
if (import.meta.env.VITE_APP_MENU_SHOW_DEMOS !== 'true' && route.path.startsWith('/demos')) {
return false;
}
return true;
});
// 排序路由:先按路径深度分组,再按 order 排序
const sortedRoutes = validRoutes.slice().sort((a: RouteRecordRaw, b: RouteRecordRaw) => {
const pathA = a.path;
const pathB = b.path;
// 1. 首先按路径深度排序(确保父路由在子路由之前)
const depthA = pathA.split('/').filter(Boolean).length;
const depthB = pathB.split('/').filter(Boolean).length;
if (depthA !== depthB) {
return depthA - depthB;
}
// 2. 获取父路径,判断是否为同一父级下的路由
const segmentsA = pathA.split('/').filter(Boolean);
const segmentsB = pathB.split('/').filter(Boolean);
const parentA = segmentsA.length > 1 ? `/${segmentsA.slice(0, -1).join('/')}` : '/';
const parentB = segmentsB.length > 1 ? `/${segmentsB.slice(0, -1).join('/')}` : '/';
// 如果父路径不同,按父路径字母顺序排序
if (parentA !== parentB) {
return parentA.localeCompare(parentB);
}
// 3. 同一父级下的路由,按 order 排序
const orderA = a.meta?.order;
const orderB = b.meta?.order;
const hasOrderA = typeof orderA === 'number';
const hasOrderB = typeof orderB === 'number';
// 有 order 的排在没有 order 的前面
if (hasOrderA && !hasOrderB) return -1;
if (!hasOrderA && hasOrderB) return 1;
// 都有 order 时,按 order 数值升序排序
if (hasOrderA && hasOrderB) {
const diff = (orderA as number) - (orderB as number);
if (diff !== 0) return diff;
}
// order 相同或都没有 order,按路径名字母顺序排序
return pathA.localeCompare(pathB);
});
// 构建菜单树
for (const route of sortedRoutes) {
const pathSegments = route.path.split('/').filter(Boolean);
const routeName = route.name as string;
let text = te(routeName) ? t(routeName) : routeName;
if (import.meta.env.VITE_APP_MENU_SHOW_ORDER === 'true' && route.meta?.order) {
const order = String(route.meta.order).padStart(orderMaxLength, '0');
text = `${order}. ${text}`;
}
const menuOption: MenuOption = {
label: () =>
route.meta?.link === false ? text : <RouterLink to={route}>{text}</RouterLink>,
key: route.path,
icon: () => <IconMenuRounded style="width: 1em; height: 1em;" />,
};
// 如果是根路径或只有一级路径,直接添加到根菜单
if (pathSegments.length === 0 || pathSegments.length === 1) {
rootMenus.push(menuOption);
menuMap.set(route.path, menuOption);
} else {
// 多级路径,需要创建或找到父菜单
let currentPath = '';
for (let i = 0; i < pathSegments.length - 1; i++) {
currentPath += `/${pathSegments[i]}`;
}
// 将当前菜单项添加到父菜单
const parentPath = currentPath;
const parent = menuMap.get(parentPath);
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(menuOption);
} else {
consola.warn(`未找到父菜单项: ${parentPath},无法将子菜单项添加到其下。`);
}
menuMap.set(route.path, menuOption);
}
}
// 添加调试日志
if (import.meta.env.DEV) {
console.debug(
'排序后的路由:',
sortedRoutes.map((route) => ({
path: route.path,
name: route.name,
order: route.meta?.order,
})),
);
}
return rootMenus;
}
if (import.meta.env.DEV) {
console.debug(
'原始路由:',
routes.map((route) => ({
path: route.path,
name: route.name,
order: route.meta?.order,
})),
);
console.debug('转换后的菜单:', options.value);
}
return {
options,
selectedKey,
// expanded-keys // 展开的子菜单标识符数组,如果设定了,菜单的展开将会进入受控状态,default-expanded-keys 不会生效
};
}
@@ -0,0 +1,18 @@
<script setup lang="ts">
import LanguageSwitchButton from './components/LanguageSwitchButton.vue';
import ThemeSwitchButton from './components/ThemeSwitchButton.vue';
import ToggleSiderButton from './components/ToggleSiderButton.vue';
import UserDropdown from './components/UserDropdown.vue';
</script>
<template>
<div class="h-full flex items-center justify-between px-12px shadow-header dark:shadow-gray-700">
<ToggleSiderButton />
<div class="flex items-center">
<LanguageSwitchButton />
<ThemeSwitchButton />
<UserDropdown />
</div>
</div>
</template>
@@ -0,0 +1,33 @@
<script setup lang="ts">
import type { DropdownOption } from 'naive-ui';
const { locale, availableLocales } = useI18n({ useScope: 'global' });
const languageLabels: Record<string, string> = {
'en-US': 'English',
'zh-CN': '简体中文',
};
const options = computed<DropdownOption[]>(() =>
availableLocales.map((lang) => ({
label: languageLabels[lang] || lang,
key: lang,
disabled: locale.value === lang,
})),
);
function handleSelect(key: string) {
locale.value = key;
}
</script>
<template>
<NDropdown trigger="hover" placement="bottom-end" :options="options" @select="handleSelect">
<NButton quaternary class="flex items-center gap-1">
<template #icon>
<IconClarityLanguageLine w-4.5 h-4.5 />
</template>
<span>{{ languageLabels[locale] }}</span>
</NButton>
</NDropdown>
</template>
@@ -0,0 +1,30 @@
<script setup lang="ts">
const appStore = useAppStore();
const themeLabels: Record<AppThemeMode, string> = {
light: '浅色',
dark: '深色',
auto: '跟随系统',
};
</script>
<template>
<NTooltip :disabled="appStore.isMobile" placement="bottom-end">
{{ themeLabels[appStore.themeMode] }}
<template #trigger>
<NButton quaternary @click="appStore.cycleTheme()">
<IconLineMdSunnyFilledLoopToMoonFilledLoopTransition
v-if="appStore.themeMode === 'light'"
w-4.5
h-4.5
/>
<IconLineMdMoonFilledToSunnyFilledLoopTransition
v-else-if="appStore.themeMode === 'dark'"
w-4.5
h-4.5
/>
<IconLineMdComputer v-else w-4.5 h-4.5 />
</NButton>
</template>
</NTooltip>
</template>
@@ -0,0 +1,23 @@
<script setup lang="ts">
const buttonRef = useTemplateRef('buttonRef');
const appStore = useAppStore();
function toggleCollapsed() {
// https://github.com/tusen-ai/naive-ui/issues/3688
// hover style 鼠标移出就会消失 如果是点击 button 会聚焦需要失去焦点才会恢复
buttonRef.value?.$el.blur();
appStore.toggleSidebar();
}
</script>
<template>
<NTooltip :disabled="appStore.isMobile" placement="bottom-start">
{{ appStore.sidebarCollapsed ? '展开菜单' : '收起菜单' }}
<template #trigger>
<NButton ref="buttonRef" quaternary @click="toggleCollapsed">
<IconLineMdMenuFoldRight v-if="appStore.sidebarCollapsed" w-4.5 h-4.5 />
<IconLineMdMenuFoldLeft v-else w-4.5 h-4.5 />
</NButton>
</template>
</NTooltip>
</template>
@@ -0,0 +1,45 @@
<script setup lang="tsx">
const router = useRouter();
const userStore = useAuthStore();
const dialog = useDialog();
const options = computed(() => [
{
label: userStore.userInfo?.nickname || userStore.userInfo?.username || '用户',
key: 'user',
disabled: true,
},
{
type: 'divider',
key: 'd1',
},
{
label: '退出登录',
key: 'logout',
icon: () => <icon-material-symbols-logout class="w-4 h-4" />,
},
]);
function handleSelect(key: string) {
if (key === 'logout') {
dialog.warning({
title: '退出登录',
content: '确定要退出登录吗?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
userStore.clearToken('用户退出登录');
router.push('/login');
},
});
}
}
</script>
<template>
<NDropdown :options="options" placement="bottom-end" @select="handleSelect">
<NButton quaternary circle>
<IconMaterialSymbolsAccountCircle w-5 h-5 />
</NButton>
</NDropdown>
</template>
@@ -0,0 +1,27 @@
<script setup lang="tsx">
import { useAppStore } from '@/stores/app-store-auto-imports';
const menuInstRef = useTemplateRef('menuInstRef');
const { options, selectedKey } = useMetaLayoutsNMenuOptions({
menuInstRef,
});
const appStore = useAppStore();
</script>
<template>
<!-- @update:value="handleMenuUpdate" -->
<NMenu
ref="menuInstRef"
v-model:value="selectedKey"
mode="vertical"
:collapsed="appStore.sidebarCollapsed"
:collapsed-width="64"
:icon-size="20"
:collapsed-icon-size="24"
:options="options"
:inverted="false"
:root-indent="32"
:indent="32"
/>
</template>
@@ -0,0 +1,52 @@
<script setup lang="ts">
import { AdminLayout } from '@sa/materials';
import BaseLayoutHeader from './base-layout-header/base-layout-header.vue';
import BaseLayoutSider from './base-layout-sider.vue';
const appStore = useAppStore();
</script>
<template>
<AdminLayout
v-model:sider-collapse="appStore.sidebarCollapsed"
mode="horizontal"
:footer-visible="!false"
:tab-visible="!false"
scroll-mode="content"
:is-mobile="appStore.isMobile"
>
<template #header>
<BaseLayoutHeader />
</template>
<template #tab>
<div
class="bg-green-100/28 dark:bg-green-900/28 text-green-900 dark:text-green-100 flex h-full items-center justify-center"
>
GlobalTab
</div>
</template>
<template #sider>
<BaseLayoutSider />
</template>
<!-- <div>GlobalContent</div> -->
<RouterView v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<component :is="Component" />
</Transition>
</RouterView>
<!-- <div>ThemeDrawer</div> -->
<template #footer>
<div
class="bg-red-100/28 dark:bg-red-900/28 text-red-900 dark:text-red-100 h-full flex items-center justify-center"
>
GlobalFooter
</div>
</template>
</AdminLayout>
</template>
<style lang="scss">
#__SCROLL_EL_ID__ {
@include scrollbar;
}
</style>
-10
View File
@@ -1,10 +0,0 @@
<script setup lang="ts"></script>
<template>
<div class="app-layout">
<div>AppLayout</div>
<router-view />
</div>
</template>
<style scoped></style>
+80
View File
@@ -0,0 +1,80 @@
/* https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n#static-bundle-importing
* All i18n resources specified in the plugin `include` option can be loaded
* at once using the import syntax
*/
import messages from '@intlify/unplugin-vue-i18n/messages';
import { router } from '@/plugins/00.router-plugin';
import { createGetRoutes } from 'virtual:meta-layouts';
import { createI18n } from 'vue-i18n';
import { START_LOCATION } from 'vue-router';
const locale = useLocalStorage<string>('app-locale', navigator.language);
watchEffect(() => {
window.document.documentElement.setAttribute('lang', locale.value);
});
// https://vue-i18n.intlify.dev/guide/essentials/started.html#registering-the-i18n-plugin
export const i18nInstance = createI18n({
legacy: false, // you must set `false`, to use Composition API
locale: locale.value,
fallbackRoot: false,
// flatJson: true,
missing: (locale, key /* , instance, type */) => {
consola.warn(`缺少国际化内容: locale='${locale}', key='${key}'`);
return `[${key}]`;
},
missingWarn: !true,
fallbackWarn: !true,
messages,
});
export const routeI18nInstance = createI18n({
legacy: false, // you must set `false`, to use Composition API
locale: locale.value,
inheritLocale: true,
useScope: 'local',
missing: (locale, key) => {
consola.warn(`菜单翻译缺失: locale=${locale}, key=${key}`);
if (__DEV__) {
ToastService.add({
severity: 'warn',
summary: '菜单翻译缺失',
detail: `菜单翻译缺失: locale=${locale}, key=${key}`,
life: 5000,
});
}
return key;
},
fallbackRoot: true,
messages: i18nRouteMessages,
});
export const routeI18nT = routeI18nInstance.global.t;
watchEffect(
() => {
locale.value = i18nInstance.global.locale.value;
},
{ flush: 'sync' },
);
watch(
() => i18nInstance.global.locale.value,
() => {
const { t, te } = routeI18nInstance.global;
routeI18nInstance.global.locale.value = i18nInstance.global.locale.value;
if (router.currentRoute.value.name && router.currentRoute.value !== START_LOCATION) {
router.currentRoute.value.meta.title = t(router.currentRoute.value.name as string);
}
const routes = createGetRoutes(router)(); // 获取路由表但是不包含布局路由
routes.forEach((route) => {
const routeName = route.name as string;
route.meta.title = te(routeName) ? t(routeName) : routeName;
});
},
{ immediate: true, flush: 'sync' },
);
@@ -0,0 +1,66 @@
# route-messages
此目录存放专门用于**路由名称**的国际化(i18n)消息。这些消息通过一套自定义的编译时安全机制,为应用的导航菜单提供标题。
## 解决什么问题?
`unplugin-vue-router``definePage()` 宏在编译时执行,无法访问 Vue `<script setup>` 作用域中的运行时变量(如 `t()` 函数)。这使得在路由元信息(`meta`)中直接定义多语言标题变得不可能。
## 解决方案:自定义的编译时安全机制
我们采用一种**约定优于配置**的策略,并利用 TypeScript 进行编译时检查,以确保所有菜单标题都已定义。
**工作流程:**
1. **`RouteNamedMap` 的生成**`unplugin-vue-router` 会扫描你的页面组件,并自动生成一个名为 `RouteNamedMap` 的 TypeScript 类型,该类型包含了项目中所有具名路由的 `name`
2. **自定义全局类型**:我们定义了一个全局类型 `PageTitleLocalizations`
```typescript
declare global {
type PageTitleLocalizations = Record<keyof RouteNamedMap, string>;
}
```
这个自定义类型创建了一个**契约**:任何满足此类型的对象,都**必须**为 `RouteNamedMap` 中的每一个路由名称提供一个字符串类型的键值对。
3. **编译时检查**:此目录下的每个语言环境文件(如 `en-US.ts`)都必须使用 `satisfies PageTitleLocalizations` 来进行类型断言。
```typescript
// ./en-US.ts
export default { ... } satisfies PageTitleLocalizations;
```
这个操作会触发 TypeScript 在**编译时**进行检查。如果你新增了一个具名路由但忘记在此处添加翻译,**TypeScript 编译将会失败**,并明确提示你缺少的键。
4. **菜单生成**:在运行时,`@/composables/useMetaLayoutsMenuOptions.tsx` 会获取当前路由的 `name`,并使用它作为键(`t(routeName)`)来查找并显示菜单标题。由于有编译时检查,我们可以确信翻译始终存在。
### 带来的好处
- **杜绝遗漏**:从根本上解决了菜单项标题缺失或显示为原始键的问题。
- **关注点分离**:路由定义只关心路由结构,显示文本则集中在此处管理。
### 开发者实践指南
1. **理解路由命名规则**
`unplugin-vue-router` 会根据页面组件的**文件路径**自动生成 `PascalCase` 格式的路由 `name`。
- **示例**
- 文件路径:`src/pages/demos/api-demo.page.vue`
- 自动生成的路由 `name``DemosApiDemo`
2. **添加对应的翻译**
使用上一步中自动生成的 `name` 作为键,在此目录的每个语言文件中添加翻译。
```ts
// ./zh-CN.ts
export default {
// ... 其他翻译
DemosApiDemo: 'API 演示',
} satisfies PageTitleLocalizations;
// ./en-US.ts
export default {
// ... 其他翻译
DemosApiDemo: 'API Demo',
} satisfies PageTitleLocalizations;
```
+25
View File
@@ -0,0 +1,25 @@
/*eslint perfectionist/sort-objects: "error"*/
/**
* 启用 perfectionist/sort-objects 规则以强制对象键按字母顺序排序
* 原因:
* 1. 减少多人协作时的合并冲突
* 2. 保持代码一致性,提高可维护性
*
* 运行以下命令自动修复排序:
* pnpm exec eslint --fix --no-ignore src/locales-utils/route-messages/
*/
export default {
$Path: '$Path',
Demos: 'Demos',
DemosApiDemo: 'API Demo',
DemosCounterDemo: 'Counter Demo',
DemosCreate: 'Create Demo',
DemosI18nDemo: 'i18n Demo',
DemosNaiveUiDemo: 'Naive UI Demo',
DemosPrimevueDemo: 'PrimeVue Demo',
DemosWebsocketDemo: 'WebSocket Demo',
Home: 'Home',
Login: 'Login',
Root: 'Index',
} satisfies PageTitleLocalizations;
@@ -0,0 +1,17 @@
import type { I18nOptions } from 'vue-i18n';
const modules = import.meta.glob(['./*.ts', '!./route-messages-auto-imports'], {
eager: true /* true 为同步,false 为异步 */,
import: 'default',
});
type MessageType = Record<string, string>;
export const i18nRouteMessages: I18nOptions['messages'] = Object.entries(modules).reduce(
(messages, [path, mod]) => {
const locale = path.replace(/(\.\/|\.ts)/g, '');
messages[locale] = mod as MessageType;
return messages;
},
{} as Record<string, MessageType>,
);
+25
View File
@@ -0,0 +1,25 @@
/*eslint perfectionist/sort-objects: "error"*/
/**
* 启用 perfectionist/sort-objects 规则以强制对象键按字母顺序排序
* 原因:
* 1. 减少多人协作时的合并冲突
* 2. 保持代码一致性,提高可维护性
*
* 运行以下命令自动修复排序:
* pnpm exec eslint --fix --no-ignore src/locales-utils/route-messages/
*/
export default {
$Path: '$Path',
Demos: '示例演示',
DemosApiDemo: 'API 调用示例',
DemosCounterDemo: '点击计数器',
DemosCreate: '创建示例',
DemosI18nDemo: '国际化示例',
DemosNaiveUiDemo: 'Naive UI 组件示例',
DemosPrimevueDemo: 'PrimeVue 组件示例',
DemosWebsocketDemo: 'WebSocket 示例',
Home: '首页',
Login: '登录',
Root: '根 (Gēn)',
} satisfies PageTitleLocalizations;
+10
View File
@@ -0,0 +1,10 @@
{
"page": {
"i18n-demo": {
"change-language": "Change Language",
"current-language": "Current Language",
"hello": "Hello, {name}!",
"title": "Vue I18n Demo"
}
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"page": {
"i18n-demo": {
"change-language": "切换语言",
"current-language": "当前语言",
"hello": "你好, {name}",
"title": "Vue I18n 示例"
}
}
}
+1
View File
@@ -0,0 +1 @@
{}
+1
View File
@@ -0,0 +1 @@
{}
+10 -8
View File
@@ -1,12 +1,14 @@
import './styles' import './styles/index.ts';
// import { LogLevels } from 'consola'; import { LogLevels } from 'consola';
// consola.level = LogLevels.verbose; import App from './App.vue';
import { setupPlugins } from './plugins';
import App from './App.vue' consola.level = LogLevels.verbose;
const autoInstallModules = import.meta.glob('./plugins/!(index).ts', { eager: true }) const app = createApp(App);
if (__DEV__) Object.defineProperty(window, '__APP__', { value: app });
setupPlugins(app);
import { setupPlugins } from './plugins' await new Promise((resolve) => setTimeout(resolve, 280));
app.mount('#app');
setupPlugins(createApp(App), autoInstallModules).mount('#app')
+7
View File
@@ -0,0 +1,7 @@
<script setup lang="ts">
definePage({ meta: { hideInMenu: false } });
</script>
<template>
<div>Home Page</div>
</template>
+87
View File
@@ -0,0 +1,87 @@
<script setup lang="ts">
definePage({ meta: { ignoreAuth: true, layout: false } });
const router = useRouter();
const userStore = useAuthStore();
const message = useMessage();
const formValue = ref({
username: 'admin',
password: 'admin',
});
const loading = ref(false);
async function handleLogin() {
if (!formValue.value.username || !formValue.value.password) {
message.warning('请输入用户名和密码');
return;
}
loading.value = true;
try {
const result = await userStore.login(formValue.value.username, formValue.value.password);
if (result.success) {
message.success('登录成功');
router.push('/');
} else {
message.error(result.message || '登录失败');
}
} catch {
message.error('登录异常');
} finally {
loading.value = false;
}
}
</script>
<template>
<div class="login-page">
<NCard class="login-card" title="用户登录">
<NForm :model="formValue" label-placement="left" label-width="80">
<NFormItem label="用户名" path="username">
<NInput
v-model:value="formValue.username"
placeholder="请输入用户名"
@keyup.enter="handleLogin"
/>
</NFormItem>
<NFormItem label="密码" path="password">
<NInput
v-model:value="formValue.password"
type="password"
show-password-on="click"
placeholder="请输入密码"
@keyup.enter="handleLogin"
/>
</NFormItem>
<NFormItem :show-label="false">
<NButton type="primary" block :loading="loading" @click="handleLogin"> 登录 </NButton>
</NFormItem>
</NForm>
<div class="login-hint">
<NText depth="3">提示用户名和密码均为 admin</NText>
</div>
</NCard>
</div>
</template>
<style scoped lang="scss">
.login-page {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-card {
width: 400px;
max-width: 90%;
}
.login-hint {
margin-top: 16px;
text-align: center;
}
</style>
+20 -2
View File
@@ -1,11 +1,29 @@
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ path: string }>() defineProps<{ path: string }>();
declare global {
interface Window {
stack?: ReturnType<typeof createStackGuard>;
}
}
const stack = window?.stack;
const canGoBack = stack && stack.length > 1;
const router = useRouter();
function handleBack() {
if (canGoBack) {
router.back();
} else {
router.push('/');
}
}
</script> </script>
<template> <template>
<main flex-1 class="flex flex-col items-center justify-center h-full space-y-4"> <main flex-1 class="flex flex-col items-center justify-center h-full space-y-4">
<h1>Not Found</h1> <h1>Not Found</h1>
<p>{{ path }} does not exist.</p> <p>{{ path }} does not exist.</p>
<Button @click="$router.back()">Back</Button> <Button @click="handleBack">{{ canGoBack ? 'Back' : 'Home' }}</Button>
</main> </main>
</template> </template>
+111
View File
@@ -0,0 +1,111 @@
<script setup lang="ts">
import { ref } from 'vue';
definePage({
meta: {},
});
// ========== API 模块 ==========
const apiResult = ref<string>('');
const loading = ref(false);
const callApi = async () => {
loading.value = true;
try {
const response = await fetch('/api/');
const data = await response.json();
apiResult.value = JSON.stringify(data, null, 2);
} catch (error) {
apiResult.value = `Error: ${error}`;
} finally {
loading.value = false;
}
};
</script>
<template>
<div
class="transition-all duration-500 p-2 sm:p-3 bg-gray-50 dark:bg-gray-900 via-blue-50 dark:via-slate-800 to-slate-100 dark:to-gray-900 bg-gradient-to-br"
>
<div class="max-w-5xl mx-auto">
<!-- API 调用示例 -->
<div
class="backdrop-blur-sm rounded-2xl shadow-lg border p-4 sm:p-5 hover:shadow-2xl hover:scale-[1.02] transition-all duration-500 cursor-pointer group bg-white dark:bg-gray-800 border-white dark:border-gray-700 hover:bg-white dark:hover:bg-gray-800"
>
<div class="flex items-center mb-3">
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg flex items-center justify-center mr-2"
>
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
</div>
<h2 class="text-lg font-bold text-gray-800 dark:text-gray-100">API 调用示例</h2>
</div>
<button
:disabled="loading"
:aria-label="loading ? '正在调用API' : '调用API接口'"
class="w-full bg-gradient-to-br from-blue-500 via-blue-600 to-purple-600 text-white font-semibold py-3 px-4 rounded-xl hover:from-blue-600 hover:via-blue-700 hover:to-purple-700 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-sm"
@click="callApi"
>
<span v-if="loading" class="flex items-center justify-center">
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
调用中...
</span>
<span v-else>调用 API</span>
</button>
<div
v-if="apiResult"
class="mt-4 rounded-lg p-4 border bg-gray-50 dark:bg-gray-800 dark:from-gray-800 dark:to-gray-700 border-gray-200 dark:border-gray-600 bg-gradient-to-r from-gray-50 dark:from-gray-800 to-gray-100 dark:to-gray-700"
>
<h3 class="font-semibold mb-2 flex items-center text-sm text-gray-700 dark:text-gray-200">
<svg
class="w-4 h-4 mr-2 text-green-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
响应结果:
</h3>
<pre
class="text-sm overflow-x-auto p-3 rounded border text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-600"
>{{ apiResult }}</pre
>
</div>
</div>
</div>
</div>
</template>
+176
View File
@@ -0,0 +1,176 @@
<script setup lang="ts">
import { ref } from 'vue';
import { NButton } from 'naive-ui';
definePage({
meta: {},
});
// ========== 计数器模块 ==========
const clickCount = ref(0);
const incrementCount = () => {
clickCount.value++;
};
const resetCount = () => {
clickCount.value = 0;
};
</script>
<template>
<div
class="transition-all duration-500 p-2 sm:p-3 bg-gray-50 dark:bg-gray-900 via-blue-50 dark:via-slate-800 to-slate-100 dark:to-gray-900 bg-gradient-to-br"
>
<div class="max-w-5xl mx-auto">
<!-- 计数器示例 -->
<div
class="mt-4 sm:mt-6 backdrop-blur-sm rounded-2xl shadow-lg border p-4 sm:p-5 bg-white dark:bg-gray-800 border-white dark:border-gray-700 hover:shadow-2xl hover:scale-[1.02] transition-all duration-500"
>
<div class="mb-4">
<div class="flex items-center mb-2">
<div
class="w-8 h-8 bg-gradient-to-r from-orange-500 to-red-600 rounded-lg flex items-center justify-center mr-2"
>
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 11.5V14m0-2.5v-6a1.5 1.5 0 113 0m-3 6a1.5 1.5 0 00-3 0v2a7.5 7.5 0 0015 0v-5a1.5 1.5 0 00-3 0m-6-3V11m0-5.5v-1a1.5 1.5 0 013 0v1m0 0V11m0-5.5a1.5 1.5 0 013 0v3m0 0V11"
/>
</svg>
</div>
<h2 class="text-lg font-bold text-gray-800 dark:text-gray-100">点击计数器</h2>
</div>
<!-- 说明文字 -->
<div
class="text-sm text-gray-600 dark:text-gray-400 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3"
>
<div class="flex items-start">
<svg
class="w-4 h-4 mr-2 mt-0.5 text-blue-500 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<div>
<span class="font-semibold text-blue-700 dark:text-blue-300">测试说明</span>
<span class="text-gray-700 dark:text-gray-300"
>用于测试移动端连点和页面缩放对按钮点击事件的影响</span
>
</div>
</div>
</div>
</div>
<div class="flex flex-col items-center justify-center space-y-4">
<!-- 计数显示 -->
<div
class="w-full p-6 rounded-xl bg-gradient-to-br from-orange-50 to-red-50 dark:from-orange-900/30 dark:to-red-900/30 border-2 border-orange-200 dark:border-orange-700"
>
<div class="text-center">
<div class="text-sm text-gray-600 dark:text-gray-300 mb-2">当前点击次数</div>
<div
class="text-6xl font-bold bg-gradient-to-r from-orange-500 to-red-600 bg-clip-text text-transparent"
>
{{ clickCount }}
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="w-full flex flex-col gap-3">
<!-- 原生按钮 ( touch 事件) -->
<button
class="w-full bg-gradient-to-br from-orange-500 via-orange-600 to-red-600 text-white font-semibold py-4 px-6 rounded-xl hover:from-orange-600 hover:via-orange-700 hover:to-red-700 transition-all duration-500 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-lg"
@touchstart="() => {}"
@touchend="() => {}"
@click="incrementCount"
>
<span class="flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
点击 +1 ( touch)
</span>
</button>
<!-- 原生按钮 ( touch 事件) -->
<button
class="w-full bg-gradient-to-br from-blue-500 via-blue-600 to-purple-600 text-white font-semibold py-4 px-6 rounded-xl hover:from-blue-600 hover:via-blue-700 hover:to-purple-700 transition-all duration-500 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-lg"
@click="incrementCount"
>
<span class="flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
点击 +1 ( touch)
</span>
</button>
<!-- Naive UI 按钮 -->
<NButton
type="warning"
size="large"
block
strong
secondary
class="text-lg"
@click="incrementCount"
>
<template #icon>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
</template>
点击 +1 (Naive UI)
</NButton>
<!-- 重置按钮 -->
<button
:disabled="clickCount === 0"
class="w-full bg-gradient-to-br from-gray-500 via-gray-600 to-gray-700 text-white font-semibold py-3 px-6 rounded-xl hover:from-gray-600 hover:via-gray-700 hover:to-gray-800 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02]"
@click="resetCount"
>
<span class="flex items-center justify-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
重置计数器
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
+12
View File
@@ -0,0 +1,12 @@
<script setup lang="ts">
definePage({
meta: {
hideInMenu: true,
activeMenuName: 'Demos',
},
});
</script>
<template>
<div></div>
</template>
+37
View File
@@ -0,0 +1,37 @@
<script setup lang="ts">
definePage({ meta: { order: 1 } });
const { t, locale } = useI18n({});
function setLocale(newLocale: 'en-US' | 'zh-CN') {
i18nInstance.global.locale.value = newLocale;
}
</script>
<template>
<div class="p-4">
<NH1>{{ t('page.i18n-demo.title') }}</NH1>
<NCard :title="t('page.i18n-demo.change-language')">
<NP>
{{ t('page.i18n-demo.current-language') }}:
<span class="font-bold">{{ locale }}</span>
</NP>
<NP>
{{ t('page.i18n-demo.hello', { name: 'Kilo' }) }}
</NP>
<NSpace>
<NButton type="primary" @click="setLocale('en-US')"> English </NButton>
<NButton type="success" @click="setLocale('zh-CN')"> 简体中文 </NButton>
</NSpace>
</NCard>
<!-- 这里响应式有问题: -->
<NP> $route.meta.title: {{ $route.meta.title }} </NP>
<!-- 这样才正常 -->
<NP>
routeI18nInstance.global.t($route.name): {{ routeI18nInstance.global.t($route.name) }}
</NP>
</div>
</template>
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="tsx">
import hljs from 'highlight.js/lib/core';
import json from 'highlight.js/lib/languages/json';
import type { FunctionalComponent } from 'vue';
hljs.registerLanguage('json', json);
definePage({ meta: { link: true } });
const FComponent: FunctionalComponent<{ prop: string }> = (props /* context */) => (
<>
<NBlockquote>
函数式组件文档:
<a
class="text-blue-500 hover:text-blue-600 transition-colors"
href="https://cn.vuejs.org/guide/extras/render-function#typing-functional-components"
rel="noopener noreferrer"
target="_blank"
>
Render Function & JSX
</a>
</NBlockquote>
<p class="my-4">这是一个函数式组件它接收到的 prop 值为</p>
<NCode code={JSON.stringify(props, null, 2)} language="json" hljs={hljs} />
</>
);
</script>
<template>
<NCard title="函数式组件(TSX)示例">
<FComponent prop="some-prop-value" />
</NCard>
</template>
@@ -0,0 +1,96 @@
<script setup lang="ts">
import type { MessageType } from 'naive-ui';
import { useDialog, useMessage } from 'naive-ui';
definePage({ meta: {} });
const message = useMessage();
const dialog = useDialog();
const messageTypes = ['info', 'success', 'warning', 'error', 'loading'] satisfies MessageType[];
const dialogTypes = ['info', 'success', 'warning', 'error'] as const;
const openAllMessages = () => {
messageTypes.forEach((type, index) => {
setTimeout(() => {
message[type](`${index + 1}. 消息内容`, {
duration: 3000,
closable: true,
});
}, index * 500);
});
};
const openDialog = (type: (typeof dialogTypes)[number]) => {
dialog[type]({
title: `${type.charAt(0).toUpperCase() + type.slice(1)} 弹窗`,
content: '这是一个命令式 API 创建的弹窗示例。',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {
message.success('点击了确定');
},
onNegativeClick: () => {
message.error('点击了取消');
},
});
};
const openModal = () => {
window.$nModal!.create({
title: '命令式 Modal 示例',
content: '这是一个命令式 API 创建的 Modal 示例,使用 preset="dialog"。',
preset: 'dialog',
onPositiveClick: () => {
message.success('点击了确定');
},
onNegativeClick: () => {
message.error('点击了取消');
},
positiveText: '确定',
negativeText: '取消',
});
};
</script>
<template>
<div class="naive-ui-demo-page">
<NCard>
<template #header>Naive UI 组件演示</template>
<NAlert title="信息" type="info" :bordered="false">
演示 Naive UI 各种组件的使用方法和功能特性
</NAlert>
<NCard title="Message 消息" class="mt-4">
<NSpace>
<NButton
v-for="(type, index) in messageTypes"
:key="type"
@click="
message[type](`${index + 1}. 消息内容`, {
duration: 3000,
closable: true,
})
"
>
{{ `${index + 1}. ${type}` }}
</NButton>
<NButton @click="openAllMessages"> 一键打开所有 </NButton>
</NSpace>
</NCard>
<NCard title="Dialog 弹窗 (命令式 API)" class="mt-4">
<NSpace>
<NButton v-for="type in dialogTypes" :key="type" @click="openDialog(type)">
{{ type }}
</NButton>
</NSpace>
</NCard>
<NCard title="Modal 弹窗 (命令式 API)" class="mt-4">
<NSpace>
<NButton @click="openModal"> 打开 Modal </NButton>
</NSpace>
</NCard>
</NCard>
</div>
</template>
+61
View File
@@ -0,0 +1,61 @@
<script setup lang="ts">
import type { ToastMessageOptions } from 'primevue/toast';
definePage({
meta: {},
});
const tostSeverities = [
'secondary',
'success',
'info' /* 默认 */,
'warn',
'error',
'contrast',
undefined,
] satisfies ToastMessageOptions['severity'][];
const openAllToasts = () => {
tostSeverities.forEach((severity, index) => {
setTimeout(() => {
ToastService.add({
severity,
summary: `severity: ${severity ?? 'default'}`,
life: 3000,
detail: `${index + 1}. 消息内容`,
});
}, index * 500);
});
};
</script>
<template>
<div class="prime-vue-demo-page">
<Card>
<template #title>PrimeVue 组件演示</template>
<template #content>
<Message severity="info">演示 PrimeVue 各种组件的使用方法和功能特性</Message>
<Panel header="Toast 消息" class="mt-1.5">
<div flex="~ wrap" gap="4">
<Button
v-for="(severity, index) in tostSeverities"
:key="severity ?? 'default'"
@click="
ToastService.add({
severity: severity,
summary: `severity: ${severity ?? 'default'}`,
life: 3000,
detail: '消息内容',
})
"
>
{{ `${index + 1}. ${severity ?? 'default'}` }}
</Button>
<Button @click="openAllToasts"> 一键打开所有 </Button>
</div>
</Panel>
</template>
</Card>
</div>
</template>
+438
View File
@@ -0,0 +1,438 @@
<script setup lang="ts">
import { ref, onUnmounted, computed, nextTick } from 'vue';
definePage({
meta: {},
});
// ========== WebSocket 模块 ==========
const ws = ref<WebSocket | null>(null);
const wsConnected = ref(false);
const wsMessages = ref<string[]>([]);
const messageInput = ref('');
const wsLoading = ref(false);
const connectionAttempts = ref(0);
const maxReconnectAttempts = 3;
const messagesContainer = ref<HTMLElement | null>(null);
const scrollToBottom = async () => {
await nextTick();
if (messagesContainer.value) {
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
}
};
const connectWebSocket = async () => {
if (ws.value?.readyState === WebSocket.OPEN) return;
wsLoading.value = true;
connectionAttempts.value++;
try {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws`;
ws.value = new WebSocket(wsUrl);
ws.value.onopen = (event) => {
console.log('[onopen] event :>> ', event);
wsConnected.value = true;
wsLoading.value = false;
connectionAttempts.value = 0;
wsMessages.value.push(`✅ WebSocket连接已建立 (${new Date().toLocaleTimeString()})`);
scrollToBottom();
};
ws.value.onmessage = (event) => {
console.log('[onmessage] event :>> ', event);
wsMessages.value.push(`📨 收到: ${event.data}`);
scrollToBottom();
};
ws.value.onclose = (event) => {
console.log('[onclose] event :>> ', event);
wsConnected.value = false;
wsLoading.value = false;
const reason = event.reason || '连接意外断开';
wsMessages.value.push(
`❌ WebSocket连接已关闭: ${reason} (${new Date().toLocaleTimeString()})`,
);
scrollToBottom();
if (connectionAttempts.value < maxReconnectAttempts && !event.wasClean) {
setTimeout(() => {
wsMessages.value.push(
`🔄 尝试重新连接 (${connectionAttempts.value}/${maxReconnectAttempts})...`,
);
scrollToBottom();
connectWebSocket();
}, 2000);
}
};
ws.value.onerror = (error) => {
console.error('[onerror] error :>> ', error);
wsLoading.value = false;
const errorMessage = error instanceof Error ? error.message : String(error);
wsMessages.value.push(
`⚠️ WebSocket连接错误: ${errorMessage} (${new Date().toLocaleTimeString()})`,
);
scrollToBottom();
};
} catch {
wsLoading.value = false;
wsMessages.value.push(`❌ 连接失败 (${new Date().toLocaleTimeString()})`);
scrollToBottom();
}
};
const disconnectWebSocket = () => {
if (ws.value) {
ws.value.close(4000, '用户主动断开连接');
ws.value = null;
}
};
const sendMessage = () => {
if (ws.value?.readyState === WebSocket.OPEN && messageInput.value.trim()) {
const message = messageInput.value.trim();
ws.value.send(message);
wsMessages.value.push(`🚀 发送: ${message} (${new Date().toLocaleTimeString()})`);
messageInput.value = '';
scrollToBottom();
}
};
const sendMockData = () => {
if (ws.value?.readyState === WebSocket.OPEN) {
const mockMessages = [
'你好,这是一条测试消息',
'WebSocket 连接正常',
'实时通信功能演示',
'模拟数据发送成功',
'Hello World!',
'这是一条中文消息',
'实时数据传输测试',
'WebSocket 功能验证',
];
const randomIndex = Math.floor(Math.random() * mockMessages.length);
const randomMessage = mockMessages[randomIndex]!;
ws.value.send(randomMessage);
wsMessages.value.push(`🚀 发送: ${randomMessage} (${new Date().toLocaleTimeString()})`);
scrollToBottom();
}
};
const clearMessages = async () => {
wsMessages.value = [];
await scrollToBottom();
};
const exportMessages = () => {
const dataStr = JSON.stringify(wsMessages.value, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `websocket-messages-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
// ========== 计算属性 ==========
const canSendMessage = computed(() => wsConnected.value && messageInput.value.trim());
const connectionStatusText = computed(() => {
if (wsLoading.value) return '连接中...';
if (wsConnected.value) return '已连接';
return '未连接';
});
// ========== 生命周期钩子 ==========
onUnmounted(() => {
if (ws.value) {
ws.value.close();
}
});
</script>
<template>
<div
class="transition-all duration-500 p-2 sm:p-3 bg-gray-50 dark:bg-gray-900 via-blue-50 dark:via-slate-800 to-slate-100 dark:to-gray-900 bg-gradient-to-br"
>
<div class="max-w-5xl mx-auto">
<!-- WebSocket 示例 -->
<div
class="backdrop-blur-sm rounded-2xl shadow-lg border p-4 sm:p-5 hover:shadow-2xl hover:scale-[1.02] transition-all duration-500 cursor-pointer group bg-white dark:bg-gray-800 border-white dark:border-gray-700 hover:bg-white dark:hover:bg-gray-800"
>
<div class="flex items-center mb-4">
<div
class="w-8 h-8 bg-gradient-to-r from-green-500 to-teal-600 rounded-lg flex items-center justify-center mr-2"
>
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 18.5V6M9 9l3-3 3 3m-3 9l3 3-3 3"
/>
</svg>
</div>
<h2 class="text-lg font-bold text-gray-800 dark:text-gray-100">WebSocket 示例</h2>
</div>
<!-- 连接状态和控制按钮 -->
<div class="mb-4">
<!-- 连接状态显示 -->
<div
class="flex items-center justify-between mb-3 p-2.5 rounded-lg bg-gray-50 dark:bg-gray-700"
role="status"
aria-live="polite"
aria-label="WebSocket连接状态"
>
<div class="flex items-center gap-3">
<div class="relative">
<div
class="w-4 h-4 rounded-full transition-all duration-500 shadow-lg"
:class="
wsConnected
? 'bg-gradient-to-br from-green-400 to-green-600 animate-pulse'
: 'bg-gradient-to-br from-red-400 to-red-600'
"
/>
<div
v-if="wsLoading"
class="absolute inset-0 w-4 h-4 rounded-full bg-gradient-to-br from-yellow-400 to-orange-500 animate-ping"
/>
<div
v-if="wsConnected"
class="absolute inset-0 w-4 h-4 rounded-full bg-green-400 animate-ping opacity-20"
/>
</div>
<div>
<div class="font-medium text-gray-800 dark:text-gray-100">
{{ connectionStatusText }}
</div>
<div v-if="connectionAttempts > 0" class="text-xs text-gray-500 dark:text-gray-300">
重连次数: {{ connectionAttempts }}/{{ maxReconnectAttempts }}
</div>
</div>
</div>
<div class="text-xs text-gray-500 dark:text-gray-300">
{{ wsConnected ? '🟢 实时通信' : wsLoading ? '🟡 连接中' : '🔴 未连接' }}
</div>
</div>
<!-- 控制按钮 -->
<div class="grid grid-cols-2 gap-2">
<button
:disabled="wsConnected || wsLoading"
:aria-label="
wsLoading ? '正在连接WebSocket' : wsConnected ? 'WebSocket已连接' : '连接WebSocket'
"
class="flex items-center justify-center bg-gradient-to-br from-green-500 via-green-600 to-emerald-600 text-white font-semibold py-2.5 px-4 rounded-xl hover:from-green-600 hover:via-green-700 hover:to-emerald-700 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-sm"
@click="connectWebSocket"
>
<svg
v-if="wsLoading"
class="animate-spin -ml-1 mr-2 h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
<span>{{ wsLoading ? '连接中...' : '连接' }}</span>
</button>
<button
:disabled="!wsConnected || wsLoading"
:aria-label="!wsConnected ? 'WebSocket未连接' : '断开WebSocket连接'"
class="flex items-center justify-center bg-gradient-to-br from-red-500 via-red-600 to-pink-600 text-white font-semibold py-2.5 px-4 rounded-xl hover:from-red-600 hover:via-red-700 hover:to-pink-700 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-sm"
@click="disconnectWebSocket"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
断开
</button>
</div>
</div>
<!-- 发送消息 -->
<div class="mb-4">
<div class="flex gap-2">
<label class="sr-only" for="messageInput">要发送的消息</label>
<input
id="messageInput"
v-model="messageInput"
placeholder="输入要发送的消息..."
:disabled="!wsConnected"
:aria-describedby="!wsConnected ? 'ws-status' : undefined"
class="flex-1 w-full border-2 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:text-gray-500 transition-all duration-300 border-gray-200 dark:border-gray-600 bg-white/60 dark:bg-gray-700/60 backdrop-blur-sm text-gray-800 dark:text-gray-100 disabled:bg-gray-100/60 dark:disabled:bg-gray-800/60 hover:border-gray-300 dark:hover:border-gray-500"
@keyup.enter="sendMessage"
/>
<button
:disabled="!canSendMessage"
:aria-label="
!wsConnected ? 'WebSocket未连接' : canSendMessage ? '发送消息' : '请输入消息内容'
"
class="flex items-center bg-gradient-to-br from-blue-500 via-blue-600 to-indigo-600 text-white font-semibold py-2.5 px-4 rounded-xl hover:from-blue-600 hover:via-blue-700 hover:to-indigo-700 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-sm"
@click="sendMessage"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
/>
</svg>
发送
</button>
<button
:disabled="!wsConnected"
:aria-label="!wsConnected ? 'WebSocket未连接' : '发送随机模拟数据'"
class="flex items-center bg-gradient-to-br from-purple-500 via-purple-600 to-violet-600 text-white font-semibold py-2.5 px-4 rounded-xl hover:from-purple-600 hover:via-purple-700 hover:to-violet-700 transition-all duration-500 disabled:opacity-50 shadow-lg hover:shadow-2xl transform hover:-translate-y-1 hover:scale-[1.02] text-sm"
:title="!wsConnected ? 'WebSocket未连接时不可用' : '发送随机模拟数据'"
@click="sendMockData"
>
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"
/>
</svg>
模拟
</button>
</div>
</div>
<!-- 消息记录 -->
<div
class="rounded-lg p-3 border bg-gray-50 dark:bg-gray-800 dark:from-gray-800 dark:to-gray-700 border-gray-200 dark:border-gray-600 bg-gradient-to-r from-gray-50 dark:from-gray-800 to-gray-100 dark:to-gray-700"
role="log"
aria-label="WebSocket消息记录"
aria-live="polite"
>
<div class="flex justify-between items-center mb-2">
<h3 class="text-gray-700 dark:text-gray-200 font-semibold flex items-center text-sm">
<svg
class="w-4 h-4 mr-2 text-blue-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
消息记录:
</h3>
<div class="flex gap-1">
<button
class="text-xs text-gray-500 hover:text-blue-500 transition-colors duration-200 flex items-center"
title="导出消息"
@click="exportMessages"
>
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 10v6m0 0l4-4m-4 4l-4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
导出
</button>
<button
class="text-xs text-gray-500 hover:text-red-500 transition-colors duration-200 flex items-center"
title="清空消息"
@click="clearMessages"
>
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
清空
</button>
</div>
</div>
<div
ref="messagesContainer"
class="max-h-48 overflow-y-auto rounded-lg border p-2 bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-600"
>
<div
v-if="wsMessages.length === 0"
class="text-gray-500 dark:text-gray-400 text-sm text-center py-6"
>
<svg
class="w-6 h-6 mx-auto mb-1 text-gray-300"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
暂无消息
</div>
<div v-else class="space-y-2">
<div
v-for="(message, index) in wsMessages"
:key="index"
class="text-sm p-2 rounded-lg transition-all duration-300 hover:shadow-lg hover:scale-[1.02] animate-fade-in"
:class="
message.includes('发送:')
? 'bg-gradient-to-r from-blue-50 to-blue-100 dark:from-blue-900/30 dark:to-blue-800/30 text-blue-800 dark:text-blue-200 border-l-4 border-blue-400 dark:border-blue-500'
: message.includes('收到:')
? 'bg-gradient-to-r from-green-50 to-green-100 dark:from-green-900/30 dark:to-green-800/30 text-green-800 dark:text-green-200 border-l-4 border-green-400 dark:border-green-500'
: message.includes('连接已建立')
? 'bg-gradient-to-r from-emerald-50 to-emerald-100 dark:from-emerald-900/30 dark:to-emerald-800/30 text-emerald-800 dark:text-emerald-200 border-l-4 border-emerald-400 dark:border-emerald-500'
: message.includes('连接已关闭') || message.includes('连接失败')
? 'bg-gradient-to-r from-red-50 to-red-100 dark:from-red-900/30 dark:to-red-800/30 text-red-800 dark:text-red-200 border-l-4 border-red-400 dark:border-red-500'
: message.includes('尝试重新连接')
? 'bg-gradient-to-r from-yellow-50 to-yellow-100 dark:from-yellow-900/30 dark:to-yellow-800/30 text-yellow-800 dark:text-yellow-200 border-l-4 border-yellow-400 dark:border-yellow-500'
: 'bg-gray-50 dark:bg-gray-700 text-gray-700 dark:text-gray-200 border-l-4 border-gray-400 dark:border-gray-500'
"
>
{{ message }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
+3 -3
View File
@@ -1,7 +1,7 @@
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<div></div> <div>
<NButton @click="$router.push({ name: 'DemosCreate' })">DemosCreate</NButton>
</div>
</template> </template>
<style scoped></style>
+14
View File
@@ -0,0 +1,14 @@
export function install({ app }: { app: import('vue').App<Element> }) {
app.config.globalProperties.__DEV__ =
__DEV__; /* vite.config.ts: define: { __DEV__: JSON.stringify(!isBuild) } */
app.config.errorHandler = (error, instance, info) => {
console.error('Global error:', error);
console.error('Component:', instance);
console.error('Error Info:', info);
// 这里你可以:
// 1. 发送错误到日志服务
// 2. 显示全局错误提示
// 3. 进行错误分析和处理
};
}
+6
View File
@@ -0,0 +1,6 @@
import { PiniaColada } from '@pinia/colada';
// import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(createPinia() /* .use(piniaPluginPersistedstate) */);
app.use(PiniaColada, {});
}
+60
View File
@@ -0,0 +1,60 @@
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders';
import { setupLayouts } from 'virtual:meta-layouts';
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
import { createRouter, createWebHistory } from 'vue-router';
import type { Router } from 'vue-router';
import { handleHotUpdate, routes } from 'vue-router/auto-routes';
const setupLayoutsResult = setupLayouts(routes);
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: /* routes ?? */ setupLayoutsResult,
scrollBehavior: (_to, _from, savedPosition) => {
return savedPosition ?? { left: 0, top: 0 };
},
strict: true,
});
router.isReady().then(() => {
console.debug('✅ [router is ready]');
});
router.onError((error) => {
console.debug('🚨 [router error]:', error);
});
export function install({ app }: { app: import('vue').App<Element> }) {
app
// 在路由之前注册插件
.use(DataLoaderPlugin, { router })
// 添加路由会触发初始导航
.use(router);
}
// ========================================================================
// =========================== Router Guards ==============================
// ========================================================================
{
// 警告:路由守卫的创建顺序会影响执行流程,请勿调整
createNProgressGuard(router);
if (import.meta.env.VITE_APP_ENABLE_ROUTER_LOG_GUARD === 'true') createLogGuard(router);
Object.assign(window, { stack: createStackGuard(router) });
// >>>
Object.values(
import.meta.glob<{
createGuard?: (router: Router) => void;
}>('./router-guard/*.ts', { eager: true /* true 为同步,false 为异步 */ }),
).forEach((module) => {
module.createGuard?.(router);
});
// <<<
}
if (__DEV__) Object.assign(window, { router });
// This will update routes at runtime without reloading the page
if (import.meta.hot) {
handleHotUpdate(router);
}
export { router, setupLayoutsResult };
+50
View File
@@ -0,0 +1,50 @@
import type { RouteNamedMap } from 'vue-router/auto-routes';
declare global {
type PageTitleLocalizations = Record<keyof RouteNamedMap, string>;
}
declare module 'vue-router' {
/* definePage({ meta: { title: '示例演示' } }); */
interface RouteMeta {
/**
* @description 是否在菜单中隐藏
*/
hideInMenu?: boolean;
/**
* @description 菜单标题 // !⚠️通过多语言标题方案(搜`PageTitleLocalizations`)维护标题
*/
title?: string;
/**
* @description 使用的布局,设置为 false 则表示不使用布局
*/
layout?: string | false;
/**
* @description 菜单项是否渲染为可点击链接,默认为 true
* - true: 使用 RouterLink 包装,可点击跳转
* - false: 仅渲染纯文本标签,不可点击(适用于分组标题)
*/
link?: boolean;
/**
* @description 菜单排序权重,数值越小越靠前,未设置则按路径字母顺序排序
*/
order?: number;
/**
* @description 是否忽略权限,默认为 false
*/
ignoreAuth?: boolean;
/**
* @description 当前路由激活时应该高亮的菜单项(通过路由名称指定)
* - 用于隐藏在菜单中的子页面,指定其父级菜单项应该高亮
* - 使用路由名称而非路径,提供更好的类型安全和重构友好性
* - 例如:`activeMenuName: 'Demos'` 会高亮 Demos 菜单项
*/
activeMenuName?: keyof RouteNamedMap;
}
}
+3
View File
@@ -0,0 +1,3 @@
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(i18nInstance);
}
+5
View File
@@ -0,0 +1,5 @@
import { autoAnimatePlugin } from '@formkit/auto-animate/vue';
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(autoAnimatePlugin); // v-auto-animate="{ duration: 100 }"
}
+39
View File
@@ -0,0 +1,39 @@
/**
* 需要把 <DynamicDialog /> <ConfirmDialog /> <Toast /> 放在 App.vue 的 template 中
*/
import Aura from '@primeuix/themes/aura';
import zhCN from 'primelocale/zh-CN.json';
import PrimeVue from 'primevue/config';
import type { PrimeVueConfiguration } from 'primevue/config';
import StyleClass from 'primevue/styleclass';
import ToastService from 'primevue/toastservice';
export function install({ app }: { app: import('vue').App<Element> }) {
app.directive('styleclass', StyleClass);
// https://github.com/primefaces/primevue/blob/afe6f58ae55e9caf7f9bc094cd453a21a6113001/packages/core/src/config/PrimeVue.js
app.use(PrimeVue, {
zIndex: {
modal: 5100,
overlay: 5000,
menu: 5000,
tooltip: 5100,
},
locale: {
...zhCN['zh-CN'],
completed: '已上传',
noFileChosenMessage: '未选择文件',
pending: '待上传',
}, // usePrimeVue().config.locale
theme: {
options: {
cssLayer: false,
darkModeSelector: '.app-dark' /* 'system' */,
prefix: 'p',
},
preset: Aura,
},
} satisfies PrimeVueConfiguration);
app.use(ToastService);
}
+5
View File
@@ -0,0 +1,5 @@
import { createHead } from '@unhead/vue/client';
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(createHead());
}
-32
View File
@@ -1,32 +0,0 @@
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
import { createHead } from '@unhead/vue/client'
export function install({ app }: { app: import('vue').App<Element> }) {
app.config.globalProperties.__DEV__ = __DEV__
app.use(autoAnimatePlugin) // v-auto-animate="{ duration: 100 }"
app.use(createHead())
app.config.errorHandler = (error, instance, info) => {
console.error('Global error:', error)
console.error('Component:', instance)
console.error('Error Info:', info)
// 这里你可以:
// 1. 发送错误到日志服务
// 2. 显示全局错误提示
// 3. 进行错误分析和处理
}
// if (import.meta.env.MODE === 'development' && '1' === ('2' as never)) {
// // TODO: https://github.com/hu3dao/vite-plugin-debug/
// // https://eruda.liriliri.io/zh/docs/#快速上手
// import('eruda').then(({ default: eruda }) => {
// eruda.init({
// defaults: {
// transparency: 0.9,
// },
// })
// /* eruda.show(); */
// })
// }
}
+21 -16
View File
@@ -1,24 +1,29 @@
/** /**
* https://github.com/antfu-collective/vitesse/blob/47618e72dfba76c77b9b85b94784d739e35c492b/src/modules/README.md * https://github.com/antfu-collective/vitesse/blob/47618e72dfba76c77b9b85b94784d739e35c492b/src/modules/README.md
*/ */
type UserPlugin = (ctx: UserPluginContext) => void type UserPlugin = (ctx: UserPluginContext) => void;
type AutoInstallModule = { [K: string]: unknown; install?: UserPlugin } type AutoInstallModule = { [K: string]: unknown; install?: UserPlugin };
type UserPluginContext = { app: import('vue').App<Element> } type UserPluginContext = { app: import('vue').App<Element> };
export function setupPlugins(
app: import('vue').App, const autoInstallModules: AutoInstallModule = import.meta.glob(
modules: AutoInstallModule | Record<string, unknown>, ['./*.ts', '!./**/*.types.ts', '!./index.ts'],
) { {
console.group('🔌 Plugins') eager: true /* true 为同步,false 为异步 */,
for (const path in modules) { },
const module = modules[path] as AutoInstallModule );
export function setupPlugins(app: import('vue').App) {
console.group(`🔌 Installing ${Object.keys(autoInstallModules).length} plugins`);
for (const path in autoInstallModules) {
const module = autoInstallModules[path] as AutoInstallModule;
if (module.install) { if (module.install) {
module.install({ app }) module.install({ app });
console.debug(`%c✔ ${path}`, 'color: #07a') console.debug(`%c✔ ${path}`, 'color: #07a');
} else { } else {
if (typeof module.setupPlugins === 'function') continue if (typeof module.setupPlugins === 'function') continue;
console.warn(`%c✘ ${path} has no install function`, 'color: #f50') console.warn(`%c✘ ${path} has no install function`, 'color: #f50');
} }
} }
console.groupEnd() console.groupEnd();
return app return app;
} }
-6
View File
@@ -1,6 +0,0 @@
import { PiniaColada } from '@pinia/colada'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(createPinia().use(piniaPluginPersistedstate))
app.use(PiniaColada, {})
}
-28
View File
@@ -1,28 +0,0 @@
/**
* 需要把 <DynamicDialog /> <ConfirmDialog /> <Toast /> 放在 App.vue 的 template 中
*/
import Aura from '@primeuix/themes/aura'
import zhCN from 'primelocale/zh-CN.json'
import PrimeVue from 'primevue/config'
import StyleClass from 'primevue/styleclass'
export function install({ app }: { app: import('vue').App<Element> }) {
app.directive('styleclass', StyleClass)
app.use(PrimeVue, {
locale: {
...zhCN['zh-CN'],
completed: '已上传',
noFileChosenMessage: '未选择文件',
pending: '待上传',
}, // usePrimeVue().config.locale
theme: {
options: {
cssLayer: false,
darkModeSelector: '.app-dark' /* 'system' */,
prefix: 'p',
},
preset: Aura,
},
})
}
@@ -0,0 +1,28 @@
import type { Router } from 'vue-router';
export function createGuard(router: Router) {
router.beforeEach(async (to /* , from */) => {
const userStore = useAuthStore();
if (to.name === 'Login') {
userStore.clearToken('User navigated to login page');
}
if (to.meta.ignoreAuth) {
return true;
}
if (!userStore.isLoggedIn) {
console.debug('🔑 [permission-guard] 用户未登录,重定向到登录页');
return { name: 'Login' };
}
});
router.beforeResolve(async (/* to, from */) => {
const userStore = useAuthStore();
if (userStore.isLoggedIn && !userStore.userInfo) {
console.debug('🔑 [permission-guard] 用户信息不存在,尝试获取用户信息');
await userStore.fetchUserInfo();
}
});
}
-58
View File
@@ -1,58 +0,0 @@
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders'
import { setupLayouts } from 'virtual:meta-layouts'
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
import { createRouter, createWebHistory } from 'vue-router'
import { handleHotUpdate, routes } from 'vue-router/auto-routes'
const setupLayoutsResult = setupLayouts(routes)
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: /* routes ?? */ setupLayoutsResult,
scrollBehavior: (_to, _from, savedPosition) => {
return savedPosition ?? { left: 0, top: 0 }
},
strict: true,
})
if (import.meta.hot) handleHotUpdate(router)
if (__DEV__) Object.assign(globalThis, { router })
router.onError((error) => {
console.debug('🚨 [router error]:', error)
})
export { router, setupLayoutsResult }
export function install({ app }: { app: import('vue').App<Element> }) {
app
// 在路由之前注册插件
.use(DataLoaderPlugin, { router })
// 添加路由会触发初始导航
.use(router)
}
// ========================================================================
// =========================== Router Guards ==============================
// ========================================================================
{
// 警告:路由守卫的创建顺序会影响执行流程,请勿调整
createNProgressGuard(router)
createLogGuard(router)
Object.assign(globalThis, { stack: createStackGuard(router) })
}
/*
definePage({
meta: { },
});
*/
declare module 'vue-router' {
interface RouteMeta {
/**
* @description 是否在菜单中隐藏
*/
hidden?: boolean
/**
* @description 菜单标题
*/
title?: string
}
}
export { createGetRoutes } from 'virtual:meta-layouts'
-17
View File
@@ -1,17 +0,0 @@
/* https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n#static-bundle-importing
* All i18n resources specified in the plugin `include` option can be loaded
* at once using the import syntax
*/
import messages from '@intlify/unplugin-vue-i18n/messages'
import { createI18n } from 'vue-i18n'
export function install({ app }: { app: import('vue').App<Element> }) {
app.use(
// https://vue-i18n.intlify.dev/guide/essentials/started.html#registering-the-i18n-plugin
createI18n({
legacy: false, // you must set `false`, to use Composition API
locale: navigator.language,
messages,
}),
)
}
+46
View File
@@ -0,0 +1,46 @@
import type { BasicColorSchema } from '@vueuse/core';
import { useLocalStorage, useMediaQuery } from '@vueuse/core';
import { defineStore } from 'pinia';
import { computed } from 'vue';
// >>>>>
// https://vueuse.org/core/useColorMode/#advanced-usage
const { system, store: themeMode } = useColorMode<BasicColorSchema>({
selector: 'html',
attribute: 'class',
modes: { light: '', dark: 'app-dark', auto: '' },
disableTransition: false,
initialValue: 'auto',
});
const { state, next: cycleTheme } = useCycleList(['light', 'dark', 'auto'] as const, {
initialValue: themeMode,
});
watchEffect(() => (themeMode.value = state.value));
export type AppThemeMode = typeof themeMode.value;
// <<<<<
export const useAppStore = defineStore('app', () => {
// 侧边栏展开/收起状态
const sidebarCollapsed = useLocalStorage<boolean>('app-sidebar-collapsed', false);
const toggleSidebar = useToggle(sidebarCollapsed);
// 主题模式
const actualTheme = computed(() => (themeMode.value === 'auto' ? system.value : themeMode.value));
const isDark = computed(() => actualTheme.value === 'dark');
// 是否是移动端
const isMobile = useMediaQuery('(max-width: 768px)');
return {
themeMode,
isDark,
isMobile,
cycleTheme,
sidebarCollapsed,
toggleSidebar,
};
});
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot));
}
+45
View File
@@ -0,0 +1,45 @@
export const useAuthStore = defineStore('auth', () => {
const token = useLocalStorage<string | null>('auth-token', null);
const userInfo = ref<Record<string, any> | null>(null);
const isLoggedIn = computed(() => !!token.value);
function clearToken(reason?: string) {
consola.info('🚮 [auth-store] clear: ', reason);
token.value = null;
userInfo.value = null;
}
async function login(username: string, password: string) {
// 模拟登录延迟
await new Promise((resolve) => setTimeout(resolve, 500));
// 模拟验证
if (username === 'admin' && password === 'admin') {
token.value = `mock-token-${Date.now()}`;
await fetchUserInfo();
return { success: true };
}
return { success: false, message: '用户名或密码错误' };
}
async function fetchUserInfo() {
if (!token.value) {
return;
}
// 模拟获取用户信息延迟
await new Promise((resolve) => setTimeout(resolve, 300));
// 模拟从服务器获取用户信息
userInfo.value = {
id: 1,
username: 'admin',
nickname: '管理员',
roles: ['admin'],
};
}
return { token, isLoggedIn, userInfo, clearToken, login, fetchUserInfo };
});
-12
View File
@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

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