Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into antdv-next

This commit is contained in:
dap
2026-03-31 10:07:28 +08:00
25 changed files with 307 additions and 75 deletions

View File

@@ -48,6 +48,7 @@
"@vitejs/plugin-vue-jsx": "catalog:",
"dayjs": "catalog:",
"dotenv": "catalog:",
"rolldown": "catalog:",
"rollup-plugin-visualizer": "catalog:",
"sass": "catalog:",
"sass-embedded": "catalog:",

View File

@@ -2,7 +2,7 @@ import type {
NormalizedOutputOptions,
OutputBundle,
OutputChunk,
} from 'rollup';
} from 'rolldown';
import type { PluginOption } from 'vite';
import { EOL } from 'node:os';

View File

@@ -16,15 +16,16 @@
],
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": "vbenjs/vue-vben-admin.git",
"license": "MIT",
"author": {
"name": "vben",
"email": "ann.vben@gmail.com",
"url": "https://github.com/anncwb"
},
"repository": "vbenjs/vue-vben-admin.git",
"type": "module",
"scripts": {
"bootstrap": "pnpm install",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
"build:analyze": "turbo build:analyze",
"build:antd": "pnpm run build --filter=@vben/web-antd build:prod",
@@ -44,6 +45,7 @@
"lint": "vsh lint",
"postinstall": "pnpm -r run stub --if-present",
"preinstall": "npx only-allow pnpm",
"prepare": "is-ci || pnpm exec lefthook install",
"preview": "turbo-run preview",
"publint": "vsh publint",
"reinstall": "pnpm clean --del-lock && pnpm install",

View File

@@ -48,6 +48,7 @@ const {
modelPropName,
renderComponentContent,
rules,
help,
} = defineProps<
Props & {
commonComponentProps: MaybeComponentProps;
@@ -166,6 +167,18 @@ const computedProps = computed(() => {
};
});
// 自定义帮助信息
const computedHelp = computed(() => {
const helpContent = help;
if (!helpContent) {
return undefined;
}
return () =>
isFunction(helpContent)
? helpContent(values.value, getFormApi())
: helpContent;
});
watch(
() => computedProps.value?.autofocus,
(value) => {
@@ -308,7 +321,7 @@ onUnmounted(() => {
labelClass,
)
"
:help="help"
:help="computedHelp"
:colon="colon"
:label="label"
:required="shouldRequired && !hideRequiredMark"

View File

@@ -67,6 +67,14 @@ export type FormActions = FormContext<GenericObject>;
export type CustomRenderType = (() => Component | string) | string;
// 动态渲染参数
export type CustomParamsRenderType =
| ((
value: Partial<Record<string, any>>,
actions: FormActions,
) => Component | string)
| string;
export type FormSchemaRuleType =
| 'required'
| 'selectRequired'
@@ -254,7 +262,7 @@ export interface FormSchema<
/** 字段名 */
fieldName: string;
/** 帮助信息 */
help?: CustomRenderType;
help?: CustomParamsRenderType;
/** 是否隐藏表单项 */
hide?: boolean;
/** 表单项 */

View File

@@ -46,6 +46,8 @@ interface Props {
alwaysLoad?: boolean;
/** 在api请求之前的回调函数 */
beforeFetch?: AnyPromiseFunction<any, any>;
/** 在api请求之前的判断是否允许请求的回调函数 */
shouldFetch?: AnyPromiseFunction<any, boolean>;
/** 在api请求之后的回调函数 */
afterFetch?: AnyPromiseFunction<any, any>;
/** 直接传入选项数据也作为api返回空数据时的后备数据 */
@@ -88,6 +90,7 @@ const props = withDefaults(defineProps<Props>(), {
alwaysLoad: false,
loadingSlot: '',
beforeFetch: undefined,
shouldFetch: undefined,
afterFetch: undefined,
modelPropName: 'modelValue',
api: undefined,
@@ -159,7 +162,7 @@ const bindProps = computed(() => {
});
async function fetchApi() {
const { api, beforeFetch, afterFetch, resultField } = props;
const { api, beforeFetch, shouldFetch, afterFetch, resultField } = props;
if (!api || !isFunction(api)) {
return;
@@ -178,6 +181,14 @@ async function fetchApi() {
if (beforeFetch && isFunction(beforeFetch)) {
finalParams = (await beforeFetch(cloneDeep(finalParams))) || finalParams;
}
// 判断是否需要控制执行中断
if (
shouldFetch &&
isFunction(shouldFetch) &&
!(await shouldFetch(finalParams))
) {
return;
}
let res = await api(finalParams);
if (afterFetch && isFunction(afterFetch)) {
res = (await afterFetch(res)) || res;

View File

@@ -12,7 +12,7 @@ import type {
import { computed, useAttrs } from 'vue';
// @ts-expect-error - vue-json-viewer does not expose compatible typings for this import path
import VueJsonViewer from 'vue-json-viewer';
import VueJsonViewerImport from 'vue-json-viewer';
import { $t } from '@vben/locales';
@@ -42,6 +42,11 @@ const emit = defineEmits<{
valueClick: [value: JsonViewerValue];
}>();
/** CJS/UMD 在 Vite 下解析为 { default: Component },需解包否则会出现 missing template or render */
const VueJsonViewer =
(VueJsonViewerImport as { default?: typeof VueJsonViewerImport }).default ??
VueJsonViewerImport;
const attrs: SetupContext['attrs'] = useAttrs();
function handleClick(event: MouseEvent) {

View File

@@ -196,7 +196,7 @@ watch(
if (val) {
handleSearch(val);
} else {
searchResults.value = [...searchHistory.value];
searchResults.value = searchHistory.value;
}
},
);

View File

@@ -14,6 +14,10 @@
"**/*.css"
],
"exports": {
".": {
"types": "./src/index.ts",
"default": "./src/index.ts"
},
"./echarts": {
"types": "./src/echarts/index.ts",
"default": "./src/echarts/index.ts"

View File

@@ -0,0 +1,41 @@
# ECharts Plugin
ECharts 图表插件,预置常用组件和图表类型。
## 导出
| 导出 | 类型 | 说明 |
| ------------ | ---- | ------------ |
| `default` | 对象 | echarts 实例 |
| `EchartsUI` | 组件 | 图表容器组件 |
| `ECOption` | 类型 | 图表配置类型 |
| `useEcharts` | 函数 | 组合式函数 |
## 使用
```ts
import { EchartsUI, useEcharts, ECOption } from '@vben/plugins/echarts';
```
## 类型
```ts
import type { ECOption } from '@vben/plugins/echarts';
```
## 预置组件
- TitleComponent
- TooltipComponent
- GridComponent
- LegendComponent
- ToolboxComponent
- DatasetComponent
- TransformComponent
## 预置图表
- BarChart
- LineChart
- PieChart
- RadarChart

View File

@@ -1,17 +1,3 @@
import type {
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption,
LineSeriesOption,
} from 'echarts/charts';
import type {
DatasetComponentOption,
GridComponentOption,
// 组件类型的定义后缀都为 ComponentOption
TitleComponentOption,
TooltipComponentOption,
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';
import {
BarChart,
GaugeChart,
@@ -21,14 +7,12 @@ import {
RadarChart,
} from 'echarts/charts';
import {
// 数据集组件
DatasetComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
// 内置数据转换器组件 (filter, sort)
TransformComponent,
VisualMapComponent,
} from 'echarts/components';
@@ -40,17 +24,6 @@ import {
} from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = ComposeOption<
| BarSeriesOption
| DatasetComponentOption
| GridComponentOption
| LineSeriesOption
| TitleComponentOption
| TooltipComponentOption
>;
// 注册必须的组件
echarts.use([
TitleComponent,
PieChart,
@@ -71,5 +44,6 @@ echarts.use([
VisualMapComponent,
MapChart,
]);
export type { ECOption } from './types';
export default echarts;

View File

@@ -1,3 +1,4 @@
export * from './echarts';
export { default as EchartsUI } from './echarts-ui.vue';
export * from './types';
export * from './use-echarts';

View File

@@ -0,0 +1,28 @@
import type {
BarSeriesOption,
LineSeriesOption,
PieSeriesOption,
RadarSeriesOption,
} from 'echarts/charts';
import type {
DatasetComponentOption,
GridComponentOption,
LegendComponentOption,
TitleComponentOption,
ToolboxComponentOption,
TooltipComponentOption,
} from 'echarts/components';
import type { ComposeOption } from 'echarts/core';
export type ECOption = ComposeOption<
| BarSeriesOption
| DatasetComponentOption
| GridComponentOption
| LegendComponentOption
| LineSeriesOption
| PieSeriesOption
| RadarSeriesOption
| TitleComponentOption
| ToolboxComponentOption
| TooltipComponentOption
>;

View File

@@ -0,0 +1,2 @@
export * from './plugins-context';
export * from './types';

View File

@@ -0,0 +1,26 @@
# Motion Plugin
基于 @vueuse/motion 的动画插件。
## 导出
| 导出 | 类型 | 说明 |
| ----------------- | ---- | ---------- |
| `Motion` | 组件 | 动画组件 |
| `MotionGroup` | 组件 | 动画组组件 |
| `MotionDirective` | 指令 | 动画指令 |
| `MotionPlugin` | 插件 | Vue 插件 |
## 使用
```ts
import { MotionPlugin, Motion, MotionDirective } from '@vben/plugins/motion';
app.use(MotionPlugin);
```
## 类型
```ts
import type { MotionOptions, MotionVariants } from '@vben/plugins/motion';
```

View File

@@ -0,0 +1,39 @@
import type { VbenPluginsOptions } from './types';
let globalPluginsOptions: null | VbenPluginsOptions = null;
export function providePluginsOptions(options: VbenPluginsOptions) {
if (!globalPluginsOptions) {
globalPluginsOptions = options;
return;
}
globalPluginsOptions = {
...globalPluginsOptions,
...options,
form:
globalPluginsOptions.form && options.form
? { ...globalPluginsOptions.form, ...options.form }
: globalPluginsOptions.form || options.form,
modal:
globalPluginsOptions.modal && options.modal
? { ...globalPluginsOptions.modal, ...options.modal }
: globalPluginsOptions.modal || options.modal,
message:
globalPluginsOptions.message && options.message
? { ...globalPluginsOptions.message, ...options.message }
: globalPluginsOptions.message || options.message,
components: {
...globalPluginsOptions.components,
...options.components,
},
};
}
export function injectPluginsOptions() {
return globalPluginsOptions;
}
export function resetPluginsOptions() {
globalPluginsOptions = null;
}

View File

@@ -0,0 +1,24 @@
import type { Component } from 'vue';
export interface VbenPluginsFormOptions {
useVbenForm: (...args: any[]) => any;
}
export interface VbenPluginsModalOptions {
useVbenModal?: () => any;
}
export interface VbenPluginsMessageOptions {
useMessage?: () => any;
}
export interface VbenPluginsComponentsOptions {
[key: string]: Component;
}
export interface VbenPluginsOptions {
form?: VbenPluginsFormOptions;
modal?: VbenPluginsModalOptions;
message?: VbenPluginsMessageOptions;
components?: VbenPluginsComponentsOptions;
}

View File

@@ -0,0 +1,50 @@
# VXE Table Plugin
基于 vxe-table 和 vxe-pc-ui 的表格组件插件。
## 导出
| 导出 | 类型 | 说明 |
| --------------------- | ---- | -------------- |
| `setupVbenVxeTable` | 函数 | 初始化配置函数 |
| `useVbenVxeGrid` | 函数 | 表格组合式函数 |
| `VbenVxeGrid` | 组件 | 表格组件 |
| `VxeTableGridColumns` | 类型 | 表格列类型 |
| `VxeTableGridOptions` | 类型 | 表格配置类型 |
| `VxeGridProps` | 类型 | 表格 Props |
| `VxeGridListeners` | 类型 | 表格事件类型 |
## 使用
```ts
import {
setupVbenVxeTable,
useVbenVxeGrid,
VbenVxeGrid,
} from '@vben/plugins/vxe-table';
```
## 初始化
在应用入口处调用:
```ts
import { setupVbenVxeTable } from '@vben/plugins/vxe-table';
import { useVbenForm } from '@vben-core/form-ui';
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
// 配置 VXE Table
},
useVbenForm,
});
```
## 类型
```ts
import type {
VxeTableGridOptions,
VxeGridProps,
} from '@vben/plugins/vxe-table';
```

View File

@@ -1,5 +1,5 @@
export { setupVbenVxeTable } from './init';
export type { VxeTableGridOptions } from './types';
export type { VxeTableGridColumns, VxeTableGridOptions } from './types';
export * from './use-vxe-grid';
export { default as VbenVxeGrid } from './use-vxe-grid.vue';
export type { VxeGridDefines } from 'vxe-table';

View File

@@ -4,8 +4,6 @@ import { defineComponent, watch } from 'vue';
import { usePreferences } from '@vben/preferences';
import { useVbenForm } from '@vben-core/form-ui';
/**
* 该插件提供了在表格中渲染第三方组件,用于兼容 antdv-next 组件库
*
@@ -18,33 +16,19 @@ import VxeUIPluginRenderAntd from '@vxe-ui/plugin-render-antd';
import {
VxeButton,
VxeCheckbox,
// VxeFormGather,
// VxeForm,
// VxeFormItem,
VxeIcon,
VxeInput,
VxeLoading,
VxeModal,
VxeNumberInput,
VxePager,
// VxeList,
// VxeModal,
// VxeOptgroup,
// VxeOption,
// VxePulldown,
// VxeRadio,
// VxeRadioButton,
VxeRadioGroup,
VxeSelect,
VxeTooltip,
VxeUI,
VxeUpload,
// VxeSwitch,
// VxeTextarea,
} from 'vxe-pc-ui';
import enUS from 'vxe-pc-ui/lib/language/en-US';
// 导入默认的语言
import enUS from 'vxe-pc-ui/lib/language/en-US'; // 导入默认的语言
import zhCN from 'vxe-pc-ui/lib/language/zh-CN';
import {
VxeColgroup,
@@ -54,14 +38,15 @@ import {
VxeToolbar,
} from 'vxe-table';
import { extendsDefaultFormatter } from './extends';
import { injectPluginsOptions } from '../plugins-context';
import { extendsDefaultFormatter } from './extends'; // 是否加载过
import '@vxe-ui/plugin-render-antd/dist/style.css';
// 是否加载过
let isInit = false;
let tableFormFactory: typeof useVbenForm | undefined;
let tableFormFactory: ((...args: any[]) => any) | undefined;
function normalizeVxeLocale<T extends Record<string, any>>(localeModule: T) {
return (
@@ -73,13 +58,19 @@ function normalizeVxeLocale<T extends Record<string, any>>(localeModule: T) {
) as T;
}
export const useTableForm: typeof useVbenForm = ((...args) => {
if (!tableFormFactory) {
throw new Error('useTableForm is not initialized');
export function useTableForm(...args: any[]) {
const pluginsOptions = injectPluginsOptions();
const contextFormFactory = pluginsOptions?.form?.useVbenForm;
const factory = tableFormFactory || contextFormFactory;
if (!factory) {
throw new Error(
'useTableForm is not initialized. Please provide useVbenForm via setupVbenVxeTable() or providePluginsOptions()',
);
}
return tableFormFactory(...args);
}) as typeof useVbenForm;
return factory(...args);
}
// 部分组件如果没注册vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积
const createVirtualComponent = (name = '') => {
@@ -130,11 +121,14 @@ export function initVxeTable() {
}
export function setupVbenVxeTable(setupOptions: SetupVxeTable) {
const { configVxeTable, useVbenForm } = setupOptions;
const { configVxeTable, useVbenForm: useVbenFormFromParam } = setupOptions;
initVxeTable();
tableFormFactory = useVbenForm;
// 优先使用参数传入的 useVbenForm否则清空让 context 注入生效
if (useVbenFormFromParam) {
tableFormFactory = useVbenFormFromParam;
}
const { isDark, locale } = usePreferences();
const localMap = {

View File

@@ -26,6 +26,8 @@ interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig {
search?: boolean;
}
export type VxeTableGridColumns<T = any> = VxeTableGridOptions<T>['columns'];
export interface VxeTableGridOptions<T = any> extends VxeTableGridProps<T> {
/** 工具栏配置 */
toolbarConfig?: ToolbarConfigOptions;
@@ -40,6 +42,10 @@ export interface VxeGridProps<
T extends Record<string, any> = any,
D extends BaseFormComponentType = BaseFormComponentType,
> {
/**
* 数据
*/
tableData?: any[];
/**
* 标题
*/
@@ -89,5 +95,5 @@ export type ExtendedVxeGridApi<
export interface SetupVxeTable {
configVxeTable: (ui: VxeUIExport) => void;
useVbenForm: typeof useVbenForm;
useVbenForm?: typeof useVbenForm;
}

View File

@@ -72,6 +72,7 @@ const {
gridEvents,
formOptions,
tableTitle,
tableData,
tableTitleHelp,
showSearchForm,
separator,
@@ -231,6 +232,9 @@ const options = computed(() => {
}
if (mergedOptions.formConfig) {
mergedOptions.formConfig.enabled = false;
if (tableData.value && tableData.value.length > 0) {
mergedOptions.data = tableData.value;
}
}
return mergedOptions;
});
@@ -332,7 +336,7 @@ async function init() {
watch(
formOptions,
() => {
formApi.setState((prev) => {
formApi.setState((prev: Record<string, any>) => {
const finalFormOptions: VbenFormProps = mergeWithArrayOverride(
{},
formOptions.value,

View File

@@ -4,7 +4,7 @@
"pageDesc": "工程化、高性能、跨组件库的前端模版",
"loginSuccess": "登录成功",
"loginSuccessDesc": "欢迎回来",
"loginSubtitle": "请输入您的户信息以开始管理您的项目",
"loginSubtitle": "请输入您的户信息以开始管理您的项目",
"selectAccount": "快速选择账号",
"username": "账号",
"password": "密码",

View File

@@ -40,7 +40,7 @@ catalog:
'@ctrl/tinycolor': ^4.2.0
'@eslint-community/eslint-plugin-eslint-comments': ^4.7.1
'@eslint/js': ^10.0.1
'@iconify/json': ^2.2.451
'@iconify/json': ^2.2.454
'@iconify/tailwind4': ^1.2.3
'@iconify/vue': ^5.0.0
'@intlify/core-base': ^11.3.0
@@ -76,7 +76,7 @@ catalog:
'@vueuse/integrations': ^14.2.1
'@vueuse/motion': ^3.0.3
alova: ^3.4.1
antdv-next: ^1.1.5
antdv-next: ^1.1.7
archiver: ^7.0.1
axios: ^1.13.6
axios-mock-adapter: ^2.1.0
@@ -100,7 +100,7 @@ catalog:
dotenv: ^17.3.1
echarts: ^6.0.0
es-toolkit: ^1.45.1
eslint: ^10.0.3
eslint: ^10.1.0
eslint-plugin-better-tailwindcss: ^4.3.2
eslint-plugin-command: ^3.5.2
eslint-plugin-jsonc: ^3.1.2
@@ -113,7 +113,7 @@ catalog:
eslint-plugin-yml: ^3.3.1
execa: ^9.6.1
find-up: ^8.0.0
get-port: ^7.1.0
get-port: ^7.2.0
globals: ^17.4.0
happy-dom: ^20.8.4
html-minifier-terser: ^7.2.0
@@ -143,12 +143,13 @@ catalog:
reka-ui: ^2.9.2
resolve.exports: ^2.0.3
rimraf: ^6.1.3
rolldown: ^1.0.0-rc.10
rollup-plugin-visualizer: ^7.0.1
sass: ^1.98.0
sass-embedded: ^1.98.0
secure-ls: ^2.0.0
sortablejs: ^1.15.7
stylelint: ^17.4.0
stylelint: ^17.5.0
stylelint-config-recess-order: ^7.7.0
stylelint-config-recommended: ^18.0.0
stylelint-config-recommended-scss: ^17.0.0
@@ -184,15 +185,12 @@ catalog:
vue-i18n: ^11.3.0
vue-json-pretty: ^2.4.0
vue-json-viewer: ^3.0.4
vue-router: ^5.0.3
vue-router: ^5.0.4
vue-tippy: ^6.7.1
vue-tsc: ^3.2.6
vxe-pc-ui: ^4.13.10
vxe-table: ^4.18.5
vxe-pc-ui: ^4.13.13
vxe-table: ^4.18.8
watermark-js-plus: ^1.6.3
yaml-eslint-parser: ^2.0.0
zod: ^3.25.76
zod-defaults: 0.1.3
catalogs:
conflicts_echarts_h5_5_1:
echarts: ^5.5.1

View File

@@ -8,6 +8,7 @@ ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# 增加内存限制以防止构建大型项目时 OOM
ENV NODE_OPTIONS=--max-old-space-size=8192
ENV TZ=Asia/Shanghai
# 启用 corepack 以使用 pnpm
RUN npm i -g corepack