Compare commits

...

64 Commits

Author SHA1 Message Date
vben
1063b2268e chore: release v5.2.1 2024-08-31 21:55:10 +08:00
Vben
8404c12129 refactor: refactor AuthLayout to configure the login page more freely (#4294) 2024-08-31 21:38:24 +08:00
afe1
a7d322019e style: optimize dashboard in small screen styles and demo (#4293) 2024-08-31 21:24:48 +08:00
Li Kui
f23821ff46 fix: redundant border on the sidebar in two column layout mode (#4291) 2024-08-31 17:37:37 +08:00
Vben
2b0aedbaba style: optimizing style issues (#4289) 2024-08-31 14:11:01 +08:00
afe1
071cc0dcec style: optimize dashboard in small screen styles (#4288)
Co-authored-by: afe1 <yunfei.zhu@nwowtec.com>
2024-08-31 13:06:53 +08:00
dependabot[bot]
7db7d6ec5f chore(deps): bump the non-breaking-changes group with 5 updates (#4285)
Bumps the non-breaking-changes group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue) | `5.1.2` | `5.1.3` |
| [vite-plugin-pwa](https://github.com/vite-pwa/vite-plugin-pwa) | `0.20.1` | `0.20.2` |
| [rollup](https://github.com/rollup/rollup) | `4.21.1` | `4.21.2` |
| [eslint-plugin-import-x](https://github.com/un-ts/eslint-plugin-import-x) | `4.1.0` | `4.1.1` |
| [watermark-js-plus](https://github.com/zhensherlock/watermark-js-plus) | `1.5.3` | `1.5.4` |


Updates `@vitejs/plugin-vue` from 5.1.2 to 5.1.3
- [Release notes](https://github.com/vitejs/vite-plugin-vue/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@5.1.3/packages/plugin-vue)

Updates `vite-plugin-pwa` from 0.20.1 to 0.20.2
- [Release notes](https://github.com/vite-pwa/vite-plugin-pwa/releases)
- [Commits](https://github.com/vite-pwa/vite-plugin-pwa/compare/v0.20.1...v0.20.2)

Updates `rollup` from 4.21.1 to 4.21.2
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.21.1...v4.21.2)

Updates `eslint-plugin-import-x` from 4.1.0 to 4.1.1
- [Release notes](https://github.com/un-ts/eslint-plugin-import-x/releases)
- [Changelog](https://github.com/un-ts/eslint-plugin-import-x/blob/master/CHANGELOG.md)
- [Commits](https://github.com/un-ts/eslint-plugin-import-x/compare/v4.1.0...v4.1.1)

Updates `watermark-js-plus` from 1.5.3 to 1.5.4
- [Release notes](https://github.com/zhensherlock/watermark-js-plus/releases)
- [Changelog](https://github.com/zhensherlock/watermark-js-plus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/zhensherlock/watermark-js-plus/compare/v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-vue"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vite-plugin-pwa
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: eslint-plugin-import-x
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: watermark-js-plus
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-31 07:38:40 +08:00
Vben
b3e3e05990 perf: axios default error interceptor allows you to customize error handling (#4283) 2024-08-30 22:06:02 +08:00
handsomeFu
cc678a2b51 fix: ensure left panel element spans full width on small screens (#4281) 2024-08-30 21:33:02 +08:00
dependabot[bot]
7d2bcf476f chore(deps): bump the non-breaking-changes group with 2 updates (#4275)
Bumps the non-breaking-changes group with 2 updates: [vue-tsc](https://github.com/vuejs/language-tools/tree/HEAD/packages/tsc) and [pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate).


Updates `vue-tsc` from 2.0.29 to 2.1.2
- [Release notes](https://github.com/vuejs/language-tools/releases)
- [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vuejs/language-tools/commits/v2.1.2/packages/tsc)

Updates `pinia-plugin-persistedstate` from 3.2.1 to 3.2.3
- [Release notes](https://github.com/prazdevs/pinia-plugin-persistedstate/releases)
- [Commits](https://github.com/prazdevs/pinia-plugin-persistedstate/compare/v3.2.1...v3.2.3)

---
updated-dependencies:
- dependency-name: vue-tsc
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: pinia-plugin-persistedstate
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-30 13:47:18 +08:00
handsomeFu
cfbe379ee4 fix: correct button layout in English locale (#4277)
Co-authored-by: vince <vince292007@gmail.com>
2024-08-30 13:36:36 +08:00
afe1
388e5b5cb3 update vscode config (#4274)
Co-authored-by: vince <vince292007@gmail.com>
2024-08-30 13:34:32 +08:00
vince
c1dfbc1ebf fix: ci error [skip ci] (#4280) 2024-08-30 13:08:24 +08:00
vben
05a52b0540 chore: release v5.2.0 2024-08-29 22:35:38 +08:00
Vben
a77cc00e9f feat: core components support simple locale switching (#4273)
* feat: core components support simple locale switching

* fix: test error

* fix: test error
2024-08-29 22:31:49 +08:00
Vben
98da0672e7 feat: add archive plug-in to output dist.zip after build (#4272)
* feat: add the archiver plug-in to output dist.zip after build

* chore: update env
2024-08-29 21:47:00 +08:00
moyaojun
be3bcc1122 fix(@vben/preferences): fix hidden header navigation bar causes the settings button to not be displayed (#4271)
bug #4268
2024-08-29 21:37:29 +08:00
handsomeFu
88a7a9b1ee perf: reset value after reopen LockScreenModal (#4269)
* perf: replace deprecated @keypress with @keydown for Enter key handling

* perf: reset value after reopen LockScreenModal
2024-08-29 17:21:08 +08:00
dependabot[bot]
3b2ed948f5 chore(deps): bump the non-breaking-changes group with 9 updates (#4266)
* chore(deps): bump the non-breaking-changes group with 9 updates

Bumps the non-breaking-changes group with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.5.0` | `22.5.1` |
| [turbo](https://github.com/vercel/turborepo) | `2.0.14` | `2.1.0` |
| [@iconify/json](https://github.com/iconify/icon-sets) | `2.2.241` | `2.2.242` |
| [@tailwindcss/typography](https://github.com/tailwindlabs/tailwindcss-typography) | `0.5.14` | `0.5.15` |
| [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) | `19.4.0` | `19.4.1` |
| [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional) | `19.2.2` | `19.4.1` |
| [eslint-config-turbo](https://github.com/vercel/turborepo/tree/HEAD/packages/eslint-config-turbo) | `2.0.14` | `2.1.0` |
| [eslint-plugin-perfectionist](https://github.com/azat-io/eslint-plugin-perfectionist) | `3.2.0` | `3.3.0` |
| [stylelint](https://github.com/stylelint/stylelint) | `16.8.2` | `16.9.0` |


Updates `@types/node` from 22.5.0 to 22.5.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `turbo` from 2.0.14 to 2.1.0
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/release.md)
- [Commits](https://github.com/vercel/turborepo/compare/v2.0.14...v2.1.0)

Updates `@iconify/json` from 2.2.241 to 2.2.242
- [Commits](https://github.com/iconify/icon-sets/compare/2.2.241...2.2.242)

Updates `@tailwindcss/typography` from 0.5.14 to 0.5.15
- [Release notes](https://github.com/tailwindlabs/tailwindcss-typography/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss-typography/compare/v0.5.14...v0.5.15)

Updates `@commitlint/cli` from 19.4.0 to 19.4.1
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.4.1/@commitlint/cli)

Updates `@commitlint/config-conventional` from 19.2.2 to 19.4.1
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v19.4.1/@commitlint/config-conventional)

Updates `eslint-config-turbo` from 2.0.14 to 2.1.0
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/release.md)
- [Commits](https://github.com/vercel/turborepo/commits/v2.1.0/packages/eslint-config-turbo)

Updates `eslint-plugin-perfectionist` from 3.2.0 to 3.3.0
- [Release notes](https://github.com/azat-io/eslint-plugin-perfectionist/releases)
- [Changelog](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/changelog.md)
- [Commits](https://github.com/azat-io/eslint-plugin-perfectionist/compare/v3.2.0...v3.3.0)

Updates `stylelint` from 16.8.2 to 16.9.0
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/16.8.2...16.9.0)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: turbo
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/json"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@tailwindcss/typography"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@commitlint/cli"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@commitlint/config-conventional"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: eslint-config-turbo
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: eslint-plugin-perfectionist
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: stylelint
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: update deps

* chore: update ci

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: vince <vince292007@gmail.com>
2024-08-29 07:28:23 +08:00
Li Kui
08acaf05f1 fix: solve the problem of recursive call to logout api (#4265) 2024-08-28 22:37:47 +08:00
Vben
36e7ca19a1 perf: improve modal and drawer component documentation and fix known problems (#4264)
* feat: improve modal and drawer component documentation and fix known problems

* chore: update ci
2024-08-28 22:26:35 +08:00
Li Kui
84816ef769 chore: add badges (#4260)
* chore: add badges

* docs: fix editLink
2024-08-28 20:43:24 +08:00
handsomeFu
1bd6c2e5f2 fix: fix modal position after drag to prevent offset on reopen (#4261)
* fix: fix typo

* fix: fix modal position after drag to prevent offset on reopen
2024-08-28 20:43:03 +08:00
handsomeFu
c439d5601f fix: replace deprecated @keypress with @keydown for Enter key handling (#4258) 2024-08-28 17:39:47 +08:00
前端爱码士
453a3a8f84 chore(docs): correct the file path in the markdown for the GitHub website (#4254) 2024-08-28 10:07:16 +08:00
Li Kui
c6b9a56b73 fix: clearPreferencesAndLogout does not take effect when the preferences button is at the top (#4253) 2024-08-28 09:58:59 +08:00
dependabot[bot]
6d24369272 chore(deps): bump the non-breaking-changes group with 2 updates (#4251)
Bumps the non-breaking-changes group with 2 updates: [rollup](https://github.com/rollup/rollup) and [radix-vue](https://github.com/radix-vue/radix-vue).


Updates `rollup` from 4.21.0 to 4.21.1
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.21.0...v4.21.1)

Updates `radix-vue` from 1.9.4 to 1.9.5
- [Release notes](https://github.com/radix-vue/radix-vue/releases)
- [Commits](https://github.com/radix-vue/radix-vue/compare/v1.9.4...v1.9.5)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: radix-vue
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-28 08:12:52 +08:00
Vben
cbf601581d feat(@vben/docs): preview components are supported within documents (#4250) 2024-08-27 23:22:34 +08:00
Li Kui
d47d051b19 fix: search and menu buttons overlapped (#4249) 2024-08-27 21:54:07 +08:00
dependabot[bot]
3b5c935bb8 chore(deps-dev): bump jsdom from 24.1.1 to 25.0.0 (#4245)
Bumps [jsdom](https://github.com/jsdom/jsdom) from 24.1.1 to 25.0.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/main/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/24.1.1...25.0.0)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: vince <vince292007@gmail.com>
2024-08-27 11:30:38 +08:00
handsomeFu
8adb22847d perf: optimize the access directive to support string passing (#4246)
* perf: 优化access指令用法并改进参数类型

重构access权限指令以接受角色和代码形式的权限检查。通过支持数组和字符串作为指令值来简化用法,从而提高灵活性。此外,改进指令绑定类型定义以提升类型安全性。

* docs: 更新中英文文档示例以支持字符串权限码绑定更新了中文和英文文档中的示例
2024-08-27 11:17:04 +08:00
dependabot[bot]
2ba28488a4 chore(deps): bump the non-breaking-changes group with 13 updates (#4244)
Bumps the non-breaking-changes group with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [ora](https://github.com/sindresorhus/ora) | `8.0.1` | `8.1.0` |
| [@iconify/json](https://github.com/iconify/icon-sets) | `2.2.240` | `2.2.241` |
| [vite-plugin-vue-devtools](https://github.com/vuejs/devtools-next/tree/HEAD/packages/vite) | `7.3.8` | `7.3.9` |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.9.0` | `9.9.1` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.2.0` | `8.3.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.2.0` | `8.3.0` |
| [eslint](https://github.com/eslint/eslint) | `9.9.0` | `9.9.1` |
| [@types/eslint](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/eslint) | `9.6.0` | `9.6.1` |
| [stylelint-scss](https://github.com/stylelint-scss/stylelint-scss) | `6.5.0` | `6.5.1` |
| [lucide-vue-next](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-vue-next) | `0.435.0` | `0.436.0` |
| [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) | `11.0.1` | `11.0.3` |
| [@vueuse/integrations](https://github.com/vueuse/vueuse/tree/HEAD/packages/integrations) | `11.0.1` | `11.0.3` |
| [vitepress](https://github.com/vuejs/vitepress) | `1.3.3` | `1.3.4` |


Updates `ora` from 8.0.1 to 8.1.0
- [Release notes](https://github.com/sindresorhus/ora/releases)
- [Commits](https://github.com/sindresorhus/ora/compare/v8.0.1...v8.1.0)

Updates `@iconify/json` from 2.2.240 to 2.2.241
- [Commits](https://github.com/iconify/icon-sets/compare/2.2.240...2.2.241)

Updates `vite-plugin-vue-devtools` from 7.3.8 to 7.3.9
- [Release notes](https://github.com/vuejs/devtools-next/releases)
- [Commits](https://github.com/vuejs/devtools-next/commits/v7.3.9/packages/vite)

Updates `@eslint/js` from 9.9.0 to 9.9.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.9.1/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.2.0 to 8.3.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.3.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.2.0 to 8.3.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.3.0/packages/parser)

Updates `eslint` from 9.9.0 to 9.9.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.9.0...v9.9.1)

Updates `@types/eslint` from 9.6.0 to 9.6.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/eslint)

Updates `stylelint-scss` from 6.5.0 to 6.5.1
- [Release notes](https://github.com/stylelint-scss/stylelint-scss/releases)
- [Changelog](https://github.com/stylelint-scss/stylelint-scss/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stylelint-scss/stylelint-scss/compare/v6.5.0...v6.5.1)

Updates `lucide-vue-next` from 0.435.0 to 0.436.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.436.0/packages/lucide-vue-next)

Updates `@vueuse/core` from 11.0.1 to 11.0.3
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v11.0.3/packages/core)

Updates `@vueuse/integrations` from 11.0.1 to 11.0.3
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v11.0.3/packages/integrations)

Updates `vitepress` from 1.3.3 to 1.3.4
- [Release notes](https://github.com/vuejs/vitepress/releases)
- [Changelog](https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vitepress/compare/v1.3.3...v1.3.4)

---
updated-dependencies:
- dependency-name: ora
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/json"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vite-plugin-vue-devtools
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@eslint/js"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@types/eslint"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: stylelint-scss
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: lucide-vue-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@vueuse/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vueuse/integrations"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vitepress
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-27 09:56:27 +08:00
Vben
d2f3a9d04f feat: preference button supports automatic positioning (#4243) 2024-08-26 23:17:27 +08:00
Vben
dc5cfab319 fix: tabbar does not meet expectations in a mixed layout (#4242) 2024-08-26 22:58:49 +08:00
Vben
87cc8a154c fix: sidebar scrolling does not meet expectations (#4241) 2024-08-26 22:09:18 +08:00
Vben
54a622ad05 chore: update vscode settings.json (#4240) 2024-08-26 21:50:56 +08:00
Vben
8a0b1e0c72 refactor: package chart-ui integrated into plugins (#4238)
* refactor: package chart-ui integrated into plugins

* fix: lint error
2024-08-26 21:42:56 +08:00
Vben
fd7b3479b4 fix: improve the display of modal and drawer on mobile (#4237) 2024-08-26 20:54:20 +08:00
handsomeFu
577cc85851 fix: change deprecated func (#4236) 2024-08-26 20:32:02 +08:00
afe1
5ca746b021 fix: ease-out (#4233)
Co-authored-by: afe1 <yunfei.zhu@nwowtec.com>
2024-08-26 10:12:24 +08:00
Donny Wang
04f29614aa chore: fix typo (#4232) 2024-08-26 10:02:09 +08:00
Li Kui
df7757d001 fix: when the 'refreshToken' expires, it does not re-authenticate (#4231) 2024-08-26 09:54:42 +08:00
Vben
20a3868594 feat: add modal and drawer components and examples (#4229)
* feat: add modal component

* feat: add drawer component

* feat: apply new modal and drawer components to the layout

* chore: typo

* feat: add some unit tests
2024-08-25 23:40:52 +08:00
vben
edb55b1fc0 chore: release v5.1.2 2024-08-25 11:02:15 +08:00
afe1
137e2073cb chore: typos (#4227) 2024-08-25 10:54:03 +08:00
dependabot[bot]
bc04286f1d chore(deps): bump the non-breaking-changes group with 6 updates (#4225)
Bumps the non-breaking-changes group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [pkg-types](https://github.com/unjs/pkg-types) | `1.1.3` | `1.2.0` |
| [@iconify/json](https://github.com/iconify/icon-sets) | `2.2.239` | `2.2.240` |
| [@iconify/tailwind](https://github.com/iconify/iconify/tree/HEAD/plugins/tailwind) | `1.1.2` | `1.1.3` |
| [lucide-vue-next](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-vue-next) | `0.429.0` | `0.435.0` |
| [axios](https://github.com/axios/axios) | `1.7.4` | `1.7.5` |
| [element-plus](https://github.com/element-plus/element-plus) | `2.8.0` | `2.8.1` |


Updates `pkg-types` from 1.1.3 to 1.2.0
- [Release notes](https://github.com/unjs/pkg-types/releases)
- [Changelog](https://github.com/unjs/pkg-types/blob/main/CHANGELOG.md)
- [Commits](https://github.com/unjs/pkg-types/compare/v1.1.3...v1.2.0)

Updates `@iconify/json` from 2.2.239 to 2.2.240
- [Commits](https://github.com/iconify/icon-sets/compare/2.2.239...2.2.240)

Updates `@iconify/tailwind` from 1.1.2 to 1.1.3
- [Commits](https://github.com/iconify/iconify/commits/HEAD/plugins/tailwind)

Updates `lucide-vue-next` from 0.429.0 to 0.435.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.435.0/packages/lucide-vue-next)

Updates `axios` from 1.7.4 to 1.7.5
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.4...v1.7.5)

Updates `element-plus` from 2.8.0 to 2.8.1
- [Release notes](https://github.com/element-plus/element-plus/releases)
- [Changelog](https://github.com/element-plus/element-plus/blob/dev/CHANGELOG.en-US.md)
- [Commits](https://github.com/element-plus/element-plus/compare/2.8.0...2.8.1)

---
updated-dependencies:
- dependency-name: pkg-types
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/json"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/tailwind"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: lucide-vue-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: element-plus
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Vben <ann.vben@gmail.com>
2024-08-24 12:51:11 +08:00
Li Kui
7fa4410eab fix: correct the SVG icon format (#4224)
* fix: correct the SVG icon format

closes #4220

* fix: ci fail

* chore: apply suggestion
2024-08-24 12:43:48 +08:00
invalid w
8230493651 feat(@vben/docs): support english documents (#4202)
* chore(@vben/docs): 完成guide文档的翻译

* chore(@vben/docs): 完成other文档的翻译

* chore: 翻译部分文档

* chore: 完成英文config的配置

* chore: 完成in-depth的文档翻译

* chore: 调整调整链接

* chore: typo

* chore: typo

* chore: update links

---------

Co-authored-by: Li Kui <90845831+likui628@users.noreply.github.com>
2024-08-23 19:46:51 +08:00
Hannah Jensen
c27b97f933 feat(@vben/web-antd): perf: home page analysis card adjusted to grid layout (#4219)
Co-authored-by: DreamyTZK <i@tzki.cn>
2024-08-23 12:59:59 +08:00
handsomeFu
d5655f02e3 chore(docs): update function parameter comments (#4215)
* docs(other): update function parameters jsdoc

- 移除`vue-router.d.ts`中`activePath`属性的默认值注释。
- 重构`generateRoutes`函数签名,增加`options`参数。
- 调整`authLogin`函数文档,增加`onSuccess`回调函数的说明。

* docs: 删除 `activePath` 属性的默认值注释

在路线文档中,移除了`activePath`属性的默认值注释,以清晰说明其行为。此更改更新了文档内容,使其更准确地反映代码实现。
2024-08-22 21:52:44 +08:00
苗大
e2732f334c chore: update vscode configuration (#4210)
* chore: vscode 配置package.json默认匹配为json而不是jsonc, 以修复依赖悬浮提示信息

* fix: settings.json eslint
2024-08-22 21:51:04 +08:00
afe1
5b24661b78 chore(docs): updated title slot description (#4192)
* fix: slots

* fix: reademe

---------

Co-authored-by: afe1 <yunfei.zhu@nwowtec.com>
2024-08-22 21:48:05 +08:00
前端爱码士
97e7bd6827 chore(docs): correct the naming convention typo in the test files (#4216) 2024-08-22 21:44:54 +08:00
dependabot[bot]
08265262e1 chore(deps): bump the non-breaking-changes group with 3 updates (#4212)
Bumps the non-breaking-changes group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [stylelint-config-recess-order](https://github.com/stormwarning/stylelint-config-recess-order) and [lucide-vue-next](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-vue-next).


Updates `@types/node` from 22.4.1 to 22.5.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `stylelint-config-recess-order` from 5.0.1 to 5.1.0
- [Release notes](https://github.com/stormwarning/stylelint-config-recess-order/releases)
- [Changelog](https://github.com/stormwarning/stylelint-config-recess-order/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stormwarning/stylelint-config-recess-order/compare/v5.0.1...v5.1.0)

Updates `lucide-vue-next` from 0.428.0 to 0.429.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.429.0/packages/lucide-vue-next)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: stylelint-config-recess-order
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: lucide-vue-next
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 09:55:09 +08:00
afe1
c3631ef555 chore: error note (#4209)
Co-authored-by: afe1 <yunfei.zhu@nwowtec.com>
2024-08-21 18:27:34 +08:00
dependabot[bot]
18627833e9 chore(deps): bump the non-breaking-changes group with 5 updates (#4206)
Bumps the non-breaking-changes group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [cspell](https://github.com/streetsidesoftware/cspell/tree/HEAD/packages/cspell) | `8.14.1` | `8.14.2` |
| [husky](https://github.com/typicode/husky) | `9.1.4` | `9.1.5` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `5.4.1` | `5.4.2` |
| [dayjs](https://github.com/iamkun/dayjs) | `1.11.12` | `1.11.13` |
| [@iconify/json](https://github.com/iconify/icon-sets) | `2.2.238` | `2.2.239` |


Updates `cspell` from 8.14.1 to 8.14.2
- [Release notes](https://github.com/streetsidesoftware/cspell/releases)
- [Changelog](https://github.com/streetsidesoftware/cspell/blob/main/packages/cspell/CHANGELOG.md)
- [Commits](https://github.com/streetsidesoftware/cspell/commits/v8.14.2/packages/cspell)

Updates `husky` from 9.1.4 to 9.1.5
- [Release notes](https://github.com/typicode/husky/releases)
- [Commits](https://github.com/typicode/husky/compare/v9.1.4...v9.1.5)

Updates `vite` from 5.4.1 to 5.4.2
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.2/packages/vite)

Updates `dayjs` from 1.11.12 to 1.11.13
- [Release notes](https://github.com/iamkun/dayjs/releases)
- [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.13/CHANGELOG.md)
- [Commits](https://github.com/iamkun/dayjs/compare/v1.11.12...v1.11.13)

Updates `@iconify/json` from 2.2.238 to 2.2.239
- [Commits](https://github.com/iconify/icon-sets/compare/2.2.238...2.2.239)

---
updated-dependencies:
- dependency-name: cspell
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: husky
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vite
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: dayjs
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/json"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-21 10:10:26 +08:00
Li Kui
cd652941cd fix: switching language does not load the translation of the component correctly (#4205)
closes #4191
2024-08-21 10:06:06 +08:00
前端爱码士
fab92ee7e1 fix: Ignore vscode-history plug-in directory git submissions (#4207) 2024-08-21 09:57:57 +08:00
vben
04c2b9d242 ci: update workflows [skip ci] 2024-08-20 22:38:45 +08:00
Vben
2288827265 fix: fixed the failure to refresh the page when login failed (#4204) 2024-08-20 22:33:25 +08:00
Li Kui
fecf55139d fix: element-plus styles (#4203)
closes #4201
2024-08-20 21:53:09 +08:00
Zhao-sj
5962804f92 style: element-plus theme info adaptation (#4082)
Co-authored-by: Li Kui <90845831+likui628@users.noreply.github.com>
2024-08-20 21:39:57 +08:00
zyy
4d7e6bba56 docs: fix document typos (#4196) 2024-08-20 10:27:07 +08:00
dependabot[bot]
33637584a8 chore(deps): bump the non-breaking-changes group with 8 updates (#4195)
Bumps the non-breaking-changes group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `22.4.0` | `22.4.1` |
| [postcss-preset-env](https://github.com/csstools/postcss-plugins/tree/HEAD/plugin-packs/postcss-preset-env) | `10.0.1` | `10.0.2` |
| [rollup](https://github.com/rollup/rollup) | `4.20.0` | `4.21.0` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.1.0` | `8.2.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.1.0` | `8.2.0` |
| [@stylistic/stylelint-plugin](https://github.com/stylelint-stylistic/stylelint-stylistic) | `3.0.0` | `3.0.1` |
| [@vueuse/core](https://github.com/vueuse/vueuse/tree/HEAD/packages/core) | `11.0.0` | `11.0.1` |
| [@vueuse/integrations](https://github.com/vueuse/vueuse/tree/HEAD/packages/integrations) | `11.0.0` | `11.0.1` |


Updates `@types/node` from 22.4.0 to 22.4.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `postcss-preset-env` from 10.0.1 to 10.0.2
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/plugin-packs/postcss-preset-env/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/plugin-packs/postcss-preset-env)

Updates `rollup` from 4.20.0 to 4.21.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.20.0...v4.21.0)

Updates `@typescript-eslint/eslint-plugin` from 8.1.0 to 8.2.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.2.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.1.0 to 8.2.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.2.0/packages/parser)

Updates `@stylistic/stylelint-plugin` from 3.0.0 to 3.0.1
- [Release notes](https://github.com/stylelint-stylistic/stylelint-stylistic/releases)
- [Changelog](https://github.com/stylelint-stylistic/stylelint-stylistic/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint-stylistic/stylelint-stylistic/compare/v3.0.0...v3.0.1)

Updates `@vueuse/core` from 11.0.0 to 11.0.1
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v11.0.1/packages/core)

Updates `@vueuse/integrations` from 11.0.0 to 11.0.1
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v11.0.1/packages/integrations)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: postcss-preset-env
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: rollup
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@stylistic/stylelint-plugin"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vueuse/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vueuse/integrations"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 10:16:56 +08:00
363 changed files with 12399 additions and 2755 deletions

View File

@@ -13,13 +13,13 @@ categories:
- title: "🚀 Features"
labels:
- "feature"
- "enhancement"
- title: "🐞 Bug Fixes"
labels:
- "bug"
- title: "📈 Performance"
labels:
- "perf"
- "enhancement"
- title: 📝 Documentation
labels:
- "documentation"

View File

@@ -23,7 +23,7 @@ jobs:
matrix:
os:
- ubuntu-latest
- macos-latest
# - macos-latest
- windows-latest
timeout-minutes: 20
steps:
@@ -62,7 +62,7 @@ jobs:
matrix:
os:
- ubuntu-latest
- macos-latest
# - macos-latest
- windows-latest
steps:
@@ -85,7 +85,7 @@ jobs:
matrix:
os:
- ubuntu-latest
- macos-latest
# - macos-latest
- windows-latest
steps:
- name: Checkout code

View File

@@ -6,7 +6,7 @@ on:
- main
jobs:
deploy-push-playground-ftp:
deploy-playground-ftp:
name: Deploy Push Playground Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
@@ -27,7 +27,7 @@ jobs:
uses: ./.github/actions/setup-node
- name: Build
run: pnpm build:play && pnpm build:docs
run: pnpm build:play
- name: Sync Playground files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
@@ -37,6 +37,22 @@ jobs:
password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }}
local-dir: ./playground/dist/
deploy-docs-ftp:
name: Deploy Push Docs Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Build
run: pnpm build:docs
- name: Sync Docs files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
@@ -45,7 +61,7 @@ jobs:
password: ${{ secrets.WEBSITE_FTP_PASSWORD }}
local-dir: ./docs/.vitepress/dist/
deploy-push-antd-ftp:
deploy-antd-ftp:
name: Deploy Push Antd Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
@@ -76,7 +92,7 @@ jobs:
password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }}
local-dir: ./apps/web-antd/dist/
deploy-push-ele-ftp:
deploy-ele-ftp:
name: Deploy Push Element Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest
@@ -107,7 +123,7 @@ jobs:
password: ${{ secrets.WEB_ELE_FTP_PASSWORD }}
local-dir: ./apps/web-ele/dist/
deploy-push-naive-ftp:
deploy-naive-ftp:
name: Deploy Push Naive Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
runs-on: ubuntu-latest

View File

@@ -21,5 +21,5 @@ jobs:
with:
actions: "close-issues" # 执行动作:关闭 Issues
token: ${{ secrets.GITHUB_TOKEN }} # GitHub Token用于认证
labels: "need reproduction" # 目标标签
labels: "needs reproduction" # 目标标签
inactive-day: 3 # 未活动天数阈值

1
.gitignore vendored
View File

@@ -48,3 +48,4 @@ vite.config.ts.*
*.njsproj
*.sln
*.sw?
.history

View File

@@ -1,28 +0,0 @@
ls:
.js: kebab-case | pointcase
.vue: kebab-case | pointcase
.ts: kebab-case | pointcase
.tsx: kebab-case | pointcase
.jsx: kebab-case | pointcase
.css: kebab-case | pointcase
.d.ts: kebab-case | pointcase
# shadcn 自动生成文件为 PascalCase 格式
packages/@core/ui-kit/shadcn-ui/src/components/ui:
.vue: PascalCase
ignore:
- "**/*.png"
- "**/*.jpg"
- "**/*.jpeg"
- "**/*.jpeg"
- "**/*.gif"
- "**/_util.ts"
- "**/deps/**"
- "**/dist/**"
- "**/node_modules/**"
- "**/.turbo/**"
- .git
- .vscode
- .idea
- node_modules
- .cache

8
.vscode/launch.json vendored
View File

@@ -9,7 +9,7 @@
"url": "http://localhost:5555",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}/playground/src"
"webRoot": "${workspaceFolder}"
},
{
"type": "chrome",
@@ -18,7 +18,7 @@
"url": "http://localhost:5666",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}/apps/web-antd/src"
"webRoot": "${workspaceFolder}"
},
{
"type": "chrome",
@@ -27,7 +27,7 @@
"url": "http://localhost:5777",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}/apps/web-ele/src"
"webRoot": "${workspaceFolder}"
},
{
"type": "chrome",
@@ -36,7 +36,7 @@
"url": "http://localhost:5888",
"env": { "NODE_ENV": "development" },
"sourceMaps": true,
"webRoot": "${workspaceFolder}/apps/web-naive/src"
"webRoot": "${workspaceFolder}"
}
]
}

41
.vscode/settings.json vendored
View File

@@ -14,7 +14,6 @@
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.cursorBlinking": "expand",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.largeFileOptimizations": false,
"editor.accessibilitySupport": "off",
"editor.cursorSmoothCaretAnimation": "on",
@@ -37,7 +36,34 @@
"source.fixAll.stylelint": "explicit",
"source.organizeImports": "never"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// extensions
"extensions.ignoreRecommendations": true,
@@ -56,7 +82,8 @@
"*.ejs": "html",
"*.art": "html",
"**/tsconfig.json": "jsonc",
"*.json": "jsonc"
"*.json": "jsonc",
"package.json": "json"
},
"files.exclude": {
@@ -167,9 +194,10 @@
"i18n-ally.localesPaths": [
"packages/locales/src/langs",
"playground/src/langs",
"playground/src/locales/langs",
"apps/*/src/locales/langs"
],
"i18n-ally.pathMatcher": "{locale}.json",
"i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
@@ -185,13 +213,12 @@
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
"Dockerfile": "Dockerfile,.docker*,docker-entrypoint.sh,build-local-docker*,nginx.conf",
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,.ls-lint*,cspell.json",
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
"tailwind.config.mjs": "postcss.*"
},
"commentTranslate.hover.enabled": false,
"i18n-ally.keystyle": "nested",
"commentTranslate.multiLineMerge": true,
"vue.server.hybridMode": true,
"typescript.tsdk": "node_modules/typescript/lib",
"vitest.disableWorkspaceWarning": true
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@@ -5,6 +5,8 @@
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**日本語** | [English](./README.md) | [中文](./README.zh-CN.md)
## 紹介

View File

@@ -5,6 +5,8 @@
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md)
## Introduction

View File

@@ -5,6 +5,8 @@
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**中文** | [English](./README.md) | [日本語](./README.ja-JP.md)
## 简介

View File

@@ -1,7 +1,7 @@
import type { NitroErrorHandler } from 'nitropack';
const errorHandler: NitroErrorHandler = function (error, event) {
event.res.end(`[error handler] ${error.stack}`);
event.node.res.end(`[Error Handler] ${error.stack}`);
};
export default errorHandler;

View File

@@ -14,7 +14,7 @@ export interface UserPayload extends UserInfo {
}
export function generateAccessToken(user: UserInfo) {
return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '2h' });
return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '7d' });
}
export function generateRefreshToken(user: UserInfo) {

View File

@@ -14,3 +14,6 @@ VITE_ROUTER_HISTORY=hash
# 是否注入全局loading
VITE_INJECT_APP_LOADING=true
# 打包后是否生成dist.zip
VITE_ARCHIVER=true

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/web-antd",
"version": "5.1.1",
"version": "5.2.1",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -27,22 +27,22 @@
},
"dependencies": {
"@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/request": "workspace:*",
"@vben/stores": "workspace:*",
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.0.0",
"@vueuse/core": "^11.0.3",
"ant-design-vue": "^4.2.3",
"dayjs": "^1.11.12",
"dayjs": "^1.11.13",
"pinia": "2.2.2",
"vue": "^3.4.38",
"vue-router": "^4.4.3"

View File

@@ -42,7 +42,9 @@ export async function refreshTokenApi() {
* 退出登录
*/
export async function logoutApi() {
return requestClient.post('/auth/logout');
return baseRequestClient.post('/auth/logout', {
withCredentials: true,
});
}
/**

View File

@@ -31,7 +31,10 @@ function createRequestClient(baseURL: string) {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (preferences.app.loginExpiredMode === 'modal') {
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();

View File

@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { AuthPageLayout } from '@vben/layouts';
import { preferences } from '@vben/preferences';
import { $t } from '#/locales';
const appName = computed(() => preferences.app.name);
const logo = computed(() => preferences.logo.source);
</script>
<template>
<AuthPageLayout
:app-name="appName"
:logo="logo"
:page-description="$t('authentication.pageDesc')"
:page-title="$t('authentication.pageTitle')"
>
<!-- 自定义工具栏 -->
<!-- <template #toolbar></template> -->
</AuthPageLayout>
</template>

View File

@@ -1,8 +1,6 @@
const BasicLayout = () => import('./basic.vue');
const AuthPageLayout = () => import('./auth.vue');
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
const AuthPageLayout = () =>
import('@vben/layouts').then((m) => m.AuthPageLayout);
export { AuthPageLayout, BasicLayout, IFrameView };

View File

@@ -91,4 +91,4 @@ async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
});
}
export { $t, antdLocale, loadMessages, setupI18n };
export { $t, antdLocale, setupI18n };

View File

@@ -92,10 +92,8 @@ function setupAccessGuard(router: Router) {
return to;
}
const accessRoutes = accessStore.accessRoutes;
// 是否已经生成过动态路由
if (accessRoutes && accessRoutes.length > 0) {
if (accessStore.isAccessChecked) {
return true;
}
@@ -115,6 +113,7 @@ function setupAccessGuard(router: Router) {
// 保存菜单信息和路由信息
accessStore.setAccessMenus(accessibleMenus);
accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true);
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
return {

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
@@ -51,12 +55,27 @@ onMounted(() => {
},
trigger: 'axis',
},
// xAxis: {
// axisTick: {
// show: false,
// },
// boundaryGap: false,
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
// type: 'category',
// },
xAxis: {
axisTick: {
show: false,
},
boundaryGap: false,
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
splitLine: {
lineStyle: {
type: 'solid',
width: 1,
},
show: true,
},
type: 'category',
},
yAxis: [
@@ -65,7 +84,10 @@ onMounted(() => {
show: false,
},
max: 80_000,
splitArea: {
show: true,
},
splitNumber: 4,
type: 'value',
},
],

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
</div>
<div class="w-full lg:w-2/5">
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
<WorkbenchQuickNav
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
/>
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
<AnalysisChartCard class="mt-5" title="访问来源">
<AnalyticsVisitsSource />

View File

@@ -37,7 +37,7 @@ function notify(type: NotificationType) {
description="支持多语言,主题功能集成切换等"
title="Ant Design Vue组件使用演示"
>
<Card title="按钮">
<Card class="mb-5" title="按钮">
<Space>
<Button>Default</Button>
<Button type="primary"> Primary </Button>

View File

@@ -14,3 +14,6 @@ VITE_ROUTER_HISTORY=hash
# 是否注入全局loading
VITE_INJECT_APP_LOADING=true
# 打包后是否生成dist.zip
VITE_ARCHIVER=true

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/web-ele",
"version": "5.1.1",
"version": "5.2.1",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -27,22 +27,22 @@
},
"dependencies": {
"@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/request": "workspace:*",
"@vben/stores": "workspace:*",
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.0.0",
"dayjs": "^1.11.12",
"element-plus": "^2.8.0",
"@vueuse/core": "^11.0.3",
"dayjs": "^1.11.13",
"element-plus": "^2.8.1",
"pinia": "2.2.2",
"vue": "^3.4.38",
"vue-router": "^4.4.3"

View File

@@ -42,7 +42,9 @@ export async function refreshTokenApi() {
* 退出登录
*/
export async function logoutApi() {
return requestClient.post('/auth/logout');
return baseRequestClient.post('/auth/logout', {
withCredentials: true,
});
}
/**

View File

@@ -31,7 +31,10 @@ function createRequestClient(baseURL: string) {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (preferences.app.loginExpiredMode === 'modal') {
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();

View File

@@ -3,6 +3,7 @@ import { createApp } from 'vue';
import { registerAccessDirective } from '@vben/access';
import { initStores } from '@vben/stores';
import '@vben/styles';
import '@vben/styles/ele';
import { setupI18n } from '#/locales';

View File

@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { AuthPageLayout } from '@vben/layouts';
import { preferences } from '@vben/preferences';
import { $t } from '#/locales';
const appName = computed(() => preferences.app.name);
const logo = computed(() => preferences.logo.source);
</script>
<template>
<AuthPageLayout
:app-name="appName"
:logo="logo"
:page-description="$t('authentication.pageDesc')"
:page-title="$t('authentication.pageTitle')"
>
<!-- 自定义工具栏 -->
<!-- <template #toolbar></template> -->
</AuthPageLayout>
</template>

View File

@@ -1,8 +1,6 @@
const BasicLayout = () => import('./basic.vue');
const AuthPageLayout = () => import('./auth.vue');
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
const AuthPageLayout = () =>
import('@vben/layouts').then((m) => m.AuthPageLayout);
export { AuthPageLayout, BasicLayout, IFrameView };

View File

@@ -91,4 +91,4 @@ async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
});
}
export { $t, elementLocale, loadMessages, setupI18n };
export { $t, elementLocale, setupI18n };

View File

@@ -92,10 +92,8 @@ function setupAccessGuard(router: Router) {
return to;
}
const accessRoutes = accessStore.accessRoutes;
// 是否已经生成过动态路由
if (accessRoutes && accessRoutes.length > 0) {
if (accessStore.isAccessChecked) {
return true;
}
@@ -115,6 +113,7 @@ function setupAccessGuard(router: Router) {
// 保存菜单信息和路由信息
accessStore.setAccessMenus(accessibleMenus);
accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true);
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
return {

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
@@ -51,12 +55,27 @@ onMounted(() => {
},
trigger: 'axis',
},
// xAxis: {
// axisTick: {
// show: false,
// },
// boundaryGap: false,
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
// type: 'category',
// },
xAxis: {
axisTick: {
show: false,
},
boundaryGap: false,
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
splitLine: {
lineStyle: {
type: 'solid',
width: 1,
},
show: true,
},
type: 'category',
},
yAxis: [
@@ -65,7 +84,10 @@ onMounted(() => {
show: false,
},
max: 80_000,
splitArea: {
show: true,
},
splitNumber: 4,
type: 'value',
},
],

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
</div>
<div class="w-full lg:w-2/5">
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
<WorkbenchQuickNav
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
/>
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
<AnalysisChartCard class="mt-5" title="访问来源">
<AnalyticsVisitsSource />

View File

@@ -14,3 +14,6 @@ VITE_ROUTER_HISTORY=hash
# 是否注入全局loading
VITE_INJECT_APP_LOADING=true
# 打包后是否生成dist.zip
VITE_ARCHIVER=true

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/web-naive",
"version": "5.1.1",
"version": "5.2.1",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -27,20 +27,20 @@
},
"dependencies": {
"@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/request": "workspace:*",
"@vben/stores": "workspace:*",
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.0.0",
"@vueuse/core": "^11.0.3",
"naive-ui": "^2.39.0",
"pinia": "2.2.2",
"vue": "^3.4.38",

View File

@@ -42,7 +42,9 @@ export async function refreshTokenApi() {
* 退出登录
*/
export async function logoutApi() {
return requestClient.post('/auth/logout');
return baseRequestClient.post('/auth/logout', {
withCredentials: true,
});
}
/**

View File

@@ -30,7 +30,10 @@ function createRequestClient(baseURL: string) {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (preferences.app.loginExpiredMode === 'modal') {
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();

View File

@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { AuthPageLayout } from '@vben/layouts';
import { preferences } from '@vben/preferences';
import { $t } from '#/locales';
const appName = computed(() => preferences.app.name);
const logo = computed(() => preferences.logo.source);
</script>
<template>
<AuthPageLayout
:app-name="appName"
:logo="logo"
:page-description="$t('authentication.pageDesc')"
:page-title="$t('authentication.pageTitle')"
>
<!-- 自定义工具栏 -->
<!-- <template #toolbar></template> -->
</AuthPageLayout>
</template>

View File

@@ -1,8 +1,6 @@
const BasicLayout = () => import('./basic.vue');
const AuthPageLayout = () => import('./auth.vue');
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
const AuthPageLayout = () =>
import('@vben/layouts').then((m) => m.AuthPageLayout);
export { AuthPageLayout, BasicLayout, IFrameView };

View File

@@ -28,4 +28,4 @@ async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
});
}
export { $t, loadMessages, setupI18n };
export { $t, setupI18n };

View File

@@ -92,13 +92,10 @@ function setupAccessGuard(router: Router) {
return to;
}
const accessRoutes = accessStore.accessRoutes;
// 是否已经生成过动态路由
if (accessRoutes && accessRoutes.length > 0) {
if (accessStore.isAccessChecked) {
return true;
}
// 生成路由表
// 当前登录用户拥有的角色标识列表
const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
@@ -115,6 +112,7 @@ function setupAccessGuard(router: Router) {
// 保存菜单信息和路由信息
accessStore.setAccessMenus(accessibleMenus);
accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true);
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
return {

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
@@ -51,12 +55,27 @@ onMounted(() => {
},
trigger: 'axis',
},
// xAxis: {
// axisTick: {
// show: false,
// },
// boundaryGap: false,
// data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
// type: 'category',
// },
xAxis: {
axisTick: {
show: false,
},
boundaryGap: false,
data: Array.from({ length: 18 }).map((_item, index) => `${index + 6}:00`),
splitLine: {
lineStyle: {
type: 'solid',
width: 1,
},
show: true,
},
type: 'category',
},
yAxis: [
@@ -65,7 +84,10 @@ onMounted(() => {
show: false,
},
max: 80_000,
splitArea: {
show: true,
},
splitNumber: 4,
type: 'value',
},
],

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -1,7 +1,11 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);

View File

@@ -214,7 +214,11 @@ const trendItems: WorkbenchTrendItem[] = [
<WorkbenchTrends :items="trendItems" class="mt-5" title="最新动态" />
</div>
<div class="w-full lg:w-2/5">
<WorkbenchQuickNav :items="quickNavItems" title="快捷导航" />
<WorkbenchQuickNav
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
/>
<WorkbenchTodo :items="todoItems" class="mt-5" title="待办事项" />
<AnalysisChartCard class="mt-5" title="访问来源">
<AnalyticsVisitsSource />

View File

@@ -37,6 +37,7 @@
"astro",
"ui-kit",
"styl",
"vnode",
"nocheck",
"prefixs",
"vitepress",
@@ -53,6 +54,9 @@
"**/*-dist/**",
"**/icons/**",
"pnpm-lock.yaml",
"**/*.log"
"**/*.log",
"**/*.test.ts",
"**/*.spec.ts",
"**/__tests__/**"
]
}

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from 'vue';
import PreviewGroup from './preview-group.vue';
interface Props {
files?: string;
}
const props = withDefaults(defineProps<Props>(), { files: '() => []' });
const parsedFiles = computed(() => {
try {
return JSON.parse(decodeURIComponent(props.files ?? ''));
} catch {
return [];
}
});
</script>
<template>
<div class="border-border shadow-float relative rounded-xl border">
<div
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
>
<div class="flex w-full max-w-[700px] px-2">
<slot v-if="parsedFiles.length > 0"></slot>
<div v-else class="text-destructive text-sm">
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
ERROR:
</span>
The preview directory does not exist. Please check the 'dir'
parameter.
</div>
</div>
</div>
<PreviewGroup v-if="parsedFiles.length > 0" :files="parsedFiles">
<template v-for="file in parsedFiles" #[file]>
<slot :name="file"></slot>
</template>
</PreviewGroup>
</div>
</template>

View File

@@ -0,0 +1 @@
export { default as DemoPreview } from './demo-preview.vue';

View File

@@ -0,0 +1,108 @@
<script setup lang="ts">
import { computed, ref, useSlots } from 'vue';
import { VbenTooltip } from '@vben-core/shadcn-ui';
import { Code } from 'lucide-vue-next';
import {
TabsContent,
TabsIndicator,
TabsList,
TabsRoot,
TabsTrigger,
} from 'radix-vue';
defineOptions({
inheritAttrs: false,
});
const props = withDefaults(
defineProps<{
files?: string[];
}>(),
{ files: () => [] },
);
const open = ref(false);
const slots = useSlots();
const tabs = computed(() => {
return props.files.map((file) => {
return {
component: slots[file],
label: file,
};
});
});
const currentTab = ref('index.vue');
const toggleOpen = () => {
open.value = !open.value;
};
</script>
<template>
<TabsRoot
v-model="currentTab"
class="bg-background-deep border-border overflow-hidden rounded-b-xl border-t"
@update:model-value="open = true"
>
<div class="border-border bg-background flex border-b-2 pr-2">
<div class="flex w-full items-center justify-between text-[13px]">
<TabsList class="relative flex">
<template v-if="open">
<TabsIndicator
class="absolute bottom-0 left-0 h-[2px] w-[--radix-tabs-indicator-size] translate-x-[--radix-tabs-indicator-position] rounded-full transition-[width,transform] duration-300"
>
<div class="size-full bg-[var(--vp-c-indigo-1)]"></div>
</TabsIndicator>
<TabsTrigger
v-for="(tab, index) in tabs"
:key="index"
:value="tab.label"
class="border-box text-foreground px-4 py-3 data-[state=active]:text-[var(--vp-c-indigo-1)]"
tabindex="-1"
>
{{ tab.label }}
</TabsTrigger>
</template>
</TabsList>
<div
:class="{
'py-2': !open,
}"
class="flex items-center"
>
<VbenTooltip side="top">
<template #trigger>
<Code
class="hover:bg-accent size-7 cursor-pointer rounded-full p-1.5"
@click="toggleOpen"
/>
</template>
{{ open ? 'Collapse code' : 'Expand code' }}
</VbenTooltip>
</div>
</div>
</div>
<div
:class="`${open ? 'h-[unset] max-h-[80vh]' : 'h-0'}`"
class="block overflow-y-scroll bg-[var(--vp-code-block-bg)] transition-all duration-300"
>
<TabsContent
v-for="tab in tabs"
:key="tab.label"
:value="tab.label"
as-child
class="rounded-xl"
>
<div class="text-foreground relative rounded-xl">
<component :is="tab.component" class="border-0" />
</div>
</TabsContent>
</div>
</TabsRoot>
</template>

View File

@@ -0,0 +1,222 @@
import { type DefaultTheme, defineConfig } from 'vitepress';
import { version } from '../../../package.json';
export const en = defineConfig({
description: 'Vben Admin & Enterprise level management system framework',
lang: 'en-US',
themeConfig: {
darkModeSwitchLabel: 'Theme',
darkModeSwitchTitle: 'Switch to Dark Mode',
docFooter: {
next: 'Next Page',
prev: 'Previous Page',
},
editLink: {
pattern:
'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/src/:path',
text: 'Edit this page on GitHub',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: 'Released under the MIT License.',
},
langMenuLabel: 'Language',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
},
text: 'Last updated on',
},
lightModeSwitchTitle: 'Switch to Light Mode',
nav: nav(),
outline: {
label: 'Navigate',
},
returnToTopLabel: 'Back to top',
sidebar: {
'/en/commercial/': {
base: '/en/commercial/',
items: sidebarCommercial(),
},
'/en/guide/': { base: '/en/guide/', items: sidebarGuide() },
},
},
});
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
collapsed: false,
text: 'Introduction',
items: [
{
link: 'introduction/vben',
text: 'About Vben Admin',
},
{
link: 'introduction/why',
text: 'Why Choose Us?',
},
{ link: 'introduction/quick-start', text: 'Quick Start' },
{ link: 'introduction/thin', text: 'Lite Version' },
],
},
{
text: 'Basics',
items: [
{ link: 'essentials/concept', text: 'Basic Concepts' },
{ link: 'essentials/development', text: 'Local Development' },
{ link: 'essentials/route', text: 'Routing and Menu' },
{ link: 'essentials/settings', text: 'Configuration' },
{ link: 'essentials/icons', text: 'Icons' },
{ link: 'essentials/styles', text: 'Styles' },
{ link: 'essentials/external-module', text: 'External Modules' },
{ link: 'essentials/build', text: 'Build and Deployment' },
{ link: 'essentials/server', text: 'Server Interaction and Data Mock' },
],
},
{
text: 'Advanced',
items: [
{ link: 'in-depth/login', text: 'Login' },
{ link: 'in-depth/theme', text: 'Theme' },
{ link: 'in-depth/access', text: 'Access Control' },
{ link: 'in-depth/locale', text: 'Internationalization' },
{ link: 'in-depth/features', text: 'Common Features' },
{ link: 'in-depth/check-updates', text: 'Check Updates' },
{ link: 'in-depth/loading', text: 'Global Loading' },
{ link: 'in-depth/ui-framework', text: 'UI Framework Switching' },
],
},
{
text: 'Engineering',
items: [
{ link: 'project/standard', text: 'Standards' },
{ link: 'project/cli', text: 'CLI' },
{ link: 'project/dir', text: 'Directory Explanation' },
{ link: 'project/test', text: 'Unit Testing' },
{ link: 'project/tailwindcss', text: 'Tailwind CSS' },
{ link: 'project/changeset', text: 'Changeset' },
{ link: 'project/vite', text: 'Vite Config' },
],
},
{
text: 'Others',
items: [
{ link: 'other/project-update', text: 'Project Update' },
{ link: 'other/remove-code', text: 'Remove Code' },
{ link: 'other/faq', text: 'FAQ' },
],
},
];
}
function sidebarCommercial(): DefaultTheme.SidebarItem[] {
return [
{
link: 'community',
text: 'Community',
},
{
link: 'technical-support',
text: 'Technical-support',
},
{
link: 'customized',
text: 'Customized',
},
];
}
function nav(): DefaultTheme.NavItem[] {
return [
{
text: 'Doc',
items: [
{
link: '/en/guide/introduction/vben',
text: 'Guide',
},
{
text: 'Historical Versions',
items: [
{
link: 'https://doc.vvbin.cn',
text: '2.x Version Documentation',
},
],
},
],
},
{
text: 'Demo',
items: [
{
text: 'Vben Admin',
items: [
{
link: 'https://www.vben.pro',
text: 'Demo Version',
},
{
link: 'https://ant.vben.pro',
text: 'Ant Design Vue Version',
},
{
link: 'https://naive.vben.pro',
text: 'Naive Version',
},
{
link: 'https://ele.vben.pro',
text: 'Element Plus Version',
},
],
},
{
text: 'Others',
items: [
{
link: 'https://vben.vvbin.cn',
text: 'Vben Admin 2.x',
},
],
},
],
},
{
text: version,
items: [
{
link: 'https://github.com/vbenjs/vue-vben-admin/releases',
text: 'Changelog',
},
{
link: 'https://github.com/orgs/vbenjs/projects/5',
text: 'Roadmap',
},
{
link: 'https://github.com/vbenjs/vue-vben-admin/blob/main/.github/contributing.md',
text: 'Contribution',
},
],
},
{
link: '/commercial/technical-support',
text: '🦄 Tech Support',
},
{
link: '/sponsor/personal',
text: '✨ Sponsor',
},
{
link: '/commercial/community',
text: '👨‍👦‍👦 Community',
},
{
link: '/friend-links/',
text: '🤝 Friend Links',
},
];
}

View File

@@ -0,0 +1,25 @@
import { withPwa } from '@vite-pwa/vitepress';
import { defineConfigWithTheme } from 'vitepress';
import { en } from './en.mts';
import { shared } from './shared.mts';
import { zh } from './zh.mts';
export default withPwa(
defineConfigWithTheme({
...shared,
locales: {
en: {
label: 'English',
lang: 'en',
link: '/en/',
...en,
},
root: {
label: '简体中文',
lang: 'zh-CN',
...zh,
},
},
}),
);

View File

@@ -0,0 +1,135 @@
import type { MarkdownEnv, MarkdownRenderer } from 'vitepress';
import crypto from 'node:crypto';
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
export const rawPathRegexp =
// eslint-disable-next-line regexp/no-super-linear-backtracking, regexp/strict
/^(.+?(?:\.([\da-z]+))?)(#[\w-]+)?(?: ?{(\d+(?:[,-]\d+)*)? ?(\S+)?})? ?(?:\[(.+)])?$/;
function rawPathToToken(rawPath: string) {
const [
filepath = '',
extension = '',
region = '',
lines = '',
lang = '',
rawTitle = '',
] = (rawPathRegexp.exec(rawPath) || []).slice(1);
const title = rawTitle || filepath.split('/').pop() || '';
return { extension, filepath, lang, lines, region, title };
}
export const demoPreviewPlugin = (md: MarkdownRenderer) => {
md.core.ruler.after('inline', 'demo-preview', (state) => {
const insertComponentImport = (importString: string) => {
const index = state.tokens.findIndex(
(i) => i.type === 'html_block' && i.content.match(/<script setup>/g),
);
if (index === -1) {
const importComponent = new state.Token('html_block', '', 0);
importComponent.content = `<script setup>\n${importString}\n</script>\n`;
state.tokens.splice(0, 0, importComponent);
} else {
if (state.tokens[index]) {
const content = state.tokens[index].content;
state.tokens[index].content = content.replace(
'</script>',
`${importString}\n</script>`,
);
}
}
};
// Define the regular expression to match the desired pattern
const regex = /<DemoPreview[^>]*\sdir="([^"]*)"/g;
// Iterate through the Markdown content and replace the pattern
state.src = state.src.replaceAll(regex, (_match, dir) => {
const componentDir = join(process.cwd(), 'src', dir);
let childFiles: string[] = [];
let dirExists = true;
try {
childFiles =
readdirSync(componentDir, {
encoding: 'utf8',
recursive: false,
withFileTypes: false,
}) || [];
} catch {
dirExists = false;
}
if (!dirExists) {
return '';
}
const uniqueWord = generateContentHash(componentDir);
const ComponentName = `DemoComponent_${uniqueWord}`;
insertComponentImport(
`import ${ComponentName} from '${componentDir}/index.vue'`,
);
const { path: _path } = state.env as MarkdownEnv;
const index = state.tokens.findIndex((i) => i.content.match(regex));
if (!state.tokens[index]) {
return '';
}
state.tokens[index].content =
`<DemoPreview files="${encodeURIComponent(JSON.stringify(childFiles))}" ><${ComponentName}/>
`;
const _dummyToken = new state.Token('', '', 0);
const tokenArray: Array<typeof _dummyToken> = [];
childFiles.forEach((filename) => {
// const slotName = filename.replace(extname(filename), '');
const templateStart = new state.Token('html_inline', '', 0);
templateStart.content = `<template #${filename}>`;
tokenArray.push(templateStart);
const resolvedPath = join(componentDir, filename);
const { extension, filepath, lang, lines, title } =
rawPathToToken(resolvedPath);
// Add code tokens for each line
const token = new state.Token('fence', 'code', 0);
token.info = `${lang || extension}${lines ? `{${lines}}` : ''}${
title ? `[${title}]` : ''
}`;
token.content = `<<< ${filepath}`;
(token as any).src = [resolvedPath];
tokenArray.push(token);
const templateEnd = new state.Token('html_inline', '', 0);
templateEnd.content = '</template>';
tokenArray.push(templateEnd);
});
const endTag = new state.Token('html_inline', '', 0);
endTag.content = '</DemoPreview>';
tokenArray.push(endTag);
state.tokens.splice(index + 1, 0, ...tokenArray);
// console.log(
// state.md.renderer.render(state.tokens, state?.options ?? [], state.env),
// );
return '';
});
});
};
function generateContentHash(input: string, length: number = 10): string {
// 使用 SHA-256 生成哈希值
const hash = crypto.createHash('sha256').update(input).digest('hex');
// 将哈希值转换为 Base36 编码,并取指定长度的字符作为结果
return Number.parseInt(hash, 16).toString(36).slice(0, length);
}

View File

@@ -0,0 +1,155 @@
import type { PwaOptions } from '@vite-pwa/vitepress';
import type { HeadConfig } from 'vitepress';
import { resolve } from 'node:path';
import { viteArchiverPlugin } from '@vben/vite-config';
import {
GitChangelog,
GitChangelogMarkdownSection,
} from '@nolebase/vitepress-plugin-git-changelog/vite';
import tailwind from 'tailwindcss';
import { defineConfig, postcssIsolateStyles } from 'vitepress';
import { demoPreviewPlugin } from './plugins/demo-preview';
import { search as zhSearch } from './zh.mts';
export const shared = defineConfig({
appearance: 'dark',
head: head(),
markdown: {
preConfig(md) {
md.use(demoPreviewPlugin);
},
},
pwa: pwa(),
srcDir: 'src',
themeConfig: {
i18nRouting: true,
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp',
search: {
options: {
locales: {
...zhSearch,
},
},
provider: 'local',
},
siteTitle: 'Vben Admin',
socialLinks: [
{ icon: 'github', link: 'https://github.com/vbenjs/vue-vben-admin' },
],
},
title: 'Vben Admin',
vite: {
build: {
chunkSizeWarningLimit: Infinity,
minify: 'terser',
},
css: {
postcss: {
plugins: [
tailwind(),
postcssIsolateStyles({ includeFiles: [/vp-doc\.css/] }),
],
},
},
json: {
stringify: true,
},
plugins: [
GitChangelog({
mapAuthors: [
{
mapByNameAliases: ['Vben'],
name: 'vben',
username: 'anncwb',
},
{
name: 'vince',
username: 'vince292007',
},
{
name: 'Li Kui',
username: 'likui628',
},
],
repoURL: () => 'https://github.com/vbenjs/vue-vben-admin',
}),
GitChangelogMarkdownSection(),
viteArchiverPlugin({ outputDir: '.vitepress' }),
],
server: {
fs: {
allow: ['../..'],
},
host: true,
port: 6173,
},
ssr: {
external: ['@vue/repl'],
},
},
});
function head(): HeadConfig[] {
return [
['meta', { content: 'Vbenjs Team', name: 'author' }],
[
'meta',
{
content: 'vben, vitejs, vite, shacdn-ui, vue',
name: 'keywords',
},
],
['link', { href: '/favicon.ico', rel: 'icon', type: 'image/svg+xml' }],
[
'meta',
{
content:
'width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no',
name: 'viewport',
},
],
['meta', { content: 'vben admin docs', name: 'keywords' }],
['link', { href: '/favicon.ico', rel: 'icon' }],
// [
// 'script',
// {
// src: 'https://cdn.tailwindcss.com',
// },
// ],
];
}
function pwa(): PwaOptions {
return {
includeManifestIcons: false,
manifest: {
description:
'Vben Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-512.png',
type: 'image/png',
},
],
id: '/',
name: 'Vben Admin Doc',
short_name: 'vben_admin_doc',
theme_color: '#ffffff',
},
outDir: resolve(process.cwd(), '.vitepress/dist'),
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2}'],
},
};
}

View File

@@ -1,126 +1,169 @@
import type { DefaultTheme, HeadConfig } from 'vitepress';
import { type DefaultTheme, defineConfig } from 'vitepress';
import { resolve } from 'node:path';
import { version } from '../../../package.json';
import {
GitChangelog,
GitChangelogMarkdownSection,
} from '@nolebase/vitepress-plugin-git-changelog/vite';
import { type PwaOptions, withPwa } from '@vite-pwa/vitepress';
import { defineConfigWithTheme } from 'vitepress';
import { version } from '../../package.json';
export default withPwa(
defineConfigWithTheme({
description: 'Vben Admin & 企业级管理系统框架',
head: head(),
lang: 'zh',
pwa: pwa(),
// locales: {
// en: {
// label: 'English',
// lang: 'en',
// link: '/en/',
// },
// root: {
// label: '简体中文',
// lang: 'zh-CN',
srcDir: 'src',
// },
themeConfig: {
darkModeSwitchLabel: '主题',
darkModeSwitchTitle: '切换到深色模式',
docFooter: {
next: '下一页',
prev: '上一页',
export const zh = defineConfig({
description: 'Vben Admin & 企业级管理系统框架',
lang: 'zh-Hans',
themeConfig: {
darkModeSwitchLabel: '主题',
darkModeSwitchTitle: '切换到深色模式',
docFooter: {
next: '下一页',
prev: '上一页',
},
editLink: {
pattern:
'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/src/:path',
text: '在 GitHub 上编辑此页面',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: '基于 MIT 许可发布.',
},
langMenuLabel: '多语言',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
},
editLink: {
pattern:
'https://github.com/vbenjs/vue-vben-admin/edit/main/docs/:path',
text: '在 GitHub 上编辑此页面',
},
footer: {
copyright: `Copyright © 2020-${new Date().getFullYear()} Vben`,
message: '基于 MIT 许可发布.',
},
i18nRouting: true,
langMenuLabel: '多语言',
lastUpdated: {
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
text: '最后更新于',
},
lightModeSwitchTitle: '切换到浅色模式',
nav: nav(),
outline: {
label: '页面导航',
},
returnToTopLabel: '回到顶部',
sidebar: {
'/commercial/': { base: '/commercial/', items: sidebarCommercial() },
'/components/': { base: '/components/', items: sidebarComponents() },
'/guide/': { base: '/guide/', items: sidebarGuide() },
},
sidebarMenuLabel: '菜单',
},
});
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
collapsed: false,
text: '简介',
items: [
{
link: 'introduction/vben',
text: '关于 Vben Admin',
},
text: '最后更新于',
},
lightModeSwitchTitle: '切换到浅色模式',
logo: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp',
nav: nav(),
outline: {
label: '页面导航',
},
returnToTopLabel: '回到顶部',
search: {
options: {
locales: {
zh: {
translations: {
button: {
buttonAriaLabel: '搜索文档',
buttonText: '搜索文档',
},
modal: {
footer: {
navigateText: '切换',
selectText: '选择',
},
noResultsText: '无法找到相关结果',
resetButtonTitle: '清除查询条件',
},
},
},
},
{
link: 'introduction/why',
text: '为什么选择我们?',
},
{ link: 'introduction/quick-start', text: '快速开始' },
{ link: 'introduction/thin', text: '精简版本' },
{
base: '/',
link: 'components/introduction',
text: '组件文档',
},
provider: 'local',
},
sidebar: {
'/commercial/': { base: '/commercial/', items: sidebarCommercial() },
'/guide/': { base: '/guide/', items: sidebarGuide() },
},
sidebarMenuLabel: '菜单',
siteTitle: 'Vben Admin',
socialLinks: [
{ icon: 'github', link: 'https://github.com/vbenjs/vue-vben-admin' },
],
},
title: 'Vben Admin',
vite: {
build: {
chunkSizeWarningLimit: Infinity,
minify: 'terser',
},
json: {
stringify: true,
},
plugins: [
GitChangelog({
repoURL: () => 'https://github.com/vbenjs/vue-vben-admin',
}),
GitChangelogMarkdownSection(),
{
text: '基础',
items: [
{ link: 'essentials/concept', text: '基础概念' },
{ link: 'essentials/development', text: '本地开发' },
{ link: 'essentials/route', text: '路由和菜单' },
{ link: 'essentials/settings', text: '配置' },
{ link: 'essentials/icons', text: '图标' },
{ link: 'essentials/styles', text: '样式' },
{ link: 'essentials/external-module', text: '外部模块' },
{ link: 'essentials/build', text: '构建与部署' },
{ link: 'essentials/server', text: '服务端交互与数据Mock' },
],
server: {
fs: {
allow: ['../..'],
},
host: true,
port: 6173,
},
ssr: {
external: ['@vue/repl'],
},
},
}),
);
{
text: '深入',
items: [
{ link: 'in-depth/login', text: '登录' },
// { link: 'in-depth/layout', text: '布局' },
{ link: 'in-depth/theme', text: '主题' },
{ link: 'in-depth/access', text: '权限' },
{ link: 'in-depth/locale', text: '国际化' },
{ link: 'in-depth/features', text: '常用功能' },
{ link: 'in-depth/check-updates', text: '检查更新' },
{ link: 'in-depth/loading', text: '全局loading' },
{ link: 'in-depth/ui-framework', text: '组件库切换' },
],
},
{
text: '工程',
items: [
{ link: 'project/standard', text: '规范' },
{ link: 'project/cli', text: 'CLI' },
{ link: 'project/dir', text: '目录说明' },
{ link: 'project/test', text: '单元测试' },
{ link: 'project/tailwindcss', text: 'Tailwind CSS' },
{ link: 'project/changeset', text: 'Changeset' },
{ link: 'project/vite', text: 'Vite Config' },
],
},
{
text: '其他',
items: [
{ link: 'other/project-update', text: '项目更新' },
{ link: 'other/remove-code', text: '移除代码' },
{ link: 'other/faq', text: '常见问题' },
],
},
];
}
function sidebarCommercial(): DefaultTheme.SidebarItem[] {
return [
{
link: 'community',
text: '社区',
},
{
link: 'technical-support',
text: '技术支持',
},
{
link: 'customized',
text: '定制开发',
},
];
}
function sidebarComponents(): DefaultTheme.SidebarItem[] {
return [
{
text: '组件',
items: [
{
link: 'introduction',
text: '介绍',
},
],
},
{
collapsed: false,
text: '通用组件',
items: [
{
link: 'common-ui/vben-modal',
text: 'Vben Modal 模态框',
},
{
link: 'common-ui/vben-drawer',
text: 'Vben Drawer 抽屉',
},
],
},
];
}
function nav(): DefaultTheme.NavItem[] {
return [
@@ -130,28 +173,10 @@ function nav(): DefaultTheme.NavItem[] {
{
link: '/guide/introduction/vben',
text: '指南',
// items: [
// {
// link: '/guide/introduction/vben',
// text: '简介',
// },
// {
// link: '/guide/essentials/concept',
// text: '基础',
// },
// {
// link: '/guide/in-depth/layout',
// text: '深入',
// },
// {
// link: '/guide/project/standard',
// text: '工程',
// },
// {
// link: '/guide/other/project-update',
// text: '其他',
// },
// ],
},
{
link: '/components/introduction',
text: '组件',
},
{
text: '历史版本',
@@ -226,7 +251,7 @@ function nav(): DefaultTheme.NavItem[] {
},
{
link: '/commercial/community',
text: '👨‍👦‍👦 社区交流',
text: '👨‍👦‍👦 社区',
// items: [
// {
// link: 'https://qun.qq.com/qqweb/qunpro/share?_wv=3&_wwv=128&appChannel=share&inviteCode=22ySzj7pKiw&businessType=9&from=246610&biz=ka&mainSourceId=share&subSourceId=others&jumpsource=shorturl#/pc',
@@ -249,148 +274,46 @@ function nav(): DefaultTheme.NavItem[] {
];
}
function sidebarGuide(): DefaultTheme.SidebarItem[] {
return [
{
collapsed: false,
text: '简介',
items: [
{
link: 'introduction/vben',
text: '关于 Vben Admin',
},
{
link: 'introduction/why',
text: '为什么选择我们?',
},
{ link: 'introduction/quick-start', text: '快速开始' },
{ link: 'introduction/thin', text: '精简版本' },
],
},
{
text: '基础',
items: [
{ link: 'essentials/concept', text: '基础概念' },
{ link: 'essentials/development', text: '本地开发' },
{ link: 'essentials/route', text: '路由和菜单' },
{ link: 'essentials/settings', text: '配置' },
{ link: 'essentials/icons', text: '图标' },
{ link: 'essentials/styles', text: '样式' },
{ link: 'essentials/external-module', text: '外部模块' },
{ link: 'essentials/build', text: '构建与部署' },
{ link: 'essentials/server', text: '服务端交互与数据Mock' },
],
},
{
text: '深入',
items: [
{ link: 'in-depth/login', text: '登录' },
// { link: 'in-depth/layout', text: '布局' },
{ link: 'in-depth/theme', text: '主题' },
{ link: 'in-depth/access', text: '权限' },
{ link: 'in-depth/locale', text: '国际化' },
{ link: 'in-depth/features', text: '常用功能' },
{ link: 'in-depth/check-updates', text: '检查更新' },
{ link: 'in-depth/loading', text: '全局loading' },
{ link: 'in-depth/ui-framework', text: '组件库切换' },
],
},
{
text: '工程',
items: [
{ link: 'project/standard', text: '规范' },
{ link: 'project/cli', text: 'CLI' },
{ link: 'project/dir', text: '目录说明' },
{ link: 'project/test', text: '单元测试' },
{ link: 'project/tailwindcss', text: 'Tailwind CSS' },
{ link: 'project/changeset', text: 'Changeset' },
{ link: 'project/vite', text: 'Vite Config' },
],
},
{
text: '其他',
items: [
{ link: 'other/project-update', text: '项目更新' },
{ link: 'other/remove-code', text: '移除代码' },
{ link: 'other/faq', text: '常见问题' },
],
},
];
}
function sidebarCommercial(): DefaultTheme.SidebarItem[] {
return [
{
link: 'community',
text: '社区交流',
},
{
link: 'technical-support',
text: '技术支持',
},
{
link: 'customized',
text: '定制开发',
},
];
}
function head(): HeadConfig[] {
return [
['meta', { content: 'Vbenjs Team', name: 'author' }],
[
'meta',
{
content: 'vben, vitejs, vite, shacdn-ui, vue',
name: 'keywords',
export const search: DefaultTheme.AlgoliaSearchOptions['locales'] = {
root: {
placeholder: '搜索文档',
translations: {
button: {
buttonAriaLabel: '搜索文档',
buttonText: '搜索文档',
},
],
['link', { href: '/favicon.ico', rel: 'icon', type: 'image/svg+xml' }],
[
'meta',
{
content:
'width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no',
name: 'viewport',
modal: {
errorScreen: {
helpText: '你可能需要检查你的网络连接',
titleText: '无法获取结果',
},
footer: {
closeText: '关闭',
navigateText: '切换',
searchByText: '搜索提供者',
selectText: '选择',
},
noResultsScreen: {
noResultsText: '无法找到相关结果',
reportMissingResultsLinkText: '点击反馈',
reportMissingResultsText: '你认为该查询应该有结果?',
suggestedQueryText: '你可以尝试查询',
},
searchBox: {
cancelButtonAriaLabel: '取消',
cancelButtonText: '取消',
resetButtonAriaLabel: '清除查询条件',
resetButtonTitle: '清除查询条件',
},
startScreen: {
favoriteSearchesTitle: '收藏',
noRecentSearchesText: '没有搜索历史',
recentSearchesTitle: '搜索历史',
removeFavoriteSearchButtonTitle: '从收藏中移除',
removeRecentSearchButtonTitle: '从搜索历史中移除',
saveRecentSearchButtonTitle: '保存至搜索历史',
},
},
],
['meta', { content: 'vben admin docs', name: 'keywords' }],
['link', { href: '/favicon.ico', rel: 'icon' }],
// [
// 'script',
// {
// src: 'https://cdn.tailwindcss.com',
// },
// ],
];
}
function pwa(): PwaOptions {
return {
includeManifestIcons: false,
manifest: {
description:
'Vben Admin is a modern admin dashboard template based on Vue 3. ',
icons: [
{
sizes: '192x192',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-192.png',
type: 'image/png',
},
{
sizes: '512x512',
src: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/pwa-icon-512.png',
type: 'image/png',
},
],
id: '/',
name: 'Vben Admin Doc',
short_name: 'vben_admin_doc',
theme_color: '#ffffff',
},
outDir: resolve(process.cwd(), '.vitepress/dist'),
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2}'],
},
};
}
},
};

View File

@@ -1,9 +1,10 @@
// https://vitepress.dev/guide/custom-theme
import type { Theme } from 'vitepress';
import type { EnhanceAppContext, Theme } from 'vitepress';
import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client';
import DefaultTheme from 'vitepress/theme';
import { DemoPreview } from '../components';
import SiteLayout from './components/site-layout.vue';
import VbenContributors from './components/vben-contributors.vue';
import { initHmPlugin } from './plugins/hm';
@@ -13,9 +14,10 @@ import './styles';
import '@nolebase/vitepress-plugin-git-changelog/client/style.css';
export default {
enhanceApp({ app }) {
// ...
enhanceApp(ctx: EnhanceAppContext) {
const { app } = ctx;
app.component('VbenContributors', VbenContributors);
app.component('DemoPreview', DemoPreview);
app.use(NolebaseGitChangelogPlugin);
// 百度统计
initHmPlugin();

View File

@@ -1,2 +1,3 @@
import './variables.css';
import './base.css';
import '@vben/styles';

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/docs",
"version": "5.1.1",
"version": "5.2.1",
"private": true,
"scripts": {
"build": "vitepress build",
@@ -8,12 +8,18 @@
"docs:preview": "vitepress preview"
},
"dependencies": {
"medium-zoom": "^1.1.0"
"@vben-core/shadcn-ui": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/styles": "workspace:*",
"lucide-vue-next": "^0.436.0",
"medium-zoom": "^1.1.0",
"radix-vue": "^1.9.5"
},
"devDependencies": {
"@nolebase/vitepress-plugin-git-changelog": "^2.4.0",
"@vben/vite-config": "workspace:*",
"@vite-pwa/vitepress": "^0.5.0",
"vitepress": "^1.3.3",
"vitepress": "^1.3.4",
"vue": "^3.4.38"
}
}

View File

@@ -0,0 +1,112 @@
---
outline: deep
---
# Vben Drawer 抽屉
框架提供的抽屉组件,支持`自动高度``loading`等功能。
## 基础用法
使用 `useVbenDrawer` 创建最基础的模态框。
<DemoPreview dir="demos/vben-drawer/basic" />
## 组件抽离
Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 drawer 内的内容抽离出来,也方便复用。通过 `connectedComponent` 参数,可以将内外组件进行连接,而不用其他任何操作。
<DemoPreview dir="demos/vben-drawer/extra" />
## 自动计算高度
弹窗会自动计算内容高度,超过一定高度会出现滚动条,同时结合 `loading` 效果以及使用 `prepend-footer` 插槽。
<DemoPreview dir="demos/vben-drawer/auto-height" />
## 使用 Api
通过 `drawerApi` 可以调用 drawer 的方法以及使用 `setState` 更新 drawer 的状态。
<DemoPreview dir="demos/vben-drawer/dynamic" />
## 数据共享
如果你使用了 `connectedComponent` 参数,那么内外组件会共享数据,比如一些表单回填等操作。可以用 `drawerApi` 来获取数据和设置数据,配合 `onOpenChange`,可以满足大部分的需求。
<DemoPreview dir="demos/vben-drawer/shared-data" />
::: info 注意
- `VbenDrawer` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
:::
## API
```ts
// Drawer 为弹窗组件
// drawerApi 为弹窗的方法
const [Drawer, drawerApi] = useVbenDrawer({
// 属性
// 事件
});
```
### Props
所有属性都可以传入 `useVbenDrawer` 的第一个参数中。
| 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - |
| isOpen | 弹窗打开状态 | `boolean` | `false` |
| loading | 弹窗加载状态 | `boolean` | `false` |
| closable | 显示关闭按钮 | `boolean` | `true` |
| modal | 显示遮罩 | `boolean` | `true` |
| header | 显示header | `boolean` | `true` |
| footer | 显示footer | `boolean\|slot` | `true` |
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
| confirmText | 确认按钮文本 | `string\|slot` | `确认` |
| cancelText | 取消按钮文本 | `string\|slot` | `取消` |
| class | modal的class宽度通过这个配置 | `string` | - |
| contentClass | modal内容区域的class | `string` | - |
| footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - |
### Event
以下事件,只有在 `useVbenDrawer({onCancel:()=>{}})` 中传入才会生效。
| 事件名 | 描述 | 类型 |
| --- | --- | --- |
| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |
| onCancel | 点击取消按钮触发 | `()=>void` |
| onConfirm | 点击确认按钮触发 | `()=>void` |
| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |
### Slots
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
| 插槽名 | 描述 |
| -------------- | ------------------- |
| default | 默认插槽 - 弹窗内容 |
| prepend-footer | 取消按钮左侧 |
| append-footer | 取消按钮右侧 |
### modalApi
| 事件名 | 描述 | 类型 |
| --- | --- | --- |
| setState | 动态设置弹窗状态属性 | `setState(props) \| setState((prev)=>(props))` |
| open | 打开弹窗 | `()=>void` |
| close | 关闭弹窗 | `()=>void` |
| setData | 设置共享数据 | `<T>(data:T)=>void` |
| getData | 获取共享数据 | `<T>()=>T` |
| useStore | 获取可响应式状态 | - |

View File

@@ -0,0 +1,122 @@
---
outline: deep
---
# Vben Modal 模态框
框架提供的模态框组件,支持`拖拽``全屏``自动高度``loading`等功能。
## 基础用法
使用 `useVbenModal` 创建最基础的模态框。
<DemoPreview dir="demos/vben-modal/basic" />
## 组件抽离
Modal 内的内容一般业务中,会比较复杂,所以我们可以将 modal 内的内容抽离出来,也方便复用。通过 `connectedComponent` 参数,可以将内外组件进行连接,而不用其他任何操作。
<DemoPreview dir="demos/vben-modal/extra" />
## 开启拖拽
通过 `draggable` 参数,可开启拖拽功能。
<DemoPreview dir="demos/vben-modal/draggable" />
## 自动计算高度
弹窗会自动计算内容高度,超过一定高度会出现滚动条,同时结合 `loading` 效果以及使用 `prepend-footer` 插槽。
<DemoPreview dir="demos/vben-modal/auto-height" />
## 使用 Api
通过 `modalApi` 可以调用 modal 的方法以及使用 `setState` 更新 modal 的状态。
<DemoPreview dir="demos/vben-modal/dynamic" />
## 数据共享
如果你使用了 `connectedComponent` 参数,那么内外组件会共享数据,比如一些表单回填等操作。可以用 `modalApi` 来获取数据和设置数据,配合 `onOpenChange`,可以满足大部分的需求。
<DemoPreview dir="demos/vben-modal/shared-data" />
::: info 注意
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
:::
## API
```ts
// Modal 为弹窗组件
// modalApi 为弹窗的方法
const [Modal, modalApi] = useVbenModal({
// 属性
// 事件
});
```
### Props
所有属性都可以传入 `useVbenModal` 的第一个参数中。
| 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- |
| title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - |
| isOpen | 弹窗打开状态 | `boolean` | `false` |
| loading | 弹窗加载状态 | `boolean` | `false` |
| fullscreen | 全屏显示 | `boolean` | `false` |
| fullscreenButton | 显示全屏按钮 | `boolean` | `true` |
| draggable | 可拖拽 | `boolean` | `false` |
| closable | 显示关闭按钮 | `boolean` | `true` |
| centered | 居中显示 | `boolean` | `false` |
| modal | 显示遮罩 | `boolean` | `true` |
| header | 显示header | `boolean` | `true` |
| footer | 显示footer | `boolean\|slot` | `true` |
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
| confirmText | 确认按钮文本 | `string\|slot` | `确认` |
| cancelText | 取消按钮文本 | `string\|slot` | `取消` |
| class | modal的class宽度通过这个配置 | `string` | - |
| contentClass | modal内容区域的class | `string` | - |
| footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - |
### Event
以下事件,只有在 `useVbenModal({onCancel:()=>{}})` 中传入才会生效。
| 事件名 | 描述 | 类型 |
| --- | --- | --- |
| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |
| onCancel | 点击取消按钮触发 | `()=>void` |
| onConfirm | 点击确认按钮触发 | `()=>void` |
| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |
### Slots
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
| 插槽名 | 描述 |
| -------------- | ------------------- |
| default | 默认插槽 - 弹窗内容 |
| prepend-footer | 取消按钮左侧 |
| append-footer | 取消按钮右侧 |
### modalApi
| 事件名 | 描述 | 类型 |
| --- | --- | --- |
| setState | 动态设置弹窗状态属性 | `setState(props) \| setState((prev)=>(props))` |
| open | 打开弹窗 | `()=>void` |
| close | 关闭弹窗 | `()=>void` |
| setData | 设置共享数据 | `<T>(data:T)=>void` |
| getData | 获取共享数据 | `<T>()=>T` |
| useStore | 获取可响应式状态 | - |

View File

@@ -0,0 +1,11 @@
# 介绍
::: info README
该文档介绍的是框架组件的使用方法、属性、事件等。如果你觉得组件封装的不好,或者不符合你的需求,你可以直接使用原生的组件,或者自己封装一个组件,不需要拘泥于框架提供的组件。我们只是提供了一些常用的组件,方便你快速开发。是否使用,取决于你的需求。
:::
## 通用组件
通用组件是一些常用的组件,比如弹窗、抽屉、表单等。大部分基于 `Tailwind CSS` 实现,可适用于不同 UI 组件库的应用。

View File

@@ -0,0 +1,45 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
const list = ref<number[]>([]);
const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onConfirm() {
console.log('onConfirm');
},
onOpenChange(isOpen) {
if (isOpen) {
handleUpdate(10);
}
},
});
function handleUpdate(len: number) {
drawerApi.setState({ loading: true });
setTimeout(() => {
list.value = Array.from({ length: len }, (_v, k) => k + 1);
drawerApi.setState({ loading: false });
}, 2000);
}
</script>
<template>
<Drawer title="自动计算高度">
<div
v-for="item in list"
:key="item"
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
>
{{ item }}
</div>
<template #prepend-footer>
<VbenButton type="link" @click="handleUpdate(6)">
点击更新数据
</VbenButton>
</template>
</Drawer>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
import ExtraDrawer from './drawer.vue';
const [Drawer, drawerApi] = useVbenDrawer({
// 连接抽离的组件
connectedComponent: ExtraDrawer,
});
function open() {
drawerApi.open();
}
</script>
<template>
<div>
<Drawer />
<VbenButton @click="open">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
const [Drawer, drawerApi] = useVbenDrawer();
</script>
<template>
<div>
<VbenButton @click="() => drawerApi.open()">Open</VbenButton>
<Drawer class="w-[600px]" title="基础示例"> drawer content </Drawer>
</div>
</template>

View File

@@ -0,0 +1,26 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onConfirm() {
console.info('onConfirm');
},
title: '动态修改配置示例',
});
function handleUpdateTitle() {
drawerApi.setState({ title: '内部动态标题' });
}
</script>
<template>
<Drawer>
<div class="flex-col-center">
<VbenButton class="mb-3" type="primary" @click="handleUpdateTitle()">
内部动态修改标题
</VbenButton>
</div>
</Drawer>
</template>

View File

@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
import ExtraDrawer from './drawer.vue';
const [Drawer, drawerApi] = useVbenDrawer({
// 连接抽离的组件
connectedComponent: ExtraDrawer,
});
function open() {
drawerApi.open();
}
function handleUpdateTitle() {
drawerApi.setState({ title: '外部动态标题' });
drawerApi.open();
}
</script>
<template>
<div>
<Drawer />
<VbenButton @click="open">Open</VbenButton>
<VbenButton class="ml-2" type="primary" @click="handleUpdateTitle">
从外部修改标题并打开
</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,8 @@
<script lang="ts" setup>
import { useVbenDrawer } from '@vben/common-ui';
const [Drawer] = useVbenDrawer();
</script>
<template>
<Drawer title="组件抽离示例"> extra drawer content </Drawer>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
import ExtraDrawer from './drawer.vue';
const [Drawer, drawerApi] = useVbenDrawer({
// 连接抽离的组件
connectedComponent: ExtraDrawer,
});
function open() {
drawerApi.open();
}
</script>
<template>
<div>
<Drawer />
<VbenButton @click="open">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,26 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenDrawer } from '@vben/common-ui';
const data = ref();
const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onConfirm() {
console.info('onConfirm');
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
data.value = drawerApi.getData<Record<string, any>>();
}
},
});
</script>
<template>
<Drawer title="数据共享示例">
<div class="flex-col-center">外部传递数据 {{ data }}</div>
</Drawer>
</template>

View File

@@ -0,0 +1,26 @@
<script lang="ts" setup>
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
import ExtraDrawer from './drawer.vue';
const [Drawer, drawerApi] = useVbenDrawer({
// 连接抽离的组件
connectedComponent: ExtraDrawer,
});
function open() {
drawerApi.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
});
drawerApi.open();
}
</script>
<template>
<div>
<Drawer />
<VbenButton @click="open">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
import ExtraModal from './modal.vue';
const [Modal, modalApi] = useVbenModal({
// 连接抽离的组件
connectedComponent: ExtraModal,
});
function openModal() {
modalApi.open();
}
</script>
<template>
<div>
<Modal />
<VbenButton @click="openModal">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,45 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal, VbenButton } from '@vben/common-ui';
const list = ref<number[]>([]);
const [Modal, modalApi] = useVbenModal({
onCancel() {
modalApi.close();
},
onConfirm() {
console.log('onConfirm');
},
onOpenChange(isOpen) {
if (isOpen) {
handleUpdate(10);
}
},
});
function handleUpdate(len: number) {
modalApi.setState({ loading: true });
setTimeout(() => {
list.value = Array.from({ length: len }, (_v, k) => k + 1);
modalApi.setState({ loading: false });
}, 2000);
}
</script>
<template>
<Modal title="自动计算高度">
<div
v-for="item in list"
:key="item"
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
>
{{ item }}
</div>
<template #prepend-footer>
<VbenButton type="link" @click="handleUpdate(6)">
点击更新数据
</VbenButton>
</template>
</Modal>
</template>

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
const [Modal, modalApi] = useVbenModal();
</script>
<template>
<div>
<VbenButton @click="() => modalApi.open()">Open</VbenButton>
<Modal class="w-[600px]" title="基础示例"> modal content </Modal>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
import ExtraModal from './modal.vue';
const [Modal, modalApi] = useVbenModal({
// 连接抽离的组件
connectedComponent: ExtraModal,
});
function openModal() {
modalApi.open();
}
</script>
<template>
<div>
<Modal />
<VbenButton @click="openModal">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,10 @@
<script lang="ts" setup>
import { useVbenModal } from '@vben/common-ui';
const [Modal] = useVbenModal({
draggable: true,
});
</script>
<template>
<Modal title="拖拽示例"> modal content </Modal>
</template>

View File

@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
import ExtraModal from './modal.vue';
const [Modal, modalApi] = useVbenModal({
// 连接抽离的组件
connectedComponent: ExtraModal,
});
function openModal() {
modalApi.open();
}
function handleUpdateTitle() {
modalApi.setState({ title: '外部动态标题' });
modalApi.open();
}
</script>
<template>
<div>
<Modal />
<VbenButton @click="openModal">Open</VbenButton>
<VbenButton class="ml-2" type="primary" @click="handleUpdateTitle">
从外部修改标题并打开
</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
const [Modal, modalApi] = useVbenModal({
draggable: true,
onCancel() {
modalApi.close();
},
onConfirm() {
console.info('onConfirm');
},
title: '动态修改配置示例',
});
const state = modalApi.useStore();
function handleUpdateTitle() {
modalApi.setState({ title: '内部动态标题' });
}
function handleToggleFullscreen() {
modalApi.setState((prev) => {
return { ...prev, fullscreen: !prev.fullscreen };
});
}
</script>
<template>
<Modal>
<div class="flex-col-center">
<VbenButton class="mb-3" type="primary" @click="handleUpdateTitle()">
内部动态修改标题
</VbenButton>
<VbenButton class="mb-3" @click="handleToggleFullscreen()">
{{ state.fullscreen ? '退出全屏' : '打开全屏' }}
</VbenButton>
</div>
</Modal>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
import ExtraModal from './modal.vue';
const [Modal, modalApi] = useVbenModal({
// 连接抽离的组件
connectedComponent: ExtraModal,
});
function openModal() {
modalApi.open();
}
</script>
<template>
<div>
<Modal />
<VbenButton @click="openModal">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,8 @@
<script lang="ts" setup>
import { useVbenModal } from '@vben/common-ui';
const [Modal] = useVbenModal();
</script>
<template>
<Modal title="组件抽离示例"> extra modal content </Modal>
</template>

View File

@@ -0,0 +1,26 @@
<script lang="ts" setup>
import { useVbenModal, VbenButton } from '@vben/common-ui';
import ExtraModal from './modal.vue';
const [Modal, modalApi] = useVbenModal({
// 连接抽离的组件
connectedComponent: ExtraModal,
});
function openModal() {
modalApi.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
});
modalApi.open();
}
</script>
<template>
<div>
<Modal />
<VbenButton @click="openModal">Open</VbenButton>
</div>
</template>

View File

@@ -0,0 +1,26 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
const data = ref();
const [Modal, modalApi] = useVbenModal({
onCancel() {
modalApi.close();
},
onConfirm() {
console.info('onConfirm');
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
data.value = modalApi.getData<Record<string, any>>();
}
},
});
</script>
<template>
<Modal title="数据共享示例">
<div class="flex-col-center">外部传递数据 {{ data }}</div>
</Modal>
</template>

View File

@@ -0,0 +1,243 @@
# Build and Deployment
::: tip Preface
Since this is a demonstration project, the package size after building is relatively large. If there are plugins in the project that are not used, you can delete the corresponding files or routes. If they are not referenced, they will not be packaged.
:::
## Building
After the project development is completed, execute the following command to build:
**Note:** Please execute the following command in the project root directory.
```bash
pnpm build
```
After the build is successful, a `dist` folder for the corresponding application will be generated in the root directory, which contains the built and packaged files, for example: `apps/web-antd/dist/`
## Preview
Before publishing, you can preview it locally in several ways, here are two:
- Using the project's custom command for preview (recommended)
**Note** Please execute the following command in the project root directory.
```bash
pnpm preview
```
After waiting for the build to succeed, visit `http://localhost:4173` to view the effect.
- Local server preview
You can globally install a `serve` service on your computer, such as `live-server`,
```bash
npm i -g live-server
```
Then execute the `live-server` command in the `dist` directory to view the effect locally.
```bash
cd apps/web-antd/dist
# Local preview, default port 8080
live-server
# Specify port
live-server --port 9000
```
## Compression
### Enable `gzip` Compression
To enable during the build process, change the `.env.production` configuration:
```bash
VITE_COMPRESS=gzip
```
### Enable `brotli` Compression
To enable during the build process, change the `.env.production` configuration:
```bash
VITE_COMPRESS=brotli
```
### Enable Both `gzip` and `brotli` Compression
To enable during the build process, change the `.env.production` configuration:
```bash
VITE_COMPRESS=gzip,brotli
```
::: tip Note
Both `gzip` and `brotli` require specific modules to be installed for use.
:::
::: details gzip 与 brotli 在 nginx 内的配置
```bash
http {
# Enable gzip
gzip on;
# Enable gzip_static
# After enabling gzip_static, there might be errors, requiring the installation of specific modules. The installation method can be researched independently.
# Only with this enabled, the .gz files packaged by vue files will be effective; otherwise, there is no need to enable gzip for packaging.
gzip_static on;
gzip_proxied any;
gzip_min_length 1k;
gzip_buffers 4 16k;
# If nginx uses multiple layers of proxy, this must be set to enable gzip.
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
# Enable brotli compression
# Requires the installation of the corresponding nginx module, which can be researched independently.
# Can coexist with gzip without conflict.
brotli on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
}
```
:::
## Build Analysis
If your build files are large, you can optimize your code by analyzing the code size with the built-in [rollup-plugin-analyzer](https://github.com/doesdev/rollup-plugin-analyzer) plugin. Just execute the following command in the `root directory`:
```bash
pnpm run build:analyze
```
After running, you can see the specific distribution of sizes on the automatically opened page to analyze which dependencies are problematic.
![Build analysis report](/guide/report.png)
## Deployment
A simple deployment only requires publishing the final static files, the static files in the dist folder, to your CDN or static server. It's important to note that the index.html is usually the entry page for your backend service. After determining the static js and css, you may need to change the page's import path.
For example, to upload to an nginx server, you can upload the files under the dist folder to the server's `/srv/www/project/index.html` directory, and then access the configured domain name.
```bash
# nginx configuration
location / {
# Do not cache html to prevent cache from continuing to be effective after program updates
if ($request_filename ~* .*\.(?:htm|html)$) {
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
access_log on;
}
# This is the storage path for the files inside the vue packaged dist folder
root /srv/www/project/;
index index.html index.htm;
}
```
If you find the resource path is incorrect during deployment, you just need to modify the `.env.production` file.
```bash
# Configure the change according to your own path
# Note that it needs to start and end with /
VITE_BASE=/
VITE_BASE=/xxx/
```
### Integration of Frontend Routing and Server
The project uses vue-router for frontend routing, so you can choose between two modes: history and hash.
- `hash` mode will append `#` to the URL by default.
- `history` mode will not, but `history` mode requires server-side support.
You can modify the mode in `.env.production`:
```bash
VITE_ROUTER_HISTORY=hash
```
### Server Configuration for History Mode Routing
Enabling `history` mode requires server configuration. For more details on server configuration, see [history-mode](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode)
Here is an example of `nginx` configuration:
#### Deployment at the Root Directory
```bash {5}
server {
listen 80;
location / {
# For use with History mode
try_files $uri $uri/ /index.html;
}
}
```
#### Deployment to a Non-root Directory
- First, you need to change the `.env.production` configuration during packaging:
```bash
VITE_BASE = /sub/
```
- Then configure in the nginx configuration file
```bash {8}
server {
listen 80;
server_name localhost;
location /sub/ {
# This is the path where the vue packaged dist files are stored
alias /srv/www/project/;
index index.html index.htm;
try_files $uri $uri/ /sub/index.html;
}
}
```
## Cross-Domain Handling
Using nginx to handle cross-domain issues after project deployment
1. Configure the frontend project API address in the `.env.production` file in the project directory:
```bash
VITE_GLOB_API_URL=/api
```
2. Configure nginx to forward requests to the backend
```bash {10-11}
server {
listen 8080;
server_name localhost;
# API proxy for solving cross-domain issues
location /api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Backend API address
proxy_pass http://110.110.1.1:8080/api;
rewrite "^/api/(.*)$" /$1 break;
proxy_redirect default;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
}
}
```

View File

@@ -0,0 +1,70 @@
# Basic Concepts
In the new version, the entire project has been restructured. Now, we will introduce some basic concepts to help you better understand the entire document. Please make sure to read this section first.
## Monorepo
Monorepo refers to the repository of the entire project, which includes all code, packages, applications, standards, documentation, configurations, etc., that is, the entire content of a `Monorepo` directory.
## Applications
Applications refer to a complete project; a project can contain multiple applications, which can reuse the code, packages, standards, etc., within the monorepo. Applications are placed in the `apps` directory. Each application is independent and can be run, built, tested, and deployed separately; it can also include different component libraries, etc.
::: tip
Applications are not limited to front-end applications; they can also be back-end applications, mobile applications, etc. For example, `apps/backend-mock` is a back-end service.
:::
## Packages
A package refers to an independent module, which can be a component, a tool, a library, etc. Packages can be referenced by multiple applications or other packages. Packages are placed in the `packages` directory.
You can consider these packages as independent `npm` packages, and they are used in the same way as `npm` packages.
### Package Import
Importing a package in `package.json`:
```json {3}
{
"dependencies": {
"@vben/utils": "workspace:*"
}
}
```
### Package Usage
Importing a package in the code:
```ts
import { isString } from '@vben/utils';
```
## Aliases
In the project, you can see some paths starting with `#`, such as `#/api`, `#/views`. These paths are aliases, used for quickly locating a certain directory. They are not implemented through `vite`'s `alias`, but through the principle of [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) in `Node.js` itself. You only need to configure the `imports` field in `package.json`.
```json {3}
{
"imports": {
"#/*": "./src/*"
}
}
```
To make these aliases recognizable by the IDE, we also need to configure them in `tsconfig.json`:
```json {5}
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/*": ["src/*"]
}
}
}
```
This way, you can use aliases in your code.

View File

@@ -0,0 +1,184 @@
# Local Development {#development}
::: tip Code Acquisition
If you haven't acquired the code yet, you can start by reading the documentation from [Quick Start](../introduction/quick-start.md).
:::
## Prerequisites
For a better development experience, we provide some tool configurations and project descriptions to facilitate your development.
### Required Basic Knowledge
This project requires some basic frontend knowledge. Please ensure you are familiar with the basics of Vue to handle common issues. It is recommended to learn the following topics before development. Understanding these will be very helpful for the project:
- [Vue3](https://vuejs.org/)
- [Tailwind CSS](https://tailwindcss.com/)
- [TypeScript](https://www.typescriptlang.org/)
- [Vue Router](https://router.vuejs.org/)
- [Vitejs](https://vitejs.dev/)
- [Pnpm](https://pnpm.io/)
- [Turbo](https://turbo.build/)
### Tool Configuration
If you are using [vscode](https://code.visualstudio.com/) (recommended) as your IDE, you can install the following tools to improve development efficiency and code formatting:
- [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - Official Vue plugin (essential).
- [Tailwind CSS](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - Tailwind CSS autocomplete plugin.
- [CSS Variable Autocomplete](https://marketplace.visualstudio.com/items?itemName=vunguyentuan.vscode-css-variables) - CSS variable autocomplete plugin.
- [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - Iconify icon plugin.
- [i18n Ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) - i18n plugin.
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - Script code linting.
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - Code formatting.
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) - CSS formatting.
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - Spelling checker.
- [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) - .env file highlighting.
## Npm Scripts
Npm scripts are common configurations used in the project to perform common tasks such as starting the project, building the project, etc. The following scripts can be found in the `package.json` file at the root of the project.
The execution command is: `pnpm run [script]` or `npm run [script]`.
```json
{
"scripts": {
// Install dependencies
"bootstrap": "pnpm install",
// Build the project
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
// Build the project with analysis
"build:analyze": "turbo build:analyze",
// Build a local Docker image
"build:docker": "./build-local-docker-image.sh",
// Build the web-antd application separately
"build:antd": "pnpm run build --filter=@vben/web-antd",
// Build the documentation separately
"build:docs": "pnpm run build --filter=@vben/docs",
// Build the web-ele application separately
"build:ele": "pnpm run build --filter=@vben/web-ele",
// Build the web-naive application separately
"build:naive": "pnpm run build --filter=@vben/naive",
// Build the playground application separately
"build:play": "pnpm run build --filter=@vben/playground",
// Changeset version management
"changeset": "pnpm exec changeset",
// Check for various issues in the project
"check": "pnpm run check:circular && pnpm run check:dep && pnpm run check:type && pnpm check:cspell",
// Check for circular dependencies
"check:circular": "vsh check-circular",
// Check spelling
"check:cspell": "cspell lint **/*.ts **/README.md .changeset/*.md --no-progress"
// Check dependencies
"check:dep": "vsh check-dep",
// Check types
"check:type": "turbo run typecheck",
// Clean the project (delete node_modules, dist, .turbo, etc.)
"clean": "vsh clean",
// Commit code
"commit": "czg",
// Start the project (by default, the dev scripts of all packages in the entire repository will run)
"dev": "turbo-run dev",
// Start the web-antd application
"dev:antd": "pnpm -F @vben/web-antd run dev",
// Start the documentation
"dev:docs": "pnpm -F @vben/docs run dev",
// Start the web-ele application
"dev:ele": "pnpm -F @vben/web-ele run dev",
// Start the web-naive application
"dev:naive": "pnpm -F @vben/web-naive run dev",
// Start the playground application
"dev:play": "pnpm -F @vben/playground run dev",
// Format code
"format": "vsh lint --format",
// Lint code
"lint": "vsh lint",
// After installing dependencies, execute the stub script for all packages
"postinstall": "turbo run stub",
// Only allow using pnpm
"preinstall": "npx only-allow pnpm",
// Install husky
"prepare": "is-ci || husky",
// Preview the application
"preview": "turbo-run preview",
// Package specification check
"publint": "vsh publint",
// Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
// Run vitest unit tests
"test:unit": "vitest",
// Update project dependencies
"update:deps": " pnpm update --latest --recursive",
// Changeset generation and versioning
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
}
}
```
## Running the Project Locally
To run the documentation locally and make adjustments, you can execute the following command. This command allows you to select the application you want to develop:
```bash
pnpm dev
```
If you want to run a specific application directly, you can execute the following commands:
To run the `web-antd` application:
```bash
pnpm dev:antd
```
To run the `web-naive` application:
```bash
pnpm dev:naive
```
To run the `web-ele` application:
```bash
pnpm dev:ele
```
To run the `docs` application:
```bash
pnpm dev:docs
```
## DevTools
The project has a built-in [Vue DevTools](https://github.com/vuejs/devtools-next) plugin, which can be used during development. It is disabled by default, but can be enabled in the `.env.development` file. After enabling it, restart the project:
```bash
VITE_DEVTOOLS=true
```
Once enabled, a Vue DevTools icon will appear at the bottom of the page during project runtime. Click it to open the DevTools.
![Vue DevTools](/guide/devtools.png)
## Running Documentation Locally
To run the documentation locally and make adjustments, you can execute the following command:
```bash
pnpm dev:docs
```
## Troubleshooting
If you encounter dependency-related issues, you can try reinstalling the dependencies:
```bash
# Execute this command at the root of the project.
# This command will delete all node_modules, yarn.lock, and package.lock.json files
# and then reinstall dependencies (this process will be noticeably slower).
pnpm reinstall
```

View File

@@ -0,0 +1,58 @@
# External Modules
In addition to the external modules that are included by default in the project, sometimes we need to import other external modules. Let's take [ant-design-vue](https://antdv.com/components/overview) as an example:
## Installing Dependencies
::: tip Install dependencies into a specific package
- Since the project uses [pnpm](https://pnpm.io/) as the package management tool, we need to use the `pnpm` command to install dependencies.
- As the project is managed using a Monorepo module, we need to install dependencies under a specific package. Please make sure you have entered the specific package directory before installing dependencies.
:::
```bash
# cd /path/to/your/package
pnpm add ant-design-vue
```
## Usage
### Global Import
```ts
import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import App from './App';
import 'ant-design-vue/dist/reset.css';
const app = createApp(App);
app.use(Antd).mount('#app');
```
#### Usage
```vue
<template>
<a-button>text</a-button>
</template>
```
### Partial Import
```vue
<script setup lang="ts">
import { Button } from 'ant-design-vue';
</script>
<template>
<Button>text</Button>
</template>
```
::: warning Note
- If the component depends on styles, you also need to import the style file.
:::

View File

@@ -0,0 +1,78 @@
# Icons
::: tip About Icon Management
- The icons in the project are mainly provided by the `@vben/icons` package. It is recommended to manage them within this package for unified management and maintenance.
- If you are using `Vscode`, it is recommended to install the [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) plugin, which makes it easy to find and use icons.
:::
There are several ways to use icons in the project, you can choose according to the actual situation:
## Iconify Icons <Badge text="Recommended" type="tip"/>
Integrated with the [iconify](https://github.com/iconify/iconify) icon library
### Adding New Icons
You can add new icons in the `packages/icons/src/iconify` directory:
```ts
// packages/icons/src/iconify/index.ts
import { createIconifyIcon } from '@vben-core/icons';
export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc');
```
### Usage
```vue
<script setup lang="ts">
import { MdiKeyboardEsc } from '@vben/icons';
</script>
<template>
<!-- An icon with a width and height of 20px -->
<MdiKeyboardEsc class="size-5" />
</template>
```
## SVG Icons <Badge text="Recommended" type="tip"/>
Instead of using Svg Sprite, SVG icons are directly imported,
### Adding New Icons
You can add new icon files `test.svg` in the `packages/icons/src/svg/icons` directory, and then import it in `packages/icons/src/svg/index.ts`:
```ts
// packages/icons/src/svg/index.ts
import { createIconifyIcon } from '@vben-core/icons';
const SvgTestIcon = createIconifyIcon('svg:test');
export { SvgTestIcon };
```
### Usage
```vue
<script setup lang="ts">
import { SvgTestIcon } from '@vben/icons';
</script>
<template>
<!-- An icon with a width and height of 20px -->
<SvgTestIcon class="size-5" />
</template>
```
## Tailwind CSS Icons <Badge text="Not Recommended" type="danger"/>
### Usage
You can use the icons by directly adding the Tailwind CSS icon class names, which can be found on [iconify](https://github.com/iconify/iconify)
```vue
<span class="icon-[mdi--ab-testing]"></span>
```

View File

@@ -0,0 +1,560 @@
---
outline: deep
---
# Routing and Menus
In the project, the framework provides a basic routing system and **automatically generates the corresponding menu structure based on the routing file**.
## Route Types
Routes are divided into static routes and dynamic routes. Static routes are routes that have been determined when the project starts. Dynamic routes are generally routes that are dynamically generated based on the user's permissions after the user logs in.
### Static Routes
If your page project does not require permission control, you can directly use static routes. The configuration of static routes is in the `src/router/routes/index` directory under the application. Open the commented file content:
```ts
// If necessary, you can open your own comments and create folders
// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true }); // [!code --]
const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); // [!code ++]
/** Dynamic routing */
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
/** External routing lists, which can be accessed without Layout, may be used for embedding in other systems */
// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles) // [!code --]
const externalRoutes: RouteRecordRaw[] = []; // [!code --]
const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles); // [!code ++]
```
### Dynamic routing
The configuration of dynamic routing is in the corresponding application `src/router/routes/modules` directory. All routing files are stored in this directory. The content format of each file is as follows, which is consistent with the routing configuration format of Vue Router. The following is the configuration of secondary routes and multi-level routes.
## Define the route
Static routes and dynamic routes are configured in the same way. The configuration of the level-2 and multi-level routes is as follows:
### Secondary route
::: details Example code of the secondary route
```ts
import type { RouteRecordRaw } from 'vue-router';
import { VBEN_LOGO_URL } from '@vben/constants';
import { BasicLayout } from '#/layouts';
import { $t } from '#/locales';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
badgeType: 'dot',
badgeVariants: 'destructive',
icon: VBEN_LOGO_URL,
order: 9999,
title: $t('page.vben.title'),
},
name: 'VbenProject',
path: '/vben-admin',
redirect: '/vben-admin/about',
children: [
{
name: 'VbenAbout',
path: '/vben-admin/about',
component: () => import('#/views/_core/about/index.vue'),
meta: {
badgeType: 'dot',
badgeVariants: 'destructive',
icon: 'lucide:copyright',
title: $t('page.vben.about'),
},
},
],
},
];
export default routes;
```
:::
### Multilevel routing
::: tip
- The parent route of multi-level routing does not need to set the 'component' attribute, only the 'children' attribute needs to be set. Unless you really need to display content under nested parent routing.
- If there are no special circumstances, the 'redirect' attribute of the parent route does not need to be specified and will default to the first child route.
:::
::: details Multilevel Routing Example Code
```ts
import type { RouteRecordRaw } from 'vue-router';
import { BasicLayout } from '#/layouts';
import { $t } from '#/locales';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'ic:baseline-view-in-ar',
keepAlive: true,
order: 1000,
title: $t('page.demos.title'),
},
name: 'Demos',
path: '/demos',
redirect: '/demos/access',
children: [
// 嵌套菜单
{
meta: {
icon: 'ic:round-menu',
title: $t('page.demos.nested.title'),
},
name: 'NestedDemos',
path: '/demos/nested',
redirect: '/demos/nested/menu1',
children: [
{
name: 'Menu1Demo',
path: '/demos/nested/menu1',
component: () => import('#/views/demos/nested/menu-1.vue'),
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu1'),
},
},
{
name: 'Menu2Demo',
path: '/demos/nested/menu2',
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu2'),
},
redirect: '/demos/nested/menu2/menu2-1',
children: [
{
name: 'Menu21Demo',
path: '/demos/nested/menu2/menu2-1',
component: () => import('#/views/demos/nested/menu-2-1.vue'),
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu2_1'),
},
},
],
},
{
name: 'Menu3Demo',
path: '/demos/nested/menu3',
meta: {
icon: 'ic:round-menu',
title: $t('page.demos.nested.menu3'),
},
redirect: '/demos/nested/menu3/menu3-1',
children: [
{
name: 'Menu31Demo',
path: 'menu3-1',
component: () => import('#/views/demos/nested/menu-3-1.vue'),
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu3_1'),
},
},
{
name: 'Menu32Demo',
path: 'menu3-2',
meta: {
icon: 'ic:round-menu',
title: $t('page.demos.nested.menu3_2'),
},
redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
children: [
{
name: 'Menu321Demo',
path: '/demos/nested/menu3/menu3-2/menu3-2-1',
component: () =>
import('#/views/demos/nested/menu-3-2-1.vue'),
meta: {
icon: 'ic:round-menu',
keepAlive: true,
title: $t('page.demos.nested.menu3_2_1'),
},
},
],
},
],
},
],
},
],
},
];
export default routes;
```
:::
## Add a New Page
To add a new page, you only need to add a route and the corresponding page component.
### Add a Route
Add a route object in the corresponding routing file as follows:
```ts
import type { RouteRecordRaw } from 'vue-router';
import { VBEN_LOGO_URL } from '@vben/constants';
import { BasicLayout } from '#/layouts';
import { $t } from '#/locales';
const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
meta: {
icon: 'mdi:home',
title: $t('page.home.title'),
},
name: 'Home',
path: '/home',
redirect: '/home/index',
children: [
{
name: 'HomeIndex',
path: '/home/index',
component: () => import('#/views/home/index.vue'),
meta: {
icon: 'mdi:home',
title: $t('page.home.index'),
},
},
],
},
];
export default routes;
```
### Add Page Component
In `#/views/home/`, add a new `index.vue` file as follows:
```vue
<template>
<div>
<h1>home page</h1>
</div>
</template>
```
### Verification
At this point, the page has been added. Access `http://localhost:5555/home/index` to see the corresponding page.
## Route Configuration
The route configuration mainly resides in the `meta` attribute of the route object. Below are some commonly used configuration items
```ts {5-8}
const routes = [
{
name: 'HomeIndex',
path: '/home/index',
meta: {
icon: 'mdi:home',
title: $t('page.home.index'),
},
},
];
```
::: details Route Meta Configuration Type Definition
```ts
interface RouteMeta {
/**
* Active icon (menu)
*/
activeIcon?: string;
/**
* The currently active menu, used when you want to activate a parent menu instead of the existing one
* @default false
*/
activePath?: string;
/**
* Whether to affix the tab
* @default false
*/
affixTab?: boolean;
/**
* The order of the affixed tab
* @default 0
*/
affixTabOrder?: number;
/**
* Specific role identifiers required for access
* @default []
*/
authority?: string[];
/**
* Badge
*/
badge?: string;
/**
* Badge type
*/
badgeType?: 'dot' | 'normal';
/**
* Badge color
*/
badgeVariants?:
| 'default'
| 'destructive'
| 'primary'
| 'success'
| 'warning'
| string;
/**
* Children of the current route do not show in the menu
* @default false
*/
hideChildrenInMenu?: boolean;
/**
* The current route does not show in the breadcrumb
* @default false
*/
hideInBreadcrumb?: boolean;
/**
* The current route does not show in the menu
* @default false
*/
hideInMenu?: boolean;
/**
* The current route does not show in tabs
* @default false
*/
hideInTab?: boolean;
/**
* Icon (menu/tab)
*/
icon?: string;
/**
* iframe address
*/
iframeSrc?: string;
/**
* Ignore access, can be accessed directly
* @default false
*/
ignoreAccess?: boolean;
/**
* Enable KeepAlive caching
*/
keepAlive?: boolean;
/**
* External link - redirect path
*/
link?: string;
/**
* Whether the route has been loaded
*/
loaded?: boolean;
/**
* Maximum number of open tabs
* @default false
*/
maxNumOfOpenTab?: number;
/**
* The menu is visible, but access will be redirected to 403
*/
menuVisibleWithForbidden?: boolean;
/**
* Used for route->menu sorting
*/
order?: number;
/**
* Title name
*/
title: string;
}
```
:::
### title
- Type: `string`
- Default value: `''`
Used to configure the page title, which will be displayed in the menu and tabs. It is generally used in conjunction with internationalization.
### icon
- Type: `string`
- Default value: `''`
Used to configure the page icon, which will be displayed in the menu and tabs. It is generally used in conjunction with an icon library. If it is an `http` link, the image will be automatically loaded.
### activeIcon
- Type: `string`
- Default value: `''`
Used to configure the active icon of the page, which will be displayed in the menu. It is generally used in conjunction with an icon library. If it is an `http` link, the image will be automatically loaded.
### keepAlive
- Type: `boolean`
- Default value: `false`
Used to configure whether the page caching is enabled. Once enabled, the page will be cached and not reloaded, only effective when tabs are enabled.
### hideInMenu
- Type: `boolean`
- Default value: `false`
Used to configure whether the page is hidden in the menu. If hidden, the page will not be displayed in the menu.
### hideInTab
- Type: `boolean`
- Default value: `false`
Used to configure whether the page is hidden in tabs. If hidden, the page will not be displayed in tabs.
### hideInBreadcrumb
- Type: `boolean`
- Default value: `false`
Used to configure whether the page is hidden in the breadcrumb. If hidden, the page will not be displayed in the breadcrumb.
### hideChildrenInMenu
- Type: `boolean`
- Default value: `false`
Used to configure whether the child pages of the page are hidden in the menu. If hidden, the child pages will not be displayed in the menu.
### authority
- Type: `string[]`
- Default value: `[]`
Used to configure the page's permissions. Only users with corresponding permissions can access the page. If not configured, no permissions are required.
### badge
- Type: `string`
- Default value: `''`
Used to configure the page's badge, which will be displayed in the menu.
### badgeType
- Type: `'dot' | 'normal'`
- Default value: `'normal'`
Used to configure the type of the page's badge. `dot` is a small red dot, `normal` is text.
### badgeVariants
- Type: `'default' | 'destructive' | 'primary' | 'success' | 'warning' | string`
- Default value: `'success'`
Used to configure the color of the page's badge.
### activePath
- Type: `string`
- Default value: `''`
Used to configure the currently active menu. Sometimes when the page is not displayed in the menu, it is used to activate the parent menu.
### affixTab
- Type: `boolean`
- Default value: `false`
Used to configure whether the page tab is pinned. Once pinned, the page cannot be closed.
### affixTabOrder
- Type: `number`
- Default value: `0`
Used to configure the order of the pinned page tabs, sorted in ascending order.
### iframeSrc
- Type: `string`
- Default value: `''`
Used to configure the `iframe` address of the embedded page. Once set, the corresponding page will be embedded in the current page.
### ignoreAccess
- Type: `boolean`
- Default value: `false`
Used to configure whether the page ignores permissions and can be accessed directly.
### link
- Type: `string`
- Default value: `''`
Used to configure the external link jump path, which will be opened in a new window.
### maxNumOfOpenTab
- Type: `number`
- Default value: `-1`
Used to configure the maximum number of open tabs. Once set, the earliest opened tab will be automatically closed when a new tab is opened (only effective when opening tabs with the same name).
### menuVisibleWithForbidden
- Type: `boolean`
- Default value: `false`
Used to configure whether the page can be seen in the menu, but access will be redirected to 403.
### order
- Type: `number`
- Default value: `0`
Used to configure the page's order, for routing to menu sorting.
## Route Refresh
The way to refresh the route is as follows:
```vue
<script setup lang="ts">
import { useRefresh } from '@vben/hooks';
const { refresh } = useRefresh();
// Refresh the current route
refresh();
</script>
```

View File

@@ -0,0 +1,347 @@
# Server Interaction and Data Mocking
::: tip Note
This document explains how to use Mock data and interact with the server in a development environment, involving technologies such as:
- [Nitro](https://nitro.unjs.io/) A lightweight backend server that can be deployed anywhere, used as a Mock server in the project.
- [axios](https://axios-http.com/docs/intro) Used to send HTTP requests to interact with the server.
:::
## Interaction in Development Environment
If the frontend application and the backend API server are not running on the same host, you need to proxy the API requests to the API server in the development environment. If they are on the same host, you can directly request the specific API endpoint.
### Local Development CORS Configuration
::: tip Hint
The CORS configuration for local development has already been set up. If you have other requirements, you can add or adjust the configuration as needed.
:::
#### Configuring Local Development API Endpoint
Configure the API endpoint in the `.env.development` file at the project root directory, here it is set to `/api`:
```bash
VITE_GLOB_API_URL=/api
```
#### Configuring Development Server Proxy
In the development environment, if you need to handle CORS, configure the API endpoint in the `vite.config.mts` file under the corresponding application directory:
```ts{8-16}
// apps/web-antd/vite.config.mts
import { defineConfig } from '@vben/vite-config';
export default defineConfig(async () => {
return {
vite: {
server: {
proxy: {// [!code focus:11]
'/api': {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
// mock proxy
target: 'http://localhost:5320/api',
ws: true,
},
},
},
},
};
});
```
#### API Requests
Based on the above configuration, we can use `/api` as the prefix for API requests in our frontend project, for example:
```ts
import axios from 'axios';
axios.get('/api/user').then((res) => {
console.log(res);
});
```
At this point, the request will be proxied to `http://localhost:5320/api/user`.
::: warning Note
From the browser's console Network tab, the request appears as `http://localhost:5555/api/user`. This is because the proxy configuration does not change the local request's URL.
:::
### Configuration Without CORS
If there is no CORS issue, you can directly ignore the [Configure Development Server Proxy](./server.md#configure-development-server-proxy) settings and set the API endpoint directly in `VITE_GLOB_API_URL`.
Configure the API endpoint in the `.env.development` file at the project root directory:
```bash
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
```
## Production Environment Interaction
### API Endpoint Configuration
Configure the API endpoint in the `.env.production` file at the project root directory:
```bash
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
```
::: tip How to Dynamically Modify API Endpoint in Production
Variables starting with `VITE_GLOB_*` in the `.env` file are injected into the `_app.config.js` file during packaging. After packaging, you can modify the corresponding API addresses in `dist/_app.config.js` and refresh the page to apply the changes. This eliminates the need to package multiple times for different environments, allowing a single package to be deployed across multiple API environments.
:::
### Cross-Origin Resource Sharing (CORS) Handling
In the production environment, if CORS issues arise, you can use `nginx` to proxy the API address or enable `cors` on the backend to handle it (refer to the mock service for examples).
## API Request Configuration
The project comes with a default basic request configuration based on `axios`, provided by the `@vben/request` package. The project does not overly complicate things but simply wraps some common configurations. If there are other requirements, you can add or adjust the configurations as needed. Depending on the app, different component libraries and `store` might be used, so under the `src/api/request.ts` folder in the application directory, there are corresponding request configuration files. For example, in the `web-antd` project, there's a `src/api/request.ts` file where you can configure according to your needs.
### Request Examples
#### GET Request
```ts
import { requestClient } from '#/api/request';
export async function getUserInfoApi() {
return requestClient.get<UserInfo>('/user/info');
}
```
#### POST/PUT Request
```ts
import { requestClient } from '#/api/request';
export async function saveUserApi(user: UserInfo) {
return requestClient.post<UserInfo>('/user', user);
}
export async function saveUserApi(user: UserInfo) {
return requestClient.put<UserInfo>('/user', user);
}
export async function saveUserApi(user: UserInfo) {
const url = user.id ? `/user/${user.id}` : '/user/';
return requestClient.request<UserInfo>(url, {
data: user,
// OR PUT
method: user.id ? 'PUT' : 'POST',
});
}
```
#### DELETE Request
```ts
import { requestClient } from '#/api/request';
export async function deleteUserApi(user: UserInfo) {
return requestClient.delete<boolean>(`/user/${user.id}`, user);
}
```
### Request Configuration
The `src/api/request.ts` within the application can be configured according to the needs of your application:
```ts
/**
* This file can be adjusted according to business logic
*/
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
import {
authenticateResponseInterceptor,
errorMessageResponseInterceptor,
RequestClient,
} from '@vben/request';
import { useAccessStore } from '@vben/stores';
import { message } from 'ant-design-vue';
import { useAuthStore } from '#/store';
import { refreshTokenApi } from './core';
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
function createRequestClient(baseURL: string) {
const client = new RequestClient({
baseURL,
});
/**
* Re-authentication Logic
*/
async function doReAuthenticate() {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (preferences.app.loginExpiredMode === 'modal') {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
}
}
/**
* Refresh token Logic
*/
async function doRefreshToken() {
const accessStore = useAccessStore();
const resp = await refreshTokenApi();
const newToken = resp.data;
accessStore.setAccessToken(newToken);
return newToken;
}
function formatToken(token: null | string) {
return token ? `Bearer ${token}` : null;
}
// Request Header Processing
client.addRequestInterceptor({
fulfilled: async (config) => {
const accessStore = useAccessStore();
config.headers.Authorization = formatToken(accessStore.accessToken);
config.headers['Accept-Language'] = preferences.app.locale;
return config;
},
});
// Deal Response Data
client.addResponseInterceptor({
fulfilled: (response) => {
const { data: responseData, status } = response;
const { code, data, message: msg } = responseData;
if (status >= 200 && status < 400 && code === 0) {
return data;
}
throw new Error(`Error ${status}: ${msg}`);
},
});
// Handling Token Expiration
client.addResponseInterceptor(
authenticateResponseInterceptor({
client,
doReAuthenticate,
doRefreshToken,
enableRefreshToken: preferences.app.enableRefreshToken,
formatToken,
}),
);
// Generic error handling; if none of the above error handling logic is triggered, it will fall back to this.
client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
);
return client;
}
export const requestClient = createRequestClient(apiURL);
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
```
### Multiple API Endpoints
To handle multiple API endpoints, simply create multiple `requestClient` instances, as follows:
```ts
const { apiURL, otherApiURL } = useAppConfig(
import.meta.env,
import.meta.env.PROD,
);
export const requestClient = createRequestClient(apiURL);
export const otherRequestClient = createRequestClient(otherApiURL);
```
## Refresh Token
The project provides a default logic for refreshing tokens. To enable it, follow the configuration below:
- Ensure the refresh token feature is enabled
Adjust the `preferences.ts` in the corresponding application directory to ensure `enableRefreshToken='true'`.
```ts
import { defineOverridesPreferences } from '@vben/preferences';
export const overridesPreferences = defineOverridesPreferences({
// overrides
app: {
enableRefreshToken: true,
},
});
```
Configure the `doRefreshToken` method in `src/api/request.ts` as follows:
```ts
// Adjust this to your token format
function formatToken(token: null | string) {
return token ? `Bearer ${token}` : null;
}
/**
* Refresh token logic
*/
async function doRefreshToken() {
const accessStore = useAccessStore();
// Adjust this to your refresh token API
const resp = await refreshTokenApi();
const newToken = resp.data;
accessStore.setAccessToken(newToken);
return newToken;
}
```
## Data Mocking
::: tip Production Environment Mock
The new version no longer supports mock in the production environment. Please use real interfaces.
:::
Mock data is an indispensable part of frontend development, serving as a key link in separating frontend and backend development. By agreeing on interfaces with the server side in advance and simulating request data and even logic, frontend development can proceed independently, without being blocked by the backend development process.
The project uses [Nitro](https://nitro.unjs.io/) for local mock data processing. The principle is to start an additional backend service locally, which is a real backend service that can handle requests and return data.
### Using Nitro
The mock service code is located in the `apps/backend-mock` directory. It does not need to be started manually and is already integrated into the project. You only need to run `pnpm dev` in the project root directory. After running successfully, the console will print `http://localhost:5320/api`, and you can access this address to view the mock service.
[Nitro](https://nitro.unjs.io/) syntax is simple, and you can configure and develop according to your needs. For specific configurations, you can refer to the [Nitro documentation](https://nitro.unjs.io/).
## Disabling Mock Service
Since mock is essentially a real backend service, if you do not need the mock service, you can configure `VITE_NITRO_MOCK=false` in the `.env.development` file in the project root directory to disable the mock service.
```bash
# .env.development
VITE_NITRO_MOCK=false
```

View File

@@ -0,0 +1,514 @@
# Configuration
## Environment Variable Configuration
The project's environment variable configuration is located in the application directory under `.env`, `.env.development`, `.env.production`.
The rules are consistent with [Vite Env Variables and Modes](https://vitejs.dev/guide/env-and-mode.html). The format is as follows:
```bash
.env # Loaded in all environments
.env.local # Loaded in all environments, but ignored by git
.env.[mode] # Only loaded in the specified mode
.env.[mode].local # Only loaded in the specified mode, but ignored by git
```
::: tip
- Only variables starting with `VITE_` will be embedded into the client-side package. You can access them in the project code like this:
```ts
console.log(import.meta.env.VITE_PROT);
```
- Variables starting with `VITE_GLOB_*` will be added to the `_app.config.js` configuration file during packaging. :::
:::
## Environment Configuration Description
::: code-group
```bash [.env]
# Application title
VITE_APP_TITLE=Vben Admin
# Application namespace, used as a prefix for caching, store, etc., to ensure isolation
VITE_APP_NAMESPACE=vben-web-antd
```
```bash [.env.development]
# Port Number
VITE_PORT=5555
# Public Path for Resources, must start and end with /
VITE_BASE=/
# API URL
VITE_GLOB_API_URL=/api
# Whether to enable Nitro Mock service, true to enable, false to disable
VITE_NITRO_MOCK=true
# Whether to open devtools, true to open, false to close
VITE_DEVTOOLS=true
# Whether to inject global loading
VITE_INJECT_APP_LOADING=true
# Whether to generate after packaging dist.zip
VITE_ARCHIVER=true
```
:::
## Dynamic Configuration in Production Environment
When executing `pnpm build` in the root directory of the monorepo, a `dist/_app.config.js` file will be automatically generated in the corresponding application and inserted into `index.html`.
`_app.config.js` is a dynamic configuration file that allows for modifications to the configuration dynamically based on different environments after the project has been built. The content is as follows:
```ts
window._VBEN_ADMIN_PRO_APP_CONF_ = {
VITE_GLOB_API_URL: 'https://mock-napi.vben.pro/api',
};
Object.freeze(window._VBEN_ADMIN_PRO_APP_CONF_);
Object.defineProperty(window, '_VBEN_ADMIN_PRO_APP_CONF_', {
configurable: false,
writable: false,
});
```
### Purpose
`_app.config.js` is used for projects that need to dynamically modify configurations after packaging, such as API endpoints. There's no need to repackage; you can simply modify the variables in `/dist/_app.config.js` after packaging, and refresh to update the variables in the code. A `js` file is used to ensure that the configuration file is loaded early in the order.
### Usage
To access the variables inside `_app.config.js`, you need to use the `useAppConfig` method provided by `@vben/hooks`.
```ts
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
```
### Adding New
To add a new dynamically modifiable configuration item, simply follow the steps below:
- First, add the variable that needs to be dynamically configurable in the `.env` file or the corresponding development environment configuration file. The variable must start with `VITE_GLOB_*`, for example:
```bash
VITE_GLOB_OTHER_API_URL=https://mock-napi.vben.pro/other-api
```
- In `packages/types/global.d.ts`, add the corresponding type definition, such as:
```ts
export interface VbenAdminProAppConfigRaw {
VITE_GLOB_API_URL: string;
VITE_GLOB_OTHER_API_URL: string; // [!code ++]
}
export interface ApplicationConfig {
apiURL: string;
otherApiURL: string; // [!code ++]
}
```
At this point, you can use the `useAppConfig` method within the project to access the newly added configuration item.
```ts
const { otherApiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
```
::: warning Warning
The `useAppConfig` method should only be used within the application and not be coupled with the internals of a package. The reason for passing `import.meta.env` and `import.meta.env.PROD` is to avoid such coupling. A pure package should avoid using variables specific to a particular build tool.
:::
## Preferences
The project offers a wide range of preference settings for dynamically configuring various features of the project:
![](/guide/preferences.png)
If you cannot find documentation for a setting, you can try configuring it yourself and then click `Copy Preferences` to override the project defaults. The configuration file is located in the application directory under `preferences.ts`, where you can override the framework's default configurations to achieve custom settings.
```ts
import { useAppConfig } from '@vben/hooks';
import { defineOverridesPreferences } from '@vben/preferences';
/**
* @description Project configuration file
* Only a part of the configuration in the project needs to be covered, and unnecessary configurations do not need to be covered. The default configuration will be automatically used
*/
export const overridesPreferences = defineOverridesPreferences({
// overrides
});
```
### Framework default configuration
::: details View the default configuration of the framework
```ts
const defaultPreferences: Preferences = {
app: {
accessMode: 'frontend',
authPageLayout: 'panel-right',
checkUpdatesInterval: 1,
colorGrayMode: false,
colorWeakMode: false,
compact: false,
contentCompact: 'wide',
defaultAvatar:
'https://unpkg.com/@vbenjs/static-source@0.1.6/source/avatar-v1.webp',
dynamicTitle: true,
enableCheckUpdates: true,
enablePreferences: true,
enableRefreshToken: false,
isMobile: false,
layout: 'sidebar-nav',
locale: 'zh-CN',
loginExpiredMode: 'modal',
name: 'Vben Admin',
preferencesButtonPosition: 'auto',
watermark: false,
},
breadcrumb: {
enable: true,
hideOnlyOne: false,
showHome: false,
showIcon: true,
styleType: 'normal',
},
copyright: {
companyName: 'Vben',
companySiteLink: 'https://www.vben.pro',
date: '2024',
enable: true,
icp: '',
icpLink: '',
},
footer: {
enable: true,
fixed: false,
},
header: {
enable: true,
hidden: false,
mode: 'fixed',
},
logo: {
enable: true,
source: 'https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp',
},
navigation: {
accordion: true,
split: true,
styleType: 'rounded',
},
shortcutKeys: {
enable: true,
globalLockScreen: true,
globalLogout: true,
globalPreferences: true,
globalSearch: true,
},
sidebar: {
collapsed: false,
collapsedShowTitle: false,
enable: true,
expandOnHover: true,
extraCollapse: true,
hidden: false,
width: 230,
},
tabbar: {
dragable: true,
enable: true,
height: 36,
keepAlive: true,
persist: true,
showIcon: true,
showMaximize: true,
showMore: true,
showRefresh: true,
styleType: 'chrome',
},
theme: {
builtinType: 'default',
colorDestructive: 'hsl(348 100% 61%)',
colorPrimary: 'hsl(212 100% 45%)',
colorSuccess: 'hsl(144 57% 58%)',
colorWarning: 'hsl(42 84% 61%)',
mode: 'dark',
radius: '0.5',
semiDarkHeader: false,
semiDarkSidebar: true,
},
transition: {
enable: true,
loading: true,
name: 'fade-slide',
progress: true,
},
widget: {
fullscreen: true,
globalSearch: true,
languageToggle: true,
lockScreen: true,
notification: true,
sidebarToggle: true,
themeToggle: true,
},
};
```
:::
::: details View the default configuration type of the framework
```ts
interface AppPreferences {
/** Permission mode */
accessMode: AccessModeType;
/** Layout of the login/registration page */
authPageLayout: AuthPageLayoutType;
/** Interval for checking updates */
checkUpdatesInterval: number;
/** Whether to enable gray mode */
colorGrayMode: boolean;
/** Whether to enable color weakness mode */
colorWeakMode: boolean;
/** Whether to enable compact mode */
compact: boolean;
/** Whether to enable content compact mode */
contentCompact: ContentCompactType;
// /** Default application avatar */
defaultAvatar: string;
// /** Enable dynamic title */
dynamicTitle: boolean;
/** Whether to enable update checks */
enableCheckUpdates: boolean;
/** Whether to display preferences */
enablePreferences: boolean;
/**
* @zh_CN Whether to enable refreshToken
*/
enableRefreshToken: boolean;
/** Whether it's mobile */
isMobile: boolean;
/** Layout method */
layout: LayoutType;
/** Supported languages */
locale: SupportedLanguagesType;
/** Login expiration mode */
loginExpiredMode: LoginExpiredModeType;
/** Application name */
name: string;
/** Position of the preferences button */
preferencesButtonPosition: PreferencesButtonPositionType;
/**
* @zh_CN Whether to enable watermark
*/
watermark: boolean;
}
interface BreadcrumbPreferences {
/** Whether breadcrumbs are enabled */
enable: boolean;
/** Whether to hide breadcrumbs when there is only one */
hideOnlyOne: boolean;
/** Whether the home icon in breadcrumbs is visible */
showHome: boolean;
/** Whether the icon in breadcrumbs is visible */
showIcon: boolean;
/** Breadcrumb style */
styleType: BreadcrumbStyleType;
}
interface CopyrightPreferences {
/** Copyright company name */
companyName: string;
/** Link to the copyright company's site */
companySiteLink: string;
/** Copyright date */
date: string;
/** Whether copyright is visible */
enable: boolean;
/** ICP number */
icp: string;
/** Link to the ICP */
icpLink: string;
}
interface FooterPreferences {
/** Whether the footer is visible */
enable: boolean;
/** Whether the footer is fixed */
fixed: boolean;
}
interface HeaderPreferences {
/** Whether the header is enabled */
enable: boolean;
/** Whether the header is hidden, css-hidden */
hidden: boolean;
/** Header display mode */
mode: LayoutHeaderModeType;
}
interface LogoPreferences {
/** Whether the logo is visible */
enable: boolean;
/** Logo URL */
source: string;
}
interface NavigationPreferences {
/** Navigation menu accordion mode */
accordion: boolean;
/** Whether the navigation menu is split, only effective in layout=mixed-nav */
split: boolean;
/** Navigation menu style */
styleType: NavigationStyleType;
}
interface SidebarPreferences {
/** Whether the sidebar is collapsed */
collapsed: boolean;
/** Whether to show title when sidebar is collapsed */
collapsedShowTitle: boolean;
/** Whether the sidebar is visible */
enable: boolean;
/** Menu auto-expand state */
expandOnHover: boolean;
/** Whether the sidebar extension area is collapsed */
extraCollapse: boolean;
/** Whether the sidebar is hidden - css */
hidden: boolean;
/** Sidebar width */
width: number;
}
interface ShortcutKeyPreferences {
/** Whether shortcut keys are enabled globally */
enable: boolean;
/** Whether the global lock screen shortcut is enabled */
globalLockScreen: boolean;
/** Whether the global logout shortcut is enabled */
globalLogout: boolean;
/** Whether the global preferences shortcut is enabled */
globalPreferences: boolean;
/** Whether the global search shortcut is enabled */
globalSearch: boolean;
}
interface TabbarPreferences {
/** Whether dragging of multiple tabs is enabled */
dragable: boolean;
/** Whether multiple tabs are enabled */
enable: boolean;
/** Tab height */
height: number;
/** Whether tab caching is enabled */
keepAlive: boolean;
/** Whether tabs are persistent */
persist: boolean;
/** Whether icons in multiple tabs are enabled */
showIcon: boolean;
/** Whether to show the maximize button */
showMaximize: boolean;
/** Whether to show the more button */
showMore: boolean;
/** Whether to show the refresh button */
showRefresh: boolean;
/** Tab style */
styleType: TabsStyleType;
}
interface ThemePreferences {
/** Built-in theme name */
builtinType: BuiltinThemeType;
/** Destructive color */
colorDestructive: string;
/** Primary color */
colorPrimary: string;
/** Success color */
colorSuccess: string;
/** Warning color */
colorWarning: string;
/** Current theme */
mode: ThemeModeType;
/** Radius */
radius: string;
/** Whether to enable semi-dark header (only effective when theme='light') */
semiDarkHeader: boolean;
/** Whether to enable semi-dark sidebar (only effective when theme='light') */
semiDarkSidebar: boolean;
}
interface TransitionPreferences {
/** Whether page transition animations are enabled */
enable: boolean;
// /** Whether page loading loading is enabled */
loading: boolean;
/** Page transition animation */
name: PageTransitionType | string;
/** Whether page loading progress animation is enabled */
progress: boolean;
}
interface WidgetPreferences {
/** Whether fullscreen widgets are enabled */
fullscreen: boolean;
/** Whether global search widget is enabled */
globalSearch: boolean;
/** Whether language switch widget is enabled */
languageToggle: boolean;
/** Whether lock screen functionality is enabled */
lockScreen: boolean;
/** Whether notification widget is displayed */
notification: boolean;
/** Whether sidebar show/hide widget is displayed */
sidebarToggle: boolean;
/** Whether theme switch widget is displayed */
themeToggle: boolean;
}
interface Preferences {
/** Global configuration */
app: AppPreferences;
/** Header configuration */
breadcrumb: BreadcrumbPreferences;
/** Copyright configuration */
copyright: CopyrightPreferences;
/** Footer configuration */
footer: FooterPreferences;
/** Breadcrumb configuration */
header: HeaderPreferences;
/** Logo configuration */
logo: LogoPreferences;
/** Navigation configuration */
navigation: NavigationPreferences;
/** Shortcut key configuration */
shortcutKeys: ShortcutKeyPreferences;
/** Sidebar configuration */
sidebar: SidebarPreferences;
/** Tab bar configuration */
tabbar: TabbarPreferences;
/** Theme configuration */
theme: ThemePreferences;
/** Animation configuration */
transition: TransitionPreferences;
/** Widget configuration */
widget: WidgetPreferences;
}
```
:::
::: warning Warning
- The `overridesPreferences` method only needs to override a part of the configurations in the project. There's no need to override configurations that are not needed; they will automatically use the default settings.
- Any configuration item can be overridden. You just need to override it within the `overridesPreferences` method. Do not modify the default configuration file.
:::

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