Jelajahi Sumber

docs: add vxe-table doc (#4807)

* docs: init vxe-table demos

* style: fix vxe-table index.scss import error

* docs: fix vxe-table style & theme toggle problem

* docs: add rest demos

* docs: add vxe-table demo desc

* fix: add maximumFileSizeToCacheInBytes to fix build error

* fix: fix vxe-table set-theme build error

* docs: fix vitepress ssr render problem

* docs: add some tips for vitepress compatibility
Arthur Darkstone 4 bulan lalu
induk
melakukan
33ce4d3cf3

+ 1 - 0
docs/.vitepress/config/shared.mts

@@ -156,6 +156,7 @@ function pwa(): PwaOptions {
     registerType: 'autoUpdate',
     workbox: {
       globPatterns: ['**/*.{css,js,html,svg,png,ico,txt,woff2}'],
+      maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
     },
   };
 }

+ 15 - 0
docs/.vitepress/theme/components/site-layout.vue

@@ -32,6 +32,21 @@ watch(
   () => nextTick(() => initZoom()),
 );
 
+function setVxeTheme(name?: string) {
+  const theme = !name || name === 'default' ? 'light' : name;
+
+  if (typeof document !== 'undefined') {
+    const documentElement = document.documentElement;
+    if (documentElement) {
+      documentElement.dataset.vxeUiTheme = theme;
+    }
+  }
+}
+
+watch(isDark, (dark) => {
+  setVxeTheme(dark ? 'dark' : 'light');
+});
+
 // initPreferences({
 //   namespace: 'docs',
 // });

+ 66 - 1
docs/.vitepress/theme/index.ts

@@ -1,7 +1,12 @@
 // https://vitepress.dev/guide/custom-theme
 import type { EnhanceAppContext, Theme } from 'vitepress';
 
+import { h } from 'vue';
+
+import { useVbenForm } from '@vben/common-ui';
+
 import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client';
+import { Button, Image } from 'ant-design-vue';
 import DefaultTheme from 'vitepress/theme';
 
 import { DemoPreview } from '../components';
@@ -15,11 +20,71 @@ import 'virtual:group-icons.css';
 import '@nolebase/vitepress-plugin-git-changelog/client/style.css';
 
 export default {
-  enhanceApp(ctx: EnhanceAppContext) {
+  async enhanceApp(ctx: EnhanceAppContext) {
     const { app } = ctx;
     app.component('VbenContributors', VbenContributors);
     app.component('DemoPreview', DemoPreview);
     app.use(NolebaseGitChangelogPlugin);
+
+    if (!import.meta.env.SSR) {
+      const plugin = await import('@vben/plugins/vxe-table');
+
+      plugin.setupVbenVxeTable({
+        configVxeTable: (vxeUI) => {
+          vxeUI.setConfig({
+            grid: {
+              align: 'center',
+              border: false,
+              columnConfig: {
+                resizable: true,
+              },
+              minHeight: 180,
+              proxyConfig: {
+                autoLoad: true,
+                response: {
+                  result: 'items',
+                  total: 'total',
+                  list: 'items',
+                },
+                showActiveMsg: true,
+                showResponseMsg: false,
+              },
+              round: true,
+              showOverflow: true,
+              size: 'small',
+            },
+          });
+
+          // 表格配置项可以用 cellRender: { name: 'CellImage' },
+          vxeUI.renderer.add('CellImage', {
+            renderDefault(_renderOpts, params) {
+              const { column, row } = params;
+              return h(Image, { src: row[column.field] } as any);
+            },
+          });
+
+          // 表格配置项可以用 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,
+      });
+
+      app.component('VbenVxeGrid', plugin.VbenVxeGrid);
+      app.provide('useVbenVxeGrid', plugin.useVbenVxeGrid);
+    }
+
     // 百度统计
     initHmPlugin();
   },

+ 2 - 0
docs/package.json

@@ -14,6 +14,7 @@
     "@vben-core/shadcn-ui": "workspace:*",
     "@vben/common-ui": "workspace:*",
     "@vben/locales": "workspace:*",
+    "@vben/plugins": "workspace:*",
     "@vben/styles": "workspace:*",
     "ant-design-vue": "catalog:",
     "lucide-vue-next": "catalog:",
@@ -25,6 +26,7 @@
     "@nolebase/vitepress-plugin-git-changelog": "catalog:",
     "@vben/vite-config": "workspace:*",
     "@vite-pwa/vitepress": "catalog:",
+    "sass": "^1.80.6",
     "vitepress": "catalog:",
     "vue": "catalog:"
   }

+ 1 - 0
docs/src/_env/adapter/index.ts

@@ -1 +1,2 @@
 export * from './form';
+export * from './vxe-table';

+ 1 - 0
docs/src/_env/adapter/vxe-table.ts

@@ -0,0 +1 @@
+export type * from '@vben/plugins/vxe-table';

+ 110 - 1
docs/src/components/common-ui/vben-vxe-table.md

@@ -4,4 +4,113 @@ outline: deep
 
 # Vben Vxe Table 表格
 
-文档待补充,如果需要使用,可先行查看 vxe-table 文档和 示例代码,内部有部分注释。
+框架提供的Table 列表组件基于 [vxe-table](https://vxetable.cn/v4/#/grid/api?apiKey=grid),结合`Vben Form 表单`进行了二次封装。
+
+其中,表头的 **表单搜索** 部分采用了`Vben Form表单`,表格主体部分使用了`vxe-grid`组件,支持表格的分页、排序、筛选等功能。
+
+> 如果文档内没有参数说明,可以尝试在在线示例或者在 [vxe-grid 官方API 文档](https://vxetable.cn/v4/#/grid/api?apiKey=grid) 内寻找
+
+::: info 写在前面
+
+如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
+
+:::
+
+::: tip README
+
+下方示例代码中, 出现的 `inject<UseVbenVxeGrid>` 是为了适配文档内的示例,实际使用时直接从 `'@vben/plugins/vxe-table'` 引入即可。
+
+```typescript
+import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
+```
+
+:::
+
+## 基础表格
+
+使用 `useVbenVxeGrid` 创建最基础的表格。
+
+<DemoPreview dir="demos/vben-vxe-table/basic" />
+
+## 远程加载
+
+通过指定 `proxyConfig.ajax` 的 `query` 方法,可以实现远程加载数据。
+
+<DemoPreview dir="demos/vben-vxe-table/remote" />
+
+## 树形表格
+
+树形表格,的数据源为扁平结构,可以指定`treeConfig`配置项,实现树形表格。
+
+```typescript
+treeConfig: {
+  transform: true, // 指定表格为树形表格
+  parentField: 'parentId', // 父节点字段名
+  rowField: 'id', // 行数据字段名
+},
+```
+
+<DemoPreview dir="demos/vben-vxe-table/tree" />
+
+## 固定表头/列
+
+列固定可选参数: `'left' | 'right' | '' | null`
+
+<DemoPreview dir="demos/vben-vxe-table/fixed" />
+
+## 自定义单元格
+
+自定义单元格有两种实现方式
+
+- 通过 `slots` 插槽
+- 通过 `customCell` 自定义单元格,但是要先添加渲染器
+
+```typescript
+// 表格配置项可以用 cellRender: { name: 'CellImage' },
+vxeUI.renderer.add('CellImage', {
+  renderDefault(_renderOpts, params) {
+    const { column, row } = params;
+    return h(Image, { src: row[column.field] } as any); // 注意此处的Image 组件,来源于Antd,需要自行引入,否则会使用js的Image类
+  },
+});
+
+// 表格配置项可以用 cellRender: { name: 'CellLink' },
+vxeUI.renderer.add('CellLink', {
+  renderDefault(renderOpts) {
+    const { props } = renderOpts;
+    return h(
+      Button,
+      { size: 'small', type: 'link' },
+      { default: () => props?.text },
+    );
+  },
+});
+```
+
+<DemoPreview dir="demos/vben-vxe-table/custom-cell" />
+
+## 搜索表单
+
+**表单搜索** 部分采用了`Vben Form 表单`,参考 [Vben Form 表单文档](/components/common-ui/vben-form)。
+
+<DemoPreview dir="demos/vben-vxe-table/form" />
+
+## 单元格编辑
+
+通过指定`editConfig.mode`为`cell`,可以实现单元格编辑。
+
+<DemoPreview dir="demos/vben-vxe-table/edit-cell" />
+
+## 行编辑
+
+通过指定`editConfig.mode`为`row`,可以实现行编辑。
+
+<DemoPreview dir="demos/vben-vxe-table/edit-row" />
+
+## 虚拟滚动
+
+通过 scroll-y.enabled 与 scroll-y.gt 组合开启,其中 enabled 为总开关,gt 是指当总行数大于指定行数时自动开启。
+
+> 参考 [vxe-table 官方文档 - 虚拟滚动](https://vxetable.cn/v4/#/component/grid/scroll/vertical)。
+
+<DemoPreview dir="demos/vben-vxe-table/virtual" />

+ 93 - 0
docs/src/demos/vben-vxe-table/basic/index.vue

@@ -0,0 +1,93 @@
+<script lang="ts" setup>
+import type {
+  UseVbenVxeGrid,
+  VxeGridListeners,
+  VxeGridProps,
+} from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button, message } from 'ant-design-vue';
+
+import { MOCK_TABLE_DATA } from '../table-data';
+
+interface RowType {
+  address: string;
+  age: number;
+  id: number;
+  name: string;
+  nickname: string;
+  role: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+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,
+  pagerConfig: {
+    enabled: false,
+  },
+  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>
+  <!-- 此处的`vp-raw` 是为了适配文档的展示效果,实际使用时不需要 -->
+  <div class="vp-raw w-full">
+    <Grid>
+      <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>
+  </div>
+</template>

+ 109 - 0
docs/src/demos/vben-vxe-table/custom-cell/index.vue

@@ -0,0 +1,109 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button, Image, Switch, Tag } from 'ant-design-vue';
+
+import { getExampleTableApi } from '../mock-api';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  imageUrl: string;
+  open: boolean;
+  price: string;
+  productName: string;
+  releaseDate: string;
+  status: 'error' | 'success' | 'warning';
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+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: 'formatDateTime',
+      title: 'Date',
+      width: 200,
+    },
+    {
+      cellRender: { name: 'CellLink', props: { text: '编辑' } },
+      field: 'action',
+      fixed: 'right',
+      title: '操作',
+      width: 120,
+    },
+  ],
+  keepSource: true,
+  pagerConfig: {},
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }) => {
+        return await getExampleTableApi({
+          page: page.currentPage,
+          pageSize: page.pageSize,
+        });
+      },
+    },
+  },
+};
+
+const [Grid] = useVbenVxeGrid({ gridOptions });
+</script>
+
+<template>
+  <div class="vp-raw w-full">
+    <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>
+  </div>
+</template>

+ 59 - 0
docs/src/demos/vben-vxe-table/edit-cell/index.vue

@@ -0,0 +1,59 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { getExampleTableApi } from '../mock-api';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  price: string;
+  productName: string;
+  releaseDate: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+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', formatter: 'formatDateTime', title: 'Date' },
+  ],
+  editConfig: {
+    mode: 'cell',
+    trigger: 'click',
+  },
+  pagerConfig: {},
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }) => {
+        return await getExampleTableApi({
+          page: page.currentPage,
+          pageSize: page.pageSize,
+        });
+      },
+    },
+  },
+  showOverflow: true,
+};
+
+const [Grid] = useVbenVxeGrid({ gridOptions });
+</script>
+
+<template>
+  <div class="vp-raw w-full">
+    <Grid />
+  </div>
+</template>

+ 96 - 0
docs/src/demos/vben-vxe-table/edit-row/index.vue

@@ -0,0 +1,96 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button, message } from 'ant-design-vue';
+
+import { getExampleTableApi } from '../mock-api';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  price: string;
+  productName: string;
+  releaseDate: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+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', formatter: 'formatDateTime', title: 'Date' },
+    { slots: { default: 'action' }, title: '操作' },
+  ],
+  editConfig: {
+    mode: 'row',
+    trigger: 'click',
+  },
+  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>
+  <div class="vp-raw w-full">
+    <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>
+  </div>
+</template>

+ 71 - 0
docs/src/demos/vben-vxe-table/fixed/index.vue

@@ -0,0 +1,71 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button } from 'ant-design-vue';
+
+import { getExampleTableApi } from '../mock-api';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  price: string;
+  productName: string;
+  releaseDate: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+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',
+      formatter: 'formatDateTime',
+      title: 'DateTime',
+      width: 500,
+    },
+    {
+      field: 'action',
+      fixed: 'right',
+      slots: { default: 'action' },
+      title: '操作',
+      width: 120,
+    },
+  ],
+  pagerConfig: {},
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }) => {
+        return await getExampleTableApi({
+          page: page.currentPage,
+          pageSize: page.pageSize,
+        });
+      },
+    },
+  },
+  rowConfig: {
+    isHover: true,
+  },
+};
+
+const [Grid] = useVbenVxeGrid({ gridOptions });
+</script>
+
+<template>
+  <div class="vp-raw w-full">
+    <Grid>
+      <template #action>
+        <Button type="link">编辑</Button>
+      </template>
+    </Grid>
+  </div>
+</template>

+ 124 - 0
docs/src/demos/vben-vxe-table/form/index.vue

@@ -0,0 +1,124 @@
+<script lang="ts" setup>
+import type { VbenFormProps } from '#/adapter/form';
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { message } from 'ant-design-vue';
+
+import { getExampleTableApi } from '../mock-api';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  price: string;
+  productName: string;
+  releaseDate: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+const formOptions: VbenFormProps = {
+  // 默认展开
+  collapsed: false,
+  schema: [
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: 'Please enter category',
+      },
+      defaultValue: '1',
+      fieldName: 'category',
+      label: 'Category',
+    },
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: 'Please enter productName',
+      },
+      fieldName: 'productName',
+      label: 'ProductName',
+    },
+    {
+      component: 'Input',
+      componentProps: {
+        placeholder: 'Please enter price',
+      },
+      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',
+    },
+  ],
+  // 控制表单是否显示折叠按钮
+  showCollapseButton: true,
+  submitButtonOptions: {
+    content: '查询',
+  },
+  // 按下回车时是否提交表单
+  submitOnEnter: false,
+};
+
+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', formatter: 'formatDateTime', title: 'Date' },
+  ],
+  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>
+  <div class="vp-raw w-full">
+    <Grid />
+  </div>
+</template>

+ 36 - 0
docs/src/demos/vben-vxe-table/mock-api.ts

@@ -0,0 +1,36 @@
+import { MOCK_API_DATA } from './table-data';
+
+export namespace DemoTableApi {
+  export interface PageFetchParams {
+    [key: string]: any;
+    page: number;
+    pageSize: number;
+  }
+}
+
+export function sleep(time = 1000) {
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve(true);
+    }, time);
+  });
+}
+
+/**
+ * 获取示例表格数据
+ */
+async function getExampleTableApi(params: DemoTableApi.PageFetchParams) {
+  return new Promise<{ items: any; total: number }>((resolve) => {
+    const { page, pageSize } = params;
+    const items = MOCK_API_DATA.slice((page - 1) * pageSize, page * pageSize);
+
+    sleep(1000).then(() => {
+      resolve({
+        total: items.length,
+        items,
+      });
+    });
+  });
+}
+
+export { getExampleTableApi };

+ 115 - 0
docs/src/demos/vben-vxe-table/remote/index.vue

@@ -0,0 +1,115 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button } from 'ant-design-vue';
+
+import { type DemoTableApi } from '../mock-api';
+import { MOCK_API_DATA } from '../table-data';
+
+interface RowType {
+  category: string;
+  color: string;
+  id: string;
+  price: string;
+  productName: string;
+  releaseDate: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+// 数据实例
+// const MOCK_TREE_TABLE_DATA = [
+//   {
+//     date: '2020-08-01',
+//     id: 10_000,
+//     name: 'Test1',
+//     parentId: null,
+//     size: 1024,
+//     type: 'mp3',
+//   },
+// ]
+
+const sleep = (time = 1000) => {
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve(true);
+    }, time);
+  });
+};
+
+/**
+ * 获取示例表格数据
+ */
+async function getExampleTableApi(params: DemoTableApi.PageFetchParams) {
+  return new Promise<{ items: any; total: number }>((resolve) => {
+    const { page, pageSize } = params;
+    const items = MOCK_API_DATA.slice((page - 1) * pageSize, page * pageSize);
+
+    sleep(1000).then(() => {
+      resolve({
+        total: items.length,
+        items,
+      });
+    });
+  });
+}
+
+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', formatter: 'formatDateTime', title: 'DateTime' },
+  ],
+  exportConfig: {},
+  // height: 'auto', // 如果设置为 auto,则必须确保存在父节点且不允许存在相邻元素,否则会出现高度闪动问题
+  keepSource: true,
+  proxyConfig: {
+    ajax: {
+      query: async ({ page }) => {
+        return await getExampleTableApi({
+          page: page.currentPage,
+          pageSize: page.pageSize,
+        });
+      },
+    },
+  },
+  toolbarConfig: {
+    custom: true,
+    export: true,
+    // import: true,
+    refresh: true,
+    zoom: true,
+  },
+};
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  gridOptions,
+});
+</script>
+
+<template>
+  <div class="vp-raw w-full">
+    <Grid>
+      <template #toolbar-tools>
+        <Button class="mr-2" type="primary" @click="() => gridApi.query()">
+          刷新当前页面
+        </Button>
+        <Button type="primary" @click="() => gridApi.reload()">
+          刷新并返回第一页
+        </Button>
+      </template>
+    </Grid>
+  </div>
+</template>

+ 384 - 0
docs/src/demos/vben-vxe-table/table-data.ts

@@ -0,0 +1,384 @@
+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 < 10; 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',
+  },
+];
+
+export const MOCK_API_DATA = [
+  {
+    available: true,
+    category: 'Computers',
+    color: 'purple',
+    currency: 'NAD',
+    description:
+      'Ergonomic executive chair upholstered in bonded black leather and PVC padded seat and back for all-day comfort and support',
+    id: '45a613df-227a-4907-a89f-4a7f1252ca0c',
+    imageUrl: 'https://avatars.githubusercontent.com/u/62715097',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/75395683',
+    inProduction: false,
+    open: true,
+    price: '48.89',
+    productName: 'Handcrafted Steel Salad',
+    quantity: 70,
+    rating: 3.780_582_329_574_367,
+    releaseDate: '2024-09-09T04:06:57.793Z',
+    status: 'error',
+    tags: ['Bespoke', 'Handmade', 'Luxurious'],
+    weight: 1.031_015_671_912_002_5,
+  },
+  {
+    available: true,
+    category: 'Toys',
+    color: 'green',
+    currency: 'CZK',
+    description:
+      'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
+    id: 'd02e5ee9-bc98-4de2-98fa-25a6567ecc19',
+    imageUrl: 'https://avatars.githubusercontent.com/u/51512330',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/58698113',
+    inProduction: false,
+    open: false,
+    price: '68.15',
+    productName: 'Generic Cotton Gloves',
+    quantity: 3,
+    rating: 1.681_749_367_682_703_3,
+    releaseDate: '2024-06-16T09:00:36.806Z',
+    status: 'warning',
+    tags: ['Rustic', 'Handcrafted', 'Recycled'],
+    weight: 9.601_076_149_300_575,
+  },
+  {
+    available: true,
+    category: 'Beauty',
+    color: 'teal',
+    currency: 'OMR',
+    description:
+      'The Apollotech B340 is an affordable wireless mouse with reliable connectivity, 12 months battery life and modern design',
+    id: '2b72521c-225c-4e64-8030-611b76b10b37',
+    imageUrl: 'https://avatars.githubusercontent.com/u/50300075',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/36541691',
+    inProduction: true,
+    open: true,
+    price: '696.94',
+    productName: 'Gorgeous Soft Ball',
+    quantity: 50,
+    rating: 2.361_581_777_372_057_5,
+    releaseDate: '2024-06-03T13:24:19.809Z',
+    status: 'warning',
+    tags: ['Gorgeous', 'Ergonomic', 'Licensed'],
+    weight: 8.882_340_049_286_19,
+  },
+  {
+    available: true,
+    category: 'Games',
+    color: 'silver',
+    currency: 'SOS',
+    description:
+      'Carbonite web goalkeeper gloves are ergonomically designed to give easy fit',
+    id: 'bafab694-3801-452c-b102-9eb519bd1143',
+    imageUrl: 'https://avatars.githubusercontent.com/u/89827115',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/55952747',
+    inProduction: false,
+    open: false,
+    price: '553.84',
+    productName: 'Bespoke Soft Computer',
+    quantity: 29,
+    rating: 2.176_412_873_760_271_7,
+    releaseDate: '2024-09-17T12:16:27.034Z',
+    status: 'error',
+    tags: ['Elegant', 'Rustic', 'Recycled'],
+    weight: 9.653_285_869_978_038,
+  },
+  {
+    available: true,
+    category: 'Toys',
+    color: 'indigo',
+    currency: 'BIF',
+    description:
+      'Andy shoes are designed to keeping in mind durability as well as trends, the most stylish range of shoes & sandals',
+    id: 'bf6dea6b-2a55-441d-8773-937e03d99389',
+    imageUrl: 'https://avatars.githubusercontent.com/u/21431092',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/3771350',
+    inProduction: true,
+    open: true,
+    price: '237.39',
+    productName: 'Handcrafted Cotton Mouse',
+    quantity: 54,
+    rating: 4.363_265_388_265_461,
+    releaseDate: '2023-10-23T13:42:34.947Z',
+    status: 'error',
+    tags: ['Unbranded', 'Handmade', 'Generic'],
+    weight: 9.513_203_612_535_571,
+  },
+  {
+    available: false,
+    category: 'Tools',
+    color: 'violet',
+    currency: 'TZS',
+    description:
+      'New ABC 13 9370, 13.3, 5th Gen CoreA5-8250U, 8GB RAM, 256GB SSD, power UHD Graphics, OS 10 Home, OS Office A & J 2016',
+    id: '135ba6ab-32ee-4989-8189-5cfa658ef970',
+    imageUrl: 'https://avatars.githubusercontent.com/u/29946092',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/23842994',
+    inProduction: false,
+    open: false,
+    price: '825.25',
+    productName: 'Awesome Bronze Ball',
+    quantity: 94,
+    rating: 4.251_159_804_726_753,
+    releaseDate: '2023-12-30T07:31:43.464Z',
+    status: 'warning',
+    tags: ['Handmade', 'Elegant', 'Unbranded'],
+    weight: 2.247_473_385_732_636_8,
+  },
+  {
+    available: true,
+    category: 'Automotive',
+    color: 'teal',
+    currency: 'BOB',
+    description: 'The Football Is Good For Training And Recreational Purposes',
+    id: '652ef256-7d4e-48b7-976c-7afaa781ea92',
+    imageUrl: 'https://avatars.githubusercontent.com/u/2531904',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/15215990',
+    inProduction: false,
+    open: false,
+    price: '780.49',
+    productName: 'Oriental Rubber Pants',
+    quantity: 70,
+    rating: 2.636_323_417_377_916,
+    releaseDate: '2024-02-23T23:30:49.628Z',
+    status: 'success',
+    tags: ['Unbranded', 'Elegant', 'Unbranded'],
+    weight: 4.812_965_858_018_838,
+  },
+  {
+    available: false,
+    category: 'Garden',
+    color: 'plum',
+    currency: 'LRD',
+    description:
+      'The slim & simple Maple Gaming Keyboard from Dev Byte comes with a sleek body and 7- Color RGB LED Back-lighting for smart functionality',
+    id: '3ea24798-6589-40cc-85f0-ab78752244a0',
+    imageUrl: 'https://avatars.githubusercontent.com/u/23165285',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/14595665',
+    inProduction: false,
+    open: true,
+    price: '583.85',
+    productName: 'Handcrafted Concrete Hat',
+    quantity: 15,
+    rating: 1.371_600_527_752_802_7,
+    releaseDate: '2024-03-02T19:40:50.255Z',
+    status: 'error',
+    tags: ['Rustic', 'Sleek', 'Ergonomic'],
+    weight: 4.926_949_366_405_728_4,
+  },
+  {
+    available: false,
+    category: 'Industrial',
+    color: 'salmon',
+    currency: 'AUD',
+    description:
+      'The Apollotech B340 is an affordable wireless mouse with reliable connectivity, 12 months battery life and modern design',
+    id: '997113dd-f6e4-4acc-9790-ef554c7498d1',
+    imageUrl: 'https://avatars.githubusercontent.com/u/49021914',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/4690621',
+    inProduction: true,
+    open: false,
+    price: '67.99',
+    productName: 'Generic Rubber Bacon',
+    quantity: 68,
+    rating: 4.129_840_682_128_08,
+    releaseDate: '2023-12-17T01:40:25.415Z',
+    status: 'error',
+    tags: ['Oriental', 'Small', 'Handcrafted'],
+    weight: 1.080_114_331_801_906_4,
+  },
+  {
+    available: false,
+    category: 'Tools',
+    color: 'sky blue',
+    currency: 'NOK',
+    description:
+      'The Nagasaki Lander is the trademarked name of several series of Nagasaki sport bikes, that started with the 1984 ABC800J',
+    id: 'f697a250-6cb2-46c8-b0f7-871ab1f2fa8d',
+    imageUrl: 'https://avatars.githubusercontent.com/u/95928385',
+    imageUrl2: 'https://avatars.githubusercontent.com/u/47588244',
+    inProduction: false,
+    open: false,
+    price: '613.89',
+    productName: 'Gorgeous Frozen Ball',
+    quantity: 55,
+    rating: 1.646_947_205_998_534_6,
+    releaseDate: '2024-10-13T12:31:04.929Z',
+    status: 'warning',
+    tags: ['Handmade', 'Unbranded', 'Unbranded'],
+    weight: 9.430_690_557_758_114,
+  },
+];

+ 84 - 0
docs/src/demos/vben-vxe-table/tree/index.vue

@@ -0,0 +1,84 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject } from 'vue';
+
+import { Button } from 'ant-design-vue';
+
+import { MOCK_TREE_TABLE_DATA } from '../table-data';
+
+interface RowType {
+  date: string;
+  id: number;
+  name: string;
+  parentId: null | number;
+  size: number;
+  type: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+// 数据实例
+// 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: 10_000,
+//     size: 0,
+//     type: 'mp4',
+//   },
+// ];
+
+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,
+  pagerConfig: {
+    enabled: false,
+  },
+  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>
+  <div class="vp-raw h-[300px] w-full">
+    <Grid>
+      <template #toolbar-tools>
+        <Button class="mr-2" type="primary" @click="expandAll">
+          展开全部
+        </Button>
+        <Button type="primary" @click="collapseAll"> 折叠全部 </Button>
+      </template>
+    </Grid>
+  </div>
+</template>

+ 66 - 0
docs/src/demos/vben-vxe-table/virtual/index.vue

@@ -0,0 +1,66 @@
+<script lang="ts" setup>
+import type { UseVbenVxeGrid, VxeGridProps } from '#/adapter/vxe-table';
+
+import { inject, onMounted } from 'vue';
+
+interface RowType {
+  id: number;
+  name: string;
+  role: string;
+  sex: string;
+}
+
+const useVbenVxeGrid = inject<UseVbenVxeGrid>(
+  'useVbenVxeGrid',
+) as UseVbenVxeGrid;
+
+const gridOptions: VxeGridProps<RowType> = {
+  columns: [
+    { type: 'seq', width: 70 },
+    { field: 'name', title: 'Name' },
+    { field: 'role', title: 'Role' },
+    { field: 'sex', title: 'Sex' },
+  ],
+  data: [],
+  height: 'auto',
+  pagerConfig: {
+    enabled: false,
+  },
+  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>
+  <div class="vp-raw h-[500px] w-full">
+    <Grid />
+  </div>
+</template>

+ 3 - 0
packages/effects/plugins/src/vxe-table/index.ts

@@ -1,4 +1,7 @@
 export { setupVbenVxeTable } from './init';
 export * from './use-vxe-grid';
 export { default as VbenVxeGrid } from './use-vxe-grid.vue';
+
+export * from 'vxe-pc-ui/styles/all.scss';
 export type { VxeGridListeners, VxeGridProps } from 'vxe-table';
+export * from 'vxe-table/styles/all.scss';

+ 2 - 0
packages/effects/plugins/src/vxe-table/use-vxe-grid.ts

@@ -41,3 +41,5 @@ export function useVbenVxeGrid(options: VxeGridProps) {
 
   return [Grid, extendedApi] as const;
 }
+
+export type UseVbenVxeGrid = typeof useVbenVxeGrid;

+ 91 - 34
pnpm-lock.yaml

@@ -539,10 +539,10 @@ importers:
         version: link:scripts/vsh
       '@vitejs/plugin-vue':
         specifier: 'catalog:'
-        version: 5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
+        version: 5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
       '@vitejs/plugin-vue-jsx':
         specifier: 'catalog:'
-        version: 4.0.1(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
+        version: 4.0.1(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
       '@vue/test-utils':
         specifier: 'catalog:'
         version: 2.4.6
@@ -584,13 +584,13 @@ importers:
         version: 5.6.3
       unbuild:
         specifier: 'catalog:'
-        version: 3.0.0-rc.11(sass@1.79.5)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))
+        version: 3.0.0-rc.11(sass@1.80.6)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))
       vite:
         specifier: 'catalog:'
-        version: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+        version: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+        version: 2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
       vue:
         specifier: ^3.5.12
         version: 3.5.12(typescript@5.6.3)
@@ -818,6 +818,9 @@ importers:
       '@vben/locales':
         specifier: workspace:*
         version: link:../packages/locales
+      '@vben/plugins':
+        specifier: workspace:*
+        version: link:../packages/effects/plugins
       '@vben/styles':
         specifier: workspace:*
         version: link:../packages/styles
@@ -839,16 +842,19 @@ importers:
     devDependencies:
       '@nolebase/vitepress-plugin-git-changelog':
         specifier: 'catalog:'
-        version: 2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
+        version: 2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
       '@vben/vite-config':
         specifier: workspace:*
         version: link:../internal/vite-config
       '@vite-pwa/vitepress':
         specifier: 'catalog:'
-        version: 0.5.3(vite-plugin-pwa@0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
+        version: 0.5.3(vite-plugin-pwa@0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
+      sass:
+        specifier: ^1.80.6
+        version: 1.80.6
       vitepress:
         specifier: 'catalog:'
-        version: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
+        version: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
       vue:
         specifier: ^3.5.12
         version: 3.5.12(typescript@5.6.3)
@@ -933,7 +939,7 @@ importers:
         version: 4.1.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))
       eslint-plugin-vitest:
         specifier: 'catalog:'
-        version: 0.5.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))
+        version: 0.5.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))
       eslint-plugin-vue:
         specifier: 'catalog:'
         version: 9.30.0(eslint@9.14.0(jiti@2.4.0))
@@ -1091,7 +1097,7 @@ importers:
         version: link:../../packages/types
       vite:
         specifier: 'catalog:'
-        version: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+        version: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
 
   internal/vite-config:
     dependencies:
@@ -8913,6 +8919,11 @@ packages:
     engines: {node: '>=14.0.0'}
     hasBin: true
 
+  sass@1.80.6:
+    resolution: {integrity: sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+
   sax@1.4.1:
     resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
 
@@ -12451,11 +12462,11 @@ snapshots:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.17.1
 
-  '@nolebase/ui@2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)':
+  '@nolebase/ui@2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)':
     dependencies:
       '@iconify-json/octicon': 1.2.1
       less: 4.2.0
-      vitepress: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
+      vitepress: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
       vue: 3.5.12(typescript@5.6.3)
     transitivePeerDependencies:
       - '@algolia/client-search'
@@ -12486,10 +12497,10 @@ snapshots:
       - typescript
       - universal-cookie
 
-  '@nolebase/vitepress-plugin-git-changelog@2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)':
+  '@nolebase/vitepress-plugin-git-changelog@2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)':
     dependencies:
       '@iconify-json/octicon': 1.2.1
-      '@nolebase/ui': 2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
+      '@nolebase/ui': 2.8.1(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
       colorette: 2.0.20
       date-fns: 4.1.0
       defu: 6.1.4
@@ -12499,7 +12510,7 @@ snapshots:
       gray-matter: 4.0.3
       less: 4.2.0
       uncrypto: 0.1.3
-      vitepress: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
+      vitepress: 1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3)
     transitivePeerDependencies:
       - '@algolia/client-search'
       - '@types/node'
@@ -13354,9 +13365,9 @@ snapshots:
       - encoding
       - supports-color
 
-  '@vite-pwa/vitepress@0.5.3(vite-plugin-pwa@0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0))':
+  '@vite-pwa/vitepress@0.5.3(vite-plugin-pwa@0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0))':
     dependencies:
-      vite-plugin-pwa: 0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0)
+      vite-plugin-pwa: 0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0)
 
   '@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))':
     dependencies:
@@ -13368,11 +13379,26 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  '@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))':
+    dependencies:
+      '@babel/core': 7.26.0
+      '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.0)
+      '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.0)
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
+      vue: 3.5.12(typescript@5.6.3)
+    transitivePeerDependencies:
+      - supports-color
+
   '@vitejs/plugin-vue@5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))':
     dependencies:
       vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
       vue: 3.5.12(typescript@5.6.3)
 
+  '@vitejs/plugin-vue@5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))':
+    dependencies:
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
+      vue: 3.5.12(typescript@5.6.3)
+
   '@vitest/expect@2.1.4':
     dependencies:
       '@vitest/spy': 2.1.4
@@ -13380,13 +13406,13 @@ snapshots:
       chai: 5.1.2
       tinyrainbow: 1.2.0
 
-  '@vitest/mocker@2.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))':
     dependencies:
       '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
       magic-string: 0.30.12
     optionalDependencies:
-      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
 
   '@vitest/pretty-format@2.1.4':
     dependencies:
@@ -15291,13 +15317,13 @@ snapshots:
     optionalDependencies:
       '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)
 
-  eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)):
+  eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)):
     dependencies:
       '@typescript-eslint/utils': 7.18.0(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)
       eslint: 9.14.0(jiti@2.4.0)
     optionalDependencies:
       '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.14.0(jiti@2.4.0))(typescript@5.6.3)
-      vitest: 2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -16777,7 +16803,7 @@ snapshots:
 
   mkdirp@1.0.4: {}
 
-  mkdist@1.6.0(sass@1.79.5)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)):
+  mkdist@1.6.0(sass@1.80.6)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)):
     dependencies:
       autoprefixer: 10.4.20(postcss@8.4.47)
       citty: 0.1.6
@@ -16793,7 +16819,7 @@ snapshots:
       semver: 7.6.3
       tinyglobby: 0.2.10
     optionalDependencies:
-      sass: 1.79.5
+      sass: 1.80.6
       typescript: 5.6.3
       vue-tsc: 2.1.10(typescript@5.6.3)
 
@@ -18199,6 +18225,14 @@ snapshots:
       immutable: 4.3.7
       source-map-js: 1.2.1
 
+  sass@1.80.6:
+    dependencies:
+      chokidar: 4.0.1
+      immutable: 4.3.7
+      source-map-js: 1.2.1
+    optionalDependencies:
+      '@parcel/watcher': 2.4.1
+
   sax@1.4.1:
     optional: true
 
@@ -18971,7 +19005,7 @@ snapshots:
       has-symbols: 1.0.3
       which-boxed-primitive: 1.0.2
 
-  unbuild@3.0.0-rc.11(sass@1.79.5)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)):
+  unbuild@3.0.0-rc.11(sass@1.80.6)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3)):
     dependencies:
       '@rollup/plugin-alias': 5.1.1(rollup@4.24.3)
       '@rollup/plugin-commonjs': 28.0.1(rollup@4.24.3)
@@ -18986,7 +19020,7 @@ snapshots:
       hookable: 5.5.3
       jiti: 2.4.0
       magic-string: 0.30.12
-      mkdist: 1.6.0(sass@1.79.5)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))
+      mkdist: 1.6.0(sass@1.80.6)(typescript@5.6.3)(vue-tsc@2.1.10(typescript@5.6.3))
       mlly: 1.7.2
       pathe: 1.1.2
       pkg-types: 1.2.1
@@ -19241,12 +19275,12 @@ snapshots:
     dependencies:
       vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
 
-  vite-node@2.1.4(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7(supports-color@9.4.0)
       pathe: 1.1.2
-      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19336,6 +19370,17 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  vite-plugin-pwa@0.20.5(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(workbox-build@7.3.0)(workbox-window@7.3.0):
+    dependencies:
+      debug: 4.3.7(supports-color@9.4.0)
+      pretty-bytes: 6.1.1
+      tinyglobby: 0.2.10
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
+      workbox-build: 7.3.0
+      workbox-window: 7.3.0
+    transitivePeerDependencies:
+      - supports-color
+
   vite-plugin-vue-devtools@7.6.2(rollup@4.24.4)(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3)):
     dependencies:
       '@vue/devtools-core': 7.6.2(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
@@ -19379,6 +19424,18 @@ snapshots:
       sass: 1.79.5
       terser: 5.36.0
 
+  vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0):
+    dependencies:
+      esbuild: 0.21.5
+      postcss: 8.4.47
+      rollup: 4.24.3
+    optionalDependencies:
+      '@types/node': 22.8.7
+      fsevents: 2.3.3
+      less: 4.2.0
+      sass: 1.80.6
+      terser: 5.36.0
+
   vitepress-plugin-group-icons@1.3.0:
     dependencies:
       '@iconify-json/logos': 1.2.3
@@ -19387,7 +19444,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  vitepress@1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.79.5)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3):
+  vitepress@1.4.5(@algolia/client-search@5.12.0)(@types/node@22.8.7)(async-validator@4.2.5)(axios@1.7.7)(change-case@5.4.4)(less@4.2.0)(nprogress@0.2.0)(postcss@8.4.47)(qrcode@1.5.4)(sass@1.80.6)(search-insights@2.17.2)(sortablejs@1.15.3)(terser@5.36.0)(typescript@5.6.3):
     dependencies:
       '@docsearch/css': 3.6.3
       '@docsearch/js': 3.6.3(@algolia/client-search@5.12.0)(search-insights@2.17.2)
@@ -19395,7 +19452,7 @@ snapshots:
       '@shikijs/transformers': 1.22.2
       '@shikijs/types': 1.22.2
       '@types/markdown-it': 14.1.2
-      '@vitejs/plugin-vue': 5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
+      '@vitejs/plugin-vue': 5.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))(vue@3.5.12(typescript@5.6.3))
       '@vue/devtools-api': 7.6.2
       '@vue/shared': 3.5.12
       '@vueuse/core': 11.2.0(vue@3.5.12(typescript@5.6.3))
@@ -19404,7 +19461,7 @@ snapshots:
       mark.js: 8.11.1
       minisearch: 7.1.0
       shiki: 1.22.2
-      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
       vue: 3.5.12(typescript@5.6.3)
     optionalDependencies:
       postcss: 8.4.47
@@ -19436,10 +19493,10 @@ snapshots:
       - typescript
       - universal-cookie
 
-  vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.79.5)(terser@5.36.0):
+  vitest@2.1.4(@types/node@22.8.7)(happy-dom@15.8.3)(less@4.2.0)(sass@1.80.6)(terser@5.36.0):
     dependencies:
       '@vitest/expect': 2.1.4
-      '@vitest/mocker': 2.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(vite@5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0))
       '@vitest/pretty-format': 2.1.4
       '@vitest/runner': 2.1.4
       '@vitest/snapshot': 2.1.4
@@ -19455,8 +19512,8 @@ snapshots:
       tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
-      vite-node: 2.1.4(@types/node@22.8.7)(less@4.2.0)(sass@1.79.5)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@22.8.7)(less@4.2.0)(sass@1.80.6)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/node': 22.8.7