diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 8455ef3aa..1d288009e 100644 --- a/apps/web-antd/src/adapter/component/index.ts +++ b/apps/web-antd/src/adapter/component/index.ts @@ -75,6 +75,9 @@ const TimePicker = defineAsyncComponent( const TreeSelect = defineAsyncComponent( () => import('ant-design-vue/es/tree-select'), ); +const Cascader = defineAsyncComponent( + () => import('ant-design-vue/es/cascader'), +); const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload')); const Image = defineAsyncComponent(() => import('ant-design-vue/es/image')); const PreviewGroup = defineAsyncComponent(() => @@ -327,9 +330,11 @@ const previewImage = async ( // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 export type ComponentType = + | 'ApiCascader' | 'ApiSelect' | 'ApiTreeSelect' | 'AutoComplete' + | 'Cascader' | 'Checkbox' | 'CheckboxGroup' | 'DatePicker' @@ -359,6 +364,13 @@ async function initComponentAdapter() { // 如果你的组件体积比较大,可以使用异步加载 // Button: () => // import('xxx').then((res) => res.Button), + ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', { + component: Cascader, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onVisibleChange', + }), ApiSelect: withDefaultPlaceholder( { ...ApiComponent, @@ -388,6 +400,7 @@ async function initComponentAdapter() { }, ), AutoComplete, + Cascader, Checkbox, CheckboxGroup, DatePicker, diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 7de2859de..b0a1f0093 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -40,9 +40,10 @@ setupVbenVxeTable({ // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; - return h(Image, { src: row[column.field] }); + return h(Image, { src: row[column.field], ...props }); }, }); diff --git a/apps/web-ele/src/adapter/vxe-table.ts b/apps/web-ele/src/adapter/vxe-table.ts index 40b8179d3..81d1cdd24 100644 --- a/apps/web-ele/src/adapter/vxe-table.ts +++ b/apps/web-ele/src/adapter/vxe-table.ts @@ -40,10 +40,11 @@ setupVbenVxeTable({ // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; const src = row[column.field]; - return h(ElImage, { src, previewSrcList: [src] }); + return h(ElImage, { src, previewSrcList: [src], ...props }); }, }); diff --git a/apps/web-naive/src/adapter/vxe-table.ts b/apps/web-naive/src/adapter/vxe-table.ts index 3bad067cd..1c3600003 100644 --- a/apps/web-naive/src/adapter/vxe-table.ts +++ b/apps/web-naive/src/adapter/vxe-table.ts @@ -40,9 +40,10 @@ setupVbenVxeTable({ // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; - return h(NImage, { src: row[column.field] }); + return h(NImage, { src: row[column.field], ...props }); }, }); diff --git a/apps/web-tdesign/src/adapter/vxe-table.ts b/apps/web-tdesign/src/adapter/vxe-table.ts index 43bd68afd..e406242f5 100644 --- a/apps/web-tdesign/src/adapter/vxe-table.ts +++ b/apps/web-tdesign/src/adapter/vxe-table.ts @@ -40,9 +40,10 @@ setupVbenVxeTable({ // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; - return h(Image, { src: row[column.field] }); + return h(Image, { src: row[column.field], ...props }); }, }); diff --git a/docs/src/_env/adapter/vxe-table.ts b/docs/src/_env/adapter/vxe-table.ts index bab7f3d38..7bc27056e 100644 --- a/docs/src/_env/adapter/vxe-table.ts +++ b/docs/src/_env/adapter/vxe-table.ts @@ -40,9 +40,10 @@ if (!import.meta.env.SSR) { // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; - return h(Image, { src: row[column.field] }); + return h(Image, { src: row[column.field], ...props }); }, }); diff --git a/docs/src/guide/introduction/thin.md b/docs/src/guide/introduction/thin.md index 157d51ffc..8a37c2578 100644 --- a/docs/src/guide/introduction/thin.md +++ b/docs/src/guide/introduction/thin.md @@ -24,7 +24,7 @@ apps/web-naive ## 演示代码精简 -如果你不需要演示代码,你可以直接删除的`playground`文件夹。 +如果你不需要演示代码,你可以直接删除 `playground` 文件夹。 ## 文档精简 @@ -88,7 +88,7 @@ pnpm install - 在应用的 `src/router/routes` 文件中,你可以删除不需要的路由。其中 `core` 文件夹内,如果只需要登录和忘记密码,你可以删除其他路由,如忘记密码、注册等。路由删除后,你可以删除对应的页面文件,在 `src/views/_core` 文件夹中。 -- 在应用的 `src/router/routes` 文件中,你可以按需求删除不需要的路由,如`demos`、`vben` 目录等。路由删除后,你可以删除对应的页面文件,在 `src/views` 文件夹中。 +- 在应用的 `src/router/routes` 文件中,你可以按需求删除不需要的路由,如`demos`、`vben` 目录等。路由删除后,你可以在 `src/views` 文件夹中删除对应的页面文件。 ### 删除不需要的组件 diff --git a/packages/@core/base/shared/src/utils/tree.ts b/packages/@core/base/shared/src/utils/tree.ts index 09a9481cb..f3056dcf5 100644 --- a/packages/@core/base/shared/src/utils/tree.ts +++ b/packages/@core/base/shared/src/utils/tree.ts @@ -94,4 +94,32 @@ function mapTree>( }); } -export { filterTree, mapTree, traverseTreeValues }; +/** + * 对树形结构数据进行递归排序 + * @param treeData - 树形数据数组 + * @param sortFunction - 排序函数,用于定义排序规则 + * @param options - 配置选项,包括子节点属性名 + * @returns 排序后的树形数据 + */ +function sortTree>( + treeData: T[], + sortFunction: (a: T, b: T) => number, + options?: TreeConfigOptions, +): T[] { + const { childProps } = options || { + childProps: 'children', + }; + + return treeData.toSorted(sortFunction).map((item) => { + const children = item[childProps]; + if (children && Array.isArray(children) && children.length > 0) { + return { + ...item, + [childProps]: sortTree(children, sortFunction, options), + }; + } + return item; + }); +} + +export { filterTree, mapTree, sortTree, traverseTreeValues }; diff --git a/packages/utils/src/helpers/generate-menus.ts b/packages/utils/src/helpers/generate-menus.ts index af36bd70c..f13d599ac 100644 --- a/packages/utils/src/helpers/generate-menus.ts +++ b/packages/utils/src/helpers/generate-menus.ts @@ -6,7 +6,7 @@ import type { RouteMeta, } from '@vben-core/typings'; -import { filterTree, mapTree } from '@vben-core/shared/utils'; +import { filterTree, mapTree, sortTree } from '@vben-core/shared/utils'; /** * 根据 routes 生成菜单列表 @@ -81,7 +81,7 @@ function generateMenus( }); // 对菜单进行排序,避免order=0时被替换成999的问题 - menus = menus.toSorted((a, b) => (a?.order ?? 999) - (b?.order ?? 999)); + menus = sortTree(menus, (a, b) => (a?.order ?? 999) - (b?.order ?? 999)); // 过滤掉隐藏的菜单项 return filterTree(menus, (menu) => !!menu.show); diff --git a/playground/src/adapter/component/index.ts b/playground/src/adapter/component/index.ts index 2e50f6dae..261a7d3be 100644 --- a/playground/src/adapter/component/index.ts +++ b/playground/src/adapter/component/index.ts @@ -75,6 +75,9 @@ const TimePicker = defineAsyncComponent( const TreeSelect = defineAsyncComponent( () => import('ant-design-vue/es/tree-select'), ); +const Cascader = defineAsyncComponent( + () => import('ant-design-vue/es/cascader'), +); const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload')); const Image = defineAsyncComponent(() => import('ant-design-vue/es/image')); const PreviewGroup = defineAsyncComponent(() => @@ -336,9 +339,11 @@ const previewImage = async ( // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 export type ComponentType = + | 'ApiCascader' | 'ApiSelect' | 'ApiTreeSelect' | 'AutoComplete' + | 'Cascader' | 'Checkbox' | 'CheckboxGroup' | 'DatePicker' @@ -369,6 +374,13 @@ async function initComponentAdapter() { // Button: () => // import('xxx').then((res) => res.Button), + ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', { + component: Cascader, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + visibleEvent: 'onVisibleChange', + }), ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', { component: Select, loadingSlot: 'suffixIcon', @@ -384,6 +396,7 @@ async function initComponentAdapter() { visibleEvent: 'onVisibleChange', }), AutoComplete, + Cascader, Checkbox, CheckboxGroup, DatePicker, diff --git a/playground/src/adapter/vxe-table.ts b/playground/src/adapter/vxe-table.ts index 24dfd4cdc..e53090a2f 100644 --- a/playground/src/adapter/vxe-table.ts +++ b/playground/src/adapter/vxe-table.ts @@ -62,9 +62,10 @@ setupVbenVxeTable({ // 表格配置项可以用 cellRender: { name: 'CellImage' }, vxeUI.renderer.add('CellImage', { - renderTableDefault(_renderOpts, params) { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; const { column, row } = params; - return h(Image, { src: row[column.field] }); + return h(Image, { src: row[column.field], ...props }); }, });