From c76db7d8d1b94cd9de430f82a22cfa35c98d62ad Mon Sep 17 00:00:00 2001 From: luoqiz Date: Mon, 1 Dec 2025 09:51:27 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dicon=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E6=A0=B9=E5=B1=9E=E6=80=A7=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E9=94=99=E8=AF=AF=20(#6986)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/icons/src/svg/load.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/icons/src/svg/load.ts b/packages/icons/src/svg/load.ts index 1557467f..59098bdb 100644 --- a/packages/icons/src/svg/load.ts +++ b/packages/icons/src/svg/load.ts @@ -13,10 +13,28 @@ function parseSvg(svgData: string): IconifyIconStructure { const xmlDoc = parser.parseFromString(svgData, 'image/svg+xml'); const svgElement = xmlDoc.documentElement; + // 提取 SVG 根元素的关键样式属性 + const getAttrs = (el: Element, attrs: string[]) => + attrs + .map((attr) => + el.hasAttribute(attr) ? `${attr}="${el.getAttribute(attr)}"` : '', + ) + .filter(Boolean) + .join(' '); + + const rootAttrs = getAttrs(svgElement, [ + 'fill', + 'stroke', + 'fill-rule', + 'stroke-width', + ]); + const svgContent = [...svgElement.childNodes] .filter((node) => node.nodeType === Node.ELEMENT_NODE) .map((node) => new XMLSerializer().serializeToString(node)) .join(''); + // 若根有属性,用一个 g 标签包裹内容并继承属性 + const body = rootAttrs ? `${svgContent}` : svgContent; const viewBoxValue = svgElement.getAttribute('viewBox') || ''; const [left, top, width, height] = viewBoxValue.split(' ').map((val) => { @@ -25,7 +43,7 @@ function parseSvg(svgData: string): IconifyIconStructure { }); return { - body: svgContent, + body, height, left, top, From 9105d4d14a14ff5d50b1b1bb360e53fac5e07192 Mon Sep 17 00:00:00 2001 From: JyQAQ <45193678+jyqwq@users.noreply.github.com> Date: Wed, 3 Dec 2025 10:03:23 +0800 Subject: [PATCH 02/10] =?UTF-8?q?feat(api-component):=20api-component?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84options=E6=94=AF=E6=8C=81=E6=8C=87?= =?UTF-8?q?=E5=AE=9Adisabled=E5=80=BC=20(#6991)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/api-component/api-component.vue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/effects/common-ui/src/components/api-component/api-component.vue b/packages/effects/common-ui/src/components/api-component/api-component.vue index 70e86e0e..38419a7d 100644 --- a/packages/effects/common-ui/src/components/api-component/api-component.vue +++ b/packages/effects/common-ui/src/components/api-component/api-component.vue @@ -36,6 +36,8 @@ interface Props { childrenField?: string; /** value字段名 */ valueField?: string; + /** disabled字段名 */ + disabledField?: string; /** 组件接收options数据的属性名 */ optionsPropName?: string; /** 是否立即调用api */ @@ -75,6 +77,7 @@ defineOptions({ name: 'ApiComponent', inheritAttrs: false }); const props = withDefaults(defineProps(), { labelField: 'label', valueField: 'value', + disabledField: 'disabled', childrenField: '', optionsPropName: 'options', resultField: '', @@ -108,17 +111,25 @@ const isFirstLoaded = ref(false); const hasPendingRequest = ref(false); const getOptions = computed(() => { - const { labelField, valueField, childrenField, numberToString } = props; + const { + labelField, + valueField, + disabledField, + childrenField, + numberToString, + } = props; const refOptionsData = unref(refOptions); function transformData(data: OptionsItem[]): OptionsItem[] { return data.map((item) => { const value = get(item, valueField); + const disabled = get(item, disabledField); return { - ...objectOmit(item, [labelField, valueField, childrenField]), + ...objectOmit(item, [labelField, valueField, disabled, childrenField]), label: get(item, labelField), value: numberToString ? `${value}` : value, + disabled: get(item, disabledField), ...(childrenField && item[childrenField] ? { children: transformData(item[childrenField]) } : {}), From 1479f159aaea6bc5df81f39091cd335c3026bcdb Mon Sep 17 00:00:00 2001 From: JyQAQ <45193678+jyqwq@users.noreply.github.com> Date: Sat, 6 Dec 2025 10:12:58 +0800 Subject: [PATCH 03/10] =?UTF-8?q?feat(CellImage):=20CellImage=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=94=AF=E6=8C=81=E5=9B=BE=E7=89=87=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=86=99=E5=85=A5=20(#6992)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/adapter/vxe-table.ts | 5 +++-- apps/web-ele/src/adapter/vxe-table.ts | 5 +++-- apps/web-naive/src/adapter/vxe-table.ts | 5 +++-- apps/web-tdesign/src/adapter/vxe-table.ts | 5 +++-- docs/src/_env/adapter/vxe-table.ts | 5 +++-- playground/src/adapter/vxe-table.ts | 5 +++-- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 7de2859d..b0a1f009 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 40b8179d..81d1cdd2 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 3bad067c..1c360000 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 43bd68af..e406242f 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 bab7f3d3..7bc27056 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/playground/src/adapter/vxe-table.ts b/playground/src/adapter/vxe-table.ts index 24dfd4cd..e53090a2 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 }); }, }); From ccf70a1b762e1d77916c3d580fda8295b7fe98a4 Mon Sep 17 00:00:00 2001 From: xueyitt <1455668754@qq.com> Date: Mon, 22 Dec 2025 19:57:21 +0800 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E6=8E=92=E5=BA=8F=E5=9C=A8=E4=BA=8C=E7=BA=A7=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E4=B8=8D=E7=94=9F=E6=95=88=E9=97=AE=E9=A2=98=20(#7007?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * treeUtil增加对树形结构数据进行递归排序 * 菜单sort排序各级菜单均生效 --- packages/@core/base/shared/src/utils/tree.ts | 30 +++++++++++++++++++- packages/utils/src/helpers/generate-menus.ts | 4 +-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/@core/base/shared/src/utils/tree.ts b/packages/@core/base/shared/src/utils/tree.ts index 09a9481c..f3056dcf 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 af36bd70..f13d599a 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); From 022d538940b80c835c2c023e7aad84e41d7292b3 Mon Sep 17 00:00:00 2001 From: zhenghaoyang24 <95458562+zhenghaoyang24@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:58:05 +0800 Subject: [PATCH 05/10] Fix formatting in thin.md for clarity (#7008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改一些语句错误 --- docs/src/guide/introduction/thin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/guide/introduction/thin.md b/docs/src/guide/introduction/thin.md index 157d51ff..8a37c257 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` 文件夹中删除对应的页面文件。 ### 删除不需要的组件 From a1bb13223380540394afdf2bb62ca5a3e966cff1 Mon Sep 17 00:00:00 2001 From: JyQAQ <45193678+jyqwq@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:00:31 +0800 Subject: [PATCH 06/10] =?UTF-8?q?feat(api-cascader):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=81=94=E7=BA=A7=E7=BB=84=E4=BB=B6ApiCascader=20(#7031)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/adapter/component/index.ts | 13 +++++++++++++ playground/src/adapter/component/index.ts | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 8455ef3a..1d288009 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/playground/src/adapter/component/index.ts b/playground/src/adapter/component/index.ts index 2e50f6da..261a7d3b 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, From 89b237f6b42d5b98ca0dcb3b9bfb5583122575c0 Mon Sep 17 00:00:00 2001 From: luoqiz Date: Fri, 2 Jan 2026 14:22:19 +0800 Subject: [PATCH 07/10] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E8=8F=9C=E5=8D=95=E6=BC=94=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=8F=9C=E5=8D=95=E9=A1=B9=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E6=80=A7=20(#7057)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: luoqiz <851092732@qq.com> --- .../components/context-menu/context-menu.vue | 1 + .../src/components/context-menu/interface.ts | 4 + .../effects/common-ui/src/components/index.ts | 1 + playground/package.json | 1 + .../src/locales/langs/en-US/examples.json | 3 + .../src/locales/langs/zh-CN/examples.json | 3 + .../src/router/routes/modules/examples.ts | 9 + .../src/views/examples/context-menu/index.vue | 60 + pnpm-lock.yaml | 4163 +++++++++-------- 9 files changed, 2234 insertions(+), 2011 deletions(-) create mode 100644 playground/src/views/examples/context-menu/index.vue diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue b/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue index 4b7a64cb..009f267e 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue @@ -73,6 +73,7 @@ function handleClick(menu: IContextMenuItem) { >