This commit is contained in:
dap 2024-09-20 08:06:53 +08:00
commit 587355dbaf
69 changed files with 2200 additions and 1218 deletions

View File

@ -10,11 +10,11 @@
"start": "nitro dev"
},
"dependencies": {
"jsonwebtoken": "^9.0.2",
"nitropack": "^2.9.7"
"jsonwebtoken": "catalog:",
"nitropack": "catalog:"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.7",
"h3": "^1.12.0"
"@types/jsonwebtoken": "catalog:",
"h3": "catalog:"
}
}

View File

@ -41,22 +41,22 @@
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"ant-design-vue": "^4.2.4",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.5.1",
"jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21",
"pinia": "2.2.2",
"tinymce": "^7.3.0",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"tinymce": "^7.3.0"
},
"devDependencies": {
"@types/crypto-js": "^4.2.2",
"@types/lodash-es": "^4.17.12",
"unplugin-vue-components": "^0.27.3"
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
"dayjs": "catalog:",
"pinia": "catalog:",
"unplugin-vue-components": "^0.27.3",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -40,14 +40,14 @@
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"dayjs": "^1.11.13",
"element-plus": "^2.8.3",
"pinia": "2.2.2",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"@vueuse/core": "catalog:",
"dayjs": "catalog:",
"element-plus": "catalog:",
"pinia": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
},
"devDependencies": {
"unplugin-element-plus": "^0.8.0"
"unplugin-element-plus": "catalog:"
}
}

View File

@ -77,7 +77,11 @@ export const useAuthStore = defineStore('auth', () => {
}
async function logout(redirect: boolean = true) {
await logoutApi();
try {
await logoutApi();
} catch {
// 不做任何处理
}
resetAllStores();
accessStore.setLoginExpired(false);

View File

@ -40,10 +40,10 @@
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"naive-ui": "^2.39.0",
"pinia": "2.2.2",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"@vueuse/core": "catalog:",
"naive-ui": "catalog:",
"pinia": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -77,7 +77,11 @@ export const useAuthStore = defineStore('auth', () => {
}
async function logout(redirect: boolean = true) {
await logoutApi();
try {
await logoutApi();
} catch {
// 不做任何处理
}
resetAllStores();
accessStore.setLoginExpired(false);

View File

@ -11,16 +11,16 @@
"@vben-core/shadcn-ui": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/styles": "workspace:*",
"lucide-vue-next": "^0.441.0",
"medium-zoom": "^1.1.0",
"radix-vue": "^1.9.5",
"vitepress-plugin-group-icons": "^1.2.4"
"lucide-vue-next": "catalog:",
"medium-zoom": "catalog:",
"radix-vue": "catalog:",
"vitepress-plugin-group-icons": "catalog:"
},
"devDependencies": {
"@nolebase/vitepress-plugin-git-changelog": "^2.5.0",
"@nolebase/vitepress-plugin-git-changelog": "catalog:",
"@vben/vite-config": "workspace:*",
"@vite-pwa/vitepress": "^0.5.3",
"vitepress": "^1.3.4",
"vue": "^3.5.6"
"@vite-pwa/vitepress": "catalog:",
"vitepress": "catalog:",
"vue": "catalog:"
}
}

View File

@ -6,6 +6,14 @@ outline: deep
框架提供的抽屉组件,支持`自动高度`、`loading`等功能。
> 如果文档内没有参数说明,可以尝试在在线示例内寻找
::: info 写在前面
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
:::
## 基础用法
使用 `useVbenDrawer` 创建最基础的模态框。

View File

@ -6,6 +6,14 @@ outline: deep
框架提供的模态框组件,支持`拖拽`、`全屏`、`自动高度`、`loading`等功能。
> 如果文档内没有参数说明,可以尝试在在线示例内寻找
::: info 写在前面
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
:::
## 基础用法
使用 `useVbenModal` 创建最基础的模态框。

View File

@ -2,7 +2,7 @@
::: info README
该文档介绍的是框架组件的使用方法、属性、事件等。如果你觉得组件封装的不好,或者不符合你的需求,你可以直接使用原生的组件,或者自己封装一个组件,不需要拘泥于框架提供的组件。我们只是提供了一些常用的组件,方便你快速开发。是否使用,取决于你的需求
该文档介绍的是框架组件的使用方法、属性、事件等。如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由
:::

View File

@ -23,11 +23,11 @@
}
},
"dependencies": {
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@commitlint/cli": "catalog:",
"@commitlint/config-conventional": "catalog:",
"@vben/node-utils": "workspace:*",
"commitlint-plugin-function-rules": "^4.0.0",
"cz-git": "^1.9.4",
"czg": "^1.9.4"
"commitlint-plugin-function-rules": "catalog:",
"cz-git": "catalog:",
"czg": "catalog:"
}
}

View File

@ -27,31 +27,30 @@
}
},
"dependencies": {
"eslint-config-turbo": "^2.1.2",
"eslint-plugin-command": "^0.2.5",
"eslint-plugin-import-x": "^4.2.1"
"eslint-config-turbo": "catalog:",
"eslint-plugin-command": "catalog:",
"eslint-plugin-import-x": "catalog:"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@types/eslint": "^9.6.1",
"@typescript-eslint/eslint-plugin": "^8.6.0",
"@typescript-eslint/parser": "^8.6.0",
"eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-jsdoc": "^50.2.3",
"eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-n": "^17.10.3",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-perfectionist": "^3.6.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-regexp": "^2.6.0",
"eslint-plugin-unicorn": "^55.0.0",
"eslint-plugin-unused-imports": "^4.1.4",
"eslint-plugin-vitest": "^0.5.4",
"eslint-plugin-vue": "^9.28.0",
"globals": "^15.9.0",
"jsonc-eslint-parser": "^2.4.0",
"vue-eslint-parser": "^9.4.3"
"@eslint/js": "catalog:",
"@types/eslint": "catalog:",
"@typescript-eslint/eslint-plugin": "catalog:",
"@typescript-eslint/parser": "catalog:",
"eslint": "catalog:",
"eslint-plugin-eslint-comments": "catalog:",
"eslint-plugin-jsdoc": "catalog:",
"eslint-plugin-jsonc": "catalog:",
"eslint-plugin-n": "catalog:",
"eslint-plugin-no-only-tests": "catalog:",
"eslint-plugin-perfectionist": "catalog:",
"eslint-plugin-prettier": "catalog:",
"eslint-plugin-regexp": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"eslint-plugin-unused-imports": "catalog:",
"eslint-plugin-vitest": "catalog:",
"eslint-plugin-vue": "catalog:",
"globals": "catalog:",
"jsonc-eslint-parser": "catalog:",
"vue-eslint-parser": "catalog:"
}
}

View File

@ -22,7 +22,7 @@
}
},
"dependencies": {
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6"
"prettier": "catalog:",
"prettier-plugin-tailwindcss": "catalog:"
}
}

View File

@ -23,21 +23,21 @@
}
},
"dependencies": {
"@stylistic/stylelint-plugin": "^3.0.1",
"stylelint-config-recess-order": "^5.1.0",
"stylelint-scss": "^6.6.0"
"@stylistic/stylelint-plugin": "catalog:",
"stylelint-config-recess-order": "catalog:",
"stylelint-scss": "catalog:"
},
"devDependencies": {
"postcss": "^8.4.47",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.3.3",
"stylelint": "^16.9.0",
"stylelint-config-recommended": "^14.0.1",
"stylelint-config-recommended-scss": "^14.1.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.1",
"stylelint-order": "^6.0.4",
"stylelint-prettier": "^5.0.2"
"postcss": "catalog:",
"postcss-html": "catalog:",
"postcss-scss": "catalog:",
"prettier": "catalog:",
"stylelint": "catalog:",
"stylelint-config-recommended": "catalog:",
"stylelint-config-recommended-scss": "catalog:",
"stylelint-config-recommended-vue": "catalog:",
"stylelint-config-standard": "catalog:",
"stylelint-order": "catalog:",
"stylelint-prettier": "catalog:"
}
}

View File

@ -28,20 +28,20 @@
}
},
"dependencies": {
"@changesets/git": "^3.0.1",
"@manypkg/get-packages": "^2.2.2",
"chalk": "^5.3.0",
"consola": "^3.2.3",
"dayjs": "^1.11.13",
"execa": "^9.4.0",
"find-up": "^7.0.0",
"nanoid": "^5.0.7",
"ora": "^8.1.0",
"pkg-types": "^1.2.0",
"prettier": "^3.3.3",
"rimraf": "^6.0.1"
"@changesets/git": "catalog:",
"@manypkg/get-packages": "catalog:",
"chalk": "catalog:",
"consola": "catalog:",
"dayjs": "catalog:",
"execa": "catalog:",
"find-up": "catalog:",
"nanoid": "catalog:",
"ora": "catalog:",
"pkg-types": "catalog:",
"prettier": "catalog:",
"rimraf": "catalog:"
},
"devDependencies": {
"@types/chalk": "^2.2.0"
"@types/chalk": "catalog:"
}
}

View File

@ -46,21 +46,21 @@
"tailwindcss": "^3.4.3"
},
"dependencies": {
"@iconify/json": "^2.2.250",
"@iconify/tailwind": "^1.1.3",
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
"@tailwindcss/typography": "^0.5.15",
"autoprefixer": "^10.4.20",
"cssnano": "^7.0.6",
"postcss": "^8.4.47",
"postcss-antd-fixes": "^0.2.0",
"postcss-import": "^16.1.0",
"postcss-preset-env": "^10.0.3",
"tailwindcss": "^3.4.12",
"tailwindcss-animate": "^1.0.7"
"@iconify/json": "catalog:",
"@iconify/tailwind": "catalog:",
"@tailwindcss/nesting": "catalog:",
"@tailwindcss/typography": "catalog:",
"autoprefixer": "catalog:",
"cssnano": "catalog:",
"postcss": "catalog:",
"postcss-antd-fixes": "catalog:",
"postcss-import": "catalog:",
"postcss-preset-env": "catalog:",
"tailwindcss": "catalog:",
"tailwindcss-animate": "catalog:"
},
"devDependencies": {
"@types/postcss-import": "^14.0.3",
"@types/postcss-import": "catalog:",
"@vben/node-utils": "workspace:*"
}
}

View File

@ -20,6 +20,6 @@
],
"dependencies": {
"@vben/types": "workspace:*",
"vite": "^5.4.6"
"vite": "catalog:"
}
}

View File

@ -27,32 +27,32 @@
}
},
"dependencies": {
"@intlify/unplugin-vue-i18n": "^5.0.0",
"@jspm/generator": "^2.3.1",
"archiver": "^7.0.1",
"cheerio": "1.0.0",
"get-port": "^7.1.0",
"html-minifier-terser": "^7.2.0",
"nitropack": "^2.9.7",
"resolve.exports": "^2.0.2",
"vite-plugin-lib-inject-css": "^2.1.1",
"vite-plugin-pwa": "^0.20.5",
"vite-plugin-vue-devtools": "^7.4.5"
"@intlify/unplugin-vue-i18n": "catalog:",
"@jspm/generator": "catalog:",
"archiver": "catalog:",
"cheerio": "catalog:",
"get-port": "catalog:",
"html-minifier-terser": "catalog:",
"nitropack": "catalog:",
"resolve.exports": "catalog:",
"vite-plugin-lib-inject-css": "catalog:",
"vite-plugin-pwa": "catalog:",
"vite-plugin-vue-devtools": "catalog:"
},
"devDependencies": {
"@types/archiver": "^6.0.2",
"@types/html-minifier-terser": "^7.0.2",
"@types/archiver": "catalog:",
"@types/html-minifier-terser": "catalog:",
"@vben/node-utils": "workspace:*",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"rollup": "^4.21.3",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.79.1",
"vite": "^5.4.6",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-dts": "4.2.1",
"vite-plugin-html": "^3.2.2"
"@vitejs/plugin-vue": "catalog:",
"@vitejs/plugin-vue-jsx": "catalog:",
"dayjs": "catalog:",
"dotenv": "catalog:",
"rollup": "catalog:",
"rollup-plugin-visualizer": "catalog:",
"sass": "catalog:",
"vite": "catalog:",
"vite-plugin-compression": "catalog:",
"vite-plugin-dts": "catalog:",
"vite-plugin-html": "catalog:"
}
}

View File

@ -61,10 +61,10 @@
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
},
"devDependencies": {
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.8",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.5.5",
"@changesets/changelog-github": "catalog:",
"@changesets/cli": "catalog:",
"@types/jsdom": "catalog:",
"@types/node": "catalog:",
"@vben/commitlint-config": "workspace:*",
"@vben/eslint-config": "workspace:*",
"@vben/prettier-config": "workspace:*",
@ -74,25 +74,25 @@
"@vben/turbo-run": "workspace:*",
"@vben/vite-config": "workspace:*",
"@vben/vsh": "workspace:*",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
"cspell": "^8.14.4",
"husky": "^9.1.6",
"is-ci": "^3.0.1",
"jsdom": "^25.0.0",
"lint-staged": "^15.2.10",
"rimraf": "^6.0.1",
"tailwindcss": "^3.4.12",
"turbo": "^2.1.2",
"typescript": "^5.6.2",
"unbuild": "^2.0.0",
"vite": "^5.4.6",
"vitest": "^2.1.1",
"vue": "^3.5.6",
"vue-tsc": "^2.1.6"
"@vitejs/plugin-vue": "catalog:",
"@vitejs/plugin-vue-jsx": "catalog:",
"@vue/test-utils": "catalog:",
"autoprefixer": "catalog:",
"cross-env": "catalog:",
"cspell": "catalog:",
"husky": "catalog:",
"is-ci": "catalog:",
"jsdom": "catalog:",
"lint-staged": "catalog:",
"rimraf": "catalog:",
"tailwindcss": "catalog:",
"turbo": "catalog:",
"typescript": "catalog:",
"unbuild": "catalog:",
"vite": "catalog:",
"vitest": "catalog:",
"vue": "catalog:",
"vue-tsc": "catalog:"
},
"engines": {
"node": ">=20",

View File

@ -34,8 +34,8 @@
}
},
"dependencies": {
"@iconify/vue": "^4.1.2",
"lucide-vue-next": "^0.441.0",
"vue": "^3.5.6"
"@iconify/vue": "catalog:",
"lucide-vue-next": "catalog:",
"vue": "catalog:"
}
}

View File

@ -69,19 +69,19 @@
}
},
"dependencies": {
"@ctrl/tinycolor": "^4.1.0",
"@tanstack/vue-store": "^0.5.5",
"@vue/reactivity": "^3.5.6",
"@vue/shared": "^3.5.6",
"clsx": "^2.1.1",
"defu": "^6.1.4",
"lodash.clonedeep": "^4.5.0",
"nprogress": "^0.2.0",
"tailwind-merge": "^2.5.2",
"theme-colors": "^0.1.0"
"@ctrl/tinycolor": "catalog:",
"@tanstack/vue-store": "catalog:",
"@vue/reactivity": "catalog:",
"@vue/shared": "catalog:",
"clsx": "catalog:",
"defu": "catalog:",
"lodash.clonedeep": "catalog:",
"nprogress": "catalog:",
"tailwind-merge": "catalog:",
"theme-colors": "catalog:"
},
"devDependencies": {
"@types/lodash.clonedeep": "^4.5.9",
"@types/nprogress": "^0.2.3"
"@types/lodash.clonedeep": "catalog:",
"@types/nprogress": "catalog:"
}
}

View File

@ -69,3 +69,19 @@ export function getScrollbarWidth() {
scrollDiv.remove();
return scrollbarWidth;
}
export function needsScrollbar() {
const doc = document.documentElement;
const body = document.body;
// 检查 body 的 overflow-y 样式
const overflowY = window.getComputedStyle(body).overflowY;
// 如果明确设置了需要滚动条的样式
if (overflowY === 'scroll' || overflowY === 'auto') {
return doc.scrollHeight > window.innerHeight;
}
// 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断
return doc.scrollHeight > window.innerHeight;
}

View File

@ -38,7 +38,7 @@
}
},
"dependencies": {
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -36,12 +36,12 @@
},
"dependencies": {
"@vben-core/shared": "workspace:*",
"@vueuse/core": "^11.1.0",
"radix-vue": "^1.9.5",
"sortablejs": "^1.15.3",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"radix-vue": "catalog:",
"sortablejs": "catalog:",
"vue": "catalog:"
},
"devDependencies": {
"@types/sortablejs": "^1.15.8"
"@types/sortablejs": "catalog:"
}
}

View File

@ -1,4 +1,4 @@
import { getScrollbarWidth } from '@vben-core/shared/utils';
import { getScrollbarWidth, needsScrollbar } from '@vben-core/shared/utils';
import {
useScrollLock as _useScrollLock,
@ -13,6 +13,9 @@ export function useScrollLock() {
const scrollbarWidth = getScrollbarWidth();
tryOnBeforeMount(() => {
if (!needsScrollbar()) {
return;
}
document.body.style.paddingRight = `${scrollbarWidth}px`;
const layoutFixedNodes = document.querySelectorAll<HTMLElement>(
@ -30,6 +33,9 @@ export function useScrollLock() {
});
tryOnBeforeUnmount(() => {
if (!needsScrollbar()) {
return;
}
isLocked.value = false;
const layoutFixedNodes = document.querySelectorAll<HTMLElement>(
`.${SCROLL_FIXED_CLASS}`,

View File

@ -0,0 +1,114 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`defaultPreferences immutability test > should not modify the config object 1`] = `
{
"app": {
"accessMode": "frontend",
"authPageLayout": "panel-right",
"checkUpdatesInterval": 1,
"colorGrayMode": false,
"colorWeakMode": false,
"compact": false,
"contentCompact": "wide",
"defaultAvatar": "https://unpkg.com/@vbenjs/static-source@0.1.6/source/avatar-v1.webp",
"dynamicTitle": true,
"enableCheckUpdates": true,
"enablePreferences": true,
"enableRefreshToken": false,
"isMobile": false,
"layout": "sidebar-nav",
"locale": "zh-CN",
"loginExpiredMode": "page",
"name": "Vben Admin",
"preferencesButtonPosition": "auto",
"watermark": false,
},
"breadcrumb": {
"enable": true,
"hideOnlyOne": false,
"showHome": false,
"showIcon": true,
"styleType": "normal",
},
"copyright": {
"companyName": "Vben",
"companySiteLink": "https://www.vben.pro",
"date": "2024",
"enable": true,
"icp": "",
"icpLink": "",
},
"footer": {
"enable": true,
"fixed": false,
},
"header": {
"enable": true,
"hidden": false,
"mode": "fixed",
},
"logo": {
"enable": true,
"source": "https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp",
},
"navigation": {
"accordion": true,
"split": true,
"styleType": "rounded",
},
"shortcutKeys": {
"enable": true,
"globalLockScreen": true,
"globalLogout": true,
"globalPreferences": true,
"globalSearch": true,
},
"sidebar": {
"collapsed": false,
"collapsedShowTitle": false,
"enable": true,
"expandOnHover": true,
"extraCollapse": true,
"hidden": false,
"width": 224,
},
"tabbar": {
"dragable": true,
"enable": true,
"height": 38,
"keepAlive": true,
"persist": true,
"showIcon": true,
"showMaximize": true,
"showMore": true,
"showRefresh": true,
"styleType": "chrome",
},
"theme": {
"builtinType": "default",
"colorDestructive": "hsl(348 100% 61%)",
"colorPrimary": "hsl(212 100% 45%)",
"colorSuccess": "hsl(144 57% 58%)",
"colorWarning": "hsl(42 84% 61%)",
"mode": "dark",
"radius": "0.5",
"semiDarkHeader": false,
"semiDarkSidebar": true,
},
"transition": {
"enable": true,
"loading": true,
"name": "fade-slide",
"progress": true,
},
"widget": {
"fullscreen": true,
"globalSearch": true,
"languageToggle": true,
"lockScreen": true,
"notification": true,
"sidebarToggle": true,
"themeToggle": true,
},
}
`;

View File

@ -0,0 +1,10 @@
import { describe, expect, it } from 'vitest';
import { defaultPreferences } from '../src/config';
describe('defaultPreferences immutability test', () => {
// 创建快照,确保默认配置对象不被修改
it('should not modify the config object', () => {
expect(defaultPreferences).toMatchSnapshot();
});
});

View File

@ -1,8 +1,8 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { defaultPreferences } from './config';
import { PreferenceManager } from './preferences';
import { isDarkTheme } from './update-css-variables';
import { defaultPreferences } from '../src/config';
import { PreferenceManager } from '../src/preferences';
import { isDarkTheme } from '../src/update-css-variables';
describe('preferences', () => {
let preferenceManager: PreferenceManager;

View File

@ -31,7 +31,7 @@
"dependencies": {
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"vue": "catalog:"
}
}

View File

@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"include": ["src", "__tests__"],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,146 @@
// 假设这个文件为 FormApi.ts
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { FormApi } from '../src/form-api';
vi.mock('@vben-core/shared/utils', () => ({
bindMethods: vi.fn(),
createMerge: vi.fn((mergeFn) => {
return (stateOrFn, prev) => {
mergeFn(prev, 'key', stateOrFn);
return { ...prev, ...stateOrFn };
};
}),
isFunction: (fn: any) => typeof fn === 'function',
StateHandler: vi.fn().mockImplementation(() => ({
reset: vi.fn(),
setConditionTrue: vi.fn(),
waitForCondition: vi.fn().mockResolvedValue(true),
})),
}));
describe('formApi', () => {
let formApi: FormApi;
beforeEach(() => {
formApi = new FormApi();
});
it('should initialize with default state', () => {
expect(formApi.state).toEqual(
expect.objectContaining({
actionWrapperClass: '',
collapsed: false,
collapsedRows: 1,
commonConfig: {},
handleReset: undefined,
handleSubmit: undefined,
layout: 'horizontal',
resetButtonOptions: {},
schema: [],
showCollapseButton: false,
showDefaultActions: true,
submitButtonOptions: {},
wrapperClass: 'grid-cols-1',
}),
);
expect(formApi.isMounted).toBe(false);
});
it('should mount form actions', async () => {
const formActions: any = {
meta: {},
resetForm: vi.fn(),
setFieldValue: vi.fn(),
setValues: vi.fn(),
submitForm: vi.fn(),
validate: vi.fn(),
values: { name: 'test' },
};
await formApi.mount(formActions);
expect(formApi.isMounted).toBe(true);
expect(formApi.form).toEqual(formActions);
});
it('should get values from form', async () => {
const formActions: any = {
meta: {},
values: { name: 'test' },
};
await formApi.mount(formActions);
const values = await formApi.getValues();
expect(values).toEqual({ name: 'test' });
});
it('should set field value', async () => {
const setFieldValueMock = vi.fn();
const formActions: any = {
meta: {},
setFieldValue: setFieldValueMock,
values: { name: 'test' },
};
await formApi.mount(formActions);
await formApi.setFieldValue('name', 'new value');
expect(setFieldValueMock).toHaveBeenCalledWith(
'name',
'new value',
undefined,
);
});
it('should reset form', async () => {
const resetFormMock = vi.fn();
const formActions: any = {
meta: {},
resetForm: resetFormMock,
values: { name: 'test' },
};
await formApi.mount(formActions);
await formApi.resetForm();
expect(resetFormMock).toHaveBeenCalled();
});
it('should call handleSubmit on submit', async () => {
const handleSubmitMock = vi.fn();
const formActions: any = {
meta: {},
submitForm: vi.fn().mockResolvedValue(true),
values: { name: 'test' },
};
const state = {
handleSubmit: handleSubmitMock,
};
formApi.setState(state);
await formApi.mount(formActions);
const result = await formApi.submitForm();
expect(formActions.submitForm).toHaveBeenCalled();
expect(handleSubmitMock).toHaveBeenCalledWith({ name: 'test' });
expect(result).toEqual({ name: 'test' });
});
it('should unmount form and reset state', () => {
formApi.unmounted();
expect(formApi.isMounted).toBe(false);
expect(formApi.stateHandler.reset).toHaveBeenCalled();
});
it('should validate form', async () => {
const validateMock = vi.fn().mockResolvedValue(true);
const formActions: any = {
meta: {},
validate: validateMock,
};
await formApi.mount(formActions);
const isValid = await formApi.validate();
expect(validateMock).toHaveBeenCalled();
expect(isValid).toBe(true);
});
});

View File

@ -40,11 +40,11 @@
"@vben-core/composables": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vee-validate/zod": "^4.13.2",
"@vueuse/core": "^11.1.0",
"vee-validate": "^4.13.2",
"vue": "^3.5.6",
"zod": "^3.23.8",
"zod-defaults": "^0.1.3"
"@vee-validate/zod": "catalog:",
"@vueuse/core": "catalog:",
"vee-validate": "catalog:",
"vue": "catalog:",
"zod": "catalog:",
"zod-defaults": "catalog:"
}
}

View File

@ -43,13 +43,13 @@ function getDefaultState(): VbenFormProps {
}
export class FormApi {
// private prevState!: ModalState;
private state: null | VbenFormProps = null;
// private api: Pick<VbenFormProps, 'handleReset' | 'handleSubmit'>;
public form = {} as FormActions;
isMounted = false;
// private prevState!: ModalState;
public state: null | VbenFormProps = null;
stateHandler: StateHandler;
public store: Store<VbenFormProps>;
@ -92,6 +92,10 @@ export class FormApi {
this.store.batch(cb);
}
getState() {
return this.state;
}
async getValues() {
const form = await this.getForm();
return form.values;

View File

@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"include": ["src"],
"include": ["src", "__tests__"],
"exclude": ["node_modules"]
}

View File

@ -41,7 +41,7 @@
"@vben-core/icons": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"vue": "catalog:"
}
}

View File

@ -42,7 +42,7 @@
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"vue": "catalog:"
}
}

View File

@ -41,7 +41,7 @@
"@vben-core/icons": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"vue": "catalog:"
}
}

View File

@ -40,16 +40,16 @@
}
},
"dependencies": {
"@radix-icons/vue": "^1.0.0",
"@radix-icons/vue": "catalog:",
"@vben-core/composables": "workspace:*",
"@vben-core/icons": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^11.1.0",
"class-variance-authority": "^0.7.0",
"lucide-vue-next": "^0.441.0",
"radix-vue": "^1.9.5",
"vee-validate": "^4.13.2",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"class-variance-authority": "catalog:",
"lucide-vue-next": "catalog:",
"radix-vue": "catalog:",
"vee-validate": "catalog:",
"vue": "catalog:"
}
}

View File

@ -42,7 +42,7 @@
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"vue": "catalog:"
}
}

View File

@ -24,6 +24,6 @@
"@vben/stores": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"vue": "^3.5.6"
"vue": "catalog:"
}
}

View File

@ -35,15 +35,15 @@
"@vben/locales": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/types": "workspace:*",
"@vueuse/integrations": "^11.1.0",
"@vueuse/integrations": "catalog:",
"codemirror": "^6.0.1",
"qrcode": "^1.5.4",
"vue": "^3.5.6",
"qrcode": "catalog:",
"vue": "catalog:",
"vue-codemirror6": "^1.3.4",
"vue-json-pretty": "^2.4.0",
"vue-router": "^4.4.5"
"vue-router": "catalog:"
},
"devDependencies": {
"@types/qrcode": "^1.5.5"
"@types/qrcode": "catalog:"
}
}

View File

@ -5,7 +5,7 @@ import { RotateCw } from '@vben/icons';
import { $t } from '@vben/locales';
import { VbenButton, VbenIconButton } from '@vben-core/shadcn-ui';
import { CaptchaCard } from '.';
import CaptchaCard from './captcha-card.vue';
import { useCaptchaPoints } from './hooks/useCaptchaPoints';
const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {

View File

@ -25,8 +25,8 @@
"@vben/stores": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"vue": "^3.5.6",
"vue-router": "^4.4.5",
"watermark-js-plus": "^1.5.6"
"vue": "catalog:",
"vue-router": "catalog:",
"watermark-js-plus": "catalog:"
}
}

View File

@ -185,6 +185,7 @@ export function useElementPlusDesignTokens() {
'--el-bg-color-page': getCssVariableValue('--background-deep'),
'--el-border-color': border,
'--el-border-color-dark': border,
'--el-border-color-extra-light': border,
'--el-border-color-hover': accent,
'--el-border-color-light': border,
'--el-border-color-lighter': border,
@ -195,26 +196,36 @@ export function useElementPlusDesignTokens() {
'--el-color-danger-light-3': getCssVariableValue('--destructive-400'),
'--el-color-danger-light-5': getCssVariableValue('--destructive-300'),
'--el-color-danger-light-7': getCssVariableValue('--destructive-200'),
'--el-color-danger-light-8': getCssVariableValue('--destructive-100'),
'--el-color-danger-light-8': isDark.value
? border
: getCssVariableValue('--destructive-100'),
'--el-color-danger-light-9': isDark.value
? accent
: getCssVariableValue('--destructive-50'),
'--el-color-danger-light-9': getCssVariableValue('--destructive-50'),
'--el-color-error': getCssVariableValue('--destructive-500'),
'--el-color-error-dark-2': getCssVariableValue('--destructive'),
'--el-color-error-light-3': getCssVariableValue('--destructive-400'),
'--el-color-error-light-5': getCssVariableValue('--destructive-300'),
'--el-color-error-light-7': getCssVariableValue('--destructive-200'),
'--el-color-error-light-8': getCssVariableValue('--destructive-100'),
'--el-color-error-light-8': isDark.value
? border
: getCssVariableValue('--destructive-100'),
'--el-color-error-light-9': isDark.value
? accent
: getCssVariableValue('--destructive-50'),
'--el-color-error-light-9': getCssVariableValue('--destructive-50'),
'--el-color-info-light-8': border,
'--el-color-info-light-9': background,
'--el-color-primary': getCssVariableValue('--primary-500'),
'--el-color-primary-dark-2': getCssVariableValue('--primary'),
'--el-color-primary-light-3': getCssVariableValue('--primary-400'),
'--el-color-primary-light-5': getCssVariableValue('--primary-300'),
'--el-color-primary-light-7': getCssVariableValue('--primary-200'),
'--el-color-primary-light-8': getCssVariableValue('--primary-100'),
'--el-color-primary-light-8': isDark.value
? border
: getCssVariableValue('--primary-100'),
'--el-color-primary-light-9': isDark.value
? accent
: getCssVariableValue('--primary-50'),
@ -224,17 +235,26 @@ export function useElementPlusDesignTokens() {
'--el-color-success-light-3': getCssVariableValue('--success-400'),
'--el-color-success-light-5': getCssVariableValue('--success-300'),
'--el-color-success-light-7': getCssVariableValue('--success-200'),
'--el-color-success-light-8': getCssVariableValue('--success-100'),
'--el-color-success-light-8': isDark.value
? border
: getCssVariableValue('--success-100'),
'--el-color-success-light-9': isDark.value
? accent
: getCssVariableValue('--success-50'),
'--el-color-success-light-9': getCssVariableValue('--success-50'),
'--el-color-warning': getCssVariableValue('--warning-500'),
'--el-color-warning-dark-2': getCssVariableValue('--warning'),
'--el-color-warning-light-3': getCssVariableValue('--warning-400'),
'--el-color-warning-light-5': getCssVariableValue('--warning-300'),
'--el-color-warning-light-7': getCssVariableValue('--warning-200'),
'--el-color-warning-light-8': getCssVariableValue('--warning-100'),
'--el-color-warning-light-8': isDark.value
? border
: getCssVariableValue('--warning-100'),
'--el-color-warning-light-9': isDark.value
? accent
: getCssVariableValue('--warning-50'),
'--el-color-warning-light-9': getCssVariableValue('--warning-50'),
'--el-fill-color': getCssVariableValue('--accent'),
'--el-fill-color-blank': background,
'--el-fill-color-light': getCssVariableValue('--accent'),
'--el-text-color-primary': getCssVariableValue('--foreground'),

View File

@ -35,8 +35,8 @@
"@vben/stores": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"@vueuse/core": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -21,8 +21,8 @@
},
"dependencies": {
"@vben/preferences": "workspace:*",
"@vueuse/core": "^11.1.0",
"echarts": "^5.5.1",
"vue": "^3.5.6"
"@vueuse/core": "catalog:",
"echarts": "catalog:",
"vue": "catalog:"
}
}

View File

@ -22,9 +22,9 @@
"dependencies": {
"@vben/locales": "workspace:*",
"@vben/utils": "workspace:*",
"axios": "^1.7.7"
"axios": "catalog:"
},
"devDependencies": {
"axios-mock-adapter": "^2.0.0"
"axios-mock-adapter": "catalog:"
}
}

View File

@ -34,7 +34,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockResolvedValueOnce(mockResponse);
const result = await fileUploader.upload(url, file);
const result = await fileUploader.upload(url, { file });
expect(result).toEqual(mockResponse);
expect(mockAxiosInstance.post).toHaveBeenCalledWith(
url,
@ -66,7 +66,7 @@ describe('fileUploader', () => {
headers: { 'Custom-Header': 'value' },
};
const result = await fileUploader.upload(url, file, customConfig);
const result = await fileUploader.upload(url, { file }, customConfig);
expect(result).toEqual(mockResponse);
expect(mockAxiosInstance.post).toHaveBeenCalledWith(
url,
@ -87,7 +87,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Network Error'));
await expect(fileUploader.upload(url, file)).rejects.toThrow(
await expect(fileUploader.upload(url, { file })).rejects.toThrow(
'Network Error',
);
});
@ -99,7 +99,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Request failed with status code 404'));
await expect(fileUploader.upload(url, file)).rejects.toThrow(
await expect(fileUploader.upload(url, { file })).rejects.toThrow(
'Request failed with status code 404',
);
});
@ -111,7 +111,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Request failed with status code 404'));
await expect(fileUploader.upload(url, file)).rejects.toThrow(
await expect(fileUploader.upload(url, { file })).rejects.toThrow(
'Request failed with status code 404',
);
});

View File

@ -11,11 +11,14 @@ class FileUploader {
public async upload(
url: string,
file: Blob | File,
data: { file: Blob | File } & Record<string, any>,
config?: AxiosRequestConfig,
): Promise<AxiosResponse> {
const formData = new FormData();
formData.append('file', file);
Object.entries(data).forEach(([key, value]) => {
formData.append(key, value);
});
const finalConfig: AxiosRequestConfig = {
...config,

View File

@ -79,7 +79,9 @@ describe('requestClient', () => {
: [400, { error: 'Bad Request' }];
});
const response = await requestClient.upload('/test/upload', fileData);
const response = await requestClient.upload('/test/upload', {
file: fileData,
});
expect(response.data).toEqual({ data: 'file uploaded' });
});

View File

@ -20,9 +20,9 @@
}
},
"dependencies": {
"@intlify/core-base": "^10.0.1",
"@intlify/core-base": "catalog:",
"@vben-core/composables": "workspace:*",
"vue": "^3.5.6",
"vue-i18n": "^10.0.1"
"vue": "catalog:",
"vue-i18n": "catalog:"
}
}

View File

@ -22,9 +22,9 @@
"dependencies": {
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"pinia": "2.2.2",
"pinia-plugin-persistedstate": "^4.0.2",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"pinia": "catalog:",
"pinia-plugin-persistedstate": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -21,7 +21,7 @@
},
"dependencies": {
"@vben-core/typings": "workspace:*",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -24,6 +24,6 @@
"dependencies": {
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"vue-router": "^4.4.5"
"vue-router": "catalog:"
}
}

View File

@ -26,7 +26,7 @@
"#/*": "./src/*"
},
"dependencies": {
"@tanstack/vue-query": "^5.56.2",
"@tanstack/vue-query": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
@ -41,11 +41,11 @@
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"ant-design-vue": "^4.2.4",
"dayjs": "^1.11.13",
"pinia": "2.2.2",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
"dayjs": "catalog:",
"pinia": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:"
}
}

View File

@ -78,7 +78,8 @@
"query": "Query Form",
"rules": "Form Rules",
"dynamic": "Dynamic Form",
"custom": "Custom Component"
"custom": "Custom Component",
"api": "Api"
},
"captcha": {
"title": "Captcha",

View File

@ -78,7 +78,8 @@
"query": "查询表单",
"rules": "表单校验",
"dynamic": "动态表单",
"custom": "自定义组件"
"custom": "自定义组件",
"api": "Api"
},
"captcha": {
"title": "验证码",

View File

@ -99,6 +99,14 @@ const routes: RouteRecordRaw[] = [
title: $t('page.examples.form.custom'),
},
},
{
name: 'FormApiExample',
path: '/examples/form/api',
component: () => import('#/views/examples/form/api.vue'),
meta: {
title: $t('page.examples.form.api'),
},
},
],
},
],

View File

@ -77,7 +77,11 @@ export const useAuthStore = defineStore('auth', () => {
}
async function logout(redirect: boolean = true) {
await logoutApi();
try {
await logoutApi();
} catch {
// 不做任何处理
}
resetAllStores();
accessStore.setLoginExpired(false);

View File

@ -0,0 +1,208 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { Button, Card, message, Space } from 'ant-design-vue';
import { useVbenForm } from '#/adapter';
const [BaseForm, formApi] = useVbenForm({
//
commonConfig: {
//
componentProps: {
class: 'w-full',
},
},
// 使 tailwindcss grid
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// labelinput
schema: [
{
// #/adapter.ts
component: 'Input',
//
componentProps: {
placeholder: '请输入用户名',
},
//
fieldName: 'field1',
// label
label: 'field1',
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'field2',
label: 'field2',
},
{
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: 'field3',
label: 'field3',
},
],
// 321
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
});
function onSubmit(values: Record<string, any>) {
message.success({
content: `form values: ${JSON.stringify(values)}`,
});
}
function handleClick(
action:
| 'batchAddSchema'
| 'batchDeleteSchema'
| 'disabled'
| 'hiddenAction'
| 'hiddenResetButton'
| 'hiddenSubmitButton'
| 'labelWidth'
| 'resetDisabled'
| 'resetLabelWidth'
| 'showAction'
| 'showResetButton'
| 'showSubmitButton'
| 'updateActionAlign'
| 'updateResetButton'
| 'updateSubmitButton',
) {
switch (action) {
case 'labelWidth': {
formApi.setState({
commonConfig: {
labelWidth: 150,
},
});
break;
}
case 'resetLabelWidth': {
formApi.setState({
commonConfig: {
labelWidth: 100,
},
});
break;
}
case 'disabled': {
formApi.setState({ commonConfig: { disabled: true } });
break;
}
case 'resetDisabled': {
formApi.setState({ commonConfig: { disabled: false } });
break;
}
case 'hiddenAction': {
formApi.setState({ showDefaultActions: false });
break;
}
case 'showAction': {
formApi.setState({ showDefaultActions: true });
break;
}
case 'hiddenResetButton': {
formApi.setState({ resetButtonOptions: { show: false } });
break;
}
case 'showResetButton': {
formApi.setState({ resetButtonOptions: { show: true } });
break;
}
case 'hiddenSubmitButton': {
formApi.setState({ submitButtonOptions: { show: false } });
break;
}
case 'showSubmitButton': {
formApi.setState({ submitButtonOptions: { show: true } });
break;
}
case 'updateResetButton': {
formApi.setState({
resetButtonOptions: { disabled: true },
});
break;
}
case 'updateSubmitButton': {
formApi.setState({
submitButtonOptions: { loading: true },
});
break;
}
case 'updateActionAlign': {
formApi.setState({
// class
actionWrapperClass: 'text-center',
});
break;
}
case 'batchAddSchema': {
formApi.setState((prev) => {
const currentSchema = prev?.schema ?? [];
const newSchema = [];
for (let i = 0; i < 3; i++) {
newSchema.push({
component: 'Input',
componentProps: {
placeholder: '请输入',
},
fieldName: `field${i}${Date.now()}`,
label: `field+`,
});
}
return {
schema: [...currentSchema, ...newSchema],
};
});
break;
}
case 'batchDeleteSchema': {
formApi.setState((prev) => {
const currentSchema = prev?.schema ?? [];
return {
schema: currentSchema.slice(0, -3),
};
});
break;
}
}
}
</script>
<template>
<Page description="表单组件api操作示例。" title="表单组件">
<Space class="mb-5 flex-wrap">
<Button @click="handleClick('labelWidth')">更改labelWidth</Button>
<Button @click="handleClick('resetLabelWidth')">还原labelWidth</Button>
<Button @click="handleClick('disabled')">禁用表单</Button>
<Button @click="handleClick('resetDisabled')">解除禁用</Button>
<Button @click="handleClick('hiddenAction')">隐藏操作按钮</Button>
<Button @click="handleClick('showAction')">显示操作按钮</Button>
<Button @click="handleClick('hiddenResetButton')">隐藏重置按钮</Button>
<Button @click="handleClick('showResetButton')">显示重置按钮</Button>
<Button @click="handleClick('hiddenSubmitButton')">隐藏提交按钮</Button>
<Button @click="handleClick('showSubmitButton')">显示提交按钮</Button>
<Button @click="handleClick('updateResetButton')">修改重置按钮</Button>
<Button @click="handleClick('updateSubmitButton')">修改提交按钮</Button>
<Button @click="handleClick('updateActionAlign')">
调整操作按钮位置
</Button>
<Button @click="handleClick('batchAddSchema')"> 批量添加表单项 </Button>
<Button @click="handleClick('batchDeleteSchema')">
批量删除表单项
</Button>
</Space>
<Card title="操作示例">
<BaseForm />
</Card>
</Page>
</template>

View File

@ -14,12 +14,11 @@ const [BaseForm, baseFormApi] = useVbenForm({
class: 'w-full',
},
},
// 使 tailwindcss grid
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// labelinput
layout: 'horizontal',
schema: [
{
// #/adapter.ts

View File

@ -16,12 +16,11 @@ const [BaseForm] = useVbenForm({
},
labelClass: 'w-2/6',
},
// 使 tailwindcss grid
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// labelinput
layout: 'horizontal',
schema: [
{
// #/adapter.ts
@ -31,7 +30,6 @@ const [BaseForm] = useVbenForm({
suffix: () => h('span', { class: 'text-red-600' }, '元'),
},
{
// #/adapter.ts
component: 'Input',
fieldName: 'field1',
label: '自定义组件slot',
@ -41,14 +39,12 @@ const [BaseForm] = useVbenForm({
}),
},
{
// #/adapter.ts
component: h(Input, { placeholder: '请输入' }),
fieldName: 'field2',
label: '自定义组件',
rules: 'required',
},
{
// #/adapter.ts
component: 'Input',
fieldName: 'field3',
label: '自定义组件(slot)',

View File

@ -6,10 +6,8 @@ import { Button, Card, message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter';
const [Form, formApi] = useVbenForm({
// 使 tailwindcss grid
//
handleSubmit: onSubmit,
// labelinput
schema: [
{
component: 'Switch',
@ -55,12 +53,9 @@ const [Form, formApi] = useVbenForm({
show(values) {
return !!values.field2Switch;
},
//
triggerFields: ['field2Switch'],
},
//
fieldName: 'field2',
// label
label: '字段2',
},
{
@ -69,12 +64,9 @@ const [Form, formApi] = useVbenForm({
disabled(values) {
return !!values.field3Switch;
},
//
triggerFields: ['field3Switch'],
},
//
fieldName: 'field3',
// label
label: '字段3',
},
{
@ -83,12 +75,9 @@ const [Form, formApi] = useVbenForm({
required(values) {
return !!values.field4Switch;
},
//
triggerFields: ['field4Switch'],
},
//
fieldName: 'field4',
// label
label: '字段4',
},
{
@ -100,13 +89,10 @@ const [Form, formApi] = useVbenForm({
}
return null;
},
//
triggerFields: ['field1'],
},
//
fieldName: 'field5',
help: '当字段1的值为`123`时,必填',
// label
label: '动态rules',
},
{
@ -150,13 +136,10 @@ const [Form, formApi] = useVbenForm({
}
return {};
},
//
triggerFields: ['field2'],
},
//
fieldName: 'field6',
help: '当字段2的值为`123`时,更改下拉选项',
// label
label: '动态配置',
},
{

View File

@ -18,9 +18,8 @@ const [QueryForm] = useVbenForm({
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// 使 tailwindcss grid
// labelinput
layout: 'horizontal',
schema: [
{
// #/adapter.ts
@ -101,9 +100,8 @@ const [QueryForm1] = useVbenForm({
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// 使 tailwindcss grid
// labelinput
layout: 'horizontal',
schema: (() => {
const schema = [];
for (let index = 0; index < 14; index++) {

View File

@ -13,12 +13,11 @@ const [Form, formApi] = useVbenForm({
class: 'w-full',
},
},
// 使 tailwindcss grid
//
handleSubmit: onSubmit,
// labelinputvertical
layout: 'horizontal',
// labelinput
layout: 'horizontal',
schema: [
{
// #/adapter.ts
@ -80,7 +79,6 @@ const [Form, formApi] = useVbenForm({
},
fieldName: 'number',
label: '数字',
// nullundefined
rules: 'required',
},
{

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,176 @@
packages:
- "internal/*"
- "internal/lint-configs/*"
- "packages/*"
- "packages/@core/base/*"
- "packages/@core/ui-kit/*"
- "packages/@core/forward/*"
- "packages/@core/*"
- "packages/effects/*"
- "packages/business/*"
- "apps/*"
- "scripts/*"
- "docs"
- "playground"
- internal/*
- internal/lint-configs/*
- packages/*
- packages/@core/base/*
- packages/@core/ui-kit/*
- packages/@core/forward/*
- packages/@core/*
- packages/effects/*
- packages/business/*
- apps/*
- scripts/*
- docs
- playground
catalog:
"@changesets/changelog-github": ^0.5.0
"@changesets/cli": ^2.27.8
"@changesets/git": ^3.0.1
"@clack/prompts": ^0.7.0
"@commitlint/cli": ^19.5.0
"@commitlint/config-conventional": ^19.5.0
"@ctrl/tinycolor": ^4.1.0
"@eslint/js": ^9.10.0
"@iconify/json": ^2.2.250
"@iconify/tailwind": ^1.1.3
"@iconify/vue": ^4.1.2
"@intlify/core-base": ^10.0.1
"@intlify/unplugin-vue-i18n": ^5.0.0
"@jspm/generator": ^2.3.1
"@manypkg/get-packages": ^2.2.2
"@nolebase/vitepress-plugin-git-changelog": ^2.5.0
"@radix-icons/vue": ^1.0.0
"@stylistic/stylelint-plugin": ^3.0.1
"@tailwindcss/nesting": 0.0.0-insiders.565cd3e
"@tailwindcss/typography": ^0.5.15
"@tanstack/vue-query": ^5.56.2
"@tanstack/vue-store": ^0.5.5
"@types/archiver": ^6.0.2
"@types/chalk": ^2.2.0
"@types/eslint": ^9.6.1
"@types/html-minifier-terser": ^7.0.2
"@types/jsdom": ^21.1.7
"@types/jsonwebtoken": ^9.0.7
"@types/lodash.clonedeep": ^4.5.9
"@types/node": ^22.5.5
"@types/nprogress": ^0.2.3
"@types/postcss-import": ^14.0.3
"@types/qrcode": ^1.5.5
"@types/sortablejs": ^1.15.8
"@typescript-eslint/eslint-plugin": ^8.6.0
"@typescript-eslint/parser": ^8.6.0
"@vee-validate/zod": ^4.13.2
"@vite-pwa/vitepress": ^0.5.3
"@vitejs/plugin-vue": ^5.1.4
"@vitejs/plugin-vue-jsx": ^4.0.1
"@vue/reactivity": ^3.5.6
"@vue/shared": ^3.5.6
"@vue/test-utils": ^2.4.6
"@vueuse/core": ^11.1.0
"@vueuse/integrations": ^11.1.0
ant-design-vue: ^4.2.5
archiver: ^7.0.1
autoprefixer: ^10.4.20
axios: ^1.7.7
axios-mock-adapter: ^2.0.0
cac: ^6.7.14
chalk: ^5.3.0
cheerio: 1.0.0
circular-dependency-scanner: ^2.3.0
class-variance-authority: ^0.7.0
clsx: ^2.1.1
commitlint-plugin-function-rules: ^4.0.0
consola: ^3.2.3
cross-env: ^7.0.3
cspell: ^8.14.4
cssnano: ^7.0.6
cz-git: ^1.9.4
czg: ^1.9.4
dayjs: ^1.11.13
defu: ^6.1.4
depcheck: ^1.4.7
dotenv: ^16.4.5
echarts: ^5.5.1
element-plus: ^2.8.3
eslint: ^9.10.0
eslint-config-turbo: ^2.1.2
eslint-plugin-command: ^0.2.5
eslint-plugin-eslint-comments: ^3.2.0
eslint-plugin-import-x: ^4.2.1
eslint-plugin-jsdoc: ^50.2.4
eslint-plugin-jsonc: ^2.16.0
eslint-plugin-n: ^17.10.3
eslint-plugin-no-only-tests: ^3.3.0
eslint-plugin-perfectionist: ^3.6.0
eslint-plugin-prettier: ^5.2.1
eslint-plugin-regexp: ^2.6.0
eslint-plugin-unicorn: ^55.0.0
eslint-plugin-unused-imports: ^4.1.4
eslint-plugin-vitest: ^0.5.4
eslint-plugin-vue: ^9.28.0
execa: ^9.4.0
find-up: ^7.0.0
get-port: ^7.1.0
globals: ^15.9.0
h3: ^1.12.0
html-minifier-terser: ^7.2.0
husky: ^9.1.6
is-ci: ^3.0.1
jsdom: ^25.0.0
jsonc-eslint-parser: ^2.4.0
jsonwebtoken: ^9.0.2
lint-staged: ^15.2.10
lodash.clonedeep: ^4.5.0
lucide-vue-next: ^0.441.0
medium-zoom: ^1.1.0
naive-ui: ^2.39.0
nanoid: ^5.0.7
nitropack: ^2.9.7
nprogress: ^0.2.0
ora: ^8.1.0
pinia: 2.2.2
pinia-plugin-persistedstate: ^4.0.2
pkg-types: ^1.2.0
postcss: ^8.4.47
postcss-antd-fixes: ^0.2.0
postcss-html: ^1.7.0
postcss-import: ^16.1.0
postcss-preset-env: ^10.0.3
postcss-scss: ^4.0.9
prettier: ^3.3.3
prettier-plugin-tailwindcss: ^0.6.6
publint: ^0.2.11
qrcode: ^1.5.4
radix-vue: ^1.9.6
resolve.exports: ^2.0.2
rimraf: ^6.0.1
rollup: ^4.22.0
rollup-plugin-visualizer: ^5.12.0
sass: ^1.79.1
sortablejs: ^1.15.3
stylelint: ^16.9.0
stylelint-config-recess-order: ^5.1.0
stylelint-config-recommended: ^14.0.1
stylelint-config-recommended-scss: ^14.1.0
stylelint-config-recommended-vue: ^1.5.0
stylelint-config-standard: ^36.0.1
stylelint-order: ^6.0.4
stylelint-prettier: ^5.0.2
stylelint-scss: ^6.7.0
tailwind-merge: ^2.5.2
tailwindcss: ^3.4.12
tailwindcss-animate: ^1.0.7
theme-colors: ^0.1.0
turbo: ^2.1.2
typescript: ^5.6.2
unbuild: ^2.0.0
unplugin-element-plus: ^0.8.0
vee-validate: ^4.13.2
vite: ^5.4.6
vite-plugin-compression: ^0.5.1
vite-plugin-dts: 4.2.1
vite-plugin-html: ^3.2.2
vite-plugin-lib-inject-css: ^2.1.1
vite-plugin-pwa: ^0.20.5
vite-plugin-vue-devtools: ^7.4.5
vitepress: ^1.3.4
vitepress-plugin-group-icons: ^1.2.4
vitest: ^2.1.1
vue: ^3.5.6
vue-eslint-parser: ^9.4.3
vue-i18n: ^10.0.1
vue-router: ^4.4.5
vue-tsc: ^2.1.6
watermark-js-plus: ^1.5.6
zod: ^3.23.8
zod-defaults: ^0.1.3

View File

@ -22,8 +22,8 @@
"./package.json": "./package.json"
},
"dependencies": {
"@clack/prompts": "^0.7.0",
"@clack/prompts": "catalog:",
"@vben/node-utils": "workspace:*",
"cac": "^6.7.14"
"cac": "catalog:"
}
}

View File

@ -23,9 +23,9 @@
},
"dependencies": {
"@vben/node-utils": "workspace:*",
"cac": "^6.7.14",
"circular-dependency-scanner": "^2.3.0",
"depcheck": "^1.4.7",
"publint": "^0.2.11"
"cac": "catalog:",
"circular-dependency-scanner": "catalog:",
"depcheck": "catalog:",
"publint": "catalog:"
}
}