Compare commits

..

56 Commits

Author SHA1 Message Date
vben
68465b5fbf chore: release v5.4.0-beta.1 2024-10-13 14:17:28 +08:00
Vben
0ea0f204cb refactor: change the shadcn-ui directory and remove rarely used components (#4626) 2024-10-13 10:58:09 +08:00
dependabot[bot]
1b65254383 chore(deps): bump the non-breaking-changes group across 1 directory with 5 updates (#4618)
* chore(deps): bump the non-breaking-changes group across 1 directory with 5 updates

Bumps the non-breaking-changes group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [cspell](https://github.com/streetsidesoftware/cspell/tree/HEAD/packages/cspell) | `8.14.4` | `8.15.0` |
| [vue](https://github.com/vuejs/core) | `3.5.11` | `3.5.12` |
| [sass](https://github.com/sass/dart-sass) | `1.79.4` | `1.79.5` |
| [vite-plugin-dts](https://github.com/qmhc/vite-plugin-dts) | `4.2.1` | `4.2.3` |
| [@vue/shared](https://github.com/vuejs/core/tree/HEAD/packages/shared) | `3.5.11` | `3.5.12` |



Updates `cspell` from 8.14.4 to 8.15.0
- [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.15.0/packages/cspell)

Updates `vue` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.11...v3.5.12)

Updates `sass` from 1.79.4 to 1.79.5
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.79.4...1.79.5)

Updates `vite-plugin-dts` from 4.2.1 to 4.2.3
- [Release notes](https://github.com/qmhc/vite-plugin-dts/releases)
- [Changelog](https://github.com/qmhc/vite-plugin-dts/blob/main/CHANGELOG.md)
- [Commits](https://github.com/qmhc/vite-plugin-dts/compare/v4.2.1...v4.2.3)

Updates `@vue/shared` from 3.5.11 to 3.5.12
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.5.12/packages/shared)

---
updated-dependencies:
- dependency-name: cspell
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: vue
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: sass
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vite-plugin-dts
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vue/shared"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

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

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-13 10:26:51 +08:00
afe1
0a99f27127 chore: modify the type of the incoming function in the vite configuration (#4622)
* fix: viteconfig funciont type error

---------

Co-authored-by: afe1 <yunfei.zhu@nwowtec.com>
2024-10-13 10:01:18 +08:00
Vben
304b1b2efc fix: when a table switches paging, no form parameters will be carried (#4607)
* fix: when a table switches paging, no form parameters will be carried

* chore: typo
2024-10-10 22:48:25 +08:00
Vben
f923f59070 fix: metadata version number injection error (#4606)
* fix: metadata version number injection error

* chore: update deps
2024-10-10 22:30:50 +08:00
GavinLucky
437cb02e11 feat: preferences settings panel to add display switches with copyright (#4603)
* feat: preferences settings panel to add display switches with copyright

* feat: 更新 snapshots 测试用例

---------

Co-authored-by: ZhangYantao <Gavin@163.com>
2024-10-10 21:59:43 +08:00
Netfan
ba539f6793 chore: correct spelling for 'dragable' (#4600) 2024-10-10 10:55:52 +08:00
dependabot[bot]
078f255e1a chore(deps-dev): bump vite-plugin-dts from 4.2.1 to 4.2.3 in the non-breaking-changes group (#4591)
* chore(deps-dev): bump vite-plugin-dts in the non-breaking-changes group

Bumps the non-breaking-changes group with 1 update: [vite-plugin-dts](https://github.com/qmhc/vite-plugin-dts).


Updates `vite-plugin-dts` from 4.2.1 to 4.2.3
- [Release notes](https://github.com/qmhc/vite-plugin-dts/releases)
- [Changelog](https://github.com/qmhc/vite-plugin-dts/blob/main/CHANGELOG.md)
- [Commits](https://github.com/qmhc/vite-plugin-dts/compare/v4.2.1...v4.2.3)

---
updated-dependencies:
- dependency-name: vite-plugin-dts
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

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

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-09 22:25:37 +08:00
Vben
ba4662522e fix: fix request not displaying interface error messages correctly (#4596) 2024-10-09 22:08:55 +08:00
yrming
8fe87b10dc fix(docs): typo (#4595) 2024-10-09 21:52:26 +08:00
Vben
2dbd323b2a fix: fix the form-api reactive failure inside the form (#4590)
* fix: fix the form-api reactive failure inside the form
2024-10-08 22:43:02 +08:00
dependabot[bot]
8ad2b8665d chore(deps): bump @iconify/json from 2.2.256 to 2.2.257 in the non-breaking-changes group (#4589)
* chore(deps): bump @iconify/json in the non-breaking-changes group

Bumps the non-breaking-changes group with 1 update: [@iconify/json](https://github.com/iconify/icon-sets).


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

---
updated-dependencies:
- 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>

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 13:51:06 +08:00
Svend
518b869f9d fix: Remove the extra separator when the user dropdown menu is empty. (#4587) 2024-10-07 18:29:09 +08:00
Vben
2d019b3c8a refactor: change the form button field from text to content to prevent conflicts with the frame (#4586)
* refactor: change the form button field from text to content to prevent conflicts with the frame
2024-10-07 16:30:41 +08:00
Vben
ab44926ec8 fix: table internal search collspaed configuration does not take effect (#4585)
* fix: table internal search collspaed configuration does not take effect

* chore: typo
2024-10-07 16:22:57 +08:00
苗大
f0edad8a51 chore: vscode extensions add pnpm-catalog-lens (#4583) 2024-10-07 15:11:08 +08:00
Vben
f8ce3fdf1f feat: useVxeTable returns formApi instance (#4582) 2024-10-07 13:32:37 +08:00
Vben
60c615ce8a refactor: adjust layout refresh button and watermark; allow static i18n on language switch (#4579)
* refactor: adjust layout refresh button and watermark; allow static i18n on language switch

* chore: typo
2024-10-06 17:27:32 +08:00
Vben
324cdd8259 fix: after resetting the search, the parameters still exist (#4577) 2024-10-06 15:10:44 +08:00
Vben
9ad4f96e38 fix: the vxeUI global configuration does not take effect (#4574) 2024-10-05 22:30:13 +08:00
Vben
47d162e6e4 fix: improve the layout of tables offline on the mobile (#4573) 2024-10-05 22:00:11 +08:00
Vben
d37e2f599c fix: naive ui form reset does not meet expectations (#4569)
* fix: naive ui form reset does not meet expectations

* fix: typo
2024-10-05 17:09:42 +08:00
Vben
402eaf4275 fix: when the server component path is wrong, a blank interface is still displayed (#4567) 2024-10-05 11:11:30 +08:00
Netfan
0fcc42a2fb fix: incorrect info color for element plus, fixed #4532 (#4566) 2024-10-05 10:30:53 +08:00
dependabot[bot]
28b54b587a chore(deps): bump the non-breaking-changes group with 3 updates (#4561)
* chore(deps): bump the non-breaking-changes group with 3 updates

Bumps the non-breaking-changes group with 3 updates: [@changesets/cli](https://github.com/changesets/changesets), [vue](https://github.com/vuejs/core) and [@vue/shared](https://github.com/vuejs/core/tree/HEAD/packages/shared).


Updates `@changesets/cli` from 2.27.8 to 2.27.9
- [Release notes](https://github.com/changesets/changesets/releases)
- [Changelog](https://github.com/changesets/changesets/blob/main/docs/modifying-changelog-format.md)
- [Commits](https://github.com/changesets/changesets/compare/@changesets/cli@2.27.8...@changesets/cli@2.27.9)

Updates `vue` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.10...v3.5.11)

Updates `@vue/shared` from 3.5.10 to 3.5.11
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.5.11/packages/shared)

---
updated-dependencies:
- dependency-name: "@changesets/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vue
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vue/shared"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

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

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 09:52:04 +08:00
Vben
4173264805 feat: add vxe-table component (#4563)
* chore: wip vxe-table

* feat: add table demo

* chore: follow ci recommendations to adjust details

* chore: add custom-cell demo

* feat: add custom-cell table demo

* feat: add table from demo
2024-10-04 23:05:28 +08:00
vben
46540a7329 chore: release v5.3.2 2024-10-03 15:43:15 +08:00
Vben
13fd0ea16c fix: try to fix the error reported by the stub command in the window system (#4560) 2024-10-03 15:34:29 +08:00
Vben
f7016466ee feat: add examples of asynchronous form verification and verification time (#4559)
* feat: add examples of asynchronous form verification and verification time
2024-10-03 15:15:50 +08:00
Vben
0cd865e211 fix: fixed an error in the form onChange within Naive (#4558)
* fix: fixed an error in the form onChange within Naive

* chore: update
2024-10-03 14:22:18 +08:00
Squall2017
64428b9b11 feat: add form field autofocus configuration (#4550)
* feat: add form field autofocus configuration
2024-10-03 13:10:21 +08:00
LinaBell
aed31a5a4e perf: [antd] default placeholder for input and select components (#4551)
* chore: demo of customizing form layout using tailwind

* perf: default placeholder for input and select components

* chore: update ts type

* chore: extract public methods
2024-10-03 13:04:19 +08:00
dependabot[bot]
b3e196f001 chore(deps-dev): bump the non-breaking-changes group across 1 directory with 3 updates (#4557)
* chore(deps-dev): bump the non-breaking-changes group across 1 directory with 3 updates

Bumps the non-breaking-changes group with 3 updates in the / directory: [turbo](https://github.com/vercel/turborepo), [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) and [rollup](https://github.com/rollup/rollup).


Updates `turbo` from 2.1.2 to 2.1.3
- [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.1.2...v2.1.3)

Updates `vitest` from 2.1.1 to 2.1.2
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v2.1.2/packages/vitest)

Updates `rollup` from 4.22.5 to 4.24.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.22.5...v4.24.0)

---
updated-dependencies:
- dependency-name: turbo
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vitest
  dependency-type: direct:development
  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
...

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

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 12:54:04 +08:00
LinaBell
b2c117f544 chore: demo of customizing form layout using tailwind (#4549) 2024-09-30 09:47:16 +08:00
vben
01391ee5a1 chore: release v5.3.1 2024-09-29 22:25:20 +08:00
Vben
3572ce1538 fix: when multiple pop-ups exist at the same time, clicking will close all (#4548) 2024-09-29 22:15:43 +08:00
Vben
d1e1256202 chore: disable sorting of non-core folder object fields (#4547)
* chore: disable sorting of non-core folder object fields

* chore: ci error
2024-09-29 22:03:17 +08:00
Squall2017
b7776c5148 fix: fix abnormal display of scroll bar on lock screen page (#4546)
*  fix abnormal display of scroll bar on lock screen page
2024-09-29 21:45:56 +08:00
dependabot[bot]
2d1519eca7 chore(deps): bump the non-breaking-changes group across 1 directory with 4 updates (#4537)
* chore(deps): bump the non-breaking-changes group across 1 directory with 4 updates

Bumps the non-breaking-changes group with 4 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [vue](https://github.com/vuejs/core), [rollup](https://github.com/rollup/rollup) and [@vue/shared](https://github.com/vuejs/core/tree/HEAD/packages/shared).


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

Updates `vue` from 3.5.8 to 3.5.10
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.8...v3.5.10)

Updates `rollup` from 4.22.4 to 4.22.5
- [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.22.4...v4.22.5)

Updates `@vue/shared` from 3.5.8 to 3.5.10
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.5.10/packages/shared)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vue
  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: "@vue/shared"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

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

* chore: update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-28 16:10:26 +08:00
jinmao88
93b5618b52 feat(@vben/stores): export defineStore to support pinia persistence within apps (#4483)
Co-authored-by: Li Kui <90845831+likui628@users.noreply.github.com>
2024-09-28 16:01:04 +08:00
Vben
639d2e1525 feat: add pagination component (#4522)
* feat: add pagination component

* chore: update
2024-09-26 23:09:17 +08:00
Vben
26646d42f7 fix: when modal and drawer exist at the same time, click Close All (#4521) 2024-09-26 22:50:37 +08:00
Vben
17fa8eb93b fix: improve ant design button icon style (#4520) 2024-09-26 22:40:23 +08:00
Vben
8250894a50 fix: improve input browser backfilling style (#4519) 2024-09-26 22:31:20 +08:00
vince
a72b8acaf9 fix: window system clean script execution problems (#4513)
* fix: fix window system clean script execution problems

* fix: lint error

* chore: remove test code
2024-09-26 11:59:19 +08:00
Vben
a46c85d77d chore: update documentation and deps (#4510)
* chore: update docs

* chore: update deps

* chore: update action

* fix: reset after preferences are refreshed

* fix: ci error
2024-09-25 23:09:48 +08:00
Squall2017
fdc5b02c30 feat(form): add merge form functionality (#4495)
* feat: captcha example

* fix: fix lint errors

* chore: event handling and methods

* chore: add accessibility features ARIA labels and roles

* refactor: refactor code structure and improve captcha demo page

* feat: add captcha internationalization

* chore: 适配时间戳国际化展示

* fix: 1. 添加点击位置边界校验,防止点击外部导致x,y误差。2. 演示页面宽度过长添加滚动条。3. 添加hooks

* feat: sync test

* feat: 添加合并表单功能

* fix: 修复上一步不展示问题

---------

Co-authored-by: vince <vince292007@gmail.com>
2024-09-25 18:11:02 +08:00
Netfan
476aa068d7 fix: stripe table style for element plus, fixed: #4501 (#4503) 2024-09-25 17:33:24 +08:00
LinaBell
bb6057cac3 perf: setValues method of the form supports assigning values only to keys that exist in the schema (#4508)
* fix: hover border style same as antd style when validate error

* fix: hover border style same as antd style when validate error

* feat(@vben-core/form-ui): Default form validation rules applicable to selector components

* fix: Missing the default required label style for components such as select

* fix: the focus style and antd of the input box validation failure should be consistent

* fix: the focus style and antd of the input box validation failure should be consistent

* fix: some antd components failed to verify styles

* perf: setValues method of the form supports assigning values only to keys that exist in the schema

* docs: update form component docs

---------

Co-authored-by: vince <vince292007@gmail.com>
2024-09-25 17:09:38 +08:00
Fifteen
abbbbfb955 fix(docs): fix the selected state of the top navigation bar (#4499)
* fix(@vben/docs): fix the selected state of the top navigation bar

* style(@vben/docs): navigation bar selected item style
2024-09-25 09:53:55 +08:00
handsomeFu
79c87c9f46 chore(@vben/playground): Add new slider captcha element and adjust references (#4490)
Add a new slider captcha action reference (`el6`) to support additional functionality and update related template and event handler to use this new reference.
2024-09-24 14:15:41 +08:00
Vben
f815dcf3ae fix: after deleting the form item, you will also get the form value (#4481)
* fix: after deleting the form item, you will also get the form value
2024-09-23 22:59:58 +08:00
neo.dowithless
1197efea26 fix: wrong style when breadcrumbs display background (#4472) 2024-09-23 14:15:46 +08:00
Vben
2a83f1d666 feat: add playwright e2e testing framework (#4468)
* feat: add playwright e2e testing framework
2024-09-22 21:35:40 +08:00
aonoa
4b3d2d21ed fix: Clear the input box when closing the search (#4467)
Signed-off-by: aonoa <1991849113@qq.com>
2024-09-22 20:38:01 +08:00
453 changed files with 7261 additions and 3910 deletions

View File

@@ -19,6 +19,7 @@ permissions:
jobs: jobs:
post-update: post-update:
if: github.repository == 'vbenjs/vue-vben-admin'
# if: ${{ github.actor == 'dependabot[bot]' }} # if: ${{ github.actor == 'dependabot[bot]' }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:

View File

@@ -18,7 +18,7 @@ env:
jobs: jobs:
version: version:
if: (github.event.pull_request.merged || github.event_name == 'workflow_dispatch') && github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: (github.event.pull_request.merged || github.event_name == 'workflow_dispatch') && github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
# if: github.repository == 'vbenjs/vue-vben-admin' # if: github.repository == 'vbenjs/vue-vben-admin'
timeout-minutes: 15 timeout-minutes: 15
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -17,6 +17,7 @@ env:
jobs: jobs:
test: test:
name: Test name: Test
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
@@ -55,6 +56,7 @@ jobs:
lint: lint:
name: Lint name: Lint
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
@@ -77,6 +79,7 @@ jobs:
check: check:
name: Check name: Check
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 20 timeout-minutes: 20
strategy: strategy:
@@ -106,6 +109,7 @@ jobs:
ci-ok: ci-ok:
name: CI OK name: CI OK
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [test, check, lint] needs: [test, check, lint]
env: env:

View File

@@ -22,6 +22,7 @@ on:
jobs: jobs:
analyze: analyze:
name: Analyze (${{ matrix.language }}) name: Analyze (${{ matrix.language }})
if: github.repository == 'vbenjs/vue-vben-admin'
# Runner size impacts CodeQL analysis time. To learn more, please see: # Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql # - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/supported-runners-and-hardware-resources

View File

@@ -8,7 +8,7 @@ on:
jobs: jobs:
deploy-playground-ftp: deploy-playground-ftp:
name: Deploy Push Playground Ftp name: Deploy Push Playground Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@@ -39,7 +39,7 @@ jobs:
deploy-docs-ftp: deploy-docs-ftp:
name: Deploy Push Docs Ftp name: Deploy Push Docs Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@@ -63,7 +63,7 @@ jobs:
deploy-antd-ftp: deploy-antd-ftp:
name: Deploy Push Antd Ftp name: Deploy Push Antd Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@@ -94,7 +94,7 @@ jobs:
deploy-ele-ftp: deploy-ele-ftp:
name: Deploy Push Element Ftp name: Deploy Push Element Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@@ -125,7 +125,7 @@ jobs:
deploy-naive-ftp: deploy-naive-ftp:
name: Deploy Push Naive Ftp name: Deploy Push Naive Ftp
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code

View File

@@ -17,6 +17,7 @@ jobs:
# write permission is required for autolabeler # write permission is required for autolabeler
# otherwise, read permission is required at least # otherwise, read permission is required at least
pull-requests: write pull-requests: write
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: release-drafter/release-drafter@v6 - uses: release-drafter/release-drafter@v6

View File

@@ -3,23 +3,29 @@ name: Issue Close Require
# 触发条件:每天零点 # 触发条件:每天零点
on: on:
workflow_dispatch:
schedule: schedule:
- cron: '0 0 * * *' - cron: '0 0 * * *'
permissions: permissions:
pull-requests: write pull-requests: write
contents: write contents: write
issues: write
jobs: jobs:
close-issues: close-issues:
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# 步骤1关闭未活动的 Issues # 关闭未活动的 Issues
- name: Close Inactive Issues - name: Close Inactive Issues
uses: actions-cool/issues-helper@v3 uses: actions/stale@v9
with: with:
actions: 'close-issues' # 执行动作:关闭 Issues days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
token: ${{ secrets.GITHUB_TOKEN }} # GitHub Token用于认证 stale-issue-label: needs-reproduction # Label that flags an issue as stale.
labels: 'needs reproduction' # 目标标签 only-labels: needs-reproduction # Only process these issues
inactive-day: 3 # 未活动天数阈值 days-before-issue-close: 3
ignore-updates: true
remove-stale-when-updated: false
close-issue-message: This issue was closed because it was open for 3 days without a valid reproduction.
close-issue-label: closed-by-action

View File

@@ -13,6 +13,7 @@ permissions:
jobs: jobs:
reply-labeled: reply-labeled:
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: remove enhancement pending - name: remove enhancement pending

View File

@@ -11,12 +11,13 @@ permissions:
jobs: jobs:
action: action:
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v5 - uses: dessant/lock-threads@v5
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
issue-inactive-days: '30' issue-inactive-days: '14'
issue-lock-reason: '' issue-lock-reason: ''
pr-inactive-days: '30' pr-inactive-days: '30'
pr-lock-reason: '' pr-lock-reason: ''

View File

@@ -15,6 +15,7 @@ permissions:
jobs: jobs:
build: build:
name: Create Release name: Create Release
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:

View File

@@ -9,8 +9,9 @@ on:
jobs: jobs:
main: main:
runs-on: ubuntu-latest
name: Semantic Pull Request name: Semantic Pull Request
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest
steps: steps:
- name: Validate PR title - name: Validate PR title
uses: amannn/action-semantic-pull-request@v5 uses: amannn/action-semantic-pull-request@v5

View File

@@ -6,6 +6,7 @@ on:
jobs: jobs:
stale: stale:
if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v9

View File

@@ -3,4 +3,4 @@ ports:
onOpen: open-preview onOpen: open-preview
tasks: tasks:
- init: corepack enable && pnpm install - init: corepack enable && pnpm install
command: pnpm run dev command: pnpm run dev:play

View File

@@ -19,7 +19,9 @@
// i18n 插件 // i18n 插件
"Lokalise.i18n-ally", "Lokalise.i18n-ally",
// CSS 变量提示 // CSS 变量提示
"vunguyentuan.vscode-css-variables" "vunguyentuan.vscode-css-variables",
// 在 package.json 中显示 PNPM catalog 的版本
"antfu.pnpm-catalog-lens"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [
// 和 volar 冲突 // 和 volar 冲突

View File

@@ -125,6 +125,10 @@ pnpm build
[@Vben](https://github.com/anncwb) [@Vben](https://github.com/anncwb)
## スター歴史
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## 寄付 ## 寄付
このプロジェクトが役に立つと思われた場合、作者にコーヒーを一杯おごってサポートを示すことができます! このプロジェクトが役に立つと思われた場合、作者にコーヒーを一杯おごってサポートを示すことができます!

View File

@@ -124,6 +124,10 @@ Support modern browsers, not IE
[@Vben](https://github.com/anncwb) [@Vben](https://github.com/anncwb)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## Donate ## Donate
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support! If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!

View File

@@ -77,6 +77,10 @@ pnpm dev
pnpm build pnpm build
``` ```
## 更新日志
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## 如何贡献 ## 如何贡献
非常欢迎你的加入![提一个 Issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 或者提交一个 Pull Request。 非常欢迎你的加入![提一个 Issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 或者提交一个 Pull Request。
@@ -120,6 +124,10 @@ pnpm build
[@Vben](https://github.com/anncwb) [@Vben](https://github.com/anncwb)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## 捐赠 ## 捐赠
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持! 如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
@@ -128,10 +136,6 @@ pnpm build
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a> <a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
## 更新日志
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## Contributor ## Contributor
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors"> <a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">

View File

@@ -1,7 +1,7 @@
import { verifyAccessToken } from '~/utils/jwt-utils'; import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse } from '~/utils/response'; import { unAuthorizedResponse } from '~/utils/response';
export default eventHandler((event) => { export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event); const userinfo = verifyAccessToken(event);
if (!userinfo) { if (!userinfo) {
return unAuthorizedResponse(event); return unAuthorizedResponse(event);

View File

@@ -0,0 +1,48 @@
import { faker } from '@faker-js/faker';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse } from '~/utils/response';
function generateMockDataList(count: number) {
const dataList = [];
for (let i = 0; i < count; i++) {
const dataItem = {
id: faker.string.uuid(),
imageUrl: faker.image.avatar(),
imageUrl2: faker.image.avatar(),
open: faker.datatype.boolean(),
status: faker.helpers.arrayElement(['success', 'error', 'warning']),
productName: faker.commerce.productName(),
price: faker.commerce.price(),
currency: faker.finance.currencyCode(),
quantity: faker.number.int({ min: 1, max: 100 }),
available: faker.datatype.boolean(),
category: faker.commerce.department(),
releaseDate: faker.date.past(),
rating: faker.number.float({ min: 1, max: 5 }),
description: faker.commerce.productDescription(),
weight: faker.number.float({ min: 0.1, max: 10 }),
color: faker.color.human(),
inProduction: faker.datatype.boolean(),
tags: Array.from({ length: 3 }, () => faker.commerce.productAdjective()),
};
dataList.push(dataItem);
}
return dataList;
}
const mockData = generateMockDataList(100);
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
await sleep(600);
const { page, pageSize } = getQuery(event);
return usePageResponseSuccess(page as string, pageSize as string, mockData);
});

View File

@@ -6,6 +6,5 @@ export default eventHandler((event) => {
if (!userinfo) { if (!userinfo) {
return unAuthorizedResponse(event); return unAuthorizedResponse(event);
} }
return useResponseSuccess(userinfo); return useResponseSuccess(userinfo);
}); });

View File

@@ -10,6 +10,7 @@
"start": "nitro dev" "start": "nitro dev"
}, },
"dependencies": { "dependencies": {
"@faker-js/faker": "catalog:",
"jsonwebtoken": "catalog:", "jsonwebtoken": "catalog:",
"nitropack": "catalog:" "nitropack": "catalog:"
}, },

View File

@@ -9,6 +9,27 @@ export function useResponseSuccess<T = any>(data: T) {
}; };
} }
export function usePageResponseSuccess<T = any>(
page: number | string,
pageSize: number | string,
list: T[],
{ message = 'ok' } = {},
) {
const pageData = pagination(
Number.parseInt(`${page}`),
Number.parseInt(`${pageSize}`),
list,
);
return {
...useResponseSuccess({
items: pageData,
total: list.length,
}),
message,
};
}
export function useResponseError(message: string, error: any = null) { export function useResponseError(message: string, error: any = null) {
return { return {
code: -1, code: -1,
@@ -20,10 +41,25 @@ export function useResponseError(message: string, error: any = null) {
export function forbiddenResponse(event: H3Event<EventHandlerRequest>) { export function forbiddenResponse(event: H3Event<EventHandlerRequest>) {
setResponseStatus(event, 403); setResponseStatus(event, 403);
return useResponseError('ForbiddenException', 'Forbidden Exception'); return useResponseError('Forbidden Exception', 'Forbidden Exception');
} }
export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) { export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) {
setResponseStatus(event, 401); setResponseStatus(event, 401);
return useResponseError('UnauthorizedException', 'Unauthorized Exception'); return useResponseError('Unauthorized Exception', 'Unauthorized Exception');
}
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function pagination<T = any>(
pageNo: number,
pageSize: number,
array: T[],
): T[] {
const offset = (pageNo - 1) * Number(pageSize);
return offset + Number(pageSize) >= array.length
? array.slice(offset)
: array.slice(offset, offset + Number(pageSize));
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/web-antd", "name": "@vben/web-antd",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://vben.pro", "homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -4,6 +4,7 @@ import type {
VbenFormProps, VbenFormProps,
} from '@vben/common-ui'; } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
@@ -57,6 +58,16 @@ export type FormComponentType =
| 'Upload' | 'Upload'
| BaseFormComponentType; | BaseFormComponentType;
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
) => {
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
return h(component, { ...props, ...attrs, placeholder }, slots);
};
};
// 初始化表单组件并注册到form组件内部 // 初始化表单组件并注册到form组件内部
setupVbenForm<FormComponentType>({ setupVbenForm<FormComponentType>({
components: { components: {
@@ -73,20 +84,20 @@ setupVbenForm<FormComponentType>({
return h(Button, { ...props, attrs, type: 'primary' }, slots); return h(Button, { ...props, attrs, type: 'primary' }, slots);
}, },
Divider, Divider,
Input, Input: withDefaultPlaceholder(Input, 'input'),
InputNumber, InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword, InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions, Mentions: withDefaultPlaceholder(Mentions, 'input'),
Radio, Radio,
RadioGroup, RadioGroup,
RangePicker, RangePicker,
Rate, Rate,
Select, Select: withDefaultPlaceholder(Select, 'select'),
Space, Space,
Switch, Switch,
Textarea, Textarea: withDefaultPlaceholder(Textarea, 'input'),
TimePicker, TimePicker,
TreeSelect, TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload, Upload,
}, },
config: { config: {

View File

@@ -1 +1,2 @@
export * from './form'; export * from './form';
export * from './vxe-table';

View File

@@ -0,0 +1,59 @@
import { h } from 'vue';
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { Button, Image } from 'ant-design-vue';
import { useVbenForm } from './form';
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
vxeUI.setConfig({
grid: {
align: 'center',
border: true,
minHeight: 180,
proxyConfig: {
autoLoad: true,
response: {
result: 'items',
total: 'total',
list: 'items',
},
showActiveMsg: true,
showResponseMsg: false,
},
round: true,
size: 'small',
},
});
// 表格配置项可以用 cellRender: { name: 'CellImage' },
vxeUI.renderer.add('CellImage', {
renderDefault(_renderOpts, params) {
const { column, row } = params;
return h(Image, { src: row[column.field] });
},
});
// 表格配置项可以用 cellRender: { name: 'CellLink' },
vxeUI.renderer.add('CellLink', {
renderDefault(renderOpts) {
const { props } = renderOpts;
return h(
Button,
{ size: 'small', type: 'link' },
{ default: () => props?.text },
);
},
});
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
// vxeUI.formats.add
},
useVbenForm,
});
export { useVbenVxeGrid };
export type * from '@vben/plugins/vxe-table';

View File

@@ -3,17 +3,13 @@ import { baseRequestClient, requestClient } from '#/api/request';
export namespace AuthApi { export namespace AuthApi {
/** 登录接口参数 */ /** 登录接口参数 */
export interface LoginParams { export interface LoginParams {
password: string; password?: string;
username: string; username?: string;
} }
/** 登录接口返回值 */ /** 登录接口返回值 */
export interface LoginResult { export interface LoginResult {
accessToken: string; accessToken: string;
desc: string;
realName: string;
userId: string;
username: string;
} }
export interface RefreshTokenResult { export interface RefreshTokenResult {

View File

@@ -1,6 +1,8 @@
/** /**
* 该文件可自行根据业务逻辑进行调整 * 该文件可自行根据业务逻辑进行调整
*/ */
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@@ -68,7 +70,7 @@ function createRequestClient(baseURL: string) {
}); });
// response数据解构 // response数据解构
client.addResponseInterceptor({ client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => { fulfilled: (response) => {
const { data: responseData, status } = response; const { data: responseData, status } = response;
@@ -93,7 +95,14 @@ function createRequestClient(baseURL: string) {
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => message.error(msg)), errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),
); );
return client; return client;

View File

@@ -1,10 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NotificationItem } from '@vben/layouts'; import type { NotificationItem } from '@vben/layouts';
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons'; import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
import { import {
BasicLayout, BasicLayout,
@@ -54,6 +55,7 @@ const notifications = ref<NotificationItem[]>([
const userStore = useUserStore(); const userStore = useUserStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
const showDot = computed(() => const showDot = computed(() =>
notifications.value.some((item) => !item.isRead), notifications.value.some((item) => !item.isRead),
); );
@@ -103,6 +105,21 @@ function handleNoticeClear() {
function handleMakeAll() { function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true)); notifications.value.forEach((item) => (item.isRead = true));
} }
watch(
() => preferences.app.watermark,
async (enable) => {
if (enable) {
await updateWatermark({
content: `${userStore.userInfo?.username}`,
});
} else {
destroyWatermark();
}
},
{
immediate: true,
},
);
</script> </script>
<template> <template>

View File

@@ -34,9 +34,7 @@ function setupCommonGuard(router: Router) {
router.afterEach((to) => { router.afterEach((to) => {
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
if (preferences.tabbar.enable) { loadedPaths.add(to.path);
loadedPaths.add(to.path);
}
// 关闭页面加载进度条 // 关闭页面加载进度条
if (preferences.transition.progress) { if (preferences.transition.progress) {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/web-ele", "name": "@vben/web-ele",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://vben.pro", "homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -4,6 +4,7 @@ import type {
VbenFormProps, VbenFormProps,
} from '@vben/common-ui'; } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
@@ -42,6 +43,16 @@ export type FormComponentType =
| 'Upload' | 'Upload'
| BaseFormComponentType; | BaseFormComponentType;
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
) => {
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
return h(component, { ...props, ...attrs, placeholder }, slots);
};
};
// 初始化表单组件并注册到form组件内部 // 初始化表单组件并注册到form组件内部
setupVbenForm<FormComponentType>({ setupVbenForm<FormComponentType>({
components: { components: {
@@ -56,14 +67,14 @@ setupVbenForm<FormComponentType>({
return h(ElButton, { ...props, attrs, type: 'primary' }, slots); return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
}, },
Divider: ElDivider, Divider: ElDivider,
Input: ElInput, Input: withDefaultPlaceholder(ElInput, 'input'),
InputNumber: ElInputNumber, InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
RadioGroup: ElRadioGroup, RadioGroup: ElRadioGroup,
Select: ElSelect, Select: withDefaultPlaceholder(ElSelect, 'select'),
Space: ElSpace, Space: ElSpace,
Switch: ElSwitch, Switch: ElSwitch,
TimePicker: ElTimePicker, TimePicker: ElTimePicker,
TreeSelect: ElTreeSelect, TreeSelect: withDefaultPlaceholder(ElTreeSelect, 'select'),
Upload: ElUpload, Upload: ElUpload,
}, },
config: { config: {

View File

@@ -1 +1,2 @@
export * from './form'; export * from './form';
export * from './vxe-table';

View File

@@ -0,0 +1,60 @@
import { h } from 'vue';
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { ElButton, ElImage } from 'element-plus';
import { useVbenForm } from './form';
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
vxeUI.setConfig({
grid: {
align: 'center',
border: true,
minHeight: 180,
proxyConfig: {
autoLoad: true,
response: {
result: 'items',
total: 'total',
list: 'items',
},
showActiveMsg: true,
showResponseMsg: false,
},
round: true,
size: 'small',
},
});
// 表格配置项可以用 cellRender: { name: 'CellImage' },
vxeUI.renderer.add('CellImage', {
renderDefault(_renderOpts, params) {
const { column, row } = params;
const src = row[column.field];
return h(ElImage, { src, previewSrcList: [src] });
},
});
// 表格配置项可以用 cellRender: { name: 'CellLink' },
vxeUI.renderer.add('CellLink', {
renderDefault(renderOpts) {
const { props } = renderOpts;
return h(
ElButton,
{ size: 'small', link: true },
{ default: () => props?.text },
);
},
});
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
// vxeUI.formats.add
},
useVbenForm,
});
export { useVbenVxeGrid };
export type * from '@vben/plugins/vxe-table';

View File

@@ -3,17 +3,13 @@ import { baseRequestClient, requestClient } from '#/api/request';
export namespace AuthApi { export namespace AuthApi {
/** 登录接口参数 */ /** 登录接口参数 */
export interface LoginParams { export interface LoginParams {
password: string; password?: string;
username: string; username?: string;
} }
/** 登录接口返回值 */ /** 登录接口返回值 */
export interface LoginResult { export interface LoginResult {
accessToken: string; accessToken: string;
desc: string;
realName: string;
userId: string;
username: string;
} }
export interface RefreshTokenResult { export interface RefreshTokenResult {

View File

@@ -1,6 +1,8 @@
/** /**
* 该文件可自行根据业务逻辑进行调整 * 该文件可自行根据业务逻辑进行调整
*/ */
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@@ -68,7 +70,7 @@ function createRequestClient(baseURL: string) {
}); });
// response数据解构 // response数据解构
client.addResponseInterceptor({ client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => { fulfilled: (response) => {
const { data: responseData, status } = response; const { data: responseData, status } = response;
@@ -93,7 +95,14 @@ function createRequestClient(baseURL: string) {
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => ElMessage.error(msg)), errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
ElMessage.error(errorMessage || msg);
}),
); );
return client; return client;

View File

@@ -1,10 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NotificationItem } from '@vben/layouts'; import type { NotificationItem } from '@vben/layouts';
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons'; import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
import { import {
BasicLayout, BasicLayout,
@@ -54,6 +55,7 @@ const notifications = ref<NotificationItem[]>([
const userStore = useUserStore(); const userStore = useUserStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
const showDot = computed(() => const showDot = computed(() =>
notifications.value.some((item) => !item.isRead), notifications.value.some((item) => !item.isRead),
); );
@@ -103,6 +105,21 @@ function handleNoticeClear() {
function handleMakeAll() { function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true)); notifications.value.forEach((item) => (item.isRead = true));
} }
watch(
() => preferences.app.watermark,
async (enable) => {
if (enable) {
await updateWatermark({
content: `${userStore.userInfo?.username}`,
});
} else {
destroyWatermark();
}
},
{
immediate: true,
},
);
</script> </script>
<template> <template>

View File

@@ -34,9 +34,7 @@ function setupCommonGuard(router: Router) {
router.afterEach((to) => { router.afterEach((to) => {
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
if (preferences.tabbar.enable) { loadedPaths.add(to.path);
loadedPaths.add(to.path);
}
// 关闭页面加载进度条 // 关闭页面加载进度条
if (preferences.transition.progress) { if (preferences.transition.progress) {

View File

@@ -7,6 +7,7 @@ import {
ElMessage, ElMessage,
ElNotification, ElNotification,
ElSpace, ElSpace,
ElTable,
} from 'element-plus'; } from 'element-plus';
type NotificationType = 'error' | 'info' | 'success' | 'warning'; type NotificationType = 'error' | 'info' | 'success' | 'warning';
@@ -38,6 +39,14 @@ function notify(type: NotificationType) {
type, type,
}); });
} }
const tableData = [
{ prop1: '1', prop2: 'A' },
{ prop1: '2', prop2: 'B' },
{ prop1: '3', prop2: 'C' },
{ prop1: '4', prop2: 'D' },
{ prop1: '5', prop2: 'E' },
{ prop1: '6', prop2: 'F' },
];
</script> </script>
<template> <template>
@@ -74,5 +83,11 @@ function notify(type: NotificationType) {
<ElButton type="success" @click="notify('success')"> 成功 </ElButton> <ElButton type="success" @click="notify('success')"> 成功 </ElButton>
</ElSpace> </ElSpace>
</ElCard> </ElCard>
<ElCard class="mb-5">
<ElTable :data="tableData" stripe>
<ElTable.TableColumn label="测试列1" prop="prop1" />
<ElTable.TableColumn label="测试列2" prop="prop2" />
</ElTable>
</ElCard>
</Page> </Page>
</template> </template>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/web-naive", "name": "@vben/web-naive",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://vben.pro", "homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -4,6 +4,7 @@ import type {
VbenFormProps, VbenFormProps,
} from '@vben/common-ui'; } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
@@ -43,6 +44,16 @@ export type FormComponentType =
| 'Upload' | 'Upload'
| BaseFormComponentType; | BaseFormComponentType;
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
) => {
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
return h(component, { ...props, ...attrs, placeholder }, slots);
};
};
// 初始化表单组件并注册到form组件内部 // 初始化表单组件并注册到form组件内部
setupVbenForm<FormComponentType>({ setupVbenForm<FormComponentType>({
components: { components: {
@@ -51,28 +62,28 @@ setupVbenForm<FormComponentType>({
DatePicker: NDatePicker, DatePicker: NDatePicker,
// 自定义默认的重置按钮 // 自定义默认的重置按钮
DefaultResetActionButton: (props, { attrs, slots }) => { DefaultResetActionButton: (props, { attrs, slots }) => {
return h(NButton, { ...props, attrs, text: false, type: 'info' }, slots); return h(NButton, { ...props, attrs, type: 'info' }, slots);
}, },
// 自定义默认的提交按钮 // 自定义默认的提交按钮
DefaultSubmitActionButton: (props, { attrs, slots }) => { DefaultSubmitActionButton: (props, { attrs, slots }) => {
return h( return h(NButton, { ...props, attrs, type: 'primary' }, slots);
NButton,
{ ...props, attrs, text: false, type: 'primary' },
slots,
);
}, },
Divider: NDivider, Divider: NDivider,
Input: NInput, Input: withDefaultPlaceholder(NInput, 'input'),
InputNumber: NInputNumber, InputNumber: withDefaultPlaceholder(NInputNumber, 'input'),
RadioGroup: NRadioGroup, RadioGroup: NRadioGroup,
Select: NSelect, Select: withDefaultPlaceholder(NSelect, 'select'),
Space: NSpace, Space: NSpace,
Switch: NSwitch, Switch: NSwitch,
TimePicker: NTimePicker, TimePicker: NTimePicker,
TreeSelect: NTreeSelect, TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'),
Upload: NUpload, Upload: NUpload,
}, },
config: { config: {
// naive-ui组件不接受onChang事件所以需要禁用
disabledOnChangeListener: true,
// naive-ui组件的空值为null,不能是undefined否则重置表单时不生效
emptyStateValue: null,
baseModelPropName: 'value', baseModelPropName: 'value',
modelPropNameMap: { modelPropNameMap: {
Checkbox: 'checked', Checkbox: 'checked',

View File

@@ -1,2 +1,3 @@
export * from './form'; export * from './form';
export * from './naive'; export * from './naive';
export * from './vxe-table';

View File

@@ -0,0 +1,59 @@
import { h } from 'vue';
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { NButton, NImage } from 'naive-ui';
import { useVbenForm } from './form';
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
vxeUI.setConfig({
grid: {
align: 'center',
border: true,
minHeight: 180,
proxyConfig: {
autoLoad: true,
response: {
result: 'items',
total: 'total',
list: 'items',
},
showActiveMsg: true,
showResponseMsg: false,
},
round: true,
size: 'small',
},
});
// 表格配置项可以用 cellRender: { name: 'CellImage' },
vxeUI.renderer.add('CellImage', {
renderDefault(_renderOpts, params) {
const { column, row } = params;
return h(NImage, { src: row[column.field] });
},
});
// 表格配置项可以用 cellRender: { name: 'CellLink' },
vxeUI.renderer.add('CellLink', {
renderDefault(renderOpts) {
const { props } = renderOpts;
return h(
NButton,
{ size: 'small', type: 'primary', quaternary: true },
{ default: () => props?.text },
);
},
});
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
// vxeUI.formats.add
},
useVbenForm,
});
export { useVbenVxeGrid };
export type * from '@vben/plugins/vxe-table';

View File

@@ -3,17 +3,13 @@ import { baseRequestClient, requestClient } from '#/api/request';
export namespace AuthApi { export namespace AuthApi {
/** 登录接口参数 */ /** 登录接口参数 */
export interface LoginParams { export interface LoginParams {
password: string; password?: string;
username: string; username?: string;
} }
/** 登录接口返回值 */ /** 登录接口返回值 */
export interface LoginResult { export interface LoginResult {
accessToken: string; accessToken: string;
desc: string;
realName: string;
userId: string;
username: string;
} }
export interface RefreshTokenResult { export interface RefreshTokenResult {

View File

@@ -1,6 +1,8 @@
/** /**
* 该文件可自行根据业务逻辑进行调整 * 该文件可自行根据业务逻辑进行调整
*/ */
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@@ -67,7 +69,7 @@ function createRequestClient(baseURL: string) {
}); });
// response数据解构 // response数据解构
client.addResponseInterceptor({ client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => { fulfilled: (response) => {
const { data: responseData, status } = response; const { data: responseData, status } = response;
@@ -92,7 +94,14 @@ function createRequestClient(baseURL: string) {
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => message.error(msg)), errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),
); );
return client; return client;

View File

@@ -1,10 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NotificationItem } from '@vben/layouts'; import type { NotificationItem } from '@vben/layouts';
import { computed, ref } from 'vue'; import { computed, ref, watch } from 'vue';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons'; import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
import { import {
BasicLayout, BasicLayout,
@@ -54,6 +55,7 @@ const notifications = ref<NotificationItem[]>([
const userStore = useUserStore(); const userStore = useUserStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
const showDot = computed(() => const showDot = computed(() =>
notifications.value.some((item) => !item.isRead), notifications.value.some((item) => !item.isRead),
); );
@@ -103,6 +105,22 @@ function handleNoticeClear() {
function handleMakeAll() { function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true)); notifications.value.forEach((item) => (item.isRead = true));
} }
watch(
() => preferences.app.watermark,
async (enable) => {
if (enable) {
await updateWatermark({
content: `${userStore.userInfo?.username}`,
});
} else {
destroyWatermark();
}
},
{
immediate: true,
},
);
</script> </script>
<template> <template>

View File

@@ -34,9 +34,7 @@ function setupCommonGuard(router: Router) {
router.afterEach((to) => { router.afterEach((to) => {
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
if (preferences.tabbar.enable) { loadedPaths.add(to.path);
loadedPaths.add(to.path);
}
// 关闭页面加载进度条 // 关闭页面加载进度条
if (preferences.transition.progress) { if (preferences.transition.progress) {

View File

@@ -25,7 +25,6 @@ const routes: RouteRecordRaw[] = [
}, },
{ {
meta: { meta: {
icon: 'mdi:shield-key-outline',
title: $t('page.demos.table'), title: $t('page.demos.table'),
}, },
name: 'Table', name: 'Table',

View File

@@ -19,6 +19,7 @@
"intlify", "intlify",
"mkdist", "mkdist",
"mockjs", "mockjs",
"vitejs",
"noopener", "noopener",
"noreferrer", "noreferrer",
"nprogress", "nprogress",

View File

@@ -133,12 +133,19 @@ function sidebarCommercial(): DefaultTheme.SidebarItem[] {
function nav(): DefaultTheme.NavItem[] { function nav(): DefaultTheme.NavItem[] {
return [ return [
{ {
activeMatch: '^/en/(guide|components)/',
text: 'Doc', text: 'Doc',
items: [ items: [
{ {
activeMatch: '^/en/guide/',
link: '/en/guide/introduction/vben', link: '/en/guide/introduction/vben',
text: 'Guide', text: 'Guide',
}, },
// {
// activeMatch: '^/en/components/',
// link: '/en/components/introduction',
// text: 'Components',
// },
{ {
text: 'Historical Versions', text: 'Historical Versions',
items: [ items: [

View File

@@ -176,13 +176,16 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
function nav(): DefaultTheme.NavItem[] { function nav(): DefaultTheme.NavItem[] {
return [ return [
{ {
activeMatch: '^/(guide|components)/',
text: '文档', text: '文档',
items: [ items: [
{ {
activeMatch: '^/guide/',
link: '/guide/introduction/vben', link: '/guide/introduction/vben',
text: '指南', text: '指南',
}, },
{ {
activeMatch: '^/components/',
link: '/components/introduction', link: '/components/introduction',
text: '组件', text: '组件',
}, },

View File

@@ -9,3 +9,14 @@ html.dark {
.form-valid-error p { .form-valid-error p {
margin: 0; margin: 0;
} }
/* 顶部导航栏选中项样式 */
.VPNavBarMenuLink,
.VPNavBarMenuGroup {
border-bottom: 1px solid transparent;
}
.VPNavBarMenuLink.active,
.VPNavBarMenuGroup.active {
border-bottom-color: var(--vp-c-brand-1);
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/docs", "name": "@vben/docs",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "vitepress build", "build": "vitepress build",
@@ -13,9 +13,7 @@
"dependencies": { "dependencies": {
"@vben-core/shadcn-ui": "workspace:*", "@vben-core/shadcn-ui": "workspace:*",
"@vben/common-ui": "workspace:*", "@vben/common-ui": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/styles": "workspace:*", "@vben/styles": "workspace:*",
"ant-design-vue": "catalog:", "ant-design-vue": "catalog:",
"lucide-vue-next": "catalog:", "lucide-vue-next": "catalog:",

View File

@@ -31,6 +31,7 @@ import type {
VbenFormProps, VbenFormProps,
} from '@vben/common-ui'; } from '@vben/common-ui';
import type { Component, SetupContext } from 'vue';
import { h } from 'vue'; import { h } from 'vue';
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
@@ -84,6 +85,16 @@ export type FormComponentType =
| 'Upload' | 'Upload'
| BaseFormComponentType; | BaseFormComponentType;
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
) => {
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
const placeholder = props?.placeholder || $t(`placeholder.${type}`);
return h(component, { ...props, ...attrs, placeholder }, slots);
};
};
// 初始化表单组件并注册到form组件内部 // 初始化表单组件并注册到form组件内部
setupVbenForm<FormComponentType>({ setupVbenForm<FormComponentType>({
components: { components: {
@@ -100,26 +111,27 @@ setupVbenForm<FormComponentType>({
return h(Button, { ...props, attrs, type: 'primary' }, slots); return h(Button, { ...props, attrs, type: 'primary' }, slots);
}, },
Divider, Divider,
Input, Input: withDefaultPlaceholder(Input, 'input'),
InputNumber, InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword, InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions, Mentions: withDefaultPlaceholder(Mentions, 'input'),
Radio, Radio,
RadioGroup, RadioGroup,
RangePicker, RangePicker,
Rate, Rate,
Select, Select: withDefaultPlaceholder(Select, 'select'),
Space, Space,
Switch, Switch,
Textarea, Textarea: withDefaultPlaceholder(Textarea, 'input'),
TimePicker, TimePicker,
TreeSelect, TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
Upload, Upload,
}, },
config: { config: {
// 是否禁用onChange事件监听naive ui组件库默认不需要监听onChange事件否则会在控制台报错
disabledOnChangeListener: true,
// ant design vue组件库默认都是 v-model:value // ant design vue组件库默认都是 v-model:value
baseModelPropName: 'value', baseModelPropName: 'value',
// 一些组件是 v-model:checked 或者 v-model:fileList // 一些组件是 v-model:checked 或者 v-model:fileList
modelPropNameMap: { modelPropNameMap: {
Checkbox: 'checked', Checkbox: 'checked',
@@ -229,7 +241,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
| --- | --- | --- | | --- | --- | --- |
| submitForm | 提交表单 | `(e:Event)=>Promise<Record<string,any>>` | | submitForm | 提交表单 | `(e:Event)=>Promise<Record<string,any>>` |
| resetForm | 重置表单 | `()=>Promise<void>` | | resetForm | 重置表单 | `()=>Promise<void>` |
| setValues | 设置表单值 | `()=>Promise<Record<string,any>>` | | setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | `(fields: Record<string, any>, filterFields?: boolean, shouldValidate?: boolean) => Promise<void>` |
| getValues | 获取表单值 | `(fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void>` | | getValues | 获取表单值 | `(fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void>` |
| validate | 表单校验 | `()=>Promise<void>` | | validate | 表单校验 | `()=>Promise<void>` |
| resetValidate | 重置表单校验 | `()=>Promise<void>` | | resetValidate | 重置表单校验 | `()=>Promise<void>` |
@@ -255,6 +267,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
| submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - |
| showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` | | showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` |
| collapsed | 是否折叠,在`是否展开在showCollapseButton=true`时生效 | `boolean` | `false` | | collapsed | 是否折叠,在`是否展开在showCollapseButton=true`时生效 | `boolean` | `false` |
| collapseTriggerResize | 折叠时,触发`resize`事件 | `boolean` | `false` |
| collapsedRows | 折叠时保持的行数 | `number` | `1` | | collapsedRows | 折叠时保持的行数 | `number` | `1` |
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - | | commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
| schema | 表单项的每一项配置 | `FormSchema` | - | | schema | 表单项的每一项配置 | `FormSchema` | - |

View File

@@ -78,7 +78,7 @@ const [QueryForm] = useVbenForm({
// 是否可展开 // 是否可展开
showCollapseButton: true, showCollapseButton: true,
submitButtonOptions: { submitButtonOptions: {
text: '查询', content: '查询',
}, },
wrapperClass: 'grid-cols-1 md:grid-cols-2', wrapperClass: 'grid-cols-1 md:grid-cols-2',
}); });

View File

@@ -46,8 +46,6 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
```json ```json
{ {
"scripts": { "scripts": {
// Install dependencies
"bootstrap": "pnpm install",
// Build the project // Build the project
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
// Build the project with analysis // Build the project with analysis
@@ -77,7 +75,7 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
// Check types // Check types
"check:type": "turbo run typecheck", "check:type": "turbo run typecheck",
// Clean the project (delete node_modules, dist, .turbo, etc.) // Clean the project (delete node_modules, dist, .turbo, etc.)
"clean": "vsh clean", "clean": "node ./scripts/clean.mjs",
// Commit code // Commit code
"commit": "czg", "commit": "czg",
// Start the project (by default, the dev scripts of all packages in the entire repository will run) // Start the project (by default, the dev scripts of all packages in the entire repository will run)
@@ -97,7 +95,7 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
// Lint code // Lint code
"lint": "vsh lint", "lint": "vsh lint",
// After installing dependencies, execute the stub script for all packages // After installing dependencies, execute the stub script for all packages
"postinstall": "turbo run stub", "postinstall": "pnpm -r run stub --if-present",
// Only allow using pnpm // Only allow using pnpm
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
// Install husky // Install husky
@@ -107,9 +105,9 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
// Package specification check // Package specification check
"publint": "vsh publint", "publint": "vsh publint",
// Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies // Delete all node_modules, yarn.lock, package.lock.json, and reinstall dependencies
"reinstall": "pnpm clean --del-lock && pnpm bootstrap", "reinstall": "pnpm clean --del-lock && pnpm install",
// Run vitest unit tests // Run vitest unit tests
"test:unit": "vitest", "test:unit": "vitest run --dom",
// Update project dependencies // Update project dependencies
"update:deps": " pnpm update --latest --recursive", "update:deps": " pnpm update --latest --recursive",
// Changeset generation and versioning // Changeset generation and versioning

View File

@@ -163,6 +163,8 @@ The `src/api/request.ts` within the application can be configured according to t
/** /**
* This file can be adjusted according to business logic * This file can be adjusted according to business logic
*/ */
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@@ -227,7 +229,7 @@ function createRequestClient(baseURL: string) {
}); });
// Deal Response Data // Deal Response Data
client.addResponseInterceptor({ client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => { fulfilled: (response) => {
const { data: responseData, status } = response; const { data: responseData, status } = response;
@@ -253,7 +255,14 @@ function createRequestClient(baseURL: string) {
// Generic error handling; if none of the above error handling logic is triggered, it will fall back to this. // Generic error handling; if none of the above error handling logic is triggered, it will fall back to this.
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => message.error(msg)), errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),
); );
return client; return client;

View File

@@ -226,7 +226,7 @@ const defaultPreferences: Preferences = {
width: 230, width: 230,
}, },
tabbar: { tabbar: {
dragable: true, draggable: true,
enable: true, enable: true,
height: 36, height: 36,
keepAlive: true, keepAlive: true,
@@ -234,7 +234,6 @@ const defaultPreferences: Preferences = {
showIcon: true, showIcon: true,
showMaximize: true, showMaximize: true,
showMore: true, showMore: true,
showRefresh: true,
styleType: 'chrome', styleType: 'chrome',
}, },
theme: { theme: {
@@ -262,6 +261,7 @@ const defaultPreferences: Preferences = {
notification: true, notification: true,
sidebarToggle: true, sidebarToggle: true,
themeToggle: true, themeToggle: true,
refresh: true,
}, },
}; };
``` ```
@@ -406,7 +406,7 @@ interface ShortcutKeyPreferences {
interface TabbarPreferences { interface TabbarPreferences {
/** Whether dragging of multiple tabs is enabled */ /** Whether dragging of multiple tabs is enabled */
dragable: boolean; draggable: boolean;
/** Whether multiple tabs are enabled */ /** Whether multiple tabs are enabled */
enable: boolean; enable: boolean;
/** Tab height */ /** Tab height */
@@ -421,8 +421,6 @@ interface TabbarPreferences {
showMaximize: boolean; showMaximize: boolean;
/** Whether to show the more button */ /** Whether to show the more button */
showMore: boolean; showMore: boolean;
/** Whether to show the refresh button */
showRefresh: boolean;
/** Tab style */ /** Tab style */
styleType: TabsStyleType; styleType: TabsStyleType;
} }
@@ -469,6 +467,8 @@ interface WidgetPreferences {
lockScreen: boolean; lockScreen: boolean;
/** Whether notification widget is displayed */ /** Whether notification widget is displayed */
notification: boolean; notification: boolean;
/** Whether to show the refresh button */
refresh: boolean;
/** Whether sidebar show/hide widget is displayed */ /** Whether sidebar show/hide widget is displayed */
sidebarToggle: boolean; sidebarToggle: boolean;
/** Whether theme switch widget is displayed */ /** Whether theme switch widget is displayed */

View File

@@ -37,8 +37,6 @@ If you want to adjust the content of the login form, you can configure the `Auth
```vue ```vue
<AuthenticationLogin <AuthenticationLogin
:loading="authStore.loginLoading" :loading="authStore.loginLoading"
password-placeholder="123456"
username-placeholder="vben"
@submit="authStore.authLogin" @submit="authStore.authLogin"
/> />
``` ```

View File

@@ -42,23 +42,6 @@ Check the dependency situation of the entire project and output `unused dependen
pnpm vsh check-dep pnpm vsh check-dep
``` ```
### vsh clean
Delete the project's `node_modules`, `dist`, `.turbo` directories, etc., to clean the project.
#### Usage
```bash
pnpm vsh clean
```
#### Options
| Option | Description |
| --- | --- |
| `-r,--recursive` | Recursively delete the entire project, default `true` |
| `--del-lock` | Whether to delete the `pnpm-lock.yaml` file, default `true` |
### vsh lint ### vsh lint
Lint checks the project to see if the code in the project conforms to standards. Lint checks the project to see if the code in the project conforms to standards.

View File

@@ -46,8 +46,6 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
```json ```json
{ {
"scripts": { "scripts": {
// 安装依赖
"bootstrap": "pnpm install",
// 构建项目 // 构建项目
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
// 构建项目并分析 // 构建项目并分析
@@ -77,7 +75,7 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
// 检查类型 // 检查类型
"check:type": "turbo run typecheck", "check:type": "turbo run typecheck",
// 清理项目删除node_modules、dist、.turbo等目录 // 清理项目删除node_modules、dist、.turbo等目录
"clean": "vsh clean", "clean": "node ./scripts/clean.mjs",
// 提交代码 // 提交代码
"commit": "czg", "commit": "czg",
// 启动项目默认会运行整个仓库所有包的dev脚本 // 启动项目默认会运行整个仓库所有包的dev脚本
@@ -97,7 +95,7 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
// lint 代码 // lint 代码
"lint": "vsh lint", "lint": "vsh lint",
// 依赖安装完成之后执行所有包的stub脚本 // 依赖安装完成之后执行所有包的stub脚本
"postinstall": "turbo run stub", "postinstall": "pnpm -r run stub --if-present",
// 只允许使用pnpm // 只允许使用pnpm
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
// husky的安装 // husky的安装
@@ -107,9 +105,9 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
// 包规范检查 // 包规范检查
"publint": "vsh publint", "publint": "vsh publint",
// 删除所有的node_modules、yarn.lock、package.lock.json重新安装依赖 // 删除所有的node_modules、yarn.lock、package.lock.json重新安装依赖
"reinstall": "pnpm clean --del-lock && pnpm bootstrap", "reinstall": "pnpm clean --del-lock && pnpm install",
// 运行 vitest 单元测试 // 运行 vitest 单元测试
"test:unit": "vitest", "test:unit": "vitest run --dom",
// 更新项目依赖 // 更新项目依赖
"update:deps": " pnpm update --latest --recursive", "update:deps": " pnpm update --latest --recursive",
// changeset生成提交集 // changeset生成提交集

View File

@@ -163,6 +163,8 @@ export async function deleteUserApi(user: UserInfo) {
/** /**
* 该文件可自行根据业务逻辑进行调整 * 该文件可自行根据业务逻辑进行调整
*/ */
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@@ -230,7 +232,7 @@ function createRequestClient(baseURL: string) {
}); });
// response数据解构 // response数据解构
client.addResponseInterceptor({ client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => { fulfilled: (response) => {
const { data: responseData, status } = response; const { data: responseData, status } = response;
@@ -256,7 +258,14 @@ function createRequestClient(baseURL: string) {
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string) => message.error(msg)), errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),
); );
return client; return client;

View File

@@ -248,7 +248,7 @@ const defaultPreferences: Preferences = {
width: 230, width: 230,
}, },
tabbar: { tabbar: {
dragable: true, draggable: true,
enable: true, enable: true,
height: 36, height: 36,
keepAlive: true, keepAlive: true,
@@ -256,7 +256,6 @@ const defaultPreferences: Preferences = {
showIcon: true, showIcon: true,
showMaximize: true, showMaximize: true,
showMore: true, showMore: true,
showRefresh: true,
styleType: 'chrome', styleType: 'chrome',
}, },
theme: { theme: {
@@ -282,6 +281,7 @@ const defaultPreferences: Preferences = {
languageToggle: true, languageToggle: true,
lockScreen: true, lockScreen: true,
notification: true, notification: true,
refresh: true,
sidebarToggle: true, sidebarToggle: true,
themeToggle: true, themeToggle: true,
}, },
@@ -430,7 +430,7 @@ interface ShortcutKeyPreferences {
interface TabbarPreferences { interface TabbarPreferences {
/** 是否开启多标签页拖拽 */ /** 是否开启多标签页拖拽 */
dragable: boolean; draggable: boolean;
/** 是否开启多标签页 */ /** 是否开启多标签页 */
enable: boolean; enable: boolean;
/** 标签页高度 */ /** 标签页高度 */
@@ -445,8 +445,6 @@ interface TabbarPreferences {
showMaximize: boolean; showMaximize: boolean;
/** 显示更多按钮 */ /** 显示更多按钮 */
showMore: boolean; showMore: boolean;
/** 显示刷新按钮 */
showRefresh: boolean;
/** 标签页风格 */ /** 标签页风格 */
styleType: TabsStyleType; styleType: TabsStyleType;
} }
@@ -494,6 +492,8 @@ interface WidgetPreferences {
lockScreen: boolean; lockScreen: boolean;
/** 是否显示通知部件 */ /** 是否显示通知部件 */
notification: boolean; notification: boolean;
/** 显示刷新按钮 */
refresh: boolean;
/** 是否显示侧边栏显示/隐藏部件 */ /** 是否显示侧边栏显示/隐藏部件 */
sidebarToggle: boolean; sidebarToggle: boolean;
/** 是否显示主题切换部件 */ /** 是否显示主题切换部件 */

View File

@@ -1,10 +1,14 @@
---
outline: deep
---
# 登录 # 登录
本文介绍如何去改造自己的应用程序登录页。 本文介绍如何去改造自己的应用程序登录页以及如何快速的对接登录页面接口
## 登录页面调整 ## 登录页面调整
如果你想调整登录页面的标题、描述和图标以及工具栏,你可以通过配置 `AuthPageLayout` 组件的 `props` 参数来实现。 如果你想调整登录页面的标题、描述和图标以及工具栏,你可以通过配置 `AuthPageLayout` 组件的参数来实现。
![login](/guide/login.png) ![login](/guide/login.png)
@@ -30,8 +34,6 @@
```vue ```vue
<AuthenticationLogin <AuthenticationLogin
:loading="authStore.loginLoading" :loading="authStore.loginLoading"
password-placeholder="123456"
username-placeholder="vben"
@submit="authStore.authLogin" @submit="authStore.authLogin"
/> />
``` ```
@@ -108,8 +110,111 @@
::: :::
::: tip ::: tip Note
如果这些配置不能满足你的需求,你可以自行实现登录表单及相关登录逻辑。 如果这些配置不能满足你的需求,你可以自行实现登录表单及相关登录逻辑或者给我们提交 `PR`
::: :::
## 接口对接流程
这里将会快速的介绍如何快速对接自己的后端。
### 前置条件
- 首先文档用的后端服务,接口返回的格式统一如下:
```ts
interface HttpResponse<T = any> {
/**
* 0 表示成功 其他表示失败
* 0 means success, others means fail
*/
code: number;
data: T;
message: string;
}
```
如果你不符合这个格式,你需要先阅读 [服务端交互](../essentials/server.md) 文档,改造你的`request.ts`配置。
- 其次你需要在先将本地代理地址改为你的真实后端地址,你可以在应用下的 `vite.config.mts` 内配置:
```ts
import { defineConfig } from '@vben/vite-config';
export default defineConfig(async () => {
return {
vite: {
server: {
proxy: {
'/api': {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
// 这里改为你的真实接口地址
target: 'http://localhost:5320/api',
ws: true,
},
},
},
},
};
});
```
### 登录接口
为了能正常登录,你的后端最少需要提供 `2-3` 个接口:
- 登录接口
接口地址可在应用下的 `src/api/core/auth` 内修改,以下为默认接口地址:
```ts
/**
* 登录
*/
export async function loginApi(data: AuthApi.LoginParams) {
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
}
/** 只需要保证登录接口返回值有 `accessToken` 字段即可 */
export interface LoginResult {
accessToken: string;
}
```
- 获取用户信息接口
接口地址可在应用下的 `src/api/core/user` 内修改,以下为默认接口地址:
```ts
export async function getUserInfoApi() {
return requestClient.get<UserInfo>('/user/info');
}
/** 只需要保证登录接口返回值有以下字段即可,多的字段可以自行使用 */
export interface UserInfo {
roles: string[];
realName: string;
}
```
- 获取权限码 (可选)
这个接口用于获取用户的权限码,权限码是用于控制用户的权限的,接口地址可在应用下的 `src/api/core/auth` 内修改,以下为默认接口地址:
```ts
export async function getAccessCodesApi() {
return requestClient.get<string[]>('/auth/codes');
}
```
如果你不需要这个权限,你只需要把代码改为返回一个空数组即可。
```ts {2}
export async function getAccessCodesApi() {
// 这里返回一个空数组即可
return [];
}
```

View File

@@ -1,3 +1,7 @@
---
outline: deep
---
# 精简版本 # 精简版本
`5.0` 版本开始,我们不再提供精简的仓库或者分支。我们的目标是提供一个更加一致的开发体验,同时减少维护成本。在这里,我们将如何介绍自己的项目,如何去精简以及移除不需要的功能。 `5.0` 版本开始,我们不再提供精简的仓库或者分支。我们的目标是提供一个更加一致的开发体验,同时减少维护成本。在这里,我们将如何介绍自己的项目,如何去精简以及移除不需要的功能。
@@ -8,7 +12,7 @@
```bash ```bash
apps/web-ele apps/web-ele
apps/web-native apps/web-naive
``` ```
@@ -74,3 +78,17 @@ pnpm install
- `.github` 文件夹用于存放 GitHub 的配置文件 - `.github` 文件夹用于存放 GitHub 的配置文件
- `.vscode` 文件夹用于存放 VSCode 的配置文件,如果你使用其他编辑器,可以删除 - `.vscode` 文件夹用于存放 VSCode 的配置文件,如果你使用其他编辑器,可以删除
- `./scripts/deploy` 文件夹用于存放部署脚本如果你不需要docker部署可以删除 - `./scripts/deploy` 文件夹用于存放部署脚本如果你不需要docker部署可以删除
## 应用精简
当你确定了某个应用,你还可以进一步精简:
### 删除不需要的路由及页面
- 在应用的 `src/router/routes` 文件中,你可以删除不需要的路由。其中 `core` 文件夹内,如果只需要登录和忘记密码,你可以删除其他路由,如忘记密码、注册等。路由删除后,你可以删除对应的页面文件,在 `src/views/_core` 文件夹中。
- 在应用的 `src/router/routes` 文件中,你可以按需求删除不需要的路由,如`demos``vben` 目录等。路由删除后,你可以删除对应的页面文件,在 `src/views` 文件夹中。
### 删除不需要的组件
- 在应用的 `packages/effects/common-ui/src/ui` 文件夹中,你可以删除不需要的组件,如`about``dashboard` 目录等。删除之前请先确保你的路由中没有引用到这些组件。

View File

@@ -42,16 +42,6 @@ pnpm vsh check-circular
pnpm vsh check-dep pnpm vsh check-dep
``` ```
### vsh clean
删除项目的`node_modules``dist``.turbo`等目录,清理项目。
#### 用法
```bash
pnpm vsh clean
```
#### 选项 #### 选项
| 选项 | 说明 | | 选项 | 说明 |

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/commitlint-config", "name": "@vben/commitlint-config",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -24,6 +24,7 @@ export async function node(): Promise<Linter.Config[]> {
'vite', 'vite',
'@vue/test-utils', '@vue/test-utils',
'@vben/tailwind-config', '@vben/tailwind-config',
'@playwright/test',
], ],
}, },
], ],

View File

@@ -26,6 +26,7 @@ export async function unicorn(): Promise<Linter.Config[]> {
'unicorn/prefer-at': 'off', 'unicorn/prefer-at': 'off',
'unicorn/prefer-dom-node-text-content': 'off', 'unicorn/prefer-dom-node-text-content': 'off',
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }], 'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }],
'unicorn/prefer-global-this': 'off',
'unicorn/prefer-top-level-await': 'off', 'unicorn/prefer-top-level-await': 'off',
'unicorn/prevent-abbreviations': 'off', 'unicorn/prevent-abbreviations': 'off',
}, },

View File

@@ -15,10 +15,17 @@ const customConfig: Linter.Config[] = [
}, },
}, },
{ {
files: ['packages/effects/**/**', 'packages/types/**/**'], files: [
'apps/**/**',
'packages/effects/**/**',
'packages/utils/**/**',
'packages/types/**/**',
'packages/locales/**/**',
],
ignores: restrictedImportIgnores, ignores: restrictedImportIgnores,
rules: { rules: {
'perfectionist/sort-interfaces': 'off', 'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-objects': 'off',
}, },
}, },
{ {
@@ -135,7 +142,15 @@ const customConfig: Linter.Config[] = [
}, },
}, },
{ {
files: ['internal/**/**'], files: ['**/**/playwright.config.ts'],
rules: {
'n/prefer-global/buffer': 'off',
'n/prefer-global/process': 'off',
'no-console': 'off',
},
},
{
files: ['internal/**/**', 'scripts/**/**'],
rules: { rules: {
'no-console': 'off', 'no-console': 'off',
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/stylelint-config", "name": "@vben/stylelint-config",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/node-utils", "name": "@vben/node-utils",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/tailwind-config", "name": "@vben/tailwind-config",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -1,6 +1,5 @@
import type { Config } from 'tailwindcss'; import type { Config } from 'tailwindcss';
import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { getPackagesSync } from '@vben/node-utils'; import { getPackagesSync } from '@vben/node-utils';
@@ -19,9 +18,9 @@ const tailwindPackages: string[] = [];
packages.forEach((pkg) => { packages.forEach((pkg) => {
// apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui // apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) { // if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
tailwindPackages.push(pkg.dir); tailwindPackages.push(pkg.dir);
} // }
}); });
const shadcnUiColors = { const shadcnUiColors = {
@@ -29,6 +28,7 @@ const shadcnUiColors = {
DEFAULT: 'hsl(var(--accent))', DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))', foreground: 'hsl(var(--accent-foreground))',
hover: 'hsl(var(--accent-hover))', hover: 'hsl(var(--accent-hover))',
lighter: 'has(val(--accent-lighter))',
}, },
background: { background: {
deep: 'hsl(var(--background-deep))', deep: 'hsl(var(--background-deep))',
@@ -90,7 +90,10 @@ const customColors = {
main: { main: {
DEFAULT: 'hsl(var(--main))', DEFAULT: 'hsl(var(--main))',
}, },
overlay: 'hsl(var(--overlay))', overlay: {
content: 'hsl(var(--overlay-content))',
DEFAULT: 'hsl(var(--overlay))',
},
red: { red: {
...createColorsPalette('red'), ...createColorsPalette('red'),
foreground: 'hsl(var(--destructive-foreground))', foreground: 'hsl(var(--destructive-foreground))',

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/tsconfig", "name": "@vben/tsconfig",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben/vite-config", "name": "@vben/vite-config",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
@@ -40,6 +40,7 @@
"vite-plugin-vue-devtools": "catalog:" "vite-plugin-vue-devtools": "catalog:"
}, },
"devDependencies": { "devDependencies": {
"@pnpm/workspace.read-manifest": "catalog:",
"@types/archiver": "catalog:", "@types/archiver": "catalog:",
"@types/html-minifier-terser": "catalog:", "@types/html-minifier-terser": "catalog:",
"@vben/node-utils": "workspace:*", "@vben/node-utils": "workspace:*",
@@ -53,6 +54,7 @@
"vite": "catalog:", "vite": "catalog:",
"vite-plugin-compression": "catalog:", "vite-plugin-compression": "catalog:",
"vite-plugin-dts": "catalog:", "vite-plugin-dts": "catalog:",
"vite-plugin-html": "catalog:" "vite-plugin-html": "catalog:",
"vite-plugin-lazy-import": "catalog:"
} }
} }

View File

@@ -47,6 +47,7 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
}, },
pwa: true, pwa: true,
pwaOptions: getDefaultPwaOptions(appTitle), pwaOptions: getDefaultPwaOptions(appTitle),
vxeTableLazyImport: true,
...envConfig, ...envConfig,
...application, ...application,
}); });

View File

@@ -26,6 +26,7 @@ import { viteMetadataPlugin } from './inject-metadata';
import { viteLicensePlugin } from './license'; import { viteLicensePlugin } from './license';
import { viteNitroMockPlugin } from './nitro-mock'; import { viteNitroMockPlugin } from './nitro-mock';
import { vitePrintPlugin } from './print'; import { vitePrintPlugin } from './print';
import { viteVxeTableImportsPlugin } from './vxe-table';
/** /**
* 获取条件成立的 vite 插件 * 获取条件成立的 vite 插件
@@ -110,6 +111,7 @@ async function loadApplicationPlugins(
printInfoMap, printInfoMap,
pwa, pwa,
pwaOptions, pwaOptions,
vxeTableLazyImport,
...commonOptions ...commonOptions
} = options; } = options;
@@ -135,6 +137,12 @@ async function loadApplicationPlugins(
return [await vitePrintPlugin({ infoMap: printInfoMap })]; return [await vitePrintPlugin({ infoMap: printInfoMap })];
}, },
}, },
{
condition: vxeTableLazyImport,
plugins: async () => {
return [await viteVxeTableImportsPlugin()];
},
},
{ {
condition: nitroMock, condition: nitroMock,
plugins: async () => { plugins: async () => {

View File

@@ -1,20 +1,35 @@
import type { PluginOption } from 'vite'; import type { PluginOption } from 'vite';
import { dateUtil, getPackages, readPackageJSON } from '@vben/node-utils'; import {
dateUtil,
findMonorepoRoot,
getPackages,
readPackageJSON,
} from '@vben/node-utils';
import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest';
function resolvePackageVersion( function resolvePackageVersion(
pkgsMeta: Record<string, string>, pkgsMeta: Record<string, string>,
name: string, name: string,
value: string, value: string,
catalog: Record<string, string>,
) { ) {
if (value.includes('catalog:')) {
return catalog[name];
}
if (value.includes('workspace')) { if (value.includes('workspace')) {
return pkgsMeta[name]; return pkgsMeta[name];
} }
return value; return value;
} }
async function resolveMonorepoDependencies() { async function resolveMonorepoDependencies() {
const { packages } = await getPackages(); const { packages } = await getPackages();
const manifest = await readWorkspaceManifest(findMonorepoRoot());
const catalog = manifest?.catalog || {};
const resultDevDependencies: Record<string, string | undefined> = {}; const resultDevDependencies: Record<string, string | undefined> = {};
const resultDependencies: Record<string, string | undefined> = {}; const resultDependencies: Record<string, string | undefined> = {};
@@ -27,10 +42,20 @@ async function resolveMonorepoDependencies() {
for (const { packageJson } of packages) { for (const { packageJson } of packages) {
const { dependencies = {}, devDependencies = {} } = packageJson; const { dependencies = {}, devDependencies = {} } = packageJson;
for (const [key, value] of Object.entries(dependencies)) { for (const [key, value] of Object.entries(dependencies)) {
resultDependencies[key] = resolvePackageVersion(pkgsMeta, key, value); resultDependencies[key] = resolvePackageVersion(
pkgsMeta,
key,
value,
catalog,
);
} }
for (const [key, value] of Object.entries(devDependencies)) { for (const [key, value] of Object.entries(devDependencies)) {
resultDevDependencies[key] = resolvePackageVersion(pkgsMeta, key, value); resultDevDependencies[key] = resolvePackageVersion(
pkgsMeta,
key,
value,
catalog,
);
} }
} }
return { return {

View File

@@ -0,0 +1,20 @@
import type { PluginOption } from 'vite';
import { lazyImport, VxeResolver } from 'vite-plugin-lazy-import';
async function viteVxeTableImportsPlugin(): Promise<PluginOption> {
return [
lazyImport({
resolvers: [
VxeResolver({
libraryName: 'vxe-table',
}),
VxeResolver({
libraryName: 'vxe-pc-ui',
}),
],
}),
];
}
export { viteVxeTableImportsPlugin };

View File

@@ -123,6 +123,8 @@ interface ApplicationPluginOptions extends CommonPluginOptions {
pwa?: boolean; pwa?: boolean;
/** pwa 插件配置 */ /** pwa 插件配置 */
pwaOptions?: Partial<PwaPluginOptions>; pwaOptions?: Partial<PwaPluginOptions>;
/** 是否开启vxe-table懒加载 */
vxeTableLazyImport?: boolean;
} }
interface LibraryPluginOptions extends CommonPluginOptions { interface LibraryPluginOptions extends CommonPluginOptions {
@@ -137,12 +139,12 @@ type ApplicationOptions = ApplicationPluginOptions;
type LibraryOptions = LibraryPluginOptions; type LibraryOptions = LibraryPluginOptions;
type DefineApplicationOptions = (config: ConfigEnv) => Promise<{ type DefineApplicationOptions = (config?: ConfigEnv) => Promise<{
application?: ApplicationOptions; application?: ApplicationOptions;
vite?: UserConfig; vite?: UserConfig;
}>; }>;
type DefineLibraryOptions = (config: ConfigEnv) => Promise<{ type DefineLibraryOptions = (config?: ConfigEnv) => Promise<{
library?: LibraryOptions; library?: LibraryOptions;
vite?: UserConfig; vite?: UserConfig;
}>; }>;

View File

@@ -1,6 +1,6 @@
{ {
"name": "vben-admin-pro", "name": "vben-admin-pro",
"version": "5.3.0", "version": "5.4.0-beta.1",
"private": true, "private": true,
"keywords": [ "keywords": [
"monorepo", "monorepo",
@@ -25,11 +25,10 @@
}, },
"type": "module", "type": "module",
"scripts": { "scripts": {
"bootstrap": "pnpm install",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
"build:analyze": "turbo build:analyze", "build:analyze": "turbo build:analyze",
"build:docker": "./build-local-docker-image.sh",
"build:antd": "pnpm run build --filter=@vben/web-antd", "build:antd": "pnpm run build --filter=@vben/web-antd",
"build:docker": "./build-local-docker-image.sh",
"build:docs": "pnpm run build --filter=@vben/docs", "build:docs": "pnpm run build --filter=@vben/docs",
"build:ele": "pnpm run build --filter=@vben/web-ele", "build:ele": "pnpm run build --filter=@vben/web-ele",
"build:naive": "pnpm run build --filter=@vben/web-naive", "build:naive": "pnpm run build --filter=@vben/web-naive",
@@ -40,7 +39,7 @@
"check:cspell": "cspell lint **/*.ts **/README.md .changeset/*.md --no-progress", "check:cspell": "cspell lint **/*.ts **/README.md .changeset/*.md --no-progress",
"check:dep": "vsh check-dep", "check:dep": "vsh check-dep",
"check:type": "turbo run typecheck", "check:type": "turbo run typecheck",
"clean": "vsh clean", "clean": "node ./scripts/clean.mjs",
"commit": "czg", "commit": "czg",
"dev": "turbo-run dev", "dev": "turbo-run dev",
"dev:antd": "pnpm -F @vben/web-antd run dev", "dev:antd": "pnpm -F @vben/web-antd run dev",
@@ -50,20 +49,21 @@
"dev:play": "pnpm -F @vben/playground run dev", "dev:play": "pnpm -F @vben/playground run dev",
"format": "vsh lint --format", "format": "vsh lint --format",
"lint": "vsh lint", "lint": "vsh lint",
"postinstall": "turbo run stub", "postinstall": "pnpm -r run stub --if-present",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"prepare": "is-ci || husky", "prepare": "is-ci || husky",
"preview": "turbo-run preview", "preview": "turbo-run preview",
"publint": "vsh publint", "publint": "vsh publint",
"reinstall": "pnpm clean --del-lock && pnpm bootstrap", "reinstall": "pnpm clean --del-lock && pnpm install",
"test:unit": "vitest", "test:unit": "vitest run --dom",
"test:e2e": "turbo run test:e2e",
"update:deps": "pnpm update --latest --recursive", "update:deps": "pnpm update --latest --recursive",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile" "version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
}, },
"devDependencies": { "devDependencies": {
"@changesets/changelog-github": "catalog:", "@changesets/changelog-github": "catalog:",
"@changesets/cli": "catalog:", "@changesets/cli": "catalog:",
"@types/jsdom": "catalog:", "@playwright/test": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"@vben/commitlint-config": "workspace:*", "@vben/commitlint-config": "workspace:*",
"@vben/eslint-config": "workspace:*", "@vben/eslint-config": "workspace:*",
@@ -80,10 +80,11 @@
"autoprefixer": "catalog:", "autoprefixer": "catalog:",
"cross-env": "catalog:", "cross-env": "catalog:",
"cspell": "catalog:", "cspell": "catalog:",
"happy-dom": "catalog:",
"husky": "catalog:", "husky": "catalog:",
"is-ci": "catalog:", "is-ci": "catalog:",
"jsdom": "catalog:",
"lint-staged": "catalog:", "lint-staged": "catalog:",
"playwright": "catalog:",
"rimraf": "catalog:", "rimraf": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"turbo": "catalog:", "turbo": "catalog:",
@@ -98,7 +99,7 @@
"node": ">=20.10.0", "node": ">=20.10.0",
"pnpm": ">=9.5.0" "pnpm": ">=9.5.0"
}, },
"packageManager": "pnpm@9.11.0", "packageManager": "pnpm@9.12.1",
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {
"allowedVersions": { "allowedVersions": {
@@ -106,15 +107,14 @@
} }
}, },
"overrides": { "overrides": {
"@ctrl/tinycolor": "4.1.0", "@ctrl/tinycolor": "catalog:",
"clsx": "2.1.1", "clsx": "catalog:",
"pinia": "2.2.2", "pinia": "catalog:",
"vue": "3.5.7" "vue": "catalog:"
}, },
"neverBuiltDependencies": [ "neverBuiltDependencies": [
"canvas", "canvas",
"node-gyp", "node-gyp"
"playwright"
] ]
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/design", "name": "@vben-core/design",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -82,11 +82,11 @@
@apply opacity-100; @apply opacity-100;
} }
input:-webkit-autofill { /* input:-webkit-autofill {
@apply border-none; @apply border-none;
box-shadow: 0 0 0 1000px transparent inset; box-shadow: 0 0 0 1000px transparent inset;
} } */
input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button { input[type='number']::-webkit-outer-spin-button {

View File

@@ -39,6 +39,11 @@
/* Used for success actions such as <message> */ /* Used for success actions such as <message> */
--info: 180, 1.54%, 12.75%;
--info-foreground: 220, 4%, 58%;
/* Used for success actions such as <message> */
--success: 144 57% 58%; --success: 144 57% 58%;
--success-foreground: 0 0% 98%; --success-foreground: 0 0% 98%;
@@ -53,6 +58,7 @@
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
--accent: 216 5% 19%; --accent: 216 5% 19%;
--accent-lighter: 216 5% 11%;
--accent-hover: 216 5% 24%; --accent-hover: 216 5% 24%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%;
@@ -78,6 +84,7 @@
/* 遮罩颜色 */ /* 遮罩颜色 */
--overlay: 0deg 0% 0% / 40%; --overlay: 0deg 0% 0% / 40%;
--overlay-content: 0deg 0% 0% / 40%;
/* 基本文字大小 */ /* 基本文字大小 */
--font-size-base: 16px; --font-size-base: 16px;

View File

@@ -38,6 +38,11 @@
/* Used for success actions such as <message> */ /* Used for success actions such as <message> */
--info: 240, 5%, 96%;
--info-foreground: 220, 4%, 58%;
/* Used for success actions such as <message> */
--success: 144 57% 58%; --success: 144 57% 58%;
--success-foreground: 0 0% 98%; --success-foreground: 0 0% 98%;
@@ -53,6 +58,7 @@
/* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */ /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
--accent: 240 5% 96%; --accent: 240 5% 96%;
--accent-lighter: 240 0% 98%;
--accent-hover: 200deg 10% 90%; --accent-hover: 200deg 10% 90%;
--accent-foreground: 240 6% 10%; --accent-foreground: 240 6% 10%;
@@ -78,7 +84,7 @@
/* 遮罩颜色 */ /* 遮罩颜色 */
--overlay: 0 0% 0% / 45%; --overlay: 0 0% 0% / 45%;
--overlay-light: 0 0% 95% / 45%; --overlay-content: 0 0% 95% / 45%;
/* 基本文字大小 */ /* 基本文字大小 */
--font-size-base: 16px; --font-size-base: 16px;

View File

@@ -3,5 +3,19 @@ import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({ export default defineBuildConfig({
clean: true, clean: true,
declaration: true, declaration: true,
entries: ['src/index'], entries: [
{
builder: 'mkdist',
input: './src',
loaders: ['vue'],
pattern: ['**/*.vue'],
},
{
builder: 'mkdist',
format: 'esm',
input: './src',
loaders: ['js'],
pattern: ['**/*.ts'],
},
],
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/icons", "name": "@vben-core/icons",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -0,0 +1,27 @@
<template>
<svg
height="41"
viewBox="0 0 64 41"
width="64"
xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fill-rule="evenodd" transform="translate(0 1)">
<ellipse
cx="32"
cy="33"
fill="hsl(var(--background-deep))"
rx="32"
ry="7"
/>
<g fill-rule="nonzero" stroke="hsl(var(--heavy))">
<path
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
/>
<path
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
fill="hsl(var(--accent))"
/>
</g>
</g>
</svg>
</template>

View File

@@ -1,4 +1,5 @@
export { default as EmptyIcon } from './components/empty.vue';
export * from './create-icon'; export * from './create-icon';
export * from './lucide';
export * from './lucide';
export * from '@iconify/vue'; export * from '@iconify/vue';

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/shared", "name": "@vben-core/shared",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -1,9 +1,11 @@
/** /** layout content 组件的高度 */
* @zh_CN 布局内容高度 css变量
* @en_US Layout content height
*/
export const CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT = `--vben-content-height`; export const CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT = `--vben-content-height`;
/** layout content 组件的宽度 */
export const CSS_VARIABLE_LAYOUT_CONTENT_WIDTH = `--vben-content-width`; export const CSS_VARIABLE_LAYOUT_CONTENT_WIDTH = `--vben-content-width`;
/** layout header 组件的高度 */
export const CSS_VARIABLE_LAYOUT_HEADER_HEIGHT = `--vben-header-height`;
/** layout footer 组件的高度 */
export const CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT = `--vben-footer-height`;
/** /**
* @zh_CN 默认命名空间 * @zh_CN 默认命名空间

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { bindMethods } from '../util'; import { bindMethods, getNestedValue } from '../util';
class TestClass { class TestClass {
public value: string; public value: string;
@@ -78,3 +78,79 @@ describe('bindMethods', () => {
expect(value).toBe('test'); expect(value).toBe('test');
}); });
}); });
describe('getNestedValue', () => {
interface UserProfile {
age: number;
name: string;
}
interface UserSettings {
theme: string;
}
interface Data {
user: {
profile: UserProfile;
settings: UserSettings;
};
}
const data: Data = {
user: {
profile: {
age: 25,
name: 'Alice',
},
settings: {
theme: 'dark',
},
},
};
it('should get a nested value when the path is valid', () => {
const result = getNestedValue(data, 'user.profile.name');
expect(result).toBe('Alice');
});
it('should return undefined for non-existent property', () => {
const result = getNestedValue(data, 'user.profile.gender');
expect(result).toBeUndefined();
});
it('should return undefined when accessing a non-existent deep path', () => {
const result = getNestedValue(data, 'user.nonexistent.field');
expect(result).toBeUndefined();
});
it('should return undefined if a middle level is undefined', () => {
const result = getNestedValue({ user: undefined }, 'user.profile.name');
expect(result).toBeUndefined();
});
it('should return the correct value for a nested setting', () => {
const result = getNestedValue(data, 'user.settings.theme');
expect(result).toBe('dark');
});
it('should work for a single-level path', () => {
const result = getNestedValue({ a: 1, b: 2 }, 'b');
expect(result).toBe(2);
});
it('should return the entire object if path is empty', () => {
expect(() => getNestedValue(data, '')()).toThrow();
});
it('should handle paths with array indexes', () => {
const complexData = { list: [{ name: 'Item1' }, { name: 'Item2' }] };
const result = getNestedValue(complexData, 'list.1.name');
expect(result).toBe('Item2');
});
it('should return undefined when accessing an out-of-bounds array index', () => {
const complexData = { list: [{ name: 'Item1' }] };
const result = getNestedValue(complexData, 'list.2.name');
expect(result).toBeUndefined();
});
});

View File

@@ -85,3 +85,11 @@ export function needsScrollbar() {
// 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断 // 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断
return doc.scrollHeight > window.innerHeight; return doc.scrollHeight > window.innerHeight;
} }
export function triggerWindowResize(): void {
// 创建一个新的 resize 事件
const resizeEvent = new Event('resize');
// 触发 window 的 resize 事件
window.dispatchEvent(resizeEvent);
}

View File

@@ -1 +1,10 @@
import { createDefu } from 'defu';
export { createDefu as createMerge, defu as merge } from 'defu'; export { createDefu as createMerge, defu as merge } from 'defu';
export const mergeWithArrayOverride = createDefu((originObj, key, updates) => {
if (Array.isArray(originObj[key]) && Array.isArray(updates)) {
originObj[key] = updates;
return true;
}
});

View File

@@ -17,3 +17,28 @@ export function bindMethods<T extends object>(instance: T): void {
} }
}); });
} }
/**
* 获取嵌套对象的字段值
* @param obj - 要查找的对象
* @param path - 用于查找字段的路径,使用小数点分隔
* @returns 字段值,或者未找到时返回 undefined
*/
export function getNestedValue<T>(obj: T, path: string): any {
if (typeof path !== 'string' || path.length === 0) {
throw new Error('Path must be a non-empty string');
}
// 把路径字符串按 "." 分割成数组
const keys = path.split('.') as (number | string)[];
let current: any = obj;
for (const key of keys) {
if (current === null || current === undefined) {
return undefined;
}
current = current[key as keyof typeof current];
}
return current;
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/typings", "name": "@vben-core/typings",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vben-core/composables", "name": "@vben-core/composables",
"version": "5.3.0", "version": "5.4.0-beta.1",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@@ -1,5 +1,5 @@
export * from './use-content-style';
export * from './use-is-mobile'; export * from './use-is-mobile';
export * from './use-layout-style';
export * from './use-namespace'; export * from './use-namespace';
export * from './use-priority-value'; export * from './use-priority-value';
export * from './use-scroll-lock'; export * from './use-scroll-lock';

View File

@@ -4,6 +4,8 @@ import { computed, onMounted, onUnmounted, ref } from 'vue';
import { import {
CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT,
CSS_VARIABLE_LAYOUT_CONTENT_WIDTH, CSS_VARIABLE_LAYOUT_CONTENT_WIDTH,
CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT,
CSS_VARIABLE_LAYOUT_HEADER_HEIGHT,
} from '@vben-core/shared/constants'; } from '@vben-core/shared/constants';
import { import {
getElementVisibleRect, getElementVisibleRect,
@@ -15,7 +17,7 @@ import { useCssVar, useDebounceFn } from '@vueuse/core';
/** /**
* @zh_CN content style * @zh_CN content style
*/ */
function useContentStyle() { export function useLayoutContentStyle() {
let resizeObserver: null | ResizeObserver = null; let resizeObserver: null | ResizeObserver = null;
const contentElement = ref<HTMLDivElement | null>(null); const contentElement = ref<HTMLDivElement | null>(null);
const visibleDomRect = ref<null | VisibleDomRect>(null); const visibleDomRect = ref<null | VisibleDomRect>(null);
@@ -40,7 +42,7 @@ function useContentStyle() {
contentHeight.value = `${visibleDomRect.value.height}px`; contentHeight.value = `${visibleDomRect.value.height}px`;
contentWidth.value = `${visibleDomRect.value.width}px`; contentWidth.value = `${visibleDomRect.value.width}px`;
}, },
100, 16,
); );
onMounted(() => { onMounted(() => {
@@ -58,4 +60,28 @@ function useContentStyle() {
return { contentElement, overlayStyle, visibleDomRect }; return { contentElement, overlayStyle, visibleDomRect };
} }
export { useContentStyle }; export function useLayoutHeaderStyle() {
const headerHeight = useCssVar(CSS_VARIABLE_LAYOUT_HEADER_HEIGHT);
return {
getLayoutHeaderHeight: () => {
return Number.parseInt(`${headerHeight.value}`, 10);
},
setLayoutHeaderHeight: (height: number) => {
headerHeight.value = `${height}px`;
},
};
}
export function useLayoutFooterStyle() {
const footerHeight = useCssVar(CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT);
return {
getLayoutFooterHeight: () => {
return Number.parseInt(`${footerHeight.value}`, 10);
},
setLayoutFooterHeight: (height: number) => {
footerHeight.value = `${height}px`;
},
};
}

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