committed by
GitHub
29 changed files with 1187 additions and 200 deletions
@ -1,47 +0,0 @@ |
|||||
import type { |
|
||||
VbenFormSchema as FormSchema, |
|
||||
VbenFormProps, |
|
||||
} from '@vben/common-ui'; |
|
||||
|
|
||||
import type { ComponentType } from './component'; |
|
||||
|
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; |
|
||||
import { $t } from '@vben/locales'; |
|
||||
|
|
||||
setupVbenForm<ComponentType>({ |
|
||||
config: { |
|
||||
// ant design vue组件库默认都是 v-model:value
|
|
||||
baseModelPropName: 'value', |
|
||||
|
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
|
||||
modelPropNameMap: { |
|
||||
Checkbox: 'checked', |
|
||||
Radio: 'checked', |
|
||||
Switch: 'checked', |
|
||||
Upload: 'fileList', |
|
||||
}, |
|
||||
}, |
|
||||
defineRules: { |
|
||||
// 输入项目必填国际化适配
|
|
||||
required: (value, _params, ctx) => { |
|
||||
if (value === undefined || value === null || value.length === 0) { |
|
||||
return $t('ui.formRules.required', [ctx.label]); |
|
||||
} |
|
||||
return true; |
|
||||
}, |
|
||||
// 选择项目必填国际化适配
|
|
||||
selectRequired: (value, _params, ctx) => { |
|
||||
if (value === undefined || value === null) { |
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]); |
|
||||
} |
|
||||
return true; |
|
||||
}, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
const useVbenForm = useForm<ComponentType>; |
|
||||
|
|
||||
export { useVbenForm, z }; |
|
||||
|
|
||||
export type VbenFormSchema = FormSchema<ComponentType>; |
|
||||
export type { VbenFormProps }; |
|
||||
@ -1,71 +0,0 @@ |
|||||
import { h } from 'vue'; |
|
||||
|
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; |
|
||||
|
|
||||
import { Button, Image } from 'ant-design-vue'; |
|
||||
|
|
||||
import { useVbenForm } from './form'; |
|
||||
|
|
||||
setupVbenVxeTable({ |
|
||||
configVxeTable: (vxeUI) => { |
|
||||
vxeUI.setConfig({ |
|
||||
grid: { |
|
||||
align: 'center', |
|
||||
border: false, |
|
||||
columnConfig: { |
|
||||
resizable: true, |
|
||||
}, |
|
||||
minHeight: 180, |
|
||||
formConfig: { |
|
||||
// 全局禁用vxe-table的表单配置,使用formOptions
|
|
||||
enabled: false, |
|
||||
}, |
|
||||
pagerConfig: { |
|
||||
pageSize: 10, |
|
||||
pageSizes: [10, 15, 25, 50, 100], |
|
||||
}, |
|
||||
proxyConfig: { |
|
||||
autoLoad: true, |
|
||||
response: { |
|
||||
result: 'items', |
|
||||
total: 'total', |
|
||||
list: 'items', |
|
||||
}, |
|
||||
showActiveMsg: true, |
|
||||
showResponseMsg: false, |
|
||||
}, |
|
||||
round: true, |
|
||||
showOverflow: true, |
|
||||
size: 'small', |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
|
||||
vxeUI.renderer.add('CellImage', { |
|
||||
renderTableDefault(_renderOpts, params) { |
|
||||
const { column, row } = params; |
|
||||
return h(Image, { src: row[column.field] }); |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
|
||||
vxeUI.renderer.add('CellLink', { |
|
||||
renderTableDefault(renderOpts) { |
|
||||
const { props } = renderOpts; |
|
||||
return h( |
|
||||
Button, |
|
||||
{ size: 'small', type: 'link' }, |
|
||||
{ default: () => props?.text }, |
|
||||
); |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
|
||||
// vxeUI.formats.add
|
|
||||
}, |
|
||||
useVbenForm, |
|
||||
}); |
|
||||
|
|
||||
export { useVbenVxeGrid }; |
|
||||
|
|
||||
export type * from '@vben/plugins/vxe-table'; |
|
||||
@ -0,0 +1,37 @@ |
|||||
|
{ |
||||
|
"name": "@abp/notifications", |
||||
|
"version": "8.3.2", |
||||
|
"homepage": "https://github.com/colinin/abp-next-admin", |
||||
|
"bugs": "https://github.com/colinin/abp-next-admin/issues", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "git+https://github.com/colinin/abp-next-admin.git", |
||||
|
"directory": "packages/@abp/notifications" |
||||
|
}, |
||||
|
"license": "MIT", |
||||
|
"type": "module", |
||||
|
"sideEffects": [ |
||||
|
"**/*.css" |
||||
|
], |
||||
|
"exports": { |
||||
|
".": { |
||||
|
"types": "./src/index.ts", |
||||
|
"default": "./src/index.ts" |
||||
|
} |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@abp/core": "workspace:*", |
||||
|
"@abp/request": "workspace:*", |
||||
|
"@abp/ui": "workspace:*", |
||||
|
"@ant-design/icons-vue": "catalog:", |
||||
|
"@vben/access": "workspace:*", |
||||
|
"@vben/common-ui": "workspace:*", |
||||
|
"@vben/hooks": "workspace:*", |
||||
|
"@vben/icons": "workspace:*", |
||||
|
"@vben/layouts": "workspace:*", |
||||
|
"@vben/locales": "workspace:*", |
||||
|
"ant-design-vue": "catalog:", |
||||
|
"dayjs": "catalog:", |
||||
|
"vue": "catalog:*" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,4 @@ |
|||||
|
export * from './api'; |
||||
|
export * from './components'; |
||||
|
export * from './constants'; |
||||
|
export * from './types'; |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './notifications'; |
||||
@ -0,0 +1,84 @@ |
|||||
|
import type { Dictionary } from '@abp/core'; |
||||
|
|
||||
|
export enum NotificationLifetime { |
||||
|
OnlyOne = 1, |
||||
|
Persistent = 0, |
||||
|
} |
||||
|
|
||||
|
export enum NotificationType { |
||||
|
Application = 0, |
||||
|
ServiceCallback = 30, |
||||
|
System = 10, |
||||
|
User = 20, |
||||
|
} |
||||
|
|
||||
|
export enum NotificationContentType { |
||||
|
Html = 1, |
||||
|
Json = 3, |
||||
|
Markdown = 2, |
||||
|
Text = 0, |
||||
|
} |
||||
|
|
||||
|
export enum NotificationSeverity { |
||||
|
Error = 30, |
||||
|
Fatal = 40, |
||||
|
Info = 10, |
||||
|
Success = 0, |
||||
|
Warn = 20, |
||||
|
} |
||||
|
|
||||
|
export enum NotificationReadState { |
||||
|
Read = 0, |
||||
|
UnRead = 1, |
||||
|
} |
||||
|
|
||||
|
interface NotificationData { |
||||
|
extraProperties: { [key: string]: any }; |
||||
|
type: string; |
||||
|
} |
||||
|
|
||||
|
interface UserIdentifier { |
||||
|
userId: string; |
||||
|
userName?: string; |
||||
|
} |
||||
|
|
||||
|
interface NotificationSendInput { |
||||
|
culture?: string; |
||||
|
data: Dictionary<string, any>; |
||||
|
name: string; |
||||
|
severity?: NotificationSeverity; |
||||
|
toUsers?: UserIdentifier[]; |
||||
|
} |
||||
|
|
||||
|
interface NotificationInfo { |
||||
|
contentType: NotificationContentType; |
||||
|
creationTime: Date; |
||||
|
data: NotificationData; |
||||
|
id: string; |
||||
|
lifetime: NotificationLifetime; |
||||
|
name: string; |
||||
|
severity: NotificationSeverity; |
||||
|
type: NotificationType; |
||||
|
} |
||||
|
|
||||
|
interface NotificationDto { |
||||
|
description: string; |
||||
|
displayName: string; |
||||
|
lifetime: NotificationLifetime; |
||||
|
name: string; |
||||
|
type: NotificationType; |
||||
|
} |
||||
|
|
||||
|
interface NotificationGroupDto { |
||||
|
displayName: string; |
||||
|
name: string; |
||||
|
notifications: NotificationDto[]; |
||||
|
} |
||||
|
|
||||
|
export type { |
||||
|
NotificationData, |
||||
|
NotificationDto, |
||||
|
NotificationGroupDto, |
||||
|
NotificationInfo, |
||||
|
NotificationSendInput, |
||||
|
}; |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"$schema": "https://json.schemastore.org/tsconfig", |
||||
|
"extends": "@vben/tsconfig/web.json", |
||||
|
"include": ["src"], |
||||
|
"exclude": ["node_modules"] |
||||
|
} |
||||
@ -1,3 +1,4 @@ |
|||||
export * from './api'; |
export * from './api'; |
||||
export * from './components'; |
export * from './components'; |
||||
|
export * from './constants'; |
||||
export * from './types'; |
export * from './types'; |
||||
|
|||||
@ -1,69 +1,76 @@ |
|||||
import { h } from 'vue'; |
import type { VxeGridProps } from '../components/vxe-table/types'; |
||||
|
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; |
import { h } from 'vue'; |
||||
|
|
||||
import { Button, Image } from 'ant-design-vue'; |
import { Button, Image } from 'ant-design-vue'; |
||||
|
|
||||
|
import { setupVbenVxeTable } from '../components/vxe-table'; |
||||
|
import { useVbenVxeGrid as useVxeGrid } from '../components/vxe-table/use-vxe-grid'; |
||||
import { useVbenForm } from './form'; |
import { useVbenForm } from './form'; |
||||
|
|
||||
setupVbenVxeTable({ |
function useVbenVxeGrid(options: VxeGridProps) { |
||||
configVxeTable: (vxeUI) => { |
setupVbenVxeTable({ |
||||
vxeUI.setConfig({ |
configVxeTable: (vxeUI) => { |
||||
grid: { |
vxeUI.setConfig({ |
||||
align: 'center', |
grid: { |
||||
border: false, |
align: 'center', |
||||
columnConfig: { |
border: false, |
||||
resizable: true, |
columnConfig: { |
||||
}, |
resizable: true, |
||||
formConfig: { |
|
||||
// 全局禁用vxe-table的表单配置,使用formOptions
|
|
||||
enabled: false, |
|
||||
}, |
|
||||
minHeight: 180, |
|
||||
pagerConfig: { |
|
||||
pageSize: 10, |
|
||||
pageSizes: [10, 25, 50, 100], |
|
||||
}, |
|
||||
proxyConfig: { |
|
||||
autoLoad: true, |
|
||||
response: { |
|
||||
result: 'items', |
|
||||
total: 'total', |
|
||||
list: 'items', |
|
||||
}, |
}, |
||||
showActiveMsg: true, |
formConfig: { |
||||
showResponseMsg: false, |
// 全局禁用vxe-table的表单配置,使用formOptions
|
||||
|
enabled: false, |
||||
|
}, |
||||
|
minHeight: 180, |
||||
|
pagerConfig: { |
||||
|
pageSize: 10, |
||||
|
pageSizes: [10, 25, 50, 100], |
||||
|
}, |
||||
|
proxyConfig: { |
||||
|
autoLoad: true, |
||||
|
response: { |
||||
|
result: 'items', |
||||
|
total: 'total', |
||||
|
list: 'items', |
||||
|
}, |
||||
|
showActiveMsg: true, |
||||
|
showResponseMsg: false, |
||||
|
}, |
||||
|
round: true, |
||||
|
showOverflow: true, |
||||
|
size: 'small', |
||||
}, |
}, |
||||
round: true, |
}); |
||||
showOverflow: true, |
|
||||
size: 'small', |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
||||
vxeUI.renderer.add('CellImage', { |
!vxeUI.renderer.get('CellImage') && |
||||
renderTableDefault(_renderOpts, params) { |
vxeUI.renderer.add('CellImage', { |
||||
const { column, row } = params; |
renderTableDefault(_renderOpts, params) { |
||||
return h(Image, { src: row[column.field] }); |
const { column, row } = params; |
||||
}, |
return h(Image, { src: row[column.field] }); |
||||
}); |
}, |
||||
|
}); |
||||
|
|
||||
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
||||
vxeUI.renderer.add('CellLink', { |
!vxeUI.renderer.get('CellLink') && |
||||
renderTableDefault(renderOpts) { |
vxeUI.renderer.add('CellLink', { |
||||
const { props } = renderOpts; |
renderTableDefault(renderOpts) { |
||||
return h( |
const { props } = renderOpts; |
||||
Button, |
return h( |
||||
{ size: 'small', type: 'link' }, |
Button, |
||||
{ default: () => props?.text }, |
{ size: 'small', type: 'link' }, |
||||
); |
{ default: () => props?.text }, |
||||
}, |
); |
||||
}); |
}, |
||||
|
}); |
||||
|
|
||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||
// vxeUI.formats.add
|
// vxeUI.formats.add
|
||||
}, |
}, |
||||
useVbenForm, |
useVbenForm, |
||||
}); |
}); |
||||
|
return useVxeGrid(options); |
||||
|
} |
||||
|
|
||||
export { useVbenVxeGrid }; |
export { useVbenVxeGrid }; |
||||
|
|||||
@ -0,0 +1,127 @@ |
|||||
|
import type { ExtendedFormApi } from '@vben-core/form-ui'; |
||||
|
import type { VxeGridInstance } from 'vxe-table'; |
||||
|
|
||||
|
import type { VxeGridProps } from './types'; |
||||
|
|
||||
|
import { toRaw } from 'vue'; |
||||
|
|
||||
|
import { Store } from '@vben-core/shared/store'; |
||||
|
import { |
||||
|
bindMethods, |
||||
|
isBoolean, |
||||
|
isFunction, |
||||
|
mergeWithArrayOverride, |
||||
|
StateHandler, |
||||
|
} from '@vben-core/shared/utils'; |
||||
|
|
||||
|
function getDefaultState(): VxeGridProps { |
||||
|
return { |
||||
|
class: '', |
||||
|
formOptions: undefined, |
||||
|
gridClass: '', |
||||
|
gridEvents: {}, |
||||
|
gridOptions: {}, |
||||
|
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(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
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.has('formatDate') && |
||||
|
vxeUI.formats.add('formatDate', { |
||||
|
tableCellFormatMethod({ cellValue }) { |
||||
|
return formatDate(cellValue); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
!vxeUI.formats.has('formatDateTime') && |
||||
|
vxeUI.formats.add('formatDateTime', { |
||||
|
tableCellFormatMethod({ cellValue }) { |
||||
|
return formatDateTime(cellValue); |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
export { setupVbenVxeTable } from './init'; |
||||
|
export type { VxeTableGridOptions } from './types'; |
||||
|
|
||||
|
export { default as VbenVxeGrid } from './use-vxe-grid.vue'; |
||||
|
export type { VxeGridListeners, VxeGridProps } from 'vxe-table'; |
||||
@ -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 preference = usePreferences(); |
||||
|
|
||||
|
const localMap = { |
||||
|
'en-US': enUS, |
||||
|
'zh-CN': zhCN, |
||||
|
}; |
||||
|
|
||||
|
watch( |
||||
|
[() => preference.theme.value, () => preference.locale.value], |
||||
|
([theme, locale]) => { |
||||
|
VxeUI.setTheme(theme === 'dark' ? 'dark' : 'light'); |
||||
|
VxeUI.setI18n(locale, localMap[locale]); |
||||
|
VxeUI.setLanguage(locale); |
||||
|
}, |
||||
|
{ |
||||
|
immediate: true, |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
extendsDefaultFormatter(VxeUI); |
||||
|
|
||||
|
configVxeTable(VxeUI); |
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
: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(var(--accent)); |
||||
|
--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-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ |
||||
|
} |
||||
|
|
||||
|
.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-buttons--wrapper:not(:empty), |
||||
|
.vxe-tools--operate:not(:empty), |
||||
|
.vxe-tools--wrapper:not(:empty) { |
||||
|
padding: 0.6em 0; |
||||
|
} |
||||
|
|
||||
|
.vxe-tools--operate:not(:has(button)) { |
||||
|
margin-left: 0; |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
import type { ClassType, DeepPartial } from '@vben/types'; |
||||
|
import type { VbenFormProps } from '@vben-core/form-ui'; |
||||
|
import type { |
||||
|
VxeGridListeners, |
||||
|
VxeGridPropTypes, |
||||
|
VxeGridProps as VxeTableGridProps, |
||||
|
VxeUIExport, |
||||
|
} from 'vxe-table'; |
||||
|
|
||||
|
import type { VxeGridApi } from './api'; |
||||
|
|
||||
|
import type { Ref } from 'vue'; |
||||
|
|
||||
|
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 VxeGridProps { |
||||
|
/** |
||||
|
* 组件class |
||||
|
*/ |
||||
|
class?: ClassType; |
||||
|
/** |
||||
|
* 表单配置 |
||||
|
*/ |
||||
|
formOptions?: VbenFormProps; |
||||
|
/** |
||||
|
* vxe-grid class |
||||
|
*/ |
||||
|
gridClass?: ClassType; |
||||
|
/** |
||||
|
* vxe-grid 事件 |
||||
|
*/ |
||||
|
gridEvents?: DeepPartial<VxeGridListeners>; |
||||
|
/** |
||||
|
* vxe-grid 配置 |
||||
|
*/ |
||||
|
gridOptions?: DeepPartial<VxeTableGridOptions>; |
||||
|
/** |
||||
|
* 显示搜索表单 |
||||
|
*/ |
||||
|
showSearchForm?: boolean; |
||||
|
/** |
||||
|
* 标题 |
||||
|
*/ |
||||
|
tableTitle?: string; |
||||
|
/** |
||||
|
* 标题帮助 |
||||
|
*/ |
||||
|
tableTitleHelp?: string; |
||||
|
} |
||||
|
|
||||
|
export type ExtendedVxeGridApi = { |
||||
|
useStore: <T = NoInfer<VxeGridProps>>( |
||||
|
selector?: (state: NoInfer<VxeGridProps>) => T, |
||||
|
) => Readonly<Ref<T>>; |
||||
|
} & VxeGridApi; |
||||
|
|
||||
|
export interface SetupVxeTable { |
||||
|
configVxeTable: (ui: VxeUIExport) => void; |
||||
|
useVbenForm: typeof useVbenForm; |
||||
|
} |
||||
@ -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); |
||||
|
}, |
||||
|
{ |
||||
|
inheritAttrs: false, |
||||
|
name: 'VbenVxeGrid', |
||||
|
}, |
||||
|
); |
||||
|
// Add reactivity support
|
||||
|
// if (IS_REACTIVE) {
|
||||
|
// watch(
|
||||
|
// () => options,
|
||||
|
// () => {
|
||||
|
// api.setState(options);
|
||||
|
// },
|
||||
|
// { immediate: true },
|
||||
|
// );
|
||||
|
// }
|
||||
|
|
||||
|
return [Grid, extendedApi] as const; |
||||
|
} |
||||
|
|
||||
|
export type UseVbenVxeGrid = typeof useVbenVxeGrid; |
||||
@ -0,0 +1,394 @@ |
|||||
|
<script lang="ts" setup> |
||||
|
import type { VbenFormProps } from '@vben-core/form-ui'; |
||||
|
import type { |
||||
|
VxeGridDefines, |
||||
|
VxeGridInstance, |
||||
|
VxeGridListeners, |
||||
|
VxeGridPropTypes, |
||||
|
VxeGridProps as VxeTableGridProps, |
||||
|
VxeToolbarPropTypes, |
||||
|
} from 'vxe-table'; |
||||
|
|
||||
|
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, mergeWithArrayOverride } from '@vben/utils'; |
||||
|
import { VbenHelpTooltip, 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 './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 gridRef = useTemplateRef<VxeGridInstance>('gridRef'); |
||||
|
|
||||
|
const state = props.api?.useStore?.(); |
||||
|
|
||||
|
const { |
||||
|
class: className, |
||||
|
formOptions, |
||||
|
gridClass, |
||||
|
gridEvents, |
||||
|
gridOptions, |
||||
|
showSearchForm, |
||||
|
tableTitle, |
||||
|
tableTitleHelp, |
||||
|
} = usePriorityValues(props, state); |
||||
|
|
||||
|
const { isMobile } = usePreferences(); |
||||
|
|
||||
|
const slots = useSlots(); |
||||
|
|
||||
|
const [Form, formApi] = useTableForm({ |
||||
|
commonConfig: { |
||||
|
componentProps: { |
||||
|
class: 'w-full', |
||||
|
}, |
||||
|
}, |
||||
|
compact: true, |
||||
|
handleReset: async () => { |
||||
|
await formApi.resetForm(); |
||||
|
const formValues = formApi.form.values; |
||||
|
formApi.setLatestSubmissionValues(formValues); |
||||
|
props.api.reload(formValues); |
||||
|
}, |
||||
|
handleSubmit: async () => { |
||||
|
const formValues = formApi.form.values; |
||||
|
formApi.setLatestSubmissionValues(toRaw(formValues)); |
||||
|
props.api.reload(formValues); |
||||
|
}, |
||||
|
showCollapseButton: true, |
||||
|
submitButtonOptions: { |
||||
|
content: $t('common.query'), |
||||
|
}, |
||||
|
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3', |
||||
|
}); |
||||
|
|
||||
|
const showTableTitle = computed(() => { |
||||
|
return !!slots.tableTitle?.() || 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 = { |
||||
|
circle: true, |
||||
|
code: 'search', |
||||
|
icon: 'vxe-icon--search', |
||||
|
status: showSearchForm.value ? 'primary' : undefined, |
||||
|
title: $t('common.search'), |
||||
|
}; |
||||
|
// 将搜索按钮合并到用户配置的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, |
||||
|
{ |
||||
|
background: true, |
||||
|
className: 'mt-2 w-full', |
||||
|
layouts: isMobile.value ? mobileLayouts : layouts, |
||||
|
pageSize: 20, |
||||
|
pageSizes: [10, 20, 30, 50, 100, 200], |
||||
|
size: 'mini' as const, |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
if (mergedOptions.formConfig) { |
||||
|
mergedOptions.formConfig.enabled = false; |
||||
|
} |
||||
|
return mergedOptions; |
||||
|
}); |
||||
|
|
||||
|
function onToolbarToolClick(event: VxeGridDefines.ToolbarToolClickEventParams) { |
||||
|
if (event.code === 'search') { |
||||
|
props.api?.toggleSearchForm?.(); |
||||
|
} |
||||
|
( |
||||
|
gridEvents.value?.toolbarToolClick as VxeGridListeners['toolbarToolClick'] |
||||
|
)?.(event); |
||||
|
} |
||||
|
|
||||
|
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].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.grid.commitProxy?.('_init', formApi.form?.values ?? {}); |
||||
|
// 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> |
||||
|
|
||||
|
<!-- form表单 --> |
||||
|
<template #form> |
||||
|
<div |
||||
|
v-if="formOptions" |
||||
|
v-show="showSearchForm !== false" |
||||
|
:class="cn('relative rounded py-3', isCompactForm ? 'pb-6' : '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> |
||||
|
<!-- 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> |
||||
Loading…
Reference in new issue