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" "start": "nitro dev"
}, },
"dependencies": { "dependencies": {
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "catalog:",
"nitropack": "^2.9.7" "nitropack": "catalog:"
}, },
"devDependencies": { "devDependencies": {
"@types/jsonwebtoken": "^9.0.7", "@types/jsonwebtoken": "catalog:",
"h3": "^1.12.0" "h3": "catalog:"
} }
} }

View File

@ -41,22 +41,22 @@
"@vben/styles": "workspace:*", "@vben/styles": "workspace:*",
"@vben/types": "workspace:*", "@vben/types": "workspace:*",
"@vben/utils": "workspace:*", "@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0",
"ant-design-vue": "^4.2.4",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"pinia": "2.2.2", "tinymce": "^7.3.0"
"tinymce": "^7.3.0",
"vue": "^3.5.6",
"vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/lodash-es": "^4.17.12", "@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/styles": "workspace:*",
"@vben/types": "workspace:*", "@vben/types": "workspace:*",
"@vben/utils": "workspace:*", "@vben/utils": "workspace:*",
"@vueuse/core": "^11.1.0", "@vueuse/core": "catalog:",
"dayjs": "^1.11.13", "dayjs": "catalog:",
"element-plus": "^2.8.3", "element-plus": "catalog:",
"pinia": "2.2.2", "pinia": "catalog:",
"vue": "^3.5.6", "vue": "catalog:",
"vue-router": "^4.4.5" "vue-router": "catalog:"
}, },
"devDependencies": { "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) { async function logout(redirect: boolean = true) {
await logoutApi(); try {
await logoutApi();
} catch {
// 不做任何处理
}
resetAllStores(); resetAllStores();
accessStore.setLoginExpired(false); accessStore.setLoginExpired(false);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -69,3 +69,19 @@ export function getScrollbarWidth() {
scrollDiv.remove(); scrollDiv.remove();
return scrollbarWidth; 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": { "dependencies": {
"vue": "^3.5.6", "vue": "catalog:",
"vue-router": "^4.4.5" "vue-router": "catalog:"
} }
} }

View File

@ -36,12 +36,12 @@
}, },
"dependencies": { "dependencies": {
"@vben-core/shared": "workspace:*", "@vben-core/shared": "workspace:*",
"@vueuse/core": "^11.1.0", "@vueuse/core": "catalog:",
"radix-vue": "^1.9.5", "radix-vue": "catalog:",
"sortablejs": "^1.15.3", "sortablejs": "catalog:",
"vue": "^3.5.6" "vue": "catalog:"
}, },
"devDependencies": { "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 { import {
useScrollLock as _useScrollLock, useScrollLock as _useScrollLock,
@ -13,6 +13,9 @@ export function useScrollLock() {
const scrollbarWidth = getScrollbarWidth(); const scrollbarWidth = getScrollbarWidth();
tryOnBeforeMount(() => { tryOnBeforeMount(() => {
if (!needsScrollbar()) {
return;
}
document.body.style.paddingRight = `${scrollbarWidth}px`; document.body.style.paddingRight = `${scrollbarWidth}px`;
const layoutFixedNodes = document.querySelectorAll<HTMLElement>( const layoutFixedNodes = document.querySelectorAll<HTMLElement>(
@ -30,6 +33,9 @@ export function useScrollLock() {
}); });
tryOnBeforeUnmount(() => { tryOnBeforeUnmount(() => {
if (!needsScrollbar()) {
return;
}
isLocked.value = false; isLocked.value = false;
const layoutFixedNodes = document.querySelectorAll<HTMLElement>( const layoutFixedNodes = document.querySelectorAll<HTMLElement>(
`.${SCROLL_FIXED_CLASS}`, `.${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 { beforeEach, describe, expect, it, vi } from 'vitest';
import { defaultPreferences } from './config'; import { defaultPreferences } from '../src/config';
import { PreferenceManager } from './preferences'; import { PreferenceManager } from '../src/preferences';
import { isDarkTheme } from './update-css-variables'; import { isDarkTheme } from '../src/update-css-variables';
describe('preferences', () => { describe('preferences', () => {
let preferenceManager: PreferenceManager; let preferenceManager: PreferenceManager;

View File

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

View File

@ -1,6 +1,6 @@
{ {
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json", "extends": "@vben/tsconfig/web.json",
"include": ["src"], "include": ["src", "__tests__"],
"exclude": ["node_modules"] "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/composables": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*", "@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*", "@vben-core/shared": "workspace:*",
"@vee-validate/zod": "^4.13.2", "@vee-validate/zod": "catalog:",
"@vueuse/core": "^11.1.0", "@vueuse/core": "catalog:",
"vee-validate": "^4.13.2", "vee-validate": "catalog:",
"vue": "^3.5.6", "vue": "catalog:",
"zod": "^3.23.8", "zod": "catalog:",
"zod-defaults": "^0.1.3" "zod-defaults": "catalog:"
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,15 +35,15 @@
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/preferences": "workspace:*", "@vben/preferences": "workspace:*",
"@vben/types": "workspace:*", "@vben/types": "workspace:*",
"@vueuse/integrations": "^11.1.0", "@vueuse/integrations": "catalog:",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"qrcode": "^1.5.4", "qrcode": "catalog:",
"vue": "^3.5.6", "vue": "catalog:",
"vue-codemirror6": "^1.3.4", "vue-codemirror6": "^1.3.4",
"vue-json-pretty": "^2.4.0", "vue-json-pretty": "^2.4.0",
"vue-router": "^4.4.5" "vue-router": "catalog:"
}, },
"devDependencies": { "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 { $t } from '@vben/locales';
import { VbenButton, VbenIconButton } from '@vben-core/shadcn-ui'; import { VbenButton, VbenIconButton } from '@vben-core/shadcn-ui';
import { CaptchaCard } from '.'; import CaptchaCard from './captcha-card.vue';
import { useCaptchaPoints } from './hooks/useCaptchaPoints'; import { useCaptchaPoints } from './hooks/useCaptchaPoints';
const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), { const props = withDefaults(defineProps<PointSelectionCaptchaProps>(), {

View File

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

View File

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

View File

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

View File

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

View File

@ -22,9 +22,9 @@
"dependencies": { "dependencies": {
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/utils": "workspace:*", "@vben/utils": "workspace:*",
"axios": "^1.7.7" "axios": "catalog:"
}, },
"devDependencies": { "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> mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockResolvedValueOnce(mockResponse); ).mockResolvedValueOnce(mockResponse);
const result = await fileUploader.upload(url, file); const result = await fileUploader.upload(url, { file });
expect(result).toEqual(mockResponse); expect(result).toEqual(mockResponse);
expect(mockAxiosInstance.post).toHaveBeenCalledWith( expect(mockAxiosInstance.post).toHaveBeenCalledWith(
url, url,
@ -66,7 +66,7 @@ describe('fileUploader', () => {
headers: { 'Custom-Header': 'value' }, 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(result).toEqual(mockResponse);
expect(mockAxiosInstance.post).toHaveBeenCalledWith( expect(mockAxiosInstance.post).toHaveBeenCalledWith(
url, url,
@ -87,7 +87,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn> mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Network Error')); ).mockRejectedValueOnce(new Error('Network Error'));
await expect(fileUploader.upload(url, file)).rejects.toThrow( await expect(fileUploader.upload(url, { file })).rejects.toThrow(
'Network Error', 'Network Error',
); );
}); });
@ -99,7 +99,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn> mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Request failed with status code 404')); ).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', 'Request failed with status code 404',
); );
}); });
@ -111,7 +111,7 @@ describe('fileUploader', () => {
mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn> mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
).mockRejectedValueOnce(new Error('Request failed with status code 404')); ).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', 'Request failed with status code 404',
); );
}); });

View File

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

View File

@ -79,7 +79,9 @@ describe('requestClient', () => {
: [400, { error: 'Bad Request' }]; : [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' }); expect(response.data).toEqual({ data: 'file uploaded' });
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,6 +99,14 @@ const routes: RouteRecordRaw[] = [
title: $t('page.examples.form.custom'), 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) { async function logout(redirect: boolean = true) {
await logoutApi(); try {
await logoutApi();
} catch {
// 不做任何处理
}
resetAllStores(); resetAllStores();
accessStore.setLoginExpired(false); 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', class: 'w-full',
}, },
}, },
// 使 tailwindcss grid
// //
handleSubmit: onSubmit, handleSubmit: onSubmit,
// labelinputvertical // labelinputvertical
layout: 'horizontal',
// labelinput // labelinput
layout: 'horizontal',
schema: [ schema: [
{ {
// #/adapter.ts // #/adapter.ts

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,176 @@
packages: packages:
- "internal/*" - internal/*
- "internal/lint-configs/*" - internal/lint-configs/*
- "packages/*" - packages/*
- "packages/@core/base/*" - packages/@core/base/*
- "packages/@core/ui-kit/*" - packages/@core/ui-kit/*
- "packages/@core/forward/*" - packages/@core/forward/*
- "packages/@core/*" - packages/@core/*
- "packages/effects/*" - packages/effects/*
- "packages/business/*" - packages/business/*
- "apps/*" - apps/*
- "scripts/*" - scripts/*
- "docs" - docs
- "playground" - 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" "./package.json": "./package.json"
}, },
"dependencies": { "dependencies": {
"@clack/prompts": "^0.7.0", "@clack/prompts": "catalog:",
"@vben/node-utils": "workspace:*", "@vben/node-utils": "workspace:*",
"cac": "^6.7.14" "cac": "catalog:"
} }
} }

View File

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