Browse Source

fix(vxe-table): 修复发布后vxe-table无法使用的问题.

pull/1063/head
colin 1 year ago
parent
commit
ba9f352496
  1. 5
      apps/vben5/apps/app-antd/src/adapter/component/index.ts
  2. 47
      apps/vben5/apps/app-antd/src/adapter/form.ts
  3. 71
      apps/vben5/apps/app-antd/src/adapter/vxe-table.ts
  4. 1
      apps/vben5/package.json
  5. 2
      apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue
  6. 1
      apps/vben5/packages/@abp/notifications/src/types/index.ts
  7. 84
      apps/vben5/packages/@abp/notifications/src/types/notifications.ts
  8. 32
      apps/vben5/packages/@abp/ui/package.json
  9. 3
      apps/vben5/packages/@abp/ui/src/adapter/component/index.ts
  10. 119
      apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts
  11. 1
      apps/vben5/packages/@abp/ui/src/components/index.ts
  12. 6
      apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue
  13. 127
      apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts
  14. 82
      apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts
  15. 5
      apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts
  16. 131
      apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts
  17. 104
      apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css
  18. 76
      apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts
  19. 45
      apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts
  20. 394
      apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue
  21. 1
      apps/vben5/packages/@abp/ui/src/index.ts

5
apps/vben5/apps/app-antd/src/adapter/component/index.ts

@ -21,6 +21,7 @@ import {
Input,
InputNumber,
InputPassword,
InputSearch,
Mentions,
notification,
Radio,
@ -60,6 +61,7 @@ export type ComponentType =
| 'Input'
| 'InputNumber'
| 'InputPassword'
| 'InputSearch'
| 'Mentions'
| 'PrimaryButton'
| 'Radio'
@ -89,8 +91,8 @@ async function initComponentAdapter() {
...attrs,
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelPropName: 'value',
visibleEvent: 'onDropdownVisibleChange',
},
slots,
);
@ -131,6 +133,7 @@ async function initComponentAdapter() {
Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
InputSearch: withDefaultPlaceholder(InputSearch, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
PrimaryButton: (props, { attrs, slots }) => {

47
apps/vben5/apps/app-antd/src/adapter/form.ts

@ -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 };

71
apps/vben5/apps/app-antd/src/adapter/vxe-table.ts

@ -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';

1
apps/vben5/package.json

@ -27,6 +27,7 @@
"scripts": {
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
"build:analyze": "turbo build:analyze",
"build:app": "pnpm run build --filter=@vben/app-antd",
"build:antd": "pnpm run build --filter=@vben/web-antd",
"build:docker": "./scripts/deploy/build-local-docker-image.sh",
"build:docs": "pnpm run build --filter=@vben/docs",

2
apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogTable.vue

@ -235,11 +235,13 @@ const gridOptions: VxeGridProps<AuditLogDto> = {
const gridEvents: VxeGridListeners<AuditLogDto> = {
sortChange: onSort,
};
const [Grid, gridApi] = useVbenVxeGrid({
formOptions,
gridEvents,
gridOptions,
});
const { getHttpMethodColor, getHttpStatusCodeColor } = useAuditlogs();
const [AuditLogDrawer, logDrawerApi] = useVbenDrawer({
connectedComponent: defineAsyncComponent(

1
apps/vben5/packages/@abp/notifications/src/types/index.ts

@ -0,0 +1 @@
export * from './notifications';

84
apps/vben5/packages/@abp/notifications/src/types/notifications.ts

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

32
apps/vben5/packages/@abp/ui/package.json

@ -10,43 +10,35 @@
},
"license": "MIT",
"type": "module",
"scripts": {
"build": "pnpm vite build",
"prepublishOnly": "npm run build"
},
"files": [
"dist",
"src"
"sideEffects": [
"**/*.css"
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"types": "./src/index.ts",
"development": "./src/index.ts",
"default": "./dist/index.mjs"
}
},
"publishConfig": {
"exports": {
".": {
"default": "./dist/index.mjs"
}
"default": "./src/index.ts"
}
},
"dependencies": {
"@abp/core": "workspace:*",
"@vben-core/form-ui": "workspace:*",
"@vben-core/preferences": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*",
"@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
"codemirror": "catalog:",
"vue": "catalog:*"
"vue": "catalog:*",
"vxe-pc-ui": "catalog:",
"vxe-table": "catalog:"
},
"devDependencies": {
"@types/codemirror": "catalog:"

3
apps/vben5/packages/@abp/ui/src/adapter/component/index.ts

@ -21,6 +21,7 @@ import {
Input,
InputNumber,
InputPassword,
InputSearch,
Mentions,
notification,
Radio,
@ -60,6 +61,7 @@ export type ComponentType =
| 'Input'
| 'InputNumber'
| 'InputPassword'
| 'InputSearch'
| 'Mentions'
| 'PrimaryButton'
| 'Radio'
@ -131,6 +133,7 @@ async function initComponentAdapter() {
Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
InputSearch: withDefaultPlaceholder(InputSearch, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
PrimaryButton: (props, { attrs, slots }) => {

119
apps/vben5/packages/@abp/ui/src/adapter/vxe-table.ts

@ -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 { setupVbenVxeTable } from '../components/vxe-table';
import { useVbenVxeGrid as useVxeGrid } from '../components/vxe-table/use-vxe-grid';
import { useVbenForm } from './form';
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
vxeUI.setConfig({
grid: {
align: 'center',
border: false,
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',
function useVbenVxeGrid(options: VxeGridProps) {
setupVbenVxeTable({
configVxeTable: (vxeUI) => {
vxeUI.setConfig({
grid: {
align: 'center',
border: false,
columnConfig: {
resizable: true,
},
showActiveMsg: true,
showResponseMsg: false,
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,
showResponseMsg: false,
},
round: true,
showOverflow: true,
size: 'small',
},
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: 'CellImage' },
!vxeUI.renderer.get('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 },
);
},
});
// 表格配置项可以用 cellRender: { name: 'CellLink' },
!vxeUI.renderer.get('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,
});
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
// vxeUI.formats.add
},
useVbenForm,
});
return useVxeGrid(options);
}
export { useVbenVxeGrid };

1
apps/vben5/packages/@abp/ui/src/components/index.ts

@ -3,3 +3,4 @@ export * from './codemirror';
export { default as LocalizableInput } from './localizable-input/LocalizableInput.vue';
export { default as PropertyTable } from './properties/PropertyTable.vue';
export * from './properties/types';
export type * from './vxe-table';

6
apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue

@ -39,7 +39,7 @@ const getDataResource = computed((): PropertyInfo[] => {
const [PropertyModal, modalApi] = useVbenModal({
connectedComponent: defineAsyncComponent(() => import('./PropertyModal.vue')),
});
const getTableColumns = computed(() => {
const getTableColumns = computed((): TableColumnsType<PropertyInfo> => {
const columns: TableColumnsType<PropertyInfo> = [
{
align: 'left',
@ -59,7 +59,7 @@ const getTableColumns = computed(() => {
...columns,
...(props.disabled
? []
: [
: ([
{
align: 'center',
dataIndex: 'action',
@ -68,7 +68,7 @@ const getTableColumns = computed(() => {
title: $t('component.extra_property_dictionary.actions.title'),
width: 150,
},
]),
] as TableColumnsType<PropertyInfo>)),
];
});

127
apps/vben5/packages/@abp/ui/src/components/vxe-table/api.ts

@ -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();
}
}

82
apps/vben5/packages/@abp/ui/src/components/vxe-table/extends.ts

@ -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);
},
});
}

5
apps/vben5/packages/@abp/ui/src/components/vxe-table/index.ts

@ -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';

131
apps/vben5/packages/@abp/ui/src/components/vxe-table/init.ts

@ -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);
}

104
apps/vben5/packages/@abp/ui/src/components/vxe-table/style.css

@ -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;
}

76
apps/vben5/packages/@abp/ui/src/components/vxe-table/types.ts

@ -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;
}

45
apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.ts

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

394
apps/vben5/packages/@abp/ui/src/components/vxe-table/use-vxe-grid.vue

@ -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-formformConfig
const formConfig = gridOptions.value?.formConfig;
// Table2Table
// defaultGridOptionsgridOptionsState
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>

1
apps/vben5/packages/@abp/ui/src/index.ts

@ -2,4 +2,3 @@ export * from './adapter/component';
export * from './adapter/form';
export * from './adapter/vxe-table';
export * from './components';
export type * from '@vben/plugins/vxe-table';

Loading…
Cancel
Save