admin-vben5/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue
2024-10-18 07:47:40 +08:00

313 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import type { VbenFormProps } from '@vben-core/form-ui';
import type {
VxeGridInstance,
VxeGridProps as VxeTableGridProps,
} from 'vxe-table';
import type { ExtendedVxeGridApi, VxeGridProps } from './types';
import {
computed,
nextTick,
onMounted,
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, mergeWithArrayOverride } from '@vben/utils';
import { VbenLoading } from '@vben-core/shadcn-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 './theme.css';
interface Props extends VxeGridProps {
api: ExtendedVxeGridApi;
}
const props = withDefaults(defineProps<Props>(), {});
const FORM_SLOT_PREFIX = 'form-';
const gridRef = useTemplateRef<VxeGridInstance>('gridRef');
const state = props.api?.useStore?.();
const {
gridOptions,
class: className,
gridClass,
gridEvents,
formOptions,
} = usePriorityValues(props, state);
const { isMobile } = usePreferences();
const slots = useSlots();
const [Form, formApi] = useTableForm({
handleSubmit: async () => {
const formValues = formApi.form.values;
props.api.reload(formValues);
},
handleReset: async () => {
await formApi.resetForm();
const formValues = formApi.form.values;
props.api.reload(formValues);
},
commonConfig: {
componentProps: {
class: 'w-full',
},
},
showCollapseButton: true,
submitButtonOptions: {
content: $t('common.query'),
},
// enter提交
submitOnEnter: true,
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
});
const showToolbar = computed(() => {
return !!slots['toolbar-actions']?.() || !!slots['toolbar-tools']?.();
});
const options = computed(() => {
const slotActions = slots['toolbar-actions']?.();
const slotTools = slots['toolbar-tools']?.();
const globalGridConfig = VxeUI?.getConfig()?.grid ?? {};
const forceUseToolbarOptions = showToolbar.value
? {
toolbarConfig: {
slots: {
...(slotActions ? { buttons: 'toolbar-actions' } : {}),
...(slotTools ? { tools: 'toolbar-tools' } : {}),
},
},
}
: {};
const mergedOptions: VxeTableGridProps = cloneDeep(
mergeWithArrayOverride(
{},
forceUseToolbarOptions,
toRaw(gridOptions.value),
globalGridConfig,
),
);
if (mergedOptions.proxyConfig) {
const { ajax } = mergedOptions.proxyConfig;
mergedOptions.proxyConfig.enabled = !!ajax;
// 不自动加载数据, 由组件控制
mergedOptions.proxyConfig.autoLoad = false;
}
if (!showToolbar.value && mergedOptions.toolbarConfig) {
mergedOptions.toolbarConfig.enabled = 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;
});
const events = computed(() => {
return {
...gridEvents.value,
};
});
const delegatedSlots = computed(() => {
const resultSlots: string[] = [];
for (const key of Object.keys(slots)) {
if (!['empty', 'form', 'loading'].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) {
props.api.reload(formApi.form?.values ?? {});
}
// form 由 vben-form代替所以不适配formConfig这里给出警告
const formConfig = gridOptions.value?.formConfig;
if (formConfig) {
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, () =>
// 这里默认是readonly的 需要深拷贝才能进行修改操作
cloneDeep(formApi.form.values),
);
}
watch(
formOptions,
() => {
formApi.setState((prev) => {
const finalFormOptions: VbenFormProps = mergeWithArrayOverride(
{},
formOptions.value,
prev,
);
return {
...finalFormOptions,
collapseTriggerResize: !!finalFormOptions.showCollapseButton,
};
});
},
{
immediate: true,
},
);
onMounted(() => {
props.api?.mount?.(gridRef.value, formApi);
init();
});
</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"
>
<template
v-for="slotName in delegatedSlots"
:key="slotName"
#[slotName]="slotProps"
>
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
<template #form>
<div v-if="formOptions" class="relative rounded py-3 pb-4">
<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
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>
<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>