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