物业代码生成
This commit is contained in:
15
packages/effects/plugins/src/echarts/echarts-ui.vue
Normal file
15
packages/effects/plugins/src/echarts/echarts-ui.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
height?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
height: '300px',
|
||||
width: '100%',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-bind="$attrs" :style="{ height, width }"></div>
|
||||
</template>
|
70
packages/effects/plugins/src/echarts/echarts.ts
Normal file
70
packages/effects/plugins/src/echarts/echarts.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
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,
|
||||
LineChart,
|
||||
MapChart,
|
||||
PieChart,
|
||||
RadarChart,
|
||||
} from 'echarts/charts';
|
||||
import {
|
||||
// 数据集组件
|
||||
DatasetComponent,
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
TitleComponent,
|
||||
ToolboxComponent,
|
||||
TooltipComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent,
|
||||
VisualMapComponent,
|
||||
} from 'echarts/components';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
|
||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
||||
export type ECOption = ComposeOption<
|
||||
| BarSeriesOption
|
||||
| DatasetComponentOption
|
||||
| GridComponentOption
|
||||
| LineSeriesOption
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
>;
|
||||
|
||||
// 注册必须的组件
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
PieChart,
|
||||
RadarChart,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
BarChart,
|
||||
LineChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer,
|
||||
LegendComponent,
|
||||
ToolboxComponent,
|
||||
GaugeChart,
|
||||
VisualMapComponent,
|
||||
MapChart,
|
||||
]);
|
||||
|
||||
export default echarts;
|
3
packages/effects/plugins/src/echarts/index.ts
Normal file
3
packages/effects/plugins/src/echarts/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './echarts';
|
||||
export { default as EchartsUI } from './echarts-ui.vue';
|
||||
export * from './use-echarts';
|
124
packages/effects/plugins/src/echarts/use-echarts.ts
Normal file
124
packages/effects/plugins/src/echarts/use-echarts.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { EChartsOption } from 'echarts';
|
||||
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import type { Nullable } from '@vben/types';
|
||||
|
||||
import type EchartsUI from './echarts-ui.vue';
|
||||
|
||||
import { computed, nextTick, watch } from 'vue';
|
||||
|
||||
import { usePreferences } from '@vben/preferences';
|
||||
|
||||
import {
|
||||
tryOnUnmounted,
|
||||
useDebounceFn,
|
||||
useResizeObserver,
|
||||
useTimeoutFn,
|
||||
useWindowSize,
|
||||
} from '@vueuse/core';
|
||||
|
||||
import echarts from './echarts';
|
||||
|
||||
type EchartsUIType = typeof EchartsUI | undefined;
|
||||
|
||||
type EchartsThemeType = 'dark' | 'light' | null;
|
||||
|
||||
function useEcharts(chartRef: Ref<EchartsUIType>) {
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
let cacheOptions: EChartsOption = {};
|
||||
|
||||
const { isDark } = usePreferences();
|
||||
const { height, width } = useWindowSize();
|
||||
const resizeHandler: () => void = useDebounceFn(resize, 200);
|
||||
|
||||
const getOptions = computed((): EChartsOption => {
|
||||
if (!isDark.value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: 'transparent',
|
||||
};
|
||||
});
|
||||
|
||||
const initCharts = (t?: EchartsThemeType) => {
|
||||
const el = chartRef?.value?.$el;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null);
|
||||
|
||||
return chartInstance;
|
||||
};
|
||||
|
||||
const renderEcharts = (
|
||||
options: EChartsOption,
|
||||
clear = true,
|
||||
): Promise<Nullable<echarts.ECharts>> => {
|
||||
cacheOptions = options;
|
||||
const currentOptions = {
|
||||
...options,
|
||||
...getOptions.value,
|
||||
};
|
||||
return new Promise((resolve) => {
|
||||
if (chartRef.value?.offsetHeight === 0) {
|
||||
useTimeoutFn(async () => {
|
||||
resolve(await renderEcharts(currentOptions));
|
||||
}, 30);
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
useTimeoutFn(() => {
|
||||
if (!chartInstance) {
|
||||
const instance = initCharts();
|
||||
if (!instance) return;
|
||||
}
|
||||
clear && chartInstance?.clear();
|
||||
chartInstance?.setOption(currentOptions);
|
||||
resolve(chartInstance);
|
||||
}, 30);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function resize(withAnimation = true) {
|
||||
chartInstance?.resize({
|
||||
animation: withAnimation
|
||||
? {
|
||||
duration: 300,
|
||||
easing: 'quadraticIn',
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
watch([width, height], () => {
|
||||
resizeHandler?.();
|
||||
});
|
||||
|
||||
useResizeObserver(chartRef as never, resizeHandler);
|
||||
|
||||
watch(isDark, () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
initCharts();
|
||||
renderEcharts(cacheOptions);
|
||||
resize();
|
||||
}
|
||||
});
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
// 销毁实例,释放资源
|
||||
chartInstance?.dispose();
|
||||
});
|
||||
return {
|
||||
renderEcharts,
|
||||
resize,
|
||||
getChartInstance: () => chartInstance,
|
||||
};
|
||||
}
|
||||
|
||||
export { useEcharts };
|
||||
|
||||
export type { EchartsUIType };
|
8
packages/effects/plugins/src/motion/index.ts
Normal file
8
packages/effects/plugins/src/motion/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './types';
|
||||
|
||||
export {
|
||||
MotionComponent as Motion,
|
||||
MotionDirective,
|
||||
MotionGroupComponent as MotionGroup,
|
||||
MotionPlugin,
|
||||
} from '@vueuse/motion';
|
26
packages/effects/plugins/src/motion/types.ts
Normal file
26
packages/effects/plugins/src/motion/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const MotionPresets = [
|
||||
'fade',
|
||||
'fadeVisible',
|
||||
'fadeVisibleOnce',
|
||||
'rollBottom',
|
||||
'rollLeft',
|
||||
'rollRight',
|
||||
'rollTop',
|
||||
'rollVisibleBottom',
|
||||
'rollVisibleLeft',
|
||||
'rollVisibleRight',
|
||||
'rollVisibleTop',
|
||||
'pop',
|
||||
'popVisible',
|
||||
'popVisibleOnce',
|
||||
'slideBottom',
|
||||
'slideLeft',
|
||||
'slideRight',
|
||||
'slideTop',
|
||||
'slideVisibleBottom',
|
||||
'slideVisibleLeft',
|
||||
'slideVisibleRight',
|
||||
'slideVisibleTop',
|
||||
] as const;
|
||||
|
||||
export type MotionPreset = (typeof MotionPresets)[number];
|
126
packages/effects/plugins/src/vxe-table/api.ts
Normal file
126
packages/effects/plugins/src/vxe-table/api.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import type { ExtendedFormApi } from '@vben-core/form-ui';
|
||||
import type { VxeGridInstance } from 'vxe-table';
|
||||
|
||||
import type { VxeGridProps } from './types';
|
||||
|
||||
import { Store } from '@vben-core/shared/store';
|
||||
import {
|
||||
bindMethods,
|
||||
isBoolean,
|
||||
isFunction,
|
||||
mergeWithArrayOverride,
|
||||
StateHandler,
|
||||
} from '@vben-core/shared/utils';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
function getDefaultState(): VxeGridProps {
|
||||
return {
|
||||
class: '',
|
||||
gridClass: '',
|
||||
gridOptions: {},
|
||||
gridEvents: {},
|
||||
formOptions: undefined,
|
||||
showSearchForm: true,
|
||||
};
|
||||
}
|
||||
|
||||
export class VxeGridApi {
|
||||
private isMounted = false;
|
||||
|
||||
private stateHandler: StateHandler;
|
||||
public formApi = {} as ExtendedFormApi;
|
||||
|
||||
// private prevState: null | VxeGridProps = null;
|
||||
public grid = {} as VxeGridInstance;
|
||||
|
||||
public state: null | VxeGridProps = null;
|
||||
|
||||
public store: Store<VxeGridProps>;
|
||||
|
||||
constructor(options: VxeGridProps = {}) {
|
||||
const storeState = { ...options };
|
||||
|
||||
const defaultState = getDefaultState();
|
||||
this.store = new Store<VxeGridProps>(
|
||||
mergeWithArrayOverride(storeState, defaultState),
|
||||
{
|
||||
onUpdate: () => {
|
||||
// this.prevState = this.state;
|
||||
this.state = this.store.state;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
this.state = this.store.state;
|
||||
this.stateHandler = new StateHandler();
|
||||
bindMethods(this);
|
||||
}
|
||||
|
||||
mount(instance: null | VxeGridInstance, formApi: ExtendedFormApi) {
|
||||
if (!this.isMounted && instance) {
|
||||
this.grid = instance;
|
||||
this.formApi = formApi;
|
||||
this.stateHandler.setConditionTrue();
|
||||
this.isMounted = true;
|
||||
}
|
||||
}
|
||||
|
||||
async query(params: Record<string, any> = {}) {
|
||||
try {
|
||||
await this.grid.commitProxy('query', toRaw(params));
|
||||
} catch (error) {
|
||||
console.error('Error occurred while querying:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async reload(params: Record<string, any> = {}) {
|
||||
try {
|
||||
await this.grid.commitProxy('reload', toRaw(params));
|
||||
} catch (error) {
|
||||
console.error('Error occurred while reloading:', error);
|
||||
}
|
||||
}
|
||||
|
||||
setGridOptions(options: Partial<VxeGridProps['gridOptions']>) {
|
||||
this.setState({
|
||||
gridOptions: options,
|
||||
});
|
||||
}
|
||||
|
||||
setLoading(isLoading: boolean) {
|
||||
this.setState({
|
||||
gridOptions: {
|
||||
loading: isLoading,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setState(
|
||||
stateOrFn:
|
||||
| ((prev: VxeGridProps) => Partial<VxeGridProps>)
|
||||
| Partial<VxeGridProps>,
|
||||
) {
|
||||
if (isFunction(stateOrFn)) {
|
||||
this.store.setState((prev) => {
|
||||
return mergeWithArrayOverride(stateOrFn(prev), prev);
|
||||
});
|
||||
} else {
|
||||
this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev));
|
||||
}
|
||||
}
|
||||
|
||||
toggleSearchForm(show?: boolean) {
|
||||
this.setState({
|
||||
showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm,
|
||||
});
|
||||
// nextTick(() => {
|
||||
// this.grid.recalculate();
|
||||
// });
|
||||
return this.state?.showSearchForm;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this.isMounted = false;
|
||||
this.stateHandler.reset();
|
||||
}
|
||||
}
|
80
packages/effects/plugins/src/vxe-table/extends.ts
Normal file
80
packages/effects/plugins/src/vxe-table/extends.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { VxeGridProps, VxeUIExport } from 'vxe-table';
|
||||
|
||||
import type { VxeGridApi } from './api';
|
||||
|
||||
import { formatDate, formatDateTime, isFunction } from '@vben/utils';
|
||||
|
||||
export function extendProxyOptions(
|
||||
api: VxeGridApi,
|
||||
options: VxeGridProps,
|
||||
getFormValues: () => Recordable<any>,
|
||||
) {
|
||||
[
|
||||
'query',
|
||||
'querySuccess',
|
||||
'queryError',
|
||||
'queryAll',
|
||||
'queryAllSuccess',
|
||||
'queryAllError',
|
||||
].forEach((key) => {
|
||||
extendProxyOption(key, api, options, getFormValues);
|
||||
});
|
||||
}
|
||||
|
||||
function extendProxyOption(
|
||||
key: string,
|
||||
api: VxeGridApi,
|
||||
options: VxeGridProps,
|
||||
getFormValues: () => Recordable<any>,
|
||||
) {
|
||||
const { proxyConfig } = options;
|
||||
const configFn = (proxyConfig?.ajax as Recordable<any>)?.[key];
|
||||
if (!isFunction(configFn)) {
|
||||
return options;
|
||||
}
|
||||
|
||||
const wrapperFn = async (
|
||||
params: Recordable<any>,
|
||||
customValues: Recordable<any>,
|
||||
...args: Recordable<any>[]
|
||||
) => {
|
||||
const formValues = getFormValues();
|
||||
const data = await configFn(
|
||||
params,
|
||||
{
|
||||
/**
|
||||
* 开启toolbarConfig.refresh功能
|
||||
* 点击刷新按钮 这里的值为PointerEvent 会携带错误参数
|
||||
*/
|
||||
...(customValues instanceof PointerEvent ? {} : customValues),
|
||||
...formValues,
|
||||
},
|
||||
...args,
|
||||
);
|
||||
return data;
|
||||
};
|
||||
api.setState({
|
||||
gridOptions: {
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
[key]: wrapperFn,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function extendsDefaultFormatter(vxeUI: VxeUIExport) {
|
||||
vxeUI.formats.add('formatDate', {
|
||||
tableCellFormatMethod({ cellValue }) {
|
||||
return formatDate(cellValue);
|
||||
},
|
||||
});
|
||||
|
||||
vxeUI.formats.add('formatDateTime', {
|
||||
tableCellFormatMethod({ cellValue }) {
|
||||
return formatDateTime(cellValue);
|
||||
},
|
||||
});
|
||||
}
|
10
packages/effects/plugins/src/vxe-table/index.ts
Normal file
10
packages/effects/plugins/src/vxe-table/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export { setupVbenVxeTable } from './init';
|
||||
export type { VxeTableGridOptions } from './types';
|
||||
export * from './use-vxe-grid';
|
||||
export { default as VbenVxeGrid } from './use-vxe-grid.vue';
|
||||
export type { VxeGridDefines } from 'vxe-table';
|
||||
export type {
|
||||
VxeGridListeners,
|
||||
VxeGridProps,
|
||||
VxeGridPropTypes,
|
||||
} from 'vxe-table';
|
131
packages/effects/plugins/src/vxe-table/init.ts
Normal file
131
packages/effects/plugins/src/vxe-table/init.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import type { SetupVxeTable } from './types';
|
||||
|
||||
import { defineComponent, watch } from 'vue';
|
||||
|
||||
import { usePreferences } from '@vben/preferences';
|
||||
|
||||
import { useVbenForm } from '@vben-core/form-ui';
|
||||
|
||||
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 zhCN from 'vxe-pc-ui/lib/language/zh-CN';
|
||||
import {
|
||||
VxeColgroup,
|
||||
VxeColumn,
|
||||
VxeGrid,
|
||||
VxeTable,
|
||||
VxeToolbar,
|
||||
} from 'vxe-table';
|
||||
|
||||
import { extendsDefaultFormatter } from './extends';
|
||||
|
||||
// 是否加载过
|
||||
let isInit = false;
|
||||
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let useTableForm: typeof useVbenForm;
|
||||
|
||||
// 部分组件,如果没注册,vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积
|
||||
const createVirtualComponent = (name = '') => {
|
||||
return defineComponent({
|
||||
name,
|
||||
});
|
||||
};
|
||||
|
||||
export function initVxeTable() {
|
||||
if (isInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
VxeUI.component(VxeTable);
|
||||
VxeUI.component(VxeColumn);
|
||||
VxeUI.component(VxeColgroup);
|
||||
VxeUI.component(VxeGrid);
|
||||
VxeUI.component(VxeToolbar);
|
||||
|
||||
VxeUI.component(VxeButton);
|
||||
// VxeUI.component(VxeButtonGroup);
|
||||
VxeUI.component(VxeCheckbox);
|
||||
// VxeUI.component(VxeCheckboxGroup);
|
||||
VxeUI.component(createVirtualComponent('VxeForm'));
|
||||
// VxeUI.component(VxeFormGather);
|
||||
// VxeUI.component(VxeFormItem);
|
||||
VxeUI.component(VxeIcon);
|
||||
VxeUI.component(VxeInput);
|
||||
// VxeUI.component(VxeList);
|
||||
VxeUI.component(VxeLoading);
|
||||
VxeUI.component(VxeModal);
|
||||
VxeUI.component(VxeNumberInput);
|
||||
// VxeUI.component(VxeOptgroup);
|
||||
// VxeUI.component(VxeOption);
|
||||
VxeUI.component(VxePager);
|
||||
// VxeUI.component(VxePulldown);
|
||||
// VxeUI.component(VxeRadio);
|
||||
// VxeUI.component(VxeRadioButton);
|
||||
VxeUI.component(VxeRadioGroup);
|
||||
VxeUI.component(VxeSelect);
|
||||
// VxeUI.component(VxeSwitch);
|
||||
// VxeUI.component(VxeTextarea);
|
||||
VxeUI.component(VxeTooltip);
|
||||
VxeUI.component(VxeUpload);
|
||||
|
||||
isInit = true;
|
||||
}
|
||||
|
||||
export function setupVbenVxeTable(setupOptions: SetupVxeTable) {
|
||||
const { configVxeTable, useVbenForm } = setupOptions;
|
||||
|
||||
initVxeTable();
|
||||
useTableForm = useVbenForm;
|
||||
|
||||
const { isDark, locale } = usePreferences();
|
||||
|
||||
const localMap = {
|
||||
'zh-CN': zhCN,
|
||||
'en-US': enUS,
|
||||
};
|
||||
|
||||
watch(
|
||||
[() => isDark.value, () => locale.value],
|
||||
([isDarkValue, localeValue]) => {
|
||||
VxeUI.setTheme(isDarkValue ? 'dark' : 'light');
|
||||
VxeUI.setI18n(localeValue, localMap[localeValue]);
|
||||
VxeUI.setLanguage(localeValue);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
extendsDefaultFormatter(VxeUI);
|
||||
|
||||
configVxeTable(VxeUI);
|
||||
}
|
125
packages/effects/plugins/src/vxe-table/style.css
Normal file
125
packages/effects/plugins/src/vxe-table/style.css
Normal file
@@ -0,0 +1,125 @@
|
||||
:root .vxe-grid {
|
||||
--vxe-ui-font-color: hsl(var(--foreground));
|
||||
--vxe-ui-font-primary-color: hsl(var(--primary));
|
||||
|
||||
/* --vxe-ui-font-lighten-color: #babdc0;
|
||||
--vxe-ui-font-darken-color: #86898e; */
|
||||
--vxe-ui-font-disabled-color: hsl(var(--foreground) / 50%);
|
||||
|
||||
/* base */
|
||||
--vxe-ui-base-popup-border-color: hsl(var(--border));
|
||||
--vxe-ui-input-disabled-color: hsl(var(--border) / 60%);
|
||||
|
||||
/* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */
|
||||
|
||||
/* layout */
|
||||
--vxe-ui-layout-background-color: hsl(var(--background));
|
||||
--vxe-ui-table-resizable-line-color: hsl(var(--heavy));
|
||||
|
||||
/* --vxe-ui-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px hsl(var(--accent));
|
||||
--vxe-ui-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px hsl(var(--accent)); */
|
||||
|
||||
/* input */
|
||||
--vxe-ui-input-border-color: hsl(var(--border));
|
||||
|
||||
/* --vxe-ui-input-placeholder-color: #8d9095; */
|
||||
|
||||
/* --vxe-ui-input-disabled-background-color: #262727; */
|
||||
|
||||
/* loading */
|
||||
--vxe-ui-loading-background-color: hsl(var(--overlay-content));
|
||||
|
||||
/* table */
|
||||
--vxe-ui-table-header-background-color: hsl(0deg 0% 98%);
|
||||
--vxe-ui-table-border-color: hsl(var(--border));
|
||||
--vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover));
|
||||
--vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%);
|
||||
--vxe-ui-table-row-hover-striped-background-color: hsl(var(--accent));
|
||||
--vxe-ui-table-row-radio-checked-background-color: hsl(var(--accent));
|
||||
--vxe-ui-table-row-hover-radio-checked-background-color: hsl(
|
||||
var(--accent-hover)
|
||||
);
|
||||
--vxe-ui-table-row-checkbox-checked-background-color: hsl(var(--accent));
|
||||
--vxe-ui-table-row-hover-checkbox-checked-background-color: hsl(
|
||||
var(--accent-hover)
|
||||
);
|
||||
--vxe-ui-table-row-current-background-color: hsl(var(--accent));
|
||||
--vxe-ui-table-row-hover-current-background-color: hsl(var(--accent-hover));
|
||||
--vxe-ui-font-primary-tinge-color: hsl(var(--primary));
|
||||
--vxe-ui-font-primary-lighten-color: hsl(var(--primary) / 60%);
|
||||
--vxe-ui-font-primary-darken-color: hsl(var(--primary));
|
||||
|
||||
/* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */
|
||||
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
html[data-vxe-ui-theme='dark'] .vxe-grid {
|
||||
--vxe-ui-table-header-background-color: hsl(var(--accent) / 50%);
|
||||
}
|
||||
|
||||
.vxe-pager {
|
||||
.vxe-pager--prev-btn:not(.is--disabled):active,
|
||||
.vxe-pager--next-btn:not(.is--disabled):active,
|
||||
.vxe-pager--num-btn:not(.is--disabled):active,
|
||||
.vxe-pager--jump-prev:not(.is--disabled):active,
|
||||
.vxe-pager--jump-next:not(.is--disabled):active,
|
||||
.vxe-pager--prev-btn:not(.is--disabled):focus,
|
||||
.vxe-pager--next-btn:not(.is--disabled):focus,
|
||||
.vxe-pager--num-btn:not(.is--disabled):focus,
|
||||
.vxe-pager--jump-prev:not(.is--disabled):focus,
|
||||
.vxe-pager--jump-next:not(.is--disabled):focus {
|
||||
color: hsl(var(--accent-foreground));
|
||||
background-color: hsl(var(--accent));
|
||||
border: 1px solid hsl(var(--border));
|
||||
box-shadow: 0 0 0 1px hsl(var(--border));
|
||||
}
|
||||
|
||||
.vxe-pager--wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vxe-pager--sizes {
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.vxe-pager--wrapper {
|
||||
@apply justify-center md:justify-end;
|
||||
}
|
||||
|
||||
.vxe-tools--operate {
|
||||
margin-right: 0.25rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
.vxe-table-custom--checkbox-option:hover {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.vxe-toolbar {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/** 关闭搜索表单 */
|
||||
.vxe-grid:not(.vxe-grid--form-wrapper form) .vxe-toolbar {
|
||||
padding: 0.6em 0 !important;
|
||||
}
|
||||
|
||||
/** 开启搜索表单 */
|
||||
.vxe-grid:has(.vxe-grid--form-wrapper form) .vxe-toolbar {
|
||||
padding: 0 0 0.6em !important;
|
||||
}
|
||||
|
||||
.vxe-tools--operate:not(:has(button)) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.vxe-grid--layout-header-wrapper {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.vxe-grid--layout-body-content-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
86
packages/effects/plugins/src/vxe-table/types.ts
Normal file
86
packages/effects/plugins/src/vxe-table/types.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type {
|
||||
VxeGridListeners,
|
||||
VxeGridPropTypes,
|
||||
VxeGridProps as VxeTableGridProps,
|
||||
VxeUIExport,
|
||||
} from 'vxe-table';
|
||||
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import type { ClassType, DeepPartial } from '@vben/types';
|
||||
|
||||
import type { VbenFormProps } from '@vben-core/form-ui';
|
||||
|
||||
import type { VxeGridApi } from './api';
|
||||
|
||||
import { useVbenForm } from '@vben-core/form-ui';
|
||||
|
||||
export interface VxePaginationInfo {
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface ToolbarConfigOptions extends VxeGridPropTypes.ToolbarConfig {
|
||||
/** 是否显示切换搜索表单的按钮 */
|
||||
search?: boolean;
|
||||
}
|
||||
|
||||
export interface VxeTableGridOptions<T = any> extends VxeTableGridProps<T> {
|
||||
/** 工具栏配置 */
|
||||
toolbarConfig?: ToolbarConfigOptions;
|
||||
}
|
||||
|
||||
export interface SeparatorOptions {
|
||||
show?: boolean;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
export interface VxeGridProps {
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
tableTitle?: string;
|
||||
/**
|
||||
* 标题帮助
|
||||
*/
|
||||
tableTitleHelp?: string;
|
||||
/**
|
||||
* 组件class
|
||||
*/
|
||||
class?: ClassType;
|
||||
/**
|
||||
* vxe-grid class
|
||||
*/
|
||||
gridClass?: ClassType;
|
||||
/**
|
||||
* vxe-grid 配置
|
||||
*/
|
||||
gridOptions?: DeepPartial<VxeTableGridOptions>;
|
||||
/**
|
||||
* vxe-grid 事件
|
||||
*/
|
||||
gridEvents?: Partial<VxeGridListeners>;
|
||||
/**
|
||||
* 表单配置
|
||||
*/
|
||||
formOptions?: VbenFormProps;
|
||||
/**
|
||||
* 显示搜索表单
|
||||
*/
|
||||
showSearchForm?: boolean;
|
||||
/**
|
||||
* 搜索表单与表格主体之间的分隔条
|
||||
*/
|
||||
separator?: boolean | SeparatorOptions;
|
||||
}
|
||||
|
||||
export type ExtendedVxeGridApi = VxeGridApi & {
|
||||
useStore: <T = NoInfer<VxeGridProps>>(
|
||||
selector?: (state: NoInfer<VxeGridProps>) => T,
|
||||
) => Readonly<Ref<T>>;
|
||||
};
|
||||
|
||||
export interface SetupVxeTable {
|
||||
configVxeTable: (ui: VxeUIExport) => void;
|
||||
useVbenForm: typeof useVbenForm;
|
||||
}
|
45
packages/effects/plugins/src/vxe-table/use-vxe-grid.ts
Normal file
45
packages/effects/plugins/src/vxe-table/use-vxe-grid.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { ExtendedVxeGridApi, VxeGridProps } from './types';
|
||||
|
||||
import { defineComponent, h, onBeforeUnmount } from 'vue';
|
||||
|
||||
import { useStore } from '@vben-core/shared/store';
|
||||
|
||||
import { VxeGridApi } from './api';
|
||||
import VxeGrid from './use-vxe-grid.vue';
|
||||
|
||||
export function useVbenVxeGrid(options: VxeGridProps) {
|
||||
// const IS_REACTIVE = isReactive(options);
|
||||
const api = new VxeGridApi(options);
|
||||
const extendedApi: ExtendedVxeGridApi = api as ExtendedVxeGridApi;
|
||||
extendedApi.useStore = (selector) => {
|
||||
return useStore(api.store, selector);
|
||||
};
|
||||
|
||||
const Grid = defineComponent(
|
||||
(props: VxeGridProps, { attrs, slots }) => {
|
||||
onBeforeUnmount(() => {
|
||||
api.unmount();
|
||||
});
|
||||
api.setState({ ...props, ...attrs });
|
||||
return () => h(VxeGrid, { ...props, ...attrs, api: extendedApi }, slots);
|
||||
},
|
||||
{
|
||||
name: 'VbenVxeGrid',
|
||||
inheritAttrs: false,
|
||||
},
|
||||
);
|
||||
// Add reactivity support
|
||||
// if (IS_REACTIVE) {
|
||||
// watch(
|
||||
// () => options,
|
||||
// () => {
|
||||
// api.setState(options);
|
||||
// },
|
||||
// { immediate: true },
|
||||
// );
|
||||
// }
|
||||
|
||||
return [Grid, extendedApi] as const;
|
||||
}
|
||||
|
||||
export type UseVbenVxeGrid = typeof useVbenVxeGrid;
|
475
packages/effects/plugins/src/vxe-table/use-vxe-grid.vue
Normal file
475
packages/effects/plugins/src/vxe-table/use-vxe-grid.vue
Normal file
@@ -0,0 +1,475 @@
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
VxeGridDefines,
|
||||
VxeGridInstance,
|
||||
VxeGridListeners,
|
||||
VxeGridPropTypes,
|
||||
VxeGridProps as VxeTableGridProps,
|
||||
VxeToolbarPropTypes,
|
||||
} from 'vxe-table';
|
||||
|
||||
import type { SetupContext } from 'vue';
|
||||
|
||||
import type { VbenFormProps } from '@vben-core/form-ui';
|
||||
|
||||
import type { ExtendedVxeGridApi, VxeGridProps } from './types';
|
||||
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
toRaw,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { usePriorityValues } from '@vben/hooks';
|
||||
import { EmptyIcon } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
import { usePreferences } from '@vben/preferences';
|
||||
import {
|
||||
cloneDeep,
|
||||
cn,
|
||||
isBoolean,
|
||||
isEqual,
|
||||
mergeWithArrayOverride,
|
||||
} from '@vben/utils';
|
||||
|
||||
import { VbenHelpTooltip, VbenLoading } from '@vben-core/shadcn-ui';
|
||||
|
||||
import { VxeButton } from 'vxe-pc-ui';
|
||||
import { VxeGrid, VxeUI } from 'vxe-table';
|
||||
|
||||
import { extendProxyOptions } from './extends';
|
||||
import { useTableForm } from './init';
|
||||
|
||||
import 'vxe-table/styles/cssvar.scss';
|
||||
import 'vxe-pc-ui/styles/cssvar.scss';
|
||||
import './style.css';
|
||||
|
||||
interface Props extends VxeGridProps {
|
||||
api: ExtendedVxeGridApi;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
|
||||
const FORM_SLOT_PREFIX = 'form-';
|
||||
|
||||
const TOOLBAR_ACTIONS = 'toolbar-actions';
|
||||
const TOOLBAR_TOOLS = 'toolbar-tools';
|
||||
const TABLE_TITLE = 'table-title';
|
||||
|
||||
const gridRef = useTemplateRef<VxeGridInstance>('gridRef');
|
||||
|
||||
const state = props.api?.useStore?.();
|
||||
|
||||
const {
|
||||
gridOptions,
|
||||
class: className,
|
||||
gridClass,
|
||||
gridEvents,
|
||||
formOptions,
|
||||
tableTitle,
|
||||
tableTitleHelp,
|
||||
showSearchForm,
|
||||
separator,
|
||||
} = usePriorityValues(props, state);
|
||||
|
||||
const { isMobile } = usePreferences();
|
||||
const isSeparator = computed(() => {
|
||||
if (
|
||||
!formOptions.value ||
|
||||
showSearchForm.value === false ||
|
||||
separator.value === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (separator.value === true || separator.value === undefined) {
|
||||
return true;
|
||||
}
|
||||
return separator.value.show !== false;
|
||||
});
|
||||
const separatorBg = computed(() => {
|
||||
return !separator.value ||
|
||||
isBoolean(separator.value) ||
|
||||
!separator.value.backgroundColor
|
||||
? undefined
|
||||
: separator.value.backgroundColor;
|
||||
});
|
||||
const slots: SetupContext['slots'] = useSlots();
|
||||
|
||||
const [Form, formApi] = useTableForm({
|
||||
compact: true,
|
||||
handleSubmit: async () => {
|
||||
const formValues = await formApi.getValues();
|
||||
formApi.setLatestSubmissionValues(toRaw(formValues));
|
||||
props.api.reload(formValues);
|
||||
},
|
||||
handleReset: async () => {
|
||||
const prevValues = await formApi.getValues();
|
||||
await formApi.resetForm();
|
||||
const formValues = await formApi.getValues();
|
||||
formApi.setLatestSubmissionValues(formValues);
|
||||
// 如果值发生了变化,submitOnChange会触发刷新。所以只在submitOnChange为false或者值没有发生变化时,手动刷新
|
||||
if (isEqual(prevValues, formValues) || !formOptions.value?.submitOnChange) {
|
||||
props.api.reload(formValues);
|
||||
}
|
||||
},
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: computed(() => $t('common.search')),
|
||||
},
|
||||
// enter提交
|
||||
submitOnEnter: true,
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
||||
});
|
||||
|
||||
const showTableTitle = computed(() => {
|
||||
return !!slots[TABLE_TITLE]?.() || tableTitle.value;
|
||||
});
|
||||
|
||||
const showToolbar = computed(() => {
|
||||
return (
|
||||
!!slots[TOOLBAR_ACTIONS]?.() ||
|
||||
!!slots[TOOLBAR_TOOLS]?.() ||
|
||||
showTableTitle.value
|
||||
);
|
||||
});
|
||||
|
||||
const toolbarOptions = computed(() => {
|
||||
const slotActions = slots[TOOLBAR_ACTIONS]?.();
|
||||
const slotTools = slots[TOOLBAR_TOOLS]?.();
|
||||
const searchBtn: VxeToolbarPropTypes.ToolConfig = {
|
||||
code: 'search',
|
||||
icon: 'vxe-icon-search',
|
||||
circle: true,
|
||||
status: showSearchForm.value ? 'primary' : undefined,
|
||||
title: showSearchForm.value
|
||||
? $t('common.hideSearchPanel')
|
||||
: $t('common.showSearchPanel'),
|
||||
};
|
||||
// 将搜索按钮合并到用户配置的toolbarConfig.tools中
|
||||
const toolbarConfig: VxeGridPropTypes.ToolbarConfig = {
|
||||
tools: (gridOptions.value?.toolbarConfig?.tools ??
|
||||
[]) as VxeToolbarPropTypes.ToolConfig[],
|
||||
};
|
||||
if (gridOptions.value?.toolbarConfig?.search && !!formOptions.value) {
|
||||
toolbarConfig.tools = Array.isArray(toolbarConfig.tools)
|
||||
? [...toolbarConfig.tools, searchBtn]
|
||||
: [searchBtn];
|
||||
}
|
||||
|
||||
if (!showToolbar.value) {
|
||||
return { toolbarConfig };
|
||||
}
|
||||
|
||||
// 强制使用固定的toolbar配置,不允许用户自定义
|
||||
// 减少配置的复杂度,以及后续维护的成本
|
||||
toolbarConfig.slots = {
|
||||
...(slotActions || showTableTitle.value
|
||||
? { buttons: TOOLBAR_ACTIONS }
|
||||
: {}),
|
||||
...(slotTools ? { tools: TOOLBAR_TOOLS } : {}),
|
||||
};
|
||||
return { toolbarConfig };
|
||||
});
|
||||
|
||||
const options = computed(() => {
|
||||
const globalGridConfig = VxeUI?.getConfig()?.grid ?? {};
|
||||
|
||||
const mergedOptions: VxeTableGridProps = cloneDeep(
|
||||
mergeWithArrayOverride(
|
||||
{},
|
||||
toRaw(toolbarOptions.value),
|
||||
toRaw(gridOptions.value),
|
||||
globalGridConfig,
|
||||
),
|
||||
);
|
||||
|
||||
if (mergedOptions.proxyConfig) {
|
||||
const { ajax } = mergedOptions.proxyConfig;
|
||||
mergedOptions.proxyConfig.enabled = !!ajax;
|
||||
// 不自动加载数据, 由组件控制
|
||||
mergedOptions.proxyConfig.autoLoad = false;
|
||||
}
|
||||
|
||||
if (mergedOptions.pagerConfig) {
|
||||
const mobileLayouts = [
|
||||
'PrevJump',
|
||||
'PrevPage',
|
||||
'Number',
|
||||
'NextPage',
|
||||
'NextJump',
|
||||
] as any;
|
||||
const layouts = [
|
||||
'Total',
|
||||
'Sizes',
|
||||
'Home',
|
||||
...mobileLayouts,
|
||||
'End',
|
||||
] as readonly string[];
|
||||
mergedOptions.pagerConfig = mergeWithArrayOverride(
|
||||
{},
|
||||
mergedOptions.pagerConfig,
|
||||
{
|
||||
pageSize: 20,
|
||||
background: true,
|
||||
pageSizes: [10, 20, 30, 50, 100, 200],
|
||||
className: 'mt-2 w-full',
|
||||
layouts: isMobile.value ? mobileLayouts : layouts,
|
||||
size: 'mini' as const,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (mergedOptions.formConfig) {
|
||||
mergedOptions.formConfig.enabled = false;
|
||||
}
|
||||
return mergedOptions;
|
||||
});
|
||||
|
||||
function onToolbarToolClick(event: VxeGridDefines.ToolbarToolClickEventParams) {
|
||||
if (event.code === 'search') {
|
||||
onSearchBtnClick();
|
||||
}
|
||||
(
|
||||
gridEvents.value?.toolbarToolClick as VxeGridListeners['toolbarToolClick']
|
||||
)?.(event);
|
||||
}
|
||||
|
||||
function onSearchBtnClick() {
|
||||
props.api?.toggleSearchForm?.();
|
||||
}
|
||||
|
||||
const events = computed(() => {
|
||||
return {
|
||||
...gridEvents.value,
|
||||
toolbarToolClick: onToolbarToolClick,
|
||||
};
|
||||
});
|
||||
|
||||
const delegatedSlots = computed(() => {
|
||||
const resultSlots: string[] = [];
|
||||
|
||||
for (const key of Object.keys(slots)) {
|
||||
if (
|
||||
!['empty', 'form', 'loading', TOOLBAR_ACTIONS, TOOLBAR_TOOLS].includes(
|
||||
key,
|
||||
)
|
||||
) {
|
||||
resultSlots.push(key);
|
||||
}
|
||||
}
|
||||
return resultSlots;
|
||||
});
|
||||
|
||||
const delegatedFormSlots = computed(() => {
|
||||
const resultSlots: string[] = [];
|
||||
|
||||
for (const key of Object.keys(slots)) {
|
||||
if (key.startsWith(FORM_SLOT_PREFIX)) {
|
||||
resultSlots.push(key);
|
||||
}
|
||||
}
|
||||
return resultSlots.map((key) => key.replace(FORM_SLOT_PREFIX, ''));
|
||||
});
|
||||
|
||||
async function init() {
|
||||
await nextTick();
|
||||
const globalGridConfig = VxeUI?.getConfig()?.grid ?? {};
|
||||
const defaultGridOptions: VxeTableGridProps = mergeWithArrayOverride(
|
||||
{},
|
||||
toRaw(gridOptions.value),
|
||||
toRaw(globalGridConfig),
|
||||
);
|
||||
// 内部主动加载数据,防止form的默认值影响
|
||||
const autoLoad = defaultGridOptions.proxyConfig?.autoLoad;
|
||||
const enableProxyConfig = options.value.proxyConfig?.enabled;
|
||||
if (enableProxyConfig && autoLoad) {
|
||||
// 第一次拿到的是readonly的数据 如果需要修改 需要cloneDeep
|
||||
props.api.grid.commitProxy?.(
|
||||
'_init',
|
||||
cloneDeep(formOptions.value)
|
||||
? (cloneDeep(await formApi.getValues()) ?? {})
|
||||
: {},
|
||||
);
|
||||
// props.api.reload(formApi.form?.values ?? {});
|
||||
}
|
||||
|
||||
// form 由 vben-form代替,所以不适配formConfig,这里给出警告
|
||||
const formConfig = gridOptions.value?.formConfig;
|
||||
// 处理某个页面加载多个Table时,第2个之后的Table初始化报出警告
|
||||
// 因为第一次初始化之后会把defaultGridOptions和gridOptions合并后缓存进State
|
||||
if (formConfig && formConfig.enabled) {
|
||||
console.warn(
|
||||
'[Vben Vxe Table]: The formConfig in the grid is not supported, please use the `formOptions` props',
|
||||
);
|
||||
}
|
||||
props.api?.setState?.({ gridOptions: defaultGridOptions });
|
||||
// form 由 vben-form 代替,所以需要保证query相关事件可以拿到参数
|
||||
extendProxyOptions(props.api, defaultGridOptions, () =>
|
||||
formApi.getLatestSubmissionValues(),
|
||||
);
|
||||
}
|
||||
|
||||
// formOptions支持响应式
|
||||
watch(
|
||||
formOptions,
|
||||
() => {
|
||||
formApi.setState((prev) => {
|
||||
const finalFormOptions: VbenFormProps = mergeWithArrayOverride(
|
||||
{},
|
||||
formOptions.value,
|
||||
prev,
|
||||
);
|
||||
return {
|
||||
...finalFormOptions,
|
||||
collapseTriggerResize: !!finalFormOptions.showCollapseButton,
|
||||
};
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const isCompactForm = computed(() => {
|
||||
return formApi.getState()?.compact;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
props.api?.mount?.(gridRef.value, formApi);
|
||||
init();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
formApi?.unmount?.();
|
||||
props.api?.unmount?.();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('bg-card h-full rounded-md', className)">
|
||||
<VxeGrid
|
||||
ref="gridRef"
|
||||
:class="
|
||||
cn(
|
||||
'p-2',
|
||||
{
|
||||
'pt-0': showToolbar && !formOptions,
|
||||
},
|
||||
gridClass,
|
||||
)
|
||||
"
|
||||
v-bind="options"
|
||||
v-on="events"
|
||||
>
|
||||
<!-- 左侧操作区域或者title -->
|
||||
<template v-if="showToolbar" #toolbar-actions="slotProps">
|
||||
<slot v-if="showTableTitle" name="table-title">
|
||||
<div class="mr-1 pl-1 text-[1rem]">
|
||||
{{ tableTitle }}
|
||||
<VbenHelpTooltip v-if="tableTitleHelp" trigger-class="pb-1">
|
||||
{{ tableTitleHelp }}
|
||||
</VbenHelpTooltip>
|
||||
</div>
|
||||
</slot>
|
||||
<slot name="toolbar-actions" v-bind="slotProps"> </slot>
|
||||
</template>
|
||||
|
||||
<!-- 继承默认的slot -->
|
||||
<template
|
||||
v-for="slotName in delegatedSlots"
|
||||
:key="slotName"
|
||||
#[slotName]="slotProps"
|
||||
>
|
||||
<slot :name="slotName" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
<template #toolbar-tools="slotProps">
|
||||
<slot name="toolbar-tools" v-bind="slotProps"></slot>
|
||||
<VxeButton
|
||||
icon="vxe-icon-search"
|
||||
circle
|
||||
class="ml-2"
|
||||
v-if="gridOptions?.toolbarConfig?.search && !!formOptions"
|
||||
:status="showSearchForm ? 'primary' : undefined"
|
||||
:title="$t('common.search')"
|
||||
@click="onSearchBtnClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- form表单 -->
|
||||
<template #form>
|
||||
<div
|
||||
v-if="formOptions"
|
||||
v-show="showSearchForm !== false"
|
||||
:class="
|
||||
cn(
|
||||
'relative rounded py-3',
|
||||
isCompactForm
|
||||
? isSeparator
|
||||
? 'pb-8'
|
||||
: 'pb-4'
|
||||
: isSeparator
|
||||
? 'pb-4'
|
||||
: 'pb-0',
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot name="form">
|
||||
<Form>
|
||||
<template
|
||||
v-for="slotName in delegatedFormSlots"
|
||||
:key="slotName"
|
||||
#[slotName]="slotProps"
|
||||
>
|
||||
<slot
|
||||
:name="`${FORM_SLOT_PREFIX}${slotName}`"
|
||||
v-bind="slotProps"
|
||||
></slot>
|
||||
</template>
|
||||
<template #reset-before="slotProps">
|
||||
<slot name="reset-before" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
<template #submit-before="slotProps">
|
||||
<slot name="submit-before" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
<template #expand-before="slotProps">
|
||||
<slot name="expand-before" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
<template #expand-after="slotProps">
|
||||
<slot name="expand-after" v-bind="slotProps"></slot>
|
||||
</template>
|
||||
</Form>
|
||||
</slot>
|
||||
<div
|
||||
v-if="isSeparator"
|
||||
:style="{
|
||||
...(separatorBg ? { backgroundColor: separatorBg } : undefined),
|
||||
}"
|
||||
class="bg-background-deep z-100 absolute -left-2 bottom-1 h-2 w-[calc(100%+1rem)] overflow-hidden md:bottom-2 md:h-3"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- loading -->
|
||||
<template #loading>
|
||||
<slot name="loading">
|
||||
<VbenLoading :spinning="true" />
|
||||
</slot>
|
||||
</template>
|
||||
<!-- 统一控状态 -->
|
||||
<template #empty>
|
||||
<slot name="empty">
|
||||
<EmptyIcon class="mx-auto" />
|
||||
<div class="mt-2">{{ $t('common.noData') }}</div>
|
||||
</slot>
|
||||
</template>
|
||||
</VxeGrid>
|
||||
</div>
|
||||
</template>
|
Reference in New Issue
Block a user