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

@@ -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,