feat: add vxe-table component (#4563)
* chore: wip vxe-table * feat: add table demo * chore: follow ci recommendations to adjust details * chore: add custom-cell demo * feat: add custom-cell table demo * feat: add table from demopull/48/MERGE
							parent
							
								
									46540a7329
								
							
						
					
					
						commit
						4173264805
					
				|  | @ -0,0 +1,48 @@ | ||||||
|  | import { faker } from '@faker-js/faker'; | ||||||
|  | import { verifyAccessToken } from '~/utils/jwt-utils'; | ||||||
|  | import { unAuthorizedResponse } from '~/utils/response'; | ||||||
|  | 
 | ||||||
|  | function generateMockDataList(count: number) { | ||||||
|  |   const dataList = []; | ||||||
|  | 
 | ||||||
|  |   for (let i = 0; i < count; i++) { | ||||||
|  |     const dataItem = { | ||||||
|  |       id: faker.string.uuid(), | ||||||
|  |       imageUrl: faker.image.avatar(), | ||||||
|  |       imageUrl2: faker.image.avatar(), | ||||||
|  |       open: faker.datatype.boolean(), | ||||||
|  |       status: faker.helpers.arrayElement(['success', 'error', 'warning']), | ||||||
|  |       productName: faker.commerce.productName(), | ||||||
|  |       price: faker.commerce.price(), | ||||||
|  |       currency: faker.finance.currencyCode(), | ||||||
|  |       quantity: faker.number.int({ min: 1, max: 100 }), | ||||||
|  |       available: faker.datatype.boolean(), | ||||||
|  |       category: faker.commerce.department(), | ||||||
|  |       releaseDate: faker.date.past(), | ||||||
|  |       rating: faker.number.float({ min: 1, max: 5 }), | ||||||
|  |       description: faker.commerce.productDescription(), | ||||||
|  |       weight: faker.number.float({ min: 0.1, max: 10 }), | ||||||
|  |       color: faker.color.human(), | ||||||
|  |       inProduction: faker.datatype.boolean(), | ||||||
|  |       tags: Array.from({ length: 3 }, () => faker.commerce.productAdjective()), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     dataList.push(dataItem); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return dataList; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const mockData = generateMockDataList(100); | ||||||
|  | 
 | ||||||
|  | export default eventHandler(async (event) => { | ||||||
|  |   const userinfo = verifyAccessToken(event); | ||||||
|  |   if (!userinfo) { | ||||||
|  |     return unAuthorizedResponse(event); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   await sleep(600); | ||||||
|  | 
 | ||||||
|  |   const { page, pageSize } = getQuery(event); | ||||||
|  |   return usePageResponseSuccess(page as string, pageSize as string, mockData); | ||||||
|  | }); | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
|     "start": "nitro dev" |     "start": "nitro dev" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@faker-js/faker": "catalog:", | ||||||
|     "jsonwebtoken": "catalog:", |     "jsonwebtoken": "catalog:", | ||||||
|     "nitropack": "catalog:" |     "nitropack": "catalog:" | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -9,6 +9,27 @@ export function useResponseSuccess<T = any>(data: T) { | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function usePageResponseSuccess<T = any>( | ||||||
|  |   page: number | string, | ||||||
|  |   pageSize: number | string, | ||||||
|  |   list: T[], | ||||||
|  |   { message = 'ok' } = {}, | ||||||
|  | ) { | ||||||
|  |   const pageData = pagination( | ||||||
|  |     Number.parseInt(`${page}`), | ||||||
|  |     Number.parseInt(`${pageSize}`), | ||||||
|  |     list, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     ...useResponseSuccess({ | ||||||
|  |       items: pageData, | ||||||
|  |       total: list.length, | ||||||
|  |     }), | ||||||
|  |     message, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export function useResponseError(message: string, error: any = null) { | export function useResponseError(message: string, error: any = null) { | ||||||
|   return { |   return { | ||||||
|     code: -1, |     code: -1, | ||||||
|  | @ -27,3 +48,18 @@ export function unAuthorizedResponse(event: H3Event<EventHandlerRequest>) { | ||||||
|   setResponseStatus(event, 401); |   setResponseStatus(event, 401); | ||||||
|   return useResponseError('UnauthorizedException', 'Unauthorized Exception'); |   return useResponseError('UnauthorizedException', 'Unauthorized Exception'); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function sleep(ms: number) { | ||||||
|  |   return new Promise((resolve) => setTimeout(resolve, ms)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function pagination<T = any>( | ||||||
|  |   pageNo: number, | ||||||
|  |   pageSize: number, | ||||||
|  |   array: T[], | ||||||
|  | ): T[] { | ||||||
|  |   const offset = (pageNo - 1) * Number(pageSize); | ||||||
|  |   return offset + Number(pageSize) >= array.length | ||||||
|  |     ? array.slice(offset) | ||||||
|  |     : array.slice(offset, offset + Number(pageSize)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1 +1,2 @@ | ||||||
| export * from './form'; | export * from './form'; | ||||||
|  | export * from './vxe-table'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | 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: true, | ||||||
|  |         minHeight: 180, | ||||||
|  |         proxyConfig: { | ||||||
|  |           autoLoad: true, | ||||||
|  |           response: { | ||||||
|  |             result: 'items', | ||||||
|  |             total: 'total', | ||||||
|  |             list: 'items', | ||||||
|  |           }, | ||||||
|  |           showActiveMsg: true, | ||||||
|  |           showResponseMsg: false, | ||||||
|  |         }, | ||||||
|  |         round: true, | ||||||
|  |         size: 'small', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellImage' },
 | ||||||
|  |     vxeUI.renderer.add('CellImage', { | ||||||
|  |       renderDefault(_renderOpts, params) { | ||||||
|  |         const { column, row } = params; | ||||||
|  |         return h(Image, { src: row[column.field] }); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellLink' },
 | ||||||
|  |     vxeUI.renderer.add('CellLink', { | ||||||
|  |       renderDefault(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 +1,2 @@ | ||||||
| export * from './form'; | export * from './form'; | ||||||
|  | export * from './vxe-table'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,60 @@ | ||||||
|  | import { h } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; | ||||||
|  | 
 | ||||||
|  | import { ElButton, ElImage } from 'element-plus'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from './form'; | ||||||
|  | 
 | ||||||
|  | setupVbenVxeTable({ | ||||||
|  |   configVxeTable: (vxeUI) => { | ||||||
|  |     vxeUI.setConfig({ | ||||||
|  |       grid: { | ||||||
|  |         align: 'center', | ||||||
|  |         border: true, | ||||||
|  |         minHeight: 180, | ||||||
|  |         proxyConfig: { | ||||||
|  |           autoLoad: true, | ||||||
|  |           response: { | ||||||
|  |             result: 'items', | ||||||
|  |             total: 'total', | ||||||
|  |             list: 'items', | ||||||
|  |           }, | ||||||
|  |           showActiveMsg: true, | ||||||
|  |           showResponseMsg: false, | ||||||
|  |         }, | ||||||
|  |         round: true, | ||||||
|  |         size: 'small', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellImage' },
 | ||||||
|  |     vxeUI.renderer.add('CellImage', { | ||||||
|  |       renderDefault(_renderOpts, params) { | ||||||
|  |         const { column, row } = params; | ||||||
|  |         const src = row[column.field]; | ||||||
|  |         return h(ElImage, { src, previewSrcList: [src] }); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellLink' },
 | ||||||
|  |     vxeUI.renderer.add('CellLink', { | ||||||
|  |       renderDefault(renderOpts) { | ||||||
|  |         const { props } = renderOpts; | ||||||
|  |         return h( | ||||||
|  |           ElButton, | ||||||
|  |           { size: 'small', link: true }, | ||||||
|  |           { default: () => props?.text }, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
 | ||||||
|  |     // vxeUI.formats.add
 | ||||||
|  |   }, | ||||||
|  |   useVbenForm, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export { useVbenVxeGrid }; | ||||||
|  | 
 | ||||||
|  | export type * from '@vben/plugins/vxe-table'; | ||||||
|  | @ -1,2 +1,3 @@ | ||||||
| export * from './form'; | export * from './form'; | ||||||
| export * from './naive'; | export * from './naive'; | ||||||
|  | export * from './vxe-table'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | import { h } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; | ||||||
|  | 
 | ||||||
|  | import { NButton, NImage } from 'naive-ui'; | ||||||
|  | 
 | ||||||
|  | import { useVbenForm } from './form'; | ||||||
|  | 
 | ||||||
|  | setupVbenVxeTable({ | ||||||
|  |   configVxeTable: (vxeUI) => { | ||||||
|  |     vxeUI.setConfig({ | ||||||
|  |       grid: { | ||||||
|  |         align: 'center', | ||||||
|  |         border: true, | ||||||
|  |         minHeight: 180, | ||||||
|  |         proxyConfig: { | ||||||
|  |           autoLoad: true, | ||||||
|  |           response: { | ||||||
|  |             result: 'items', | ||||||
|  |             total: 'total', | ||||||
|  |             list: 'items', | ||||||
|  |           }, | ||||||
|  |           showActiveMsg: true, | ||||||
|  |           showResponseMsg: false, | ||||||
|  |         }, | ||||||
|  |         round: true, | ||||||
|  |         size: 'small', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellImage' },
 | ||||||
|  |     vxeUI.renderer.add('CellImage', { | ||||||
|  |       renderDefault(_renderOpts, params) { | ||||||
|  |         const { column, row } = params; | ||||||
|  |         return h(NImage, { src: row[column.field] }); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellLink' },
 | ||||||
|  |     vxeUI.renderer.add('CellLink', { | ||||||
|  |       renderDefault(renderOpts) { | ||||||
|  |         const { props } = renderOpts; | ||||||
|  |         return h( | ||||||
|  |           NButton, | ||||||
|  |           { size: 'small', type: 'primary', quaternary: true }, | ||||||
|  |           { default: () => props?.text }, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
 | ||||||
|  |     // vxeUI.formats.add
 | ||||||
|  |   }, | ||||||
|  |   useVbenForm, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export { useVbenVxeGrid }; | ||||||
|  | 
 | ||||||
|  | export type * from '@vben/plugins/vxe-table'; | ||||||
|  | @ -19,6 +19,7 @@ | ||||||
|     "intlify", |     "intlify", | ||||||
|     "mkdist", |     "mkdist", | ||||||
|     "mockjs", |     "mockjs", | ||||||
|  |     "vitejs", | ||||||
|     "noopener", |     "noopener", | ||||||
|     "noreferrer", |     "noreferrer", | ||||||
|     "nprogress", |     "nprogress", | ||||||
|  |  | ||||||
|  | @ -13,9 +13,7 @@ | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@vben-core/shadcn-ui": "workspace:*", |     "@vben-core/shadcn-ui": "workspace:*", | ||||||
|     "@vben/common-ui": "workspace:*", |     "@vben/common-ui": "workspace:*", | ||||||
|     "@vben/hooks": "workspace:*", |  | ||||||
|     "@vben/locales": "workspace:*", |     "@vben/locales": "workspace:*", | ||||||
|     "@vben/preferences": "workspace:*", |  | ||||||
|     "@vben/styles": "workspace:*", |     "@vben/styles": "workspace:*", | ||||||
|     "ant-design-vue": "catalog:", |     "ant-design-vue": "catalog:", | ||||||
|     "lucide-vue-next": "catalog:", |     "lucide-vue-next": "catalog:", | ||||||
|  |  | ||||||
|  | @ -267,6 +267,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | ||||||
| | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | | | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | | ||||||
| | showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` | | | showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` | | ||||||
| | collapsed | 是否折叠,在`是否展开,在showCollapseButton=true`时生效 | `boolean` | `false` | | | collapsed | 是否折叠,在`是否展开,在showCollapseButton=true`时生效 | `boolean` | `false` | | ||||||
|  | | collapseTriggerResize | 折叠时,触发`resize`事件 | `boolean` | `false` | | ||||||
| | collapsedRows | 折叠时保持的行数 | `number` | `1` | | | collapsedRows | 折叠时保持的行数 | `number` | `1` | | ||||||
| | commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - | | | commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - | | ||||||
| | schema | 表单项的每一项配置 | `FormSchema` | - | | | schema | 表单项的每一项配置 | `FormSchema` | - | | ||||||
|  |  | ||||||
|  | @ -91,7 +91,10 @@ const customColors = { | ||||||
|   main: { |   main: { | ||||||
|     DEFAULT: 'hsl(var(--main))', |     DEFAULT: 'hsl(var(--main))', | ||||||
|   }, |   }, | ||||||
|   overlay: 'hsl(var(--overlay))', |   overlay: { | ||||||
|  |     content: 'hsl(var(--overlay-content))', | ||||||
|  |     DEFAULT: 'hsl(var(--overlay))', | ||||||
|  |   }, | ||||||
|   red: { |   red: { | ||||||
|     ...createColorsPalette('red'), |     ...createColorsPalette('red'), | ||||||
|     foreground: 'hsl(var(--destructive-foreground))', |     foreground: 'hsl(var(--destructive-foreground))', | ||||||
|  |  | ||||||
|  | @ -53,6 +53,7 @@ | ||||||
|     "vite": "catalog:", |     "vite": "catalog:", | ||||||
|     "vite-plugin-compression": "catalog:", |     "vite-plugin-compression": "catalog:", | ||||||
|     "vite-plugin-dts": "catalog:", |     "vite-plugin-dts": "catalog:", | ||||||
|     "vite-plugin-html": "catalog:" |     "vite-plugin-html": "catalog:", | ||||||
|  |     "vite-plugin-lazy-import": "catalog:" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -47,6 +47,7 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) { | ||||||
|       }, |       }, | ||||||
|       pwa: true, |       pwa: true, | ||||||
|       pwaOptions: getDefaultPwaOptions(appTitle), |       pwaOptions: getDefaultPwaOptions(appTitle), | ||||||
|  |       vxeTableLazyImport: true, | ||||||
|       ...envConfig, |       ...envConfig, | ||||||
|       ...application, |       ...application, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ import { viteMetadataPlugin } from './inject-metadata'; | ||||||
| import { viteLicensePlugin } from './license'; | import { viteLicensePlugin } from './license'; | ||||||
| import { viteNitroMockPlugin } from './nitro-mock'; | import { viteNitroMockPlugin } from './nitro-mock'; | ||||||
| import { vitePrintPlugin } from './print'; | import { vitePrintPlugin } from './print'; | ||||||
|  | import { viteVxeTableImportsPlugin } from './vxe-table'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 获取条件成立的 vite 插件 |  * 获取条件成立的 vite 插件 | ||||||
|  | @ -110,6 +111,7 @@ async function loadApplicationPlugins( | ||||||
|     printInfoMap, |     printInfoMap, | ||||||
|     pwa, |     pwa, | ||||||
|     pwaOptions, |     pwaOptions, | ||||||
|  |     vxeTableLazyImport, | ||||||
|     ...commonOptions |     ...commonOptions | ||||||
|   } = options; |   } = options; | ||||||
| 
 | 
 | ||||||
|  | @ -135,6 +137,12 @@ async function loadApplicationPlugins( | ||||||
|         return [await vitePrintPlugin({ infoMap: printInfoMap })]; |         return [await vitePrintPlugin({ infoMap: printInfoMap })]; | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |       condition: vxeTableLazyImport, | ||||||
|  |       plugins: async () => { | ||||||
|  |         return [await viteVxeTableImportsPlugin()]; | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       condition: nitroMock, |       condition: nitroMock, | ||||||
|       plugins: async () => { |       plugins: async () => { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | import type { PluginOption } from 'vite'; | ||||||
|  | 
 | ||||||
|  | import { lazyImport, VxeResolver } from 'vite-plugin-lazy-import'; | ||||||
|  | 
 | ||||||
|  | async function viteVxeTableImportsPlugin(): Promise<PluginOption> { | ||||||
|  |   return [ | ||||||
|  |     lazyImport({ | ||||||
|  |       resolvers: [ | ||||||
|  |         VxeResolver({ | ||||||
|  |           libraryName: 'vxe-table', | ||||||
|  |         }), | ||||||
|  |         VxeResolver({ | ||||||
|  |           libraryName: 'vxe-pc-ui', | ||||||
|  |         }), | ||||||
|  |       ], | ||||||
|  |     }), | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { viteVxeTableImportsPlugin }; | ||||||
|  | @ -123,6 +123,8 @@ interface ApplicationPluginOptions extends CommonPluginOptions { | ||||||
|   pwa?: boolean; |   pwa?: boolean; | ||||||
|   /** pwa 插件配置 */ |   /** pwa 插件配置 */ | ||||||
|   pwaOptions?: Partial<PwaPluginOptions>; |   pwaOptions?: Partial<PwaPluginOptions>; | ||||||
|  |   /** 是否开启vxe-table懒加载 */ | ||||||
|  |   vxeTableLazyImport?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| interface LibraryPluginOptions extends CommonPluginOptions { | interface LibraryPluginOptions extends CommonPluginOptions { | ||||||
|  |  | ||||||
|  | @ -79,6 +79,7 @@ | ||||||
| 
 | 
 | ||||||
|   /* 遮罩颜色 */ |   /* 遮罩颜色 */ | ||||||
|   --overlay: 0deg 0% 0% / 40%; |   --overlay: 0deg 0% 0% / 40%; | ||||||
|  |   --overlay-content: 0deg 0% 0% / 40%; | ||||||
| 
 | 
 | ||||||
|   /* 基本文字大小 */ |   /* 基本文字大小 */ | ||||||
|   --font-size-base: 16px; |   --font-size-base: 16px; | ||||||
|  |  | ||||||
|  | @ -79,7 +79,7 @@ | ||||||
| 
 | 
 | ||||||
|   /* 遮罩颜色 */ |   /* 遮罩颜色 */ | ||||||
|   --overlay: 0 0% 0% / 45%; |   --overlay: 0 0% 0% / 45%; | ||||||
|   --overlay-light: 0 0% 95% / 45%; |   --overlay-content: 0 0% 95% / 45%; | ||||||
| 
 | 
 | ||||||
|   /* 基本文字大小 */ |   /* 基本文字大小 */ | ||||||
|   --font-size-base: 16px; |   --font-size-base: 16px; | ||||||
|  |  | ||||||
|  | @ -3,5 +3,19 @@ import { defineBuildConfig } from 'unbuild'; | ||||||
| export default defineBuildConfig({ | export default defineBuildConfig({ | ||||||
|   clean: true, |   clean: true, | ||||||
|   declaration: true, |   declaration: true, | ||||||
|   entries: ['src/index'], |   entries: [ | ||||||
|  |     { | ||||||
|  |       builder: 'mkdist', | ||||||
|  |       input: './src', | ||||||
|  |       loaders: ['vue'], | ||||||
|  |       pattern: ['**/*.vue'], | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       builder: 'mkdist', | ||||||
|  |       format: 'esm', | ||||||
|  |       input: './src', | ||||||
|  |       loaders: ['js'], | ||||||
|  |       pattern: ['**/*.ts'], | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,27 @@ | ||||||
|  | <template> | ||||||
|  |   <svg | ||||||
|  |     height="41" | ||||||
|  |     viewBox="0 0 64 41" | ||||||
|  |     width="64" | ||||||
|  |     xmlns="http://www.w3.org/2000/svg" | ||||||
|  |   > | ||||||
|  |     <g fill="none" fill-rule="evenodd" transform="translate(0 1)"> | ||||||
|  |       <ellipse | ||||||
|  |         cx="32" | ||||||
|  |         cy="33" | ||||||
|  |         fill="hsl(var(--background-deep))" | ||||||
|  |         rx="32" | ||||||
|  |         ry="7" | ||||||
|  |       /> | ||||||
|  |       <g fill-rule="nonzero" stroke="hsl(var(--heavy))"> | ||||||
|  |         <path | ||||||
|  |           d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z" | ||||||
|  |         /> | ||||||
|  |         <path | ||||||
|  |           d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" | ||||||
|  |           fill="hsl(var(--accent))" | ||||||
|  |         /> | ||||||
|  |       </g> | ||||||
|  |     </g> | ||||||
|  |   </svg> | ||||||
|  | </template> | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
|  | export { default as EmptyIcon } from './components/empty.vue'; | ||||||
| export * from './create-icon'; | export * from './create-icon'; | ||||||
| export * from './lucide'; |  | ||||||
| 
 | 
 | ||||||
|  | export * from './lucide'; | ||||||
| export * from '@iconify/vue'; | export * from '@iconify/vue'; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| /** | /** layout content 组件的高度 */ | ||||||
|  * @zh_CN 布局内容高度 css变量 |  | ||||||
|  * @en_US Layout content height |  | ||||||
|  */ |  | ||||||
| export const CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT = `--vben-content-height`; | export const CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT = `--vben-content-height`; | ||||||
|  | /** layout content 组件的宽度 */ | ||||||
| export const CSS_VARIABLE_LAYOUT_CONTENT_WIDTH = `--vben-content-width`; | export const CSS_VARIABLE_LAYOUT_CONTENT_WIDTH = `--vben-content-width`; | ||||||
|  | /** layout header 组件的高度 */ | ||||||
|  | export const CSS_VARIABLE_LAYOUT_HEADER_HEIGHT = `--vben-header-height`; | ||||||
|  | /** layout footer 组件的高度 */ | ||||||
|  | export const CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT = `--vben-footer-height`; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @zh_CN 默认命名空间 |  * @zh_CN 默认命名空间 | ||||||
|  |  | ||||||
|  | @ -85,3 +85,11 @@ export function needsScrollbar() { | ||||||
|   // 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断
 |   // 在其他情况下,根据 scrollHeight 和 innerHeight 比较判断
 | ||||||
|   return doc.scrollHeight > window.innerHeight; |   return doc.scrollHeight > window.innerHeight; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function triggerWindowResize(): void { | ||||||
|  |   // 创建一个新的 resize 事件
 | ||||||
|  |   const resizeEvent = new Event('resize'); | ||||||
|  | 
 | ||||||
|  |   // 触发 window 的 resize 事件
 | ||||||
|  |   window.dispatchEvent(resizeEvent); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| export * from './use-content-style'; |  | ||||||
| export * from './use-is-mobile'; | export * from './use-is-mobile'; | ||||||
|  | export * from './use-layout-style'; | ||||||
| export * from './use-namespace'; | export * from './use-namespace'; | ||||||
| export * from './use-priority-value'; | export * from './use-priority-value'; | ||||||
| export * from './use-scroll-lock'; | export * from './use-scroll-lock'; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,8 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'; | ||||||
| import { | import { | ||||||
|   CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, |   CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT, | ||||||
|   CSS_VARIABLE_LAYOUT_CONTENT_WIDTH, |   CSS_VARIABLE_LAYOUT_CONTENT_WIDTH, | ||||||
|  |   CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT, | ||||||
|  |   CSS_VARIABLE_LAYOUT_HEADER_HEIGHT, | ||||||
| } from '@vben-core/shared/constants'; | } from '@vben-core/shared/constants'; | ||||||
| import { | import { | ||||||
|   getElementVisibleRect, |   getElementVisibleRect, | ||||||
|  | @ -15,7 +17,7 @@ import { useCssVar, useDebounceFn } from '@vueuse/core'; | ||||||
| /** | /** | ||||||
|  * @zh_CN content style |  * @zh_CN content style | ||||||
|  */ |  */ | ||||||
| function useContentStyle() { | export function useLayoutContentStyle() { | ||||||
|   let resizeObserver: null | ResizeObserver = null; |   let resizeObserver: null | ResizeObserver = null; | ||||||
|   const contentElement = ref<HTMLDivElement | null>(null); |   const contentElement = ref<HTMLDivElement | null>(null); | ||||||
|   const visibleDomRect = ref<null | VisibleDomRect>(null); |   const visibleDomRect = ref<null | VisibleDomRect>(null); | ||||||
|  | @ -40,7 +42,7 @@ function useContentStyle() { | ||||||
|       contentHeight.value = `${visibleDomRect.value.height}px`; |       contentHeight.value = `${visibleDomRect.value.height}px`; | ||||||
|       contentWidth.value = `${visibleDomRect.value.width}px`; |       contentWidth.value = `${visibleDomRect.value.width}px`; | ||||||
|     }, |     }, | ||||||
|     100, |     16, | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   onMounted(() => { |   onMounted(() => { | ||||||
|  | @ -58,4 +60,28 @@ function useContentStyle() { | ||||||
|   return { contentElement, overlayStyle, visibleDomRect }; |   return { contentElement, overlayStyle, visibleDomRect }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { useContentStyle }; | export function useLayoutHeaderStyle() { | ||||||
|  |   const headerHeight = useCssVar(CSS_VARIABLE_LAYOUT_HEADER_HEIGHT); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     getLayoutHeaderHeight: () => { | ||||||
|  |       return Number.parseInt(`${headerHeight.value}`, 10); | ||||||
|  |     }, | ||||||
|  |     setLayoutHeaderHeight: (height: number) => { | ||||||
|  |       headerHeight.value = `${height}px`; | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function useLayoutFooterStyle() { | ||||||
|  |   const footerHeight = useCssVar(CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     getLayoutFooterHeight: () => { | ||||||
|  |       return Number.parseInt(`${footerHeight.value}`, 10); | ||||||
|  |     }, | ||||||
|  |     setLayoutFooterHeight: (height: number) => { | ||||||
|  |       footerHeight.value = `${height}px`; | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | @ -39,7 +39,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj | ||||||
|     "icpLink": "", |     "icpLink": "", | ||||||
|   }, |   }, | ||||||
|   "footer": { |   "footer": { | ||||||
|     "enable": true, |     "enable": false, | ||||||
|     "fixed": false, |     "fixed": false, | ||||||
|   }, |   }, | ||||||
|   "header": { |   "header": { | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ const defaultPreferences: Preferences = { | ||||||
|     icpLink: '', |     icpLink: '', | ||||||
|   }, |   }, | ||||||
|   footer: { |   footer: { | ||||||
|     enable: true, |     enable: false, | ||||||
|     fixed: false, |     fixed: false, | ||||||
|   }, |   }, | ||||||
|   header: { |   header: { | ||||||
|  |  | ||||||
|  | @ -28,6 +28,10 @@ function usePreferences() { | ||||||
|     return isDarkTheme(preferences.theme.mode); |     return isDarkTheme(preferences.theme.mode); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   const locale = computed(() => { | ||||||
|  |     return preferences.app.locale; | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   const isMobile = computed(() => { |   const isMobile = computed(() => { | ||||||
|     return appPreferences.value.isMobile; |     return appPreferences.value.isMobile; | ||||||
|   }); |   }); | ||||||
|  | @ -218,6 +222,7 @@ function usePreferences() { | ||||||
|     isSideNav, |     isSideNav, | ||||||
|     keepAlive, |     keepAlive, | ||||||
|     layout, |     layout, | ||||||
|  |     locale, | ||||||
|     preferencesButtonPosition, |     preferencesButtonPosition, | ||||||
|     sidebarCollapsed, |     sidebarCollapsed, | ||||||
|     theme, |     theme, | ||||||
|  |  | ||||||
|  | @ -109,7 +109,7 @@ describe('formApi', () => { | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should unmount form and reset state', () => { |   it('should unmount form and reset state', () => { | ||||||
|     formApi.unmounted(); |     formApi.unmount(); | ||||||
|     expect(formApi.isMounted).toBe(false); |     expect(formApi.isMounted).toBe(false); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { computed, toRaw, unref } from 'vue'; | import { computed, toRaw, unref, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { useSimpleLocale } from '@vben-core/composables'; | import { useSimpleLocale } from '@vben-core/composables'; | ||||||
| import { VbenExpandableArrow } from '@vben-core/shadcn-ui'; | import { VbenExpandableArrow } from '@vben-core/shadcn-ui'; | ||||||
| import { cn, isFunction } from '@vben-core/shared/utils'; | import { cn, isFunction, triggerWindowResize } from '@vben-core/shared/utils'; | ||||||
| 
 | 
 | ||||||
| import { COMPONENT_MAP } from '../config'; | import { COMPONENT_MAP } from '../config'; | ||||||
| import { injectFormProps } from '../use-form-context'; | import { injectFormProps } from '../use-form-context'; | ||||||
|  | @ -65,6 +65,16 @@ async function handleReset(e: Event) { | ||||||
|     form.resetForm(); |     form.resetForm(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => collapsed.value, | ||||||
|  |   () => { | ||||||
|  |     const props = unref(rootProps); | ||||||
|  |     if (props.collapseTriggerResize) { | ||||||
|  |       triggerWindowResize(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | ); | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|   <div |   <div | ||||||
|  |  | ||||||
|  | @ -24,9 +24,11 @@ function getDefaultState(): VbenFormProps { | ||||||
|     actionWrapperClass: '', |     actionWrapperClass: '', | ||||||
|     collapsed: false, |     collapsed: false, | ||||||
|     collapsedRows: 1, |     collapsedRows: 1, | ||||||
|  |     collapseTriggerResize: false, | ||||||
|     commonConfig: {}, |     commonConfig: {}, | ||||||
|     handleReset: undefined, |     handleReset: undefined, | ||||||
|     handleSubmit: undefined, |     handleSubmit: undefined, | ||||||
|  |     handleValuesChange: undefined, | ||||||
|     layout: 'horizontal', |     layout: 'horizontal', | ||||||
|     resetButtonOptions: {}, |     resetButtonOptions: {}, | ||||||
|     schema: [], |     schema: [], | ||||||
|  | @ -249,7 +251,7 @@ export class FormApi { | ||||||
|     return rawValues; |     return rawValues; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   unmounted() { |   unmount() { | ||||||
|     // this.state = null;
 |     // this.state = null;
 | ||||||
|     this.isMounted = false; |     this.isMounted = false; | ||||||
|     this.stateHandler.reset(); |     this.stateHandler.reset(); | ||||||
|  |  | ||||||
|  | @ -244,6 +244,11 @@ export interface FormRenderProps< | ||||||
|    * @default 1 |    * @default 1 | ||||||
|    */ |    */ | ||||||
|   collapsedRows?: number; |   collapsedRows?: number; | ||||||
|  |   /** | ||||||
|  |    * 是否触发resize事件 | ||||||
|  |    * @default false | ||||||
|  |    */ | ||||||
|  |   collapseTriggerResize?: boolean; | ||||||
|   /** |   /** | ||||||
|    * 表单项通用后备配置,当子项目没配置时使用这里的配置,子项目配置优先级高于此配置 |    * 表单项通用后备配置,当子项目没配置时使用这里的配置,子项目配置优先级高于此配置 | ||||||
|    */ |    */ | ||||||
|  | @ -302,6 +307,10 @@ export interface VbenFormProps< | ||||||
|    * 表单提交回调 |    * 表单提交回调 | ||||||
|    */ |    */ | ||||||
|   handleSubmit?: HandleSubmitFn; |   handleSubmit?: HandleSubmitFn; | ||||||
|  |   /** | ||||||
|  |    * 表单值变化回调 | ||||||
|  |    */ | ||||||
|  |   handleValuesChange?: (values: Record<string, any>) => void; | ||||||
|   /** |   /** | ||||||
|    * 重置按钮参数 |    * 重置按钮参数 | ||||||
|    */ |    */ | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ export function useVbenForm< | ||||||
|   const Form = defineComponent( |   const Form = defineComponent( | ||||||
|     (props: VbenFormProps, { attrs, slots }) => { |     (props: VbenFormProps, { attrs, slots }) => { | ||||||
|       onBeforeUnmount(() => { |       onBeforeUnmount(() => { | ||||||
|         api.unmounted(); |         api.unmount(); | ||||||
|       }); |       }); | ||||||
|       return () => |       return () => | ||||||
|         h(VbenUseForm, { ...props, ...attrs, formApi: extendedApi }, slots); |         h(VbenUseForm, { ...props, ...attrs, formApi: extendedApi }, slots); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type { ExtendedFormApi, VbenFormProps } from './types'; | import type { ExtendedFormApi, VbenFormProps } from './types'; | ||||||
| 
 | 
 | ||||||
|  | // import { toRaw, watch } from 'vue'; | ||||||
|  | 
 | ||||||
| import { useForwardPriorityValues } from '@vben-core/composables'; | import { useForwardPriorityValues } from '@vben-core/composables'; | ||||||
|  | // import { isFunction } from '@vben-core/shared/utils'; | ||||||
| 
 | 
 | ||||||
| import FormActions from './components/form-actions.vue'; | import FormActions from './components/form-actions.vue'; | ||||||
| import { | import { | ||||||
|  | @ -31,6 +34,18 @@ props.formApi?.mount?.(form); | ||||||
| const handleUpdateCollapsed = (value: boolean) => { | const handleUpdateCollapsed = (value: boolean) => { | ||||||
|   props.formApi?.setState({ collapsed: !!value }); |   props.formApi?.setState({ collapsed: !!value }); | ||||||
| }; | }; | ||||||
|  | // if (isFunction(forward.value.handleValuesChange)) { | ||||||
|  | //   watch( | ||||||
|  | //     () => form.values, | ||||||
|  | //     (val) => { | ||||||
|  | //       forward.value.handleValuesChange?.(toRaw(val)); | ||||||
|  | //     }, | ||||||
|  | //     { | ||||||
|  | //       deep: true, | ||||||
|  | //       immediate: true, | ||||||
|  | //     }, | ||||||
|  | //   ); | ||||||
|  | // } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import type { ContentCompactType } from '@vben-core/typings'; | ||||||
| import type { CSSProperties } from 'vue'; | import type { CSSProperties } from 'vue'; | ||||||
| import { computed } from 'vue'; | import { computed } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { useContentStyle } from '@vben-core/composables'; | import { useLayoutContentStyle } from '@vben-core/composables'; | ||||||
| import { Slot } from '@vben-core/shadcn-ui'; | import { Slot } from '@vben-core/shadcn-ui'; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|  | @ -25,7 +25,7 @@ interface Props { | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<Props>(), {}); | const props = withDefaults(defineProps<Props>(), {}); | ||||||
| 
 | 
 | ||||||
| const { contentElement, overlayStyle } = useContentStyle(); | const { contentElement, overlayStyle } = useLayoutContentStyle(); | ||||||
| 
 | 
 | ||||||
| const style = computed((): CSSProperties => { | const style = computed((): CSSProperties => { | ||||||
|   const { |   const { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,11 @@ import type { VbenLayoutProps } from './vben-layout'; | ||||||
| import type { CSSProperties } from 'vue'; | import type { CSSProperties } from 'vue'; | ||||||
| import { computed, ref, watch } from 'vue'; | import { computed, ref, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { SCROLL_FIXED_CLASS } from '@vben-core/composables'; | import { | ||||||
|  |   SCROLL_FIXED_CLASS, | ||||||
|  |   useLayoutFooterStyle, | ||||||
|  |   useLayoutHeaderStyle, | ||||||
|  | } from '@vben-core/composables'; | ||||||
| import { Menu } from '@vben-core/icons'; | import { Menu } from '@vben-core/icons'; | ||||||
| import { VbenIconButton } from '@vben-core/shadcn-ui'; | import { VbenIconButton } from '@vben-core/shadcn-ui'; | ||||||
| 
 | 
 | ||||||
|  | @ -74,6 +78,9 @@ const { | ||||||
|   y: scrollY, |   y: scrollY, | ||||||
| } = useScroll(document); | } = useScroll(document); | ||||||
| 
 | 
 | ||||||
|  | const { setLayoutHeaderHeight } = useLayoutHeaderStyle(); | ||||||
|  | const { setLayoutFooterHeight } = useLayoutFooterStyle(); | ||||||
|  | 
 | ||||||
| const { y: mouseY } = useMouse({ target: contentRef, type: 'client' }); | const { y: mouseY } = useMouse({ target: contentRef, type: 'client' }); | ||||||
| 
 | 
 | ||||||
| const { | const { | ||||||
|  | @ -356,6 +363,26 @@ watch( | ||||||
|   }, |   }, | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
|  | watch( | ||||||
|  |   [() => headerWrapperHeight.value, () => isFullContent.value], | ||||||
|  |   ([height]) => { | ||||||
|  |     setLayoutHeaderHeight(isFullContent.value ? 0 : height); | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     immediate: true, | ||||||
|  |   }, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   () => props.footerHeight, | ||||||
|  |   (height: number) => { | ||||||
|  |     setLayoutFooterHeight(height); | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     immediate: true, | ||||||
|  |   }, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
| { | { | ||||||
|   const mouseMove = () => { |   const mouseMove = () => { | ||||||
|     mouseY.value > headerWrapperHeight.value |     mouseY.value > headerWrapperHeight.value | ||||||
|  |  | ||||||
|  | @ -15,13 +15,6 @@ export default defineBuildConfig({ | ||||||
|       loaders: ['vue'], |       loaders: ['vue'], | ||||||
|       pattern: ['**/*.vue'], |       pattern: ['**/*.vue'], | ||||||
|     }, |     }, | ||||||
|     // {
 |  | ||||||
|     //   builder: 'mkdist',
 |  | ||||||
|     //   format: 'cjs',
 |  | ||||||
|     //   input: './src',
 |  | ||||||
|     //   loaders: ['js'],
 |  | ||||||
|     //   pattern: ['**/*.ts'],
 |  | ||||||
|     // },
 |  | ||||||
|     { |     { | ||||||
|       builder: 'mkdist', |       builder: 'mkdist', | ||||||
|       format: 'esm', |       format: 'esm', | ||||||
|  |  | ||||||
|  | @ -16,13 +16,6 @@ export default defineBuildConfig({ | ||||||
|       loaders: ['vue'], |       loaders: ['vue'], | ||||||
|       pattern: ['**/*.vue'], |       pattern: ['**/*.vue'], | ||||||
|     }, |     }, | ||||||
|     // {
 |  | ||||||
|     //   builder: 'mkdist',
 |  | ||||||
|     //   format: 'cjs',
 |  | ||||||
|     //   input: './src',
 |  | ||||||
|     //   loaders: ['js'],
 |  | ||||||
|     //   pattern: ['**/*.ts'],
 |  | ||||||
|     // },
 |  | ||||||
|     { |     { | ||||||
|       builder: 'mkdist', |       builder: 'mkdist', | ||||||
|       format: 'esm', |       format: 'esm', | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { computed } from 'vue'; | import { computed, watch } from 'vue'; | ||||||
| 
 | 
 | ||||||
| import { cn } from '@vben-core/shared/utils'; | import { cn } from '@vben-core/shared/utils'; | ||||||
| 
 | 
 | ||||||
|  | @ -32,10 +32,13 @@ const { | ||||||
|   showRowsPerPage = true, |   showRowsPerPage = true, | ||||||
|   showTotalText = true, |   showTotalText = true, | ||||||
|   siblingCount = 1, |   siblingCount = 1, | ||||||
|   size = 'default', |   size = 'small', | ||||||
|   total = 500, |   total = 500, | ||||||
| } = defineProps<Props>(); | } = defineProps<Props>(); | ||||||
| 
 | 
 | ||||||
|  | const emit = defineEmits<{ | ||||||
|  |   pageChange: [currentPage: number, pageSize: number]; | ||||||
|  | }>(); | ||||||
| const currentPage = defineModel<number>('currentPage', { default: 1 }); | const currentPage = defineModel<number>('currentPage', { default: 1 }); | ||||||
| const itemPerPage = defineModel<number>('itemPerPage', { default: 20 }); | const itemPerPage = defineModel<number>('itemPerPage', { default: 20 }); | ||||||
| 
 | 
 | ||||||
|  | @ -53,6 +56,13 @@ const options = computed(() => { | ||||||
| function handleUpdateModelValue(value: string) { | function handleUpdateModelValue(value: string) { | ||||||
|   itemPerPage.value = Number(value); |   itemPerPage.value = Number(value); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | watch( | ||||||
|  |   [() => itemPerPage.value, () => currentPage.value], | ||||||
|  |   ([itemPerPage, currentPage]) => { | ||||||
|  |     emit('pageChange', currentPage, itemPerPage); | ||||||
|  |   }, | ||||||
|  | ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  |  | ||||||
|  | @ -69,7 +69,7 @@ function onTransitionEnd() { | ||||||
|   <div |   <div | ||||||
|     :class=" |     :class=" | ||||||
|       cn( |       cn( | ||||||
|         'z-100 dark:bg-overlay pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center bg-[hsl(var(--overlay-light))] transition-all duration-500', |         'z-100 dark:bg-overlay bg-overlay-content pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center transition-all duration-500', | ||||||
|         { |         { | ||||||
|           'invisible opacity-0': !showSpinner, |           'invisible opacity-0': !showSpinner, | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ function onTransitionEnd() { | ||||||
|   <div |   <div | ||||||
|     :class=" |     :class=" | ||||||
|       cn( |       cn( | ||||||
|         'flex-center z-100 dark:bg-overlay absolute left-0 top-0 size-full bg-[hsl(var(--overlay-light))] backdrop-blur-sm transition-all duration-500', |         'flex-center z-100 bg-overlay-content absolute left-0 top-0 size-full backdrop-blur-sm transition-all duration-500', | ||||||
|         { |         { | ||||||
|           'invisible opacity-0': !showSpinner, |           'invisible opacity-0': !showSpinner, | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ describe('page.vue', () => { | ||||||
|       }, |       }, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const contentDiv = wrapper.find('.m-4'); |     const contentDiv = wrapper.find('.p-4'); | ||||||
|     expect(contentDiv.classes()).toContain('custom-class'); |     expect(contentDiv.classes()).toContain('custom-class'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,37 +1,79 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|  | import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'; | ||||||
|  | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   title?: string; |   title?: string; | ||||||
|   description?: string; |   description?: string; | ||||||
|   contentClass?: string; |   contentClass?: string; | ||||||
|   showFooter?: boolean; |   /** | ||||||
|  |    * 根据content可见高度自适应 | ||||||
|  |    */ | ||||||
|  |   autoContentHeight?: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| defineOptions({ | defineOptions({ | ||||||
|   name: 'Page', |   name: 'Page', | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<Props>(), { | const { | ||||||
|   contentClass: '', |   contentClass = '', | ||||||
|   description: '', |   description = '', | ||||||
|   showFooter: false, |   autoContentHeight = false, | ||||||
|   title: '', |   title = '', | ||||||
|  | } = defineProps<Props>(); | ||||||
|  | 
 | ||||||
|  | const headerHeight = ref(0); | ||||||
|  | const footerHeight = ref(0); | ||||||
|  | const shouldAutoHeight = ref(false); | ||||||
|  | 
 | ||||||
|  | const headerRef = useTemplateRef<HTMLDivElement>('headerRef'); | ||||||
|  | const footerRef = useTemplateRef<HTMLDivElement>('footerRef'); | ||||||
|  | 
 | ||||||
|  | const contentStyle = computed(() => { | ||||||
|  |   if (autoContentHeight) { | ||||||
|  |     return { | ||||||
|  |       height: shouldAutoHeight.value | ||||||
|  |         ? `calc(var(--vben-content-height) - ${headerHeight.value}px - ${footerHeight.value}px)` | ||||||
|  |         : '0', | ||||||
|  |       // 'overflow-y': shouldAutoHeight.value?'auto':'unset', | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |   return {}; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | async function calcContentHeight() { | ||||||
|  |   if (!autoContentHeight) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   await nextTick(); | ||||||
|  |   headerHeight.value = headerRef.value?.offsetHeight || 0; | ||||||
|  |   footerHeight.value = footerRef.value?.offsetHeight || 0; | ||||||
|  |   setTimeout(() => { | ||||||
|  |     shouldAutoHeight.value = true; | ||||||
|  |   }, 30); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   calcContentHeight(); | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <div class="relative h-full"> |   <div class="relative"> | ||||||
|     <div |     <div | ||||||
|       v-if="description || $slots.description || title" |       v-if=" | ||||||
|       class="bg-card px-6 py-4" |         description || | ||||||
|  |         $slots.description || | ||||||
|  |         title || | ||||||
|  |         $slots.title || | ||||||
|  |         $slots.extra | ||||||
|  |       " | ||||||
|  |       ref="headerRef" | ||||||
|  |       class="bg-card relative px-6 py-4" | ||||||
|     > |     > | ||||||
|       <slot name="title"> |       <slot name="title"> | ||||||
|         <div |         <div v-if="title" class="mb-2 flex text-lg font-semibold"> | ||||||
|           v-if="title" |  | ||||||
|           class="mb-2 flex justify-between text-lg font-semibold" |  | ||||||
|         > |  | ||||||
|           {{ title }} |           {{ title }} | ||||||
| 
 |  | ||||||
|           <slot name="extra"></slot> |  | ||||||
|         </div> |         </div> | ||||||
|       </slot> |       </slot> | ||||||
| 
 | 
 | ||||||
|  | @ -40,14 +82,19 @@ const props = withDefaults(defineProps<Props>(), { | ||||||
|           {{ description }} |           {{ description }} | ||||||
|         </p> |         </p> | ||||||
|       </slot> |       </slot> | ||||||
|  | 
 | ||||||
|  |       <div v-if="$slots.extra" class="absolute bottom-4 right-4"> | ||||||
|  |         <slot name="extra"></slot> | ||||||
|  |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div :class="contentClass" class="m-4"> |     <div :class="contentClass" :style="contentStyle" class="h-full p-4"> | ||||||
|       <slot></slot> |       <slot></slot> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div |     <div | ||||||
|       v-if="props.showFooter" |       v-if="$slots.footer" | ||||||
|  |       ref="footerRef" | ||||||
|       class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4" |       class="bg-card align-center absolute bottom-0 left-0 right-0 flex px-6 py-4" | ||||||
|     > |     > | ||||||
|       <slot name="footer"></slot> |       <slot name="footer"></slot> | ||||||
|  |  | ||||||
|  | @ -17,12 +17,26 @@ | ||||||
|     "./echarts": { |     "./echarts": { | ||||||
|       "types": "./src/echarts/index.ts", |       "types": "./src/echarts/index.ts", | ||||||
|       "default": "./src/echarts/index.ts" |       "default": "./src/echarts/index.ts" | ||||||
|  |     }, | ||||||
|  |     "./vxe-table": { | ||||||
|  |       "types": "./src/vxe-table/index.ts", | ||||||
|  |       "default": "./src/vxe-table/index.ts" | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "@vben-core/form-ui": "workspace:*", | ||||||
|  |     "@vben-core/shadcn-ui": "workspace:*", | ||||||
|  |     "@vben-core/shared": "workspace:*", | ||||||
|  |     "@vben/hooks": "workspace:*", | ||||||
|  |     "@vben/icons": "workspace:*", | ||||||
|  |     "@vben/locales": "workspace:*", | ||||||
|     "@vben/preferences": "workspace:*", |     "@vben/preferences": "workspace:*", | ||||||
|  |     "@vben/types": "workspace:*", | ||||||
|  |     "@vben/utils": "workspace:*", | ||||||
|     "@vueuse/core": "catalog:", |     "@vueuse/core": "catalog:", | ||||||
|     "echarts": "catalog:", |     "echarts": "catalog:", | ||||||
|     "vue": "catalog:" |     "vue": "catalog:", | ||||||
|  |     "vxe-pc-ui": "catalog:", | ||||||
|  |     "vxe-table": "catalog:" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | export { default } from '@vben/tailwind-config/postcss'; | ||||||
|  | @ -0,0 +1,111 @@ | ||||||
|  | import type { VxeGridInstance } from 'vxe-table'; | ||||||
|  | 
 | ||||||
|  | import type { VxeGridProps } from './types'; | ||||||
|  | 
 | ||||||
|  | import { toRaw } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { Store } from '@vben-core/shared/store'; | ||||||
|  | import { | ||||||
|  |   bindMethods, | ||||||
|  |   isFunction, | ||||||
|  |   mergeWithArrayOverride, | ||||||
|  |   StateHandler, | ||||||
|  | } from '@vben-core/shared/utils'; | ||||||
|  | 
 | ||||||
|  | function getDefaultState(): VxeGridProps { | ||||||
|  |   return { | ||||||
|  |     class: '', | ||||||
|  |     gridClass: '', | ||||||
|  |     gridOptions: {}, | ||||||
|  |     gridEvents: {}, | ||||||
|  |     formOptions: undefined, | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class VxeGridApi { | ||||||
|  |   // private prevState: null | VxeGridProps = null;
 | ||||||
|  |   public grid = {} as VxeGridInstance; | ||||||
|  | 
 | ||||||
|  |   isMounted = false; | ||||||
|  |   public state: null | VxeGridProps = null; | ||||||
|  | 
 | ||||||
|  |   stateHandler: StateHandler; | ||||||
|  | 
 | ||||||
|  |   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) { | ||||||
|  |     if (!this.isMounted && instance) { | ||||||
|  |       this.grid = instance; | ||||||
|  |       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)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   unmount() { | ||||||
|  |     this.isMounted = false; | ||||||
|  |     this.stateHandler.reset(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | export { setupVbenVxeTable } from './init'; | ||||||
|  | export * from './use-vxe-grid'; | ||||||
|  | export { default as VbenVxeGrid } from './use-vxe-grid.vue'; | ||||||
|  | export type { VxeGridListeners, VxeGridProps } from 'vxe-table'; | ||||||
|  | @ -0,0 +1,122 @@ | ||||||
|  | import type { SetupVxeTable } from './types'; | ||||||
|  | 
 | ||||||
|  | import { defineComponent, watch } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { usePreferences } from '@vben/preferences'; | ||||||
|  | import { useVbenForm } from '@vben-core/form-ui'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   VxeButton, | ||||||
|  |   VxeButtonGroup, | ||||||
|  |   // VxeFormGather,
 | ||||||
|  |   // VxeForm,
 | ||||||
|  |   // VxeFormItem,
 | ||||||
|  |   VxeIcon, | ||||||
|  |   VxeInput, | ||||||
|  |   VxeLoading, | ||||||
|  |   VxePager, | ||||||
|  |   // VxeList,
 | ||||||
|  |   // VxeModal,
 | ||||||
|  |   // VxeOptgroup,
 | ||||||
|  |   // VxeOption,
 | ||||||
|  |   // VxePulldown,
 | ||||||
|  |   // VxeRadio,
 | ||||||
|  |   // VxeRadioButton,
 | ||||||
|  |   // VxeRadioGroup,
 | ||||||
|  |   VxeSelect, | ||||||
|  |   VxeTooltip, | ||||||
|  |   VxeUI, | ||||||
|  |   // 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'; | ||||||
|  | 
 | ||||||
|  | // 是否加载过
 | ||||||
|  | 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(VxeLoading); | ||||||
|  |   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(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); | ||||||
|  | 
 | ||||||
|  |   isInit = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function setupVbenVxeTable(setupOptions: SetupVxeTable) { | ||||||
|  |   const { configVxeTable, useVbenForm } = setupOptions; | ||||||
|  | 
 | ||||||
|  |   initVxeTable(); | ||||||
|  |   useTableForm = useVbenForm; | ||||||
|  | 
 | ||||||
|  |   const preference = usePreferences(); | ||||||
|  | 
 | ||||||
|  |   const localMap = { | ||||||
|  |     'zh-CN': zhCN, | ||||||
|  |     'en-US': enUS, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   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, | ||||||
|  |     }, | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   configVxeTable(VxeUI); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | :root { | ||||||
|  |   --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-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(--border)); | ||||||
|  | 
 | ||||||
|  |   /* --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; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &--sizes { | ||||||
|  |       margin-right: auto; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,53 @@ | ||||||
|  | import type { DeepPartial } from '@vben/types'; | ||||||
|  | import type { VbenFormProps } from '@vben-core/form-ui'; | ||||||
|  | import type { | ||||||
|  |   VxeGridListeners, | ||||||
|  |   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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface VxeGridProps { | ||||||
|  |   /** | ||||||
|  |    * 组件class | ||||||
|  |    */ | ||||||
|  |   class?: any; | ||||||
|  |   /** | ||||||
|  |    * vxe-grid class | ||||||
|  |    */ | ||||||
|  |   gridClass?: any; | ||||||
|  |   /** | ||||||
|  |    * vxe-grid 配置 | ||||||
|  |    */ | ||||||
|  |   gridOptions?: DeepPartial<VxeTableGridProps>; | ||||||
|  |   /** | ||||||
|  |    * vxe-grid 事件 | ||||||
|  |    */ | ||||||
|  |   gridEvents?: DeepPartial<VxeGridListeners>; | ||||||
|  |   /** | ||||||
|  |    * 表单配置 | ||||||
|  |    */ | ||||||
|  |   formOptions?: VbenFormProps; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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,42 @@ | ||||||
|  | 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(); | ||||||
|  |       }); | ||||||
|  |       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; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,264 @@ | ||||||
|  | <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, | ||||||
|  | } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { usePriorityValues } from '@vben/hooks'; | ||||||
|  | import { EmptyIcon } from '@vben/icons'; | ||||||
|  | import { $t } from '@vben/locales'; | ||||||
|  | import { cloneDeep, cn, mergeWithArrayOverride } from '@vben/utils'; | ||||||
|  | import { VbenLoading } from '@vben-core/shadcn-ui'; | ||||||
|  | 
 | ||||||
|  | import { VxeGrid, VxeUI } from 'vxe-table'; | ||||||
|  | 
 | ||||||
|  | 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 gridRef = useTemplateRef<VxeGridInstance>('gridRef'); | ||||||
|  | 
 | ||||||
|  | const state = props.api?.useStore?.(); | ||||||
|  | 
 | ||||||
|  | const { | ||||||
|  |   gridOptions, | ||||||
|  |   class: className, | ||||||
|  |   gridClass, | ||||||
|  |   gridEvents, | ||||||
|  |   formOptions, | ||||||
|  | } = usePriorityValues(props, state); | ||||||
|  | 
 | ||||||
|  | const slots = useSlots(); | ||||||
|  | 
 | ||||||
|  | const [Form, formApi] = useTableForm({}); | ||||||
|  | 
 | ||||||
|  | const showToolbar = computed(() => { | ||||||
|  |   return !!slots['toolbar-actions']?.() || !!slots['toolbar-tools']?.(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const options = computed(() => { | ||||||
|  |   const slotActions = slots['toolbar-actions']?.(); | ||||||
|  |   const slotTools = slots['toolbar-tools']?.(); | ||||||
|  | 
 | ||||||
|  |   const forceUseToolbarOptions = showToolbar.value | ||||||
|  |     ? { | ||||||
|  |         toolbarConfig: { | ||||||
|  |           slots: { | ||||||
|  |             ...(slotActions ? { buttons: 'toolbar-actions' } : {}), | ||||||
|  |             ...(slotTools ? { tools: 'toolbar-tools' } : {}), | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       } | ||||||
|  |     : {}; | ||||||
|  | 
 | ||||||
|  |   const mergedOptions: VxeTableGridProps = cloneDeep( | ||||||
|  |     mergeWithArrayOverride( | ||||||
|  |       {}, | ||||||
|  |       forceUseToolbarOptions, | ||||||
|  |       toRaw(gridOptions.value), | ||||||
|  |     ), | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   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) { | ||||||
|  |     mergedOptions.pagerConfig = mergeWithArrayOverride( | ||||||
|  |       {}, | ||||||
|  |       mergedOptions.pagerConfig, | ||||||
|  |       { | ||||||
|  |         pageSize: 20, | ||||||
|  |         background: true, | ||||||
|  |         pageSizes: [10, 20, 30, 50, 100, 200], | ||||||
|  |         className: 'mt-2 w-full', | ||||||
|  |         layouts: [ | ||||||
|  |           'Total', | ||||||
|  |           'Sizes', | ||||||
|  |           'Home', | ||||||
|  |           'PrevJump', | ||||||
|  |           'PrevPage', | ||||||
|  |           'Number', | ||||||
|  |           'NextPage', | ||||||
|  |           'NextJump', | ||||||
|  |           'End', | ||||||
|  |           // 'FullJump', | ||||||
|  |         ] as any[], | ||||||
|  |         size: 'mini' as const, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |   if (mergedOptions.formConfig) { | ||||||
|  |     mergedOptions.formConfig.enabled = false; | ||||||
|  |   } | ||||||
|  |   return mergedOptions; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const events = computed(() => { | ||||||
|  |   return { | ||||||
|  |     ...gridEvents.value, | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const vbenFormOptions = computed(() => { | ||||||
|  |   const defaultFormProps: VbenFormProps = { | ||||||
|  |     handleSubmit: async () => { | ||||||
|  |       const formValues = formApi.form.values; | ||||||
|  |       props.api.reload(formValues); | ||||||
|  |     }, | ||||||
|  |     handleReset: async () => { | ||||||
|  |       formApi.resetForm(); | ||||||
|  |       const formValues = formApi.form.values; | ||||||
|  |       props.api.reload(formValues); | ||||||
|  |     }, | ||||||
|  |     collapseTriggerResize: true, | ||||||
|  |     commonConfig: { | ||||||
|  |       componentProps: { | ||||||
|  |         class: 'w-full', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     showCollapseButton: true, | ||||||
|  |     submitButtonOptions: { | ||||||
|  |       text: $t('common.query'), | ||||||
|  |     }, | ||||||
|  |     wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3', | ||||||
|  |   }; | ||||||
|  |   return { | ||||||
|  |     ...mergeWithArrayOverride({}, formOptions.value, defaultFormProps), | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | 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-')) { | ||||||
|  |       resultSlots.push(key); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return resultSlots; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | 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 = defaultGridOptions.formConfig; | ||||||
|  |   if (formConfig?.enabled) { | ||||||
|  |     console.warn( | ||||||
|  |       '[Vben Vxe Table]: The formConfig in the grid is not supported, please use the `formOptions` props', | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   props.api?.mount?.(gridRef.value); | ||||||
|  |   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-6"> | ||||||
|  |           <slot name="form"> | ||||||
|  |             <Form v-bind="vbenFormOptions"> | ||||||
|  |               <template | ||||||
|  |                 v-for="slotName in delegatedFormSlots" | ||||||
|  |                 :key="slotName" | ||||||
|  |                 #[slotName]="slotProps" | ||||||
|  |               > | ||||||
|  |                 <slot :name="slotName" v-bind="slotProps"></slot> | ||||||
|  |               </template> | ||||||
|  |             </Form> | ||||||
|  |           </slot> | ||||||
|  |           <div | ||||||
|  |             class="bg-background-deep z-100 absolute -left-2 bottom-2 h-4 w-[calc(100%+1rem)] overflow-hidden" | ||||||
|  |           ></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> | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | export { default } from '@vben/tailwind-config'; | ||||||
|  | @ -31,7 +31,8 @@ | ||||||
|     "confirm": "Comfirm", |     "confirm": "Comfirm", | ||||||
|     "noData": "No Data", |     "noData": "No Data", | ||||||
|     "refresh": "Refresh", |     "refresh": "Refresh", | ||||||
|     "loadingMenu": "Loading Menu" |     "loadingMenu": "Loading Menu", | ||||||
|  |     "query": "Search" | ||||||
|   }, |   }, | ||||||
|   "fallback": { |   "fallback": { | ||||||
|     "pageNotFound": "Oops! Page Not Found", |     "pageNotFound": "Oops! Page Not Found", | ||||||
|  |  | ||||||
|  | @ -31,7 +31,8 @@ | ||||||
|     "confirm": "确认", |     "confirm": "确认", | ||||||
|     "noData": "暂无数据", |     "noData": "暂无数据", | ||||||
|     "refresh": "刷新", |     "refresh": "刷新", | ||||||
|     "loadingMenu": "加载菜单中" |     "loadingMenu": "加载菜单中", | ||||||
|  |     "query": "查询" | ||||||
|   }, |   }, | ||||||
|   "fallback": { |   "fallback": { | ||||||
|     "pageNotFound": "哎呀!未找到页面", |     "pageNotFound": "哎呀!未找到页面", | ||||||
|  |  | ||||||
|  | @ -1 +1,2 @@ | ||||||
| export * from './form'; | export * from './form'; | ||||||
|  | export * from './vxe-table'; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | 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: true, | ||||||
|  |         minHeight: 180, | ||||||
|  |         proxyConfig: { | ||||||
|  |           autoLoad: true, | ||||||
|  |           response: { | ||||||
|  |             result: 'items', | ||||||
|  |             total: 'total', | ||||||
|  |             list: 'items', | ||||||
|  |           }, | ||||||
|  |           showActiveMsg: true, | ||||||
|  |           showResponseMsg: false, | ||||||
|  |         }, | ||||||
|  |         round: true, | ||||||
|  |         size: 'small', | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellImage' },
 | ||||||
|  |     vxeUI.renderer.add('CellImage', { | ||||||
|  |       renderDefault(_renderOpts, params) { | ||||||
|  |         const { column, row } = params; | ||||||
|  |         return h(Image, { src: row[column.field] }); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // 表格配置项可以用 cellRender: { name: 'CellLink' },
 | ||||||
|  |     vxeUI.renderer.add('CellLink', { | ||||||
|  |       renderDefault(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 +1,2 @@ | ||||||
| export * from './status'; | export * from './status'; | ||||||
|  | export * from './table'; | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | import { requestClient } from '#/api/request'; | ||||||
|  | 
 | ||||||
|  | export namespace DemoTableApi { | ||||||
|  |   export interface PageFetchParams { | ||||||
|  |     [key: string]: any; | ||||||
|  |     page: number; | ||||||
|  |     pageSize: number; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 获取示例表格数据 | ||||||
|  |  */ | ||||||
|  | async function getExampleTableApi(params: DemoTableApi.PageFetchParams) { | ||||||
|  |   return requestClient.get('/table/list', { params }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { getExampleTableApi }; | ||||||
|  | @ -1,2 +1,2 @@ | ||||||
| export * from './core'; | export * from './core'; | ||||||
| export * from './demos'; | export * from './examples'; | ||||||
|  |  | ||||||
|  | @ -82,6 +82,18 @@ | ||||||
|         "api": "Api", |         "api": "Api", | ||||||
|         "merge": "Merge Form" |         "merge": "Merge Form" | ||||||
|       }, |       }, | ||||||
|  |       "vxeTable": { | ||||||
|  |         "title": "Vxe Table", | ||||||
|  |         "basic": "Basic Table", | ||||||
|  |         "remote": "Remote Load", | ||||||
|  |         "tree": "Tree Table", | ||||||
|  |         "fixed": "Fixed Header/Column", | ||||||
|  |         "virtual": "Virtual Scroll", | ||||||
|  |         "editCell": "Edit Cell", | ||||||
|  |         "editRow": "Edit Row", | ||||||
|  |         "custom-cell": "Custom Cell", | ||||||
|  |         "form": "Form Table" | ||||||
|  |       }, | ||||||
|       "captcha": { |       "captcha": { | ||||||
|         "title": "Captcha", |         "title": "Captcha", | ||||||
|         "pointSelection": "Point Selection Captcha", |         "pointSelection": "Point Selection Captcha", | ||||||
|  |  | ||||||
|  | @ -82,6 +82,18 @@ | ||||||
|         "api": "Api", |         "api": "Api", | ||||||
|         "merge": "合并表单" |         "merge": "合并表单" | ||||||
|       }, |       }, | ||||||
|  |       "vxeTable": { | ||||||
|  |         "title": "Vxe 表格", | ||||||
|  |         "basic": "基础表格", | ||||||
|  |         "remote": "远程加载", | ||||||
|  |         "tree": "树形表格", | ||||||
|  |         "fixed": "固定表头/列", | ||||||
|  |         "virtual": "虚拟滚动", | ||||||
|  |         "editCell": "单元格编辑", | ||||||
|  |         "editRow": "行编辑", | ||||||
|  |         "custom-cell": "自定义单元格", | ||||||
|  |         "form": "开启搜索表单" | ||||||
|  |       }, | ||||||
|       "captcha": { |       "captcha": { | ||||||
|         "title": "验证码", |         "title": "验证码", | ||||||
|         "pointSelection": "点选验证", |         "pointSelection": "点选验证", | ||||||
|  |  | ||||||
|  | @ -42,7 +42,6 @@ const routes: RouteRecordRaw[] = [ | ||||||
|           title: $t('page.examples.ellipsis.title'), |           title: $t('page.examples.ellipsis.title'), | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| 
 |  | ||||||
|       { |       { | ||||||
|         name: 'FormExample', |         name: 'FormExample', | ||||||
|         path: '/examples/form', |         path: '/examples/form', | ||||||
|  | @ -109,6 +108,89 @@ const routes: RouteRecordRaw[] = [ | ||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         name: 'VxeTableExample', | ||||||
|  |         path: '/examples/vxe-table', | ||||||
|  |         meta: { | ||||||
|  |           icon: 'lucide:table', | ||||||
|  |           title: $t('page.examples.vxeTable.title'), | ||||||
|  |         }, | ||||||
|  |         children: [ | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableBasicExample', | ||||||
|  |             path: '/examples/vxe-table/basic', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/basic.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.basic'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableRemoteExample', | ||||||
|  |             path: '/examples/vxe-table/remote', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/remote.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.remote'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableTreeExample', | ||||||
|  |             path: '/examples/vxe-table/tree', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/tree.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.tree'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableFixedExample', | ||||||
|  |             path: '/examples/vxe-table/fixed', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/fixed.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.fixed'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableCustomCellExample', | ||||||
|  |             path: '/examples/vxe-table/custom-cell', | ||||||
|  |             component: () => | ||||||
|  |               import('#/views/examples/vxe-table/custom-cell.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.custom-cell'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableFormExample', | ||||||
|  |             path: '/examples/vxe-table/form', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/form.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.form'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableEditCellExample', | ||||||
|  |             path: '/examples/vxe-table/edit-cell', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/edit-cell.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.editCell'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableEditRowExample', | ||||||
|  |             path: '/examples/vxe-table/edit-row', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/edit-row.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.editRow'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: 'VxeTableVirtualExample', | ||||||
|  |             path: '/examples/vxe-table/virtual', | ||||||
|  |             component: () => import('#/views/examples/vxe-table/virtual.vue'), | ||||||
|  |             meta: { | ||||||
|  |               title: $t('page.examples.vxeTable.virtual'), | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         name: 'CaptchaExample', |         name: 'CaptchaExample', | ||||||
|         path: '/examples/captcha', |         path: '/examples/captcha', | ||||||
|  |  | ||||||
|  | @ -12,5 +12,5 @@ function handleClick() { | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|   <Button type="link" @click="handleClick">查看组件文档</Button> |   <Button @click="handleClick">查看组件文档</Button> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,8 @@ import dayjs from 'dayjs'; | ||||||
| 
 | 
 | ||||||
| import { useVbenForm } from '#/adapter'; | import { useVbenForm } from '#/adapter'; | ||||||
| 
 | 
 | ||||||
|  | import DocButton from '../doc-button.vue'; | ||||||
|  | 
 | ||||||
| const [BaseForm, baseFormApi] = useVbenForm({ | const [BaseForm, baseFormApi] = useVbenForm({ | ||||||
|   // 所有表单项共用,可单独在表单内覆盖 |   // 所有表单项共用,可单独在表单内覆盖 | ||||||
|   commonConfig: { |   commonConfig: { | ||||||
|  | @ -329,6 +331,9 @@ function handleSetFormValue() { | ||||||
|     description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。" |     description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。" | ||||||
|     title="表单组件" |     title="表单组件" | ||||||
|   > |   > | ||||||
|  |     <template #extra> | ||||||
|  |       <DocButton path="/components/common-ui/vben-form" /> | ||||||
|  |     </template> | ||||||
|     <Card title="基础示例"> |     <Card title="基础示例"> | ||||||
|       <template #extra> |       <template #extra> | ||||||
|         <Button type="primary" @click="handleSetFormValue">设置表单值</Button> |         <Button type="primary" @click="handleSetFormValue">设置表单值</Button> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,93 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridListeners, VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import DocButton from '../doc-button.vue'; | ||||||
|  | import { MOCK_TABLE_DATA } from './table-data'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   address: string; | ||||||
|  |   age: number; | ||||||
|  |   id: number; | ||||||
|  |   name: string; | ||||||
|  |   nickname: string; | ||||||
|  |   role: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { field: 'name', title: 'Name' }, | ||||||
|  |     { field: 'age', sortable: true, title: 'Age' }, | ||||||
|  |     { field: 'nickname', title: 'Nickname' }, | ||||||
|  |     { field: 'role', title: 'Role' }, | ||||||
|  |     { field: 'address', showOverflow: true, title: 'Address' }, | ||||||
|  |   ], | ||||||
|  |   data: MOCK_TABLE_DATA, | ||||||
|  |   sortConfig: { | ||||||
|  |     multiple: true, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const gridEvents: VxeGridListeners<RowType> = { | ||||||
|  |   cellClick: ({ row }) => { | ||||||
|  |     message.info(`cell-click: ${row.name}`); | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ gridEvents, gridOptions }); | ||||||
|  | 
 | ||||||
|  | const showBorder = gridApi.useStore((state) => state.gridOptions?.border); | ||||||
|  | const showStripe = gridApi.useStore((state) => state.gridOptions?.stripe); | ||||||
|  | 
 | ||||||
|  | function changeBorder() { | ||||||
|  |   gridApi.setGridOptions({ | ||||||
|  |     border: !showBorder.value, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function changeStripe() { | ||||||
|  |   gridApi.setGridOptions({ | ||||||
|  |     stripe: !showStripe.value, | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function changeLoading() { | ||||||
|  |   gridApi.setLoading(true); | ||||||
|  |   setTimeout(() => { | ||||||
|  |     gridApi.setLoading(false); | ||||||
|  |   }, 2000); | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page | ||||||
|  |     description="表格组件常用于快速开发数据展示与交互界面,示例数据为静态数据。该组件是对vxe-table进行简单的二次封装,大部分属性与方法与vxe-table保持一致。" | ||||||
|  |     title="表格基础示例" | ||||||
|  |   > | ||||||
|  |     <template #extra> | ||||||
|  |       <DocButton path="/components/common-ui/vben-vxe-table" /> | ||||||
|  |     </template> | ||||||
|  |     <Grid> | ||||||
|  |       <template #toolbar-actions> | ||||||
|  |         <Button class="mr-2" type="primary">左右按钮插槽</Button> | ||||||
|  |       </template> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button class="mr-2" type="primary" @click="changeBorder"> | ||||||
|  |           {{ showBorder ? '隐藏' : '显示' }}边框 | ||||||
|  |         </Button> | ||||||
|  |         <Button class="mr-2" type="primary" @click="changeLoading"> | ||||||
|  |           显示loading | ||||||
|  |         </Button> | ||||||
|  |         <Button class="mr-2" type="primary" @click="changeStripe"> | ||||||
|  |           {{ showStripe ? '隐藏' : '显示' }}斑马纹 | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,110 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button, Image, Switch, Tag } from 'ant-design-vue'; | ||||||
|  | import dayjs from 'dayjs'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   imageUrl: string; | ||||||
|  |   open: boolean; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  |   status: 'error' | 'success' | 'warning'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   checkboxConfig: { | ||||||
|  |     highlight: true, | ||||||
|  |     labelField: 'name', | ||||||
|  |   }, | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { field: 'category', title: 'Category', width: 100 }, | ||||||
|  |     { | ||||||
|  |       field: 'imageUrl', | ||||||
|  |       slots: { default: 'image-url' }, | ||||||
|  |       title: 'Image', | ||||||
|  |       width: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       cellRender: { name: 'CellImage' }, | ||||||
|  |       field: 'imageUrl2', | ||||||
|  |       title: 'Render Image', | ||||||
|  |       width: 130, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'open', | ||||||
|  |       slots: { default: 'open' }, | ||||||
|  |       title: 'Open', | ||||||
|  |       width: 100, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       field: 'status', | ||||||
|  |       slots: { default: 'status' }, | ||||||
|  |       title: 'Status', | ||||||
|  |       width: 100, | ||||||
|  |     }, | ||||||
|  |     { field: 'color', title: 'Color', width: 100 }, | ||||||
|  |     { field: 'productName', title: 'Product Name', width: 200 }, | ||||||
|  |     { field: 'price', title: 'Price', width: 100 }, | ||||||
|  |     { | ||||||
|  |       field: 'releaseDate', | ||||||
|  |       formatter: ({ cellValue }) => { | ||||||
|  |         return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss'); | ||||||
|  |       }, | ||||||
|  |       title: 'Date', | ||||||
|  |       width: 200, | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       cellRender: { name: 'CellLink', props: { text: '编辑' } }, | ||||||
|  |       field: 'action', | ||||||
|  |       fixed: 'right', | ||||||
|  |       title: '操作', | ||||||
|  |       width: 120, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |   height: 'auto', | ||||||
|  |   keepSource: true, | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }) => { | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid> | ||||||
|  |       <template #image-url="{ row }"> | ||||||
|  |         <Image :src="row.imageUrl" height="30" width="30" /> | ||||||
|  |       </template> | ||||||
|  |       <template #open="{ row }"> | ||||||
|  |         <Switch v-model:checked="row.open" /> | ||||||
|  |       </template> | ||||||
|  |       <template #status="{ row }"> | ||||||
|  |         <Tag :color="row.color">{{ row.status }}</Tag> | ||||||
|  |       </template> | ||||||
|  |       <template #action> | ||||||
|  |         <Button type="link">编辑</Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { editRender: { name: 'input' }, field: 'category', title: 'Category' }, | ||||||
|  |     { editRender: { name: 'input' }, field: 'color', title: 'Color' }, | ||||||
|  |     { | ||||||
|  |       editRender: { name: 'input' }, | ||||||
|  |       field: 'productName', | ||||||
|  |       title: 'Product Name', | ||||||
|  |     }, | ||||||
|  |     { field: 'price', title: 'Price' }, | ||||||
|  |     { field: 'releaseDate', title: 'Date' }, | ||||||
|  |   ], | ||||||
|  |   editConfig: { | ||||||
|  |     mode: 'cell', | ||||||
|  |     trigger: 'click', | ||||||
|  |   }, | ||||||
|  |   height: 'auto', | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }) => { | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   showOverflow: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid /> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,94 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button, message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { editRender: { name: 'input' }, field: 'category', title: 'Category' }, | ||||||
|  |     { editRender: { name: 'input' }, field: 'color', title: 'Color' }, | ||||||
|  |     { | ||||||
|  |       editRender: { name: 'input' }, | ||||||
|  |       field: 'productName', | ||||||
|  |       title: 'Product Name', | ||||||
|  |     }, | ||||||
|  |     { field: 'price', title: 'Price' }, | ||||||
|  |     { field: 'releaseDate', title: 'Date' }, | ||||||
|  |     { slots: { default: 'action' }, title: '操作' }, | ||||||
|  |   ], | ||||||
|  |   editConfig: { | ||||||
|  |     mode: 'row', | ||||||
|  |     trigger: 'click', | ||||||
|  |   }, | ||||||
|  |   height: 'auto', | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }) => { | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   showOverflow: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | 
 | ||||||
|  | function hasEditStatus(row: RowType) { | ||||||
|  |   return gridApi.grid?.isEditByRow(row); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function editRowEvent(row: RowType) { | ||||||
|  |   gridApi.grid?.setEditRow(row); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | async function saveRowEvent(row: RowType) { | ||||||
|  |   await gridApi.grid?.clearEdit(); | ||||||
|  | 
 | ||||||
|  |   gridApi.setLoading(true); | ||||||
|  |   setTimeout(() => { | ||||||
|  |     gridApi.setLoading(false); | ||||||
|  |     message.success({ | ||||||
|  |       content: `保存成功!category=${row.category}`, | ||||||
|  |     }); | ||||||
|  |   }, 600); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const cancelRowEvent = (_row: RowType) => { | ||||||
|  |   gridApi.grid?.clearEdit(); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid> | ||||||
|  |       <template #action="{ row }"> | ||||||
|  |         <template v-if="hasEditStatus(row)"> | ||||||
|  |           <Button type="link" @click="saveRowEvent(row)">保存</Button> | ||||||
|  |           <Button type="link" @click="cancelRowEvent(row)">取消</Button> | ||||||
|  |         </template> | ||||||
|  |         <template v-else> | ||||||
|  |           <Button type="link" @click="editRowEvent(row)">编辑</Button> | ||||||
|  |         </template> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,64 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { fixed: 'left', title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { field: 'category', title: 'Category', width: 300 }, | ||||||
|  |     { field: 'color', title: 'Color', width: 300 }, | ||||||
|  |     { field: 'productName', title: 'Product Name', width: 300 }, | ||||||
|  |     { field: 'price', title: 'Price', width: 300 }, | ||||||
|  |     { field: 'releaseDate', title: 'Date', width: 500 }, | ||||||
|  |     { | ||||||
|  |       field: 'action', | ||||||
|  |       fixed: 'right', | ||||||
|  |       slots: { default: 'action' }, | ||||||
|  |       title: '操作', | ||||||
|  |       width: 120, | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  |   height: 'auto', | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }) => { | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   rowConfig: { | ||||||
|  |     isHover: true, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid> | ||||||
|  |       <template #action> | ||||||
|  |         <Button type="link">编辑</Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,102 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VbenFormProps, VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { message } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const formOptions: VbenFormProps = { | ||||||
|  |   schema: [ | ||||||
|  |     { | ||||||
|  |       component: 'Input', | ||||||
|  |       fieldName: 'category', | ||||||
|  |       label: 'Category', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'Input', | ||||||
|  |       fieldName: 'productName', | ||||||
|  |       label: 'ProductName', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'Input', | ||||||
|  |       fieldName: 'price', | ||||||
|  |       label: 'Price', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'Select', | ||||||
|  |       componentProps: { | ||||||
|  |         allowClear: true, | ||||||
|  |         options: [ | ||||||
|  |           { | ||||||
|  |             label: 'Color1', | ||||||
|  |             value: '1', | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             label: 'Color2', | ||||||
|  |             value: '2', | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         placeholder: '请选择', | ||||||
|  |       }, | ||||||
|  |       fieldName: 'color', | ||||||
|  |       label: 'Color', | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       component: 'DatePicker', | ||||||
|  |       fieldName: 'datePicker', | ||||||
|  |       label: 'Date', | ||||||
|  |     }, | ||||||
|  |   ], | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   checkboxConfig: { | ||||||
|  |     highlight: true, | ||||||
|  |     labelField: 'name', | ||||||
|  |   }, | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { align: 'left', title: 'Name', type: 'checkbox', width: 100 }, | ||||||
|  |     { field: 'category', title: 'Category' }, | ||||||
|  |     { field: 'color', title: 'Color' }, | ||||||
|  |     { field: 'productName', title: 'Product Name' }, | ||||||
|  |     { field: 'price', title: 'Price' }, | ||||||
|  |     { field: 'releaseDate', title: 'Date' }, | ||||||
|  |   ], | ||||||
|  |   height: 'auto', | ||||||
|  |   keepSource: true, | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }, formValues) => { | ||||||
|  |         message.success(`Query params: ${JSON.stringify(formValues)}`); | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |           ...formValues, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid] = useVbenVxeGrid({ formOptions, gridOptions }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid /> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,65 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | import { getExampleTableApi } from '#/api'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   category: string; | ||||||
|  |   color: string; | ||||||
|  |   id: string; | ||||||
|  |   price: string; | ||||||
|  |   productName: string; | ||||||
|  |   releaseDate: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   checkboxConfig: { | ||||||
|  |     highlight: true, | ||||||
|  |     labelField: 'name', | ||||||
|  |   }, | ||||||
|  |   columns: [ | ||||||
|  |     { title: '序号', type: 'seq', width: 50 }, | ||||||
|  |     { align: 'left', title: 'Name', type: 'checkbox', width: 100 }, | ||||||
|  |     { field: 'category', title: 'Category' }, | ||||||
|  |     { field: 'color', title: 'Color' }, | ||||||
|  |     { field: 'productName', title: 'Product Name' }, | ||||||
|  |     { field: 'price', title: 'Price' }, | ||||||
|  |     { field: 'releaseDate', title: 'Date' }, | ||||||
|  |   ], | ||||||
|  |   height: 'auto', | ||||||
|  |   keepSource: true, | ||||||
|  |   pagerConfig: {}, | ||||||
|  |   proxyConfig: { | ||||||
|  |     ajax: { | ||||||
|  |       query: async ({ page }) => { | ||||||
|  |         return await getExampleTableApi({ | ||||||
|  |           page: page.currentPage, | ||||||
|  |           pageSize: page.pageSize, | ||||||
|  |         }); | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button class="mr-2" type="primary" @click="() => gridApi.query()"> | ||||||
|  |           刷新当前页面 | ||||||
|  |         </Button> | ||||||
|  |         <Button type="primary" @click="() => gridApi.reload()"> | ||||||
|  |           刷新并返回第一页 | ||||||
|  |         </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,172 @@ | ||||||
|  | interface TableRowData { | ||||||
|  |   address: string; | ||||||
|  |   age: number; | ||||||
|  |   id: number; | ||||||
|  |   name: string; | ||||||
|  |   nickname: string; | ||||||
|  |   role: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const roles = ['User', 'Admin', 'Manager', 'Guest']; | ||||||
|  | 
 | ||||||
|  | export const MOCK_TABLE_DATA: TableRowData[] = (() => { | ||||||
|  |   const data: TableRowData[] = []; | ||||||
|  |   for (let i = 0; i < 40; i++) { | ||||||
|  |     data.push({ | ||||||
|  |       address: `New York${i}`, | ||||||
|  |       age: i + 1, | ||||||
|  |       id: i, | ||||||
|  |       name: `Test${i}`, | ||||||
|  |       nickname: `Test${i}`, | ||||||
|  |       role: roles[Math.floor(Math.random() * roles.length)] as string, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   return data; | ||||||
|  | })(); | ||||||
|  | 
 | ||||||
|  | export const MOCK_TREE_TABLE_DATA = [ | ||||||
|  |   { | ||||||
|  |     date: '2020-08-01', | ||||||
|  |     id: 10_000, | ||||||
|  |     name: 'Test1', | ||||||
|  |     parentId: null, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'mp3', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-04-01', | ||||||
|  |     id: 10_050, | ||||||
|  |     name: 'Test2', | ||||||
|  |     parentId: null, | ||||||
|  |     size: 0, | ||||||
|  |     type: 'mp4', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2020-03-01', | ||||||
|  |     id: 24_300, | ||||||
|  |     name: 'Test3', | ||||||
|  |     parentId: 10_050, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'avi', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-04-01', | ||||||
|  |     id: 20_045, | ||||||
|  |     name: 'Test4', | ||||||
|  |     parentId: 24_300, | ||||||
|  |     size: 600, | ||||||
|  |     type: 'html', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-04-01', | ||||||
|  |     id: 10_053, | ||||||
|  |     name: 'Test5', | ||||||
|  |     parentId: 24_300, | ||||||
|  |     size: 0, | ||||||
|  |     type: 'avi', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-10-01', | ||||||
|  |     id: 24_330, | ||||||
|  |     name: 'Test6', | ||||||
|  |     parentId: 10_053, | ||||||
|  |     size: 25, | ||||||
|  |     type: 'txt', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2020-01-01', | ||||||
|  |     id: 21_011, | ||||||
|  |     name: 'Test7', | ||||||
|  |     parentId: 10_053, | ||||||
|  |     size: 512, | ||||||
|  |     type: 'pdf', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 22_200, | ||||||
|  |     name: 'Test8', | ||||||
|  |     parentId: 10_053, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2020-11-01', | ||||||
|  |     id: 23_666, | ||||||
|  |     name: 'Test9', | ||||||
|  |     parentId: null, | ||||||
|  |     size: 2048, | ||||||
|  |     type: 'xlsx', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_677, | ||||||
|  |     name: 'Test10', | ||||||
|  |     parentId: 23_666, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_671, | ||||||
|  |     name: 'Test11', | ||||||
|  |     parentId: 23_677, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_672, | ||||||
|  |     name: 'Test12', | ||||||
|  |     parentId: 23_677, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_688, | ||||||
|  |     name: 'Test13', | ||||||
|  |     parentId: 23_666, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_681, | ||||||
|  |     name: 'Test14', | ||||||
|  |     parentId: 23_688, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 23_682, | ||||||
|  |     name: 'Test15', | ||||||
|  |     parentId: 23_688, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2020-10-01', | ||||||
|  |     id: 24_555, | ||||||
|  |     name: 'Test16', | ||||||
|  |     parentId: null, | ||||||
|  |     size: 224, | ||||||
|  |     type: 'avi', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 24_566, | ||||||
|  |     name: 'Test17', | ||||||
|  |     parentId: 24_555, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     date: '2021-06-01', | ||||||
|  |     id: 24_577, | ||||||
|  |     name: 'Test18', | ||||||
|  |     parentId: 24_555, | ||||||
|  |     size: 1024, | ||||||
|  |     type: 'js', | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { Button } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { MOCK_TREE_TABLE_DATA } from './table-data'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   date: string; | ||||||
|  |   id: number; | ||||||
|  |   name: string; | ||||||
|  |   parentId: null | number; | ||||||
|  |   size: number; | ||||||
|  |   type: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { type: 'seq', width: 70 }, | ||||||
|  |     { field: 'name', minWidth: 300, title: 'Name', treeNode: true }, | ||||||
|  |     { field: 'size', title: 'Size' }, | ||||||
|  |     { field: 'type', title: 'Type' }, | ||||||
|  |     { field: 'date', title: 'Date' }, | ||||||
|  |   ], | ||||||
|  |   data: MOCK_TREE_TABLE_DATA, | ||||||
|  |   treeConfig: { | ||||||
|  |     parentField: 'parentId', | ||||||
|  |     rowField: 'id', | ||||||
|  |     transform: true, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | 
 | ||||||
|  | const expandAll = () => { | ||||||
|  |   gridApi.grid?.setAllTreeExpand(true); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const collapseAll = () => { | ||||||
|  |   gridApi.grid?.setAllTreeExpand(false); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page> | ||||||
|  |     <Grid> | ||||||
|  |       <template #toolbar-tools> | ||||||
|  |         <Button class="mr-2" type="primary" @click="expandAll"> | ||||||
|  |           展开全部 | ||||||
|  |         </Button> | ||||||
|  |         <Button type="primary" @click="collapseAll"> 折叠全部 </Button> | ||||||
|  |       </template> | ||||||
|  |     </Grid> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
|  | @ -0,0 +1,63 @@ | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import type { VxeGridProps } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | import { onMounted } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { Page } from '@vben/common-ui'; | ||||||
|  | 
 | ||||||
|  | import { useVbenVxeGrid } from '#/adapter'; | ||||||
|  | 
 | ||||||
|  | interface RowType { | ||||||
|  |   id: number; | ||||||
|  |   name: string; | ||||||
|  |   role: string; | ||||||
|  |   sex: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const gridOptions: VxeGridProps<RowType> = { | ||||||
|  |   columns: [ | ||||||
|  |     { type: 'seq', width: 70 }, | ||||||
|  |     { field: 'name', title: 'Name' }, | ||||||
|  |     { field: 'role', title: 'Role' }, | ||||||
|  |     { field: 'sex', title: 'Sex' }, | ||||||
|  |   ], | ||||||
|  |   data: [], | ||||||
|  |   height: 'auto', | ||||||
|  |   scrollY: { | ||||||
|  |     enabled: true, | ||||||
|  |     gt: 0, | ||||||
|  |   }, | ||||||
|  |   showOverflow: true, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const [Grid, gridApi] = useVbenVxeGrid({ gridOptions }); | ||||||
|  | 
 | ||||||
|  | // 模拟行数据 | ||||||
|  | const loadList = (size = 200) => { | ||||||
|  |   try { | ||||||
|  |     const dataList: RowType[] = []; | ||||||
|  |     for (let i = 0; i < size; i++) { | ||||||
|  |       dataList.push({ | ||||||
|  |         id: 10_000 + i, | ||||||
|  |         name: `Test${i}`, | ||||||
|  |         role: 'Developer', | ||||||
|  |         sex: '男', | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     gridApi.setGridOptions({ data: dataList }); | ||||||
|  |   } catch (error) { | ||||||
|  |     console.error('Failed to load data:', error); | ||||||
|  |     // Implement user-friendly error handling | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  |   loadList(1000); | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <Page auto-content-height> | ||||||
|  |     <Grid /> | ||||||
|  |   </Page> | ||||||
|  | </template> | ||||||
							
								
								
									
										114
									
								
								pnpm-lock.yaml
								
								
								
								
							
							
						
						
									
										114
									
								
								pnpm-lock.yaml
								
								
								
								
							|  | @ -27,9 +27,12 @@ catalogs: | ||||||
|     '@eslint/js': |     '@eslint/js': | ||||||
|       specifier: ^9.11.1 |       specifier: ^9.11.1 | ||||||
|       version: 9.11.1 |       version: 9.11.1 | ||||||
|  |     '@faker-js/faker': | ||||||
|  |       specifier: ^9.0.3 | ||||||
|  |       version: 9.0.3 | ||||||
|     '@iconify/json': |     '@iconify/json': | ||||||
|       specifier: ^2.2.255 |       specifier: ^2.2.256 | ||||||
|       version: 2.2.255 |       version: 2.2.256 | ||||||
|     '@iconify/tailwind': |     '@iconify/tailwind': | ||||||
|       specifier: ^1.1.3 |       specifier: ^1.1.3 | ||||||
|       version: 1.1.3 |       version: 1.1.3 | ||||||
|  | @ -438,6 +441,9 @@ catalogs: | ||||||
|     vite-plugin-html: |     vite-plugin-html: | ||||||
|       specifier: ^3.2.2 |       specifier: ^3.2.2 | ||||||
|       version: 3.2.2 |       version: 3.2.2 | ||||||
|  |     vite-plugin-lazy-import: | ||||||
|  |       specifier: ^1.0.7 | ||||||
|  |       version: 1.0.7 | ||||||
|     vite-plugin-lib-inject-css: |     vite-plugin-lib-inject-css: | ||||||
|       specifier: ^2.1.1 |       specifier: ^2.1.1 | ||||||
|       version: 2.1.1 |       version: 2.1.1 | ||||||
|  | @ -468,6 +474,12 @@ catalogs: | ||||||
|     vue-tsc: |     vue-tsc: | ||||||
|       specifier: ^2.1.6 |       specifier: ^2.1.6 | ||||||
|       version: 2.1.6 |       version: 2.1.6 | ||||||
|  |     vxe-pc-ui: | ||||||
|  |       specifier: ^4.2.9 | ||||||
|  |       version: 4.2.12 | ||||||
|  |     vxe-table: | ||||||
|  |       specifier: ^4.7.84 | ||||||
|  |       version: 4.7.85 | ||||||
|     watermark-js-plus: |     watermark-js-plus: | ||||||
|       specifier: ^1.5.7 |       specifier: ^1.5.7 | ||||||
|       version: 1.5.7 |       version: 1.5.7 | ||||||
|  | @ -590,6 +602,9 @@ importers: | ||||||
| 
 | 
 | ||||||
|   apps/backend-mock: |   apps/backend-mock: | ||||||
|     dependencies: |     dependencies: | ||||||
|  |       '@faker-js/faker': | ||||||
|  |         specifier: 'catalog:' | ||||||
|  |         version: 9.0.3 | ||||||
|       jsonwebtoken: |       jsonwebtoken: | ||||||
|         specifier: 'catalog:' |         specifier: 'catalog:' | ||||||
|         version: 9.0.2 |         version: 9.0.2 | ||||||
|  | @ -802,15 +817,9 @@ importers: | ||||||
|       '@vben/common-ui': |       '@vben/common-ui': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../packages/effects/common-ui |         version: link:../packages/effects/common-ui | ||||||
|       '@vben/hooks': |  | ||||||
|         specifier: workspace:* |  | ||||||
|         version: link:../packages/effects/hooks |  | ||||||
|       '@vben/locales': |       '@vben/locales': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../packages/locales |         version: link:../packages/locales | ||||||
|       '@vben/preferences': |  | ||||||
|         specifier: workspace:* |  | ||||||
|         version: link:../packages/preferences |  | ||||||
|       '@vben/styles': |       '@vben/styles': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../packages/styles |         version: link:../packages/styles | ||||||
|  | @ -1042,7 +1051,7 @@ importers: | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@iconify/json': |       '@iconify/json': | ||||||
|         specifier: 'catalog:' |         specifier: 'catalog:' | ||||||
|         version: 2.2.255 |         version: 2.2.256 | ||||||
|       '@iconify/tailwind': |       '@iconify/tailwind': | ||||||
|         specifier: 'catalog:' |         specifier: 'catalog:' | ||||||
|         version: 1.1.3 |         version: 1.1.3 | ||||||
|  | @ -1171,6 +1180,9 @@ importers: | ||||||
|       vite-plugin-html: |       vite-plugin-html: | ||||||
|         specifier: 'catalog:' |         specifier: 'catalog:' | ||||||
|         version: 3.2.2(vite@5.4.8(@types/node@22.7.4)(less@4.2.0)(sass@1.79.4)(terser@5.33.0)) |         version: 3.2.2(vite@5.4.8(@types/node@22.7.4)(less@4.2.0)(sass@1.79.4)(terser@5.33.0)) | ||||||
|  |       vite-plugin-lazy-import: | ||||||
|  |         specifier: 'catalog:' | ||||||
|  |         version: 1.0.7 | ||||||
| 
 | 
 | ||||||
|   packages/@core/base/design: {} |   packages/@core/base/design: {} | ||||||
| 
 | 
 | ||||||
|  | @ -1584,9 +1596,33 @@ importers: | ||||||
| 
 | 
 | ||||||
|   packages/effects/plugins: |   packages/effects/plugins: | ||||||
|     dependencies: |     dependencies: | ||||||
|  |       '@vben-core/form-ui': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../@core/ui-kit/form-ui | ||||||
|  |       '@vben-core/shadcn-ui': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../@core/ui-kit/shadcn-ui | ||||||
|  |       '@vben-core/shared': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../@core/base/shared | ||||||
|  |       '@vben/hooks': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../hooks | ||||||
|  |       '@vben/icons': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../icons | ||||||
|  |       '@vben/locales': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../locales | ||||||
|       '@vben/preferences': |       '@vben/preferences': | ||||||
|         specifier: workspace:* |         specifier: workspace:* | ||||||
|         version: link:../../preferences |         version: link:../../preferences | ||||||
|  |       '@vben/types': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../types | ||||||
|  |       '@vben/utils': | ||||||
|  |         specifier: workspace:* | ||||||
|  |         version: link:../../utils | ||||||
|       '@vueuse/core': |       '@vueuse/core': | ||||||
|         specifier: 'catalog:' |         specifier: 'catalog:' | ||||||
|         version: 11.1.0(vue@3.5.10(typescript@5.6.2)) |         version: 11.1.0(vue@3.5.10(typescript@5.6.2)) | ||||||
|  | @ -1596,6 +1632,12 @@ importers: | ||||||
|       vue: |       vue: | ||||||
|         specifier: 3.5.10 |         specifier: 3.5.10 | ||||||
|         version: 3.5.10(typescript@5.6.2) |         version: 3.5.10(typescript@5.6.2) | ||||||
|  |       vxe-pc-ui: | ||||||
|  |         specifier: 'catalog:' | ||||||
|  |         version: 4.2.12 | ||||||
|  |       vxe-table: | ||||||
|  |         specifier: 'catalog:' | ||||||
|  |         version: 4.7.85 | ||||||
| 
 | 
 | ||||||
|   packages/effects/request: |   packages/effects/request: | ||||||
|     dependencies: |     dependencies: | ||||||
|  | @ -3959,6 +4001,10 @@ packages: | ||||||
|     resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} |     resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==} | ||||||
|     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} |     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} | ||||||
| 
 | 
 | ||||||
|  |   '@faker-js/faker@9.0.3': | ||||||
|  |     resolution: {integrity: sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==} | ||||||
|  |     engines: {node: '>=18.0.0', npm: '>=9.0.0'} | ||||||
|  | 
 | ||||||
|   '@fastify/busboy@2.1.1': |   '@fastify/busboy@2.1.1': | ||||||
|     resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} |     resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} | ||||||
|     engines: {node: '>=14'} |     engines: {node: '>=14'} | ||||||
|  | @ -3995,8 +4041,8 @@ packages: | ||||||
|   '@iconify-json/vscode-icons@1.2.2': |   '@iconify-json/vscode-icons@1.2.2': | ||||||
|     resolution: {integrity: sha512-bTpT0HJDRqGkxQv8oiETNHLEnBZpnA1QaRD35CQyO7M7qgWVLx2xwn/lK6e4waojmlPC3ckMBx3WFIUUn0/Jdg==} |     resolution: {integrity: sha512-bTpT0HJDRqGkxQv8oiETNHLEnBZpnA1QaRD35CQyO7M7qgWVLx2xwn/lK6e4waojmlPC3ckMBx3WFIUUn0/Jdg==} | ||||||
| 
 | 
 | ||||||
|   '@iconify/json@2.2.255': |   '@iconify/json@2.2.256': | ||||||
|     resolution: {integrity: sha512-wtBKGYrKHOmRlbai6cd4yTfHakLQ4lLD68w5pb1RDf1+6o0QcvLdun1sWZNcqkOvZOZAUOUPVISqKkYDlWY6YA==} |     resolution: {integrity: sha512-u2RwfBUuDE3A8qx3vnXdcJMtirHc9QrRRULfGY6Il6/K76Odfrm4yVqS/fYIh+wXwWl/fZdAZEozqxpZftfnIQ==} | ||||||
| 
 | 
 | ||||||
|   '@iconify/tailwind@1.1.3': |   '@iconify/tailwind@1.1.3': | ||||||
|     resolution: {integrity: sha512-SfyeT+2b/aKWA6DjwdevXdLUqaEqJ5xWTegD92KItaWc47IYsGuqrt/GOz4dJCPcTVCrsUjlvMpy8cNd+uV5nQ==} |     resolution: {integrity: sha512-SfyeT+2b/aKWA6DjwdevXdLUqaEqJ5xWTegD92KItaWc47IYsGuqrt/GOz4dJCPcTVCrsUjlvMpy8cNd+uV5nQ==} | ||||||
|  | @ -5177,6 +5223,9 @@ packages: | ||||||
|   '@vueuse/shared@9.13.0': |   '@vueuse/shared@9.13.0': | ||||||
|     resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} |     resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} | ||||||
| 
 | 
 | ||||||
|  |   '@vxe-ui/core@4.0.12': | ||||||
|  |     resolution: {integrity: sha512-ft8f874eQSv4N9+oulFKeg8APgd8RMHeFeUUUTNckIRJ/cNi0dbR0Fe2+ZZpRl3BwRtbE2hHb2FKWmL2oyZkfw==} | ||||||
|  | 
 | ||||||
|   JSONStream@1.3.5: |   JSONStream@1.3.5: | ||||||
|     resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} |     resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} | ||||||
|     hasBin: true |     hasBin: true | ||||||
|  | @ -6221,6 +6270,9 @@ packages: | ||||||
|   dom-serializer@2.0.0: |   dom-serializer@2.0.0: | ||||||
|     resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} |     resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} | ||||||
| 
 | 
 | ||||||
|  |   dom-zindex@1.0.6: | ||||||
|  |     resolution: {integrity: sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA==} | ||||||
|  | 
 | ||||||
|   domelementtype@2.3.0: |   domelementtype@2.3.0: | ||||||
|     resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} |     resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} | ||||||
| 
 | 
 | ||||||
|  | @ -10251,6 +10303,9 @@ packages: | ||||||
|       '@nuxt/kit': |       '@nuxt/kit': | ||||||
|         optional: true |         optional: true | ||||||
| 
 | 
 | ||||||
|  |   vite-plugin-lazy-import@1.0.7: | ||||||
|  |     resolution: {integrity: sha512-mE6oAObOb4wqso4AoUGi9cLjdR+4vay1RCaKJvziBuFPlziZl7J0aw2hsqRTokLVRx3bli0a0VyjMOwsNDv58A==} | ||||||
|  | 
 | ||||||
|   vite-plugin-lib-inject-css@2.1.1: |   vite-plugin-lib-inject-css@2.1.1: | ||||||
|     resolution: {integrity: sha512-RIMeVnqBK/8I0E9nnQWzws6pdj5ilRMPJSnXYb6nWxNR4EmDPnksnb/ACoR5Fy7QfzULqS4gtQMrjwnNCC9zoA==} |     resolution: {integrity: sha512-RIMeVnqBK/8I0E9nnQWzws6pdj5ilRMPJSnXYb6nWxNR4EmDPnksnb/ACoR5Fy7QfzULqS4gtQMrjwnNCC9zoA==} | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|  | @ -10417,6 +10472,12 @@ packages: | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       vue: 3.5.10 |       vue: 3.5.10 | ||||||
| 
 | 
 | ||||||
|  |   vxe-pc-ui@4.2.12: | ||||||
|  |     resolution: {integrity: sha512-zJ7sJLCtMahW5KNgiqQE+qDuBMoiOCIc0kl/W6ByoPgX5E1KzQTE3qvRc+v7pU/4GW//Vr3No/x1RwHMJix6Kg==} | ||||||
|  | 
 | ||||||
|  |   vxe-table@4.7.85: | ||||||
|  |     resolution: {integrity: sha512-sNQ4jKnU6vZkStTK2JDDKgIz5kKCCWtTtOVl7dpNsLJ16NYWMCDlNby5m/DJC+xa0dPvSdr7+AH4TXfD1vpRFg==} | ||||||
|  | 
 | ||||||
|   warning@4.0.3: |   warning@4.0.3: | ||||||
|     resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} |     resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} | ||||||
| 
 | 
 | ||||||
|  | @ -10569,6 +10630,9 @@ packages: | ||||||
|     resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} |     resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} | ||||||
|     engines: {node: '>=12'} |     engines: {node: '>=12'} | ||||||
| 
 | 
 | ||||||
|  |   xe-utils@3.5.30: | ||||||
|  |     resolution: {integrity: sha512-5Ez6JUANpMakduiTLxrNObzqMebnM4697KvHW5okedkUjXvYgGvkbg0tABTkvwDW/Pb09v7vT68dzBOeAuOu0g==} | ||||||
|  | 
 | ||||||
|   xml-name-validator@4.0.0: |   xml-name-validator@4.0.0: | ||||||
|     resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} |     resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} | ||||||
|     engines: {node: '>=12'} |     engines: {node: '>=12'} | ||||||
|  | @ -12914,6 +12978,8 @@ snapshots: | ||||||
|     dependencies: |     dependencies: | ||||||
|       levn: 0.4.1 |       levn: 0.4.1 | ||||||
| 
 | 
 | ||||||
|  |   '@faker-js/faker@9.0.3': {} | ||||||
|  | 
 | ||||||
|   '@fastify/busboy@2.1.1': {} |   '@fastify/busboy@2.1.1': {} | ||||||
| 
 | 
 | ||||||
|   '@floating-ui/core@1.6.8': |   '@floating-ui/core@1.6.8': | ||||||
|  | @ -12954,7 +13020,7 @@ snapshots: | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@iconify/types': 2.0.0 |       '@iconify/types': 2.0.0 | ||||||
| 
 | 
 | ||||||
|   '@iconify/json@2.2.255': |   '@iconify/json@2.2.256': | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@iconify/types': 2.0.0 |       '@iconify/types': 2.0.0 | ||||||
|       pathe: 1.1.2 |       pathe: 1.1.2 | ||||||
|  | @ -14463,6 +14529,11 @@ snapshots: | ||||||
|       - '@vue/composition-api' |       - '@vue/composition-api' | ||||||
|       - vue |       - vue | ||||||
| 
 | 
 | ||||||
|  |   '@vxe-ui/core@4.0.12': | ||||||
|  |     dependencies: | ||||||
|  |       dom-zindex: 1.0.6 | ||||||
|  |       xe-utils: 3.5.30 | ||||||
|  | 
 | ||||||
|   JSONStream@1.3.5: |   JSONStream@1.3.5: | ||||||
|     dependencies: |     dependencies: | ||||||
|       jsonparse: 1.3.1 |       jsonparse: 1.3.1 | ||||||
|  | @ -15620,6 +15691,8 @@ snapshots: | ||||||
|       domhandler: 5.0.3 |       domhandler: 5.0.3 | ||||||
|       entities: 4.5.0 |       entities: 4.5.0 | ||||||
| 
 | 
 | ||||||
|  |   dom-zindex@1.0.6: {} | ||||||
|  | 
 | ||||||
|   domelementtype@2.3.0: {} |   domelementtype@2.3.0: {} | ||||||
| 
 | 
 | ||||||
|   domhandler@4.3.1: |   domhandler@4.3.1: | ||||||
|  | @ -20137,6 +20210,13 @@ snapshots: | ||||||
|       - rollup |       - rollup | ||||||
|       - supports-color |       - supports-color | ||||||
| 
 | 
 | ||||||
|  |   vite-plugin-lazy-import@1.0.7: | ||||||
|  |     dependencies: | ||||||
|  |       '@rollup/pluginutils': 5.1.2(rollup@4.24.0) | ||||||
|  |       es-module-lexer: 1.5.4 | ||||||
|  |       rollup: 4.24.0 | ||||||
|  |       xe-utils: 3.5.30 | ||||||
|  | 
 | ||||||
|   vite-plugin-lib-inject-css@2.1.1(vite@5.4.8(@types/node@22.7.4)(less@4.2.0)(sass@1.79.4)(terser@5.33.0)): |   vite-plugin-lib-inject-css@2.1.1(vite@5.4.8(@types/node@22.7.4)(less@4.2.0)(sass@1.79.4)(terser@5.33.0)): | ||||||
|     dependencies: |     dependencies: | ||||||
|       '@ast-grep/napi': 0.22.6 |       '@ast-grep/napi': 0.22.6 | ||||||
|  | @ -20362,6 +20442,14 @@ snapshots: | ||||||
|       vooks: 0.2.12(vue@3.5.10(typescript@5.6.2)) |       vooks: 0.2.12(vue@3.5.10(typescript@5.6.2)) | ||||||
|       vue: 3.5.10(typescript@5.6.2) |       vue: 3.5.10(typescript@5.6.2) | ||||||
| 
 | 
 | ||||||
|  |   vxe-pc-ui@4.2.12: | ||||||
|  |     dependencies: | ||||||
|  |       '@vxe-ui/core': 4.0.12 | ||||||
|  | 
 | ||||||
|  |   vxe-table@4.7.85: | ||||||
|  |     dependencies: | ||||||
|  |       vxe-pc-ui: 4.2.12 | ||||||
|  | 
 | ||||||
|   warning@4.0.3: |   warning@4.0.3: | ||||||
|     dependencies: |     dependencies: | ||||||
|       loose-envify: 1.4.0 |       loose-envify: 1.4.0 | ||||||
|  | @ -20584,6 +20672,8 @@ snapshots: | ||||||
| 
 | 
 | ||||||
|   xdg-basedir@5.1.0: {} |   xdg-basedir@5.1.0: {} | ||||||
| 
 | 
 | ||||||
|  |   xe-utils@3.5.30: {} | ||||||
|  | 
 | ||||||
|   xml-name-validator@4.0.0: {} |   xml-name-validator@4.0.0: {} | ||||||
| 
 | 
 | ||||||
|   y18n@4.0.3: {} |   y18n@4.0.3: {} | ||||||
|  |  | ||||||
|  | @ -21,7 +21,8 @@ catalog: | ||||||
|   '@commitlint/config-conventional': ^19.5.0 |   '@commitlint/config-conventional': ^19.5.0 | ||||||
|   '@ctrl/tinycolor': ^4.1.0 |   '@ctrl/tinycolor': ^4.1.0 | ||||||
|   '@eslint/js': ^9.11.1 |   '@eslint/js': ^9.11.1 | ||||||
|   '@iconify/json': ^2.2.255 |   '@faker-js/faker': ^9.0.3 | ||||||
|  |   '@iconify/json': ^2.2.256 | ||||||
|   '@iconify/tailwind': ^1.1.3 |   '@iconify/tailwind': ^1.1.3 | ||||||
|   '@iconify/vue': ^4.1.2 |   '@iconify/vue': ^4.1.2 | ||||||
|   '@intlify/core-base': ^10.0.3 |   '@intlify/core-base': ^10.0.3 | ||||||
|  | @ -160,6 +161,7 @@ catalog: | ||||||
|   vite-plugin-compression: ^0.5.1 |   vite-plugin-compression: ^0.5.1 | ||||||
|   vite-plugin-dts: 4.2.1 |   vite-plugin-dts: 4.2.1 | ||||||
|   vite-plugin-html: ^3.2.2 |   vite-plugin-html: ^3.2.2 | ||||||
|  |   vite-plugin-lazy-import: ^1.0.7 | ||||||
|   vite-plugin-lib-inject-css: ^2.1.1 |   vite-plugin-lib-inject-css: ^2.1.1 | ||||||
|   vite-plugin-pwa: ^0.20.5 |   vite-plugin-pwa: ^0.20.5 | ||||||
|   vite-plugin-vue-devtools: ^7.4.6 |   vite-plugin-vue-devtools: ^7.4.6 | ||||||
|  | @ -171,6 +173,8 @@ catalog: | ||||||
|   vue-i18n: ^10.0.3 |   vue-i18n: ^10.0.3 | ||||||
|   vue-router: ^4.4.5 |   vue-router: ^4.4.5 | ||||||
|   vue-tsc: ^2.1.6 |   vue-tsc: ^2.1.6 | ||||||
|  |   vxe-pc-ui: ^4.2.9 | ||||||
|  |   vxe-table: ^4.7.84 | ||||||
|   watermark-js-plus: ^1.5.7 |   watermark-js-plus: ^1.5.7 | ||||||
|   zod: ^3.23.8 |   zod: ^3.23.8 | ||||||
|   zod-defaults: ^0.1.3 |   zod-defaults: ^0.1.3 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Vben
						Vben