From ea79b7d6a16a37f02be275311ff16ad55d92536d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 11:33:11 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90antd=E3=80=91cropper-?= =?UTF-8?q?modal=20=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99=E7=9A=84=20Image?= =?UTF-8?q?=20=E9=A2=84=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web-antd/src/components/cropper/cropper-modal.vue | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/web-antd/src/components/cropper/cropper-modal.vue b/apps/web-antd/src/components/cropper/cropper-modal.vue index 43e9766dc..d4edd11c9 100644 --- a/apps/web-antd/src/components/cropper/cropper-modal.vue +++ b/apps/web-antd/src/components/cropper/cropper-modal.vue @@ -43,15 +43,6 @@ const [Modal, modalApi] = useVbenModal({ if (isOpen) { // 打开时,进行 loading 加载。后续 CropperImage 组件加载完毕,会自动关闭 loading(通过 handleReady) modalLoading(true); - // TODO @puhui999:这里比 ele 多了,是符合预期的哇? - const img = new Image(); - img.src = src.value; - img.addEventListener('load', () => { - modalLoading(false); - }); - img.addEventListener('error', () => { - modalLoading(false); - }); } else { // 关闭时,清空右侧预览 previewSource.value = ''; From bbc74ae663406804b542368d0c5441e638ef3c8d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 11:55:13 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90antd/ele=E3=80=91disc?= =?UTF-8?q?ountActivity=20=E7=A7=BB=E9=99=A4=20structuredClone=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20cloneDeep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discountActivity/modules/form.vue | 14 +- apps/web-ele/src/utils/tree.ts | 442 ------------------ .../discountActivity/modules/form.vue | 12 +- 3 files changed, 8 insertions(+), 460 deletions(-) delete mode 100644 apps/web-ele/src/utils/tree.ts diff --git a/apps/web-antd/src/views/mall/promotion/discountActivity/modules/form.vue b/apps/web-antd/src/views/mall/promotion/discountActivity/modules/form.vue index f1a9a5690..a0eff3e1d 100644 --- a/apps/web-antd/src/views/mall/promotion/discountActivity/modules/form.vue +++ b/apps/web-antd/src/views/mall/promotion/discountActivity/modules/form.vue @@ -10,7 +10,9 @@ import type { import { computed, nextTick, ref } from 'vue'; import { useVbenForm, useVbenModal } from '@vben/common-ui'; +import { PromotionDiscountTypeEnum } from '@vben/constants'; import { + cloneDeep, convertToInteger, erpCalculatePercentage, formatToFraction, @@ -39,13 +41,6 @@ defineOptions({ name: 'DiscountActivityForm' }); const emit = defineEmits(['success']); -/** 折扣类型枚举 */ -// TODO @puhui999:这里可以使用 biz-mall 里的枚举噢; -const PromotionDiscountTypeEnum = { - PRICE: { type: 1 }, // 满减 - PERCENT: { type: 2 }, // 折扣 -}; - // ================= 表单相关 ================= const formData = ref>({}); const getTitle = computed(() => { @@ -243,8 +238,7 @@ const [Modal, modalApi] = useVbenModal({ // 提交表单 try { // 获取折扣商品配置 - // TODO @puhui999:structuredClone 执行会报错; - const products = structuredClone( + const products = cloneDeep( spuAndSkuListRef.value?.getSkuConfigs('productConfig') || [], ) as MallDiscountActivityApi.DiscountProduct[]; // 转换金额为分 @@ -252,7 +246,7 @@ const [Modal, modalApi] = useVbenModal({ item.discountPercent = convertToInteger(item.discountPercent); item.discountPrice = convertToInteger(item.discountPrice); }); - const data = structuredClone( + const data = cloneDeep( await formApi.getValues(), ) as MallDiscountActivityApi.DiscountActivity; data.products = products; diff --git a/apps/web-ele/src/utils/tree.ts b/apps/web-ele/src/utils/tree.ts deleted file mode 100644 index 172ed2ba1..000000000 --- a/apps/web-ele/src/utils/tree.ts +++ /dev/null @@ -1,442 +0,0 @@ -interface TreeHelperConfig { - id: string; - children: string; - pid: string; -} - -const DEFAULT_CONFIG: TreeHelperConfig = { - id: 'id', - children: 'children', - pid: 'pid', -}; -export const defaultProps = { - children: 'children', - label: 'name', - value: 'id', - isLeaf: 'leaf', - emitPath: false, // 用于 cascader 组件:在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 -}; - -const getConfig = (config: Partial) => - Object.assign({}, DEFAULT_CONFIG, config); - -// tree from list -export const listToTree = ( - list: any[], - config: Partial = {}, -): T[] => { - const conf = getConfig(config) as TreeHelperConfig; - const nodeMap = new Map(); - const result: T[] = []; - const { id, children, pid } = conf; - - for (const node of list) { - node[children] = node[children] || []; - nodeMap.set(node[id], node); - } - for (const node of list) { - const parent = nodeMap.get(node[pid]); - (parent ? parent.children : result).push(node); - } - return result; -}; - -export const treeToList = ( - tree: any, - config: Partial = {}, -): T => { - config = getConfig(config); - const { children } = config; - const result: any = [...tree]; - for (let i = 0; i < result.length; i++) { - const childNodes = result[i][children]; - if (!childNodes) continue; - result.splice(i + 1, 0, ...childNodes); - } - return result; -}; - -export const findNode = ( - tree: any, - func: Fn, - config: Partial = {}, -): null | T => { - config = getConfig(config); - const { children } = config; - const list = [...tree]; - for (const node of list) { - if (func(node)) return node; - const childNodes = node[children]; - if (childNodes) { - list.push(...childNodes); - } - } - return null; -}; - -export const findNodeAll = ( - tree: any, - func: Fn, - config: Partial = {}, -): T[] => { - config = getConfig(config); - const { children } = config; - const list = [...tree]; - const result: T[] = []; - for (const node of list) { - func(node) && result.push(node); - const childNodes = node[children]; - if (childNodes) { - list.push(...childNodes); - } - } - return result; -}; - -export const findPath = ( - tree: any, - func: Fn, - config: Partial = {}, -): null | T | T[] => { - config = getConfig(config); - const path: T[] = []; - const list = [...tree]; - const visitedSet = new Set(); - const { children } = config; - while (list.length > 0) { - const node = list[0]; - if (visitedSet.has(node)) { - path.pop(); - list.shift(); - } else { - visitedSet.add(node); - const childNodes = node[children]; - if (childNodes) { - list.unshift(...childNodes); - } - path.push(node); - if (func(node)) { - return path; - } - } - } - return null; -}; - -export const findPathAll = ( - tree: any, - func: Fn, - config: Partial = {}, -) => { - config = getConfig(config); - const path: any[] = []; - const list = [...tree]; - const result: any[] = []; - const visitedSet = new Set(); - const { children } = config; - while (list.length > 0) { - const node = list[0]; - if (visitedSet.has(node)) { - path.pop(); - list.shift(); - } else { - visitedSet.add(node); - const childNodes = node[children]; - if (childNodes) { - list.unshift(...childNodes); - } - path.push(node); - func(node) && result.push([...path]); - } - } - return result; -}; - -export const filter = ( - tree: T[], - func: (n: T) => boolean, - config: Partial = {}, -): T[] => { - config = getConfig(config); - const children = config.children as string; - - function listFilter(list: T[]) { - return list - .map((node: any) => ({ ...node })) - .filter((node) => { - node[children] = node[children] && listFilter(node[children]); - return func(node) || node[children]?.length > 0; - }); - } - - return listFilter(tree); -}; - -export const forEach = ( - tree: T[], - func: (n: T) => any, - config: Partial = {}, -): void => { - config = getConfig(config); - const list: any[] = [...tree]; - const { children } = config; - for (let i = 0; i < list.length; i++) { - // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 - if (func(list[i])) { - return; - } - children && - list[i][children] && - list.splice(i + 1, 0, ...list[i][children]); - } -}; - -/** - * @description: Extract tree specified structure - */ -export const treeMap = ( - treeData: T[], - opt: { children?: string; conversion: Fn }, -): T[] => { - return treeData.map((item) => treeMapEach(item, opt)); -}; - -/** - * @description: Extract tree specified structure - */ -export const treeMapEach = ( - data: any, - { children = 'children', conversion }: { children?: string; conversion: Fn }, -) => { - const haveChildren = - Array.isArray(data[children]) && data[children].length > 0; - const conversionData = conversion(data) || {}; - return haveChildren - ? { - ...conversionData, - [children]: data[children].map((i: number) => - treeMapEach(i, { - children, - conversion, - }), - ), - } - : { - ...conversionData, - }; -}; - -/** - * 递归遍历树结构 - * @param treeDatas 树 - * @param callBack 回调 - * @param parentNode 父节点 - */ -export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { - treeDatas.forEach((element) => { - const newNode = callBack(element, parentNode) || element; - if (element.children) { - eachTree(element.children, callBack, newNode); - } - }); -}; - -/** - * 构造树型结构数据 - * @param {*} data 数据源 - * @param {*} id id字段 默认 'id' - * @param {*} parentId 父节点字段 默认 'parentId' - * @param {*} children 孩子节点字段 默认 'children' - */ -export const handleTree = ( - data: any[], - id?: string, - parentId?: string, - children?: string, -) => { - if (!Array.isArray(data)) { - console.warn('data must be an array'); - return []; - } - const config = { - id: id || 'id', - parentId: parentId || 'parentId', - childrenList: children || 'children', - }; - - const childrenListMap = {}; - const nodeIds = {}; - const tree: any[] = []; - - for (const d of data) { - const parentId = d[config.parentId]; - if ( - childrenListMap[parentId] === null || - childrenListMap[parentId] === undefined - ) { - childrenListMap[parentId] = []; - } - nodeIds[d[config.id]] = d; - childrenListMap[parentId].push(d); - } - - for (const d of data) { - const parentId = d[config.parentId]; - if (nodeIds[parentId] === null || nodeIds[parentId] === undefined) { - tree.push(d); - } - } - - for (const t of tree) { - adaptToChildrenList(t); - } - - function adaptToChildrenList(o) { - if (childrenListMap[o[config.id]] !== null) { - o[config.childrenList] = childrenListMap[o[config.id]]; - } - if (o[config.childrenList]) { - for (const c of o[config.childrenList]) { - adaptToChildrenList(c); - } - } - } - - return tree; -}; - -/** - * 构造树型结构数据 - * @param {*} data 数据源 - * @param {*} id id字段 默认 'id' - * @param {*} parentId 父节点字段 默认 'parentId' - * @param {*} children 孩子节点字段 默认 'children' - * @param {*} rootId 根Id 默认 0 - */ -// @ts-ignore: 遗留函数,保持原有逻辑不变 -export const handleTree2 = (data, id, parentId, children, rootId) => { - id = id || 'id'; - parentId = parentId || 'parentId'; - // children = children || 'children' - rootId = - rootId || - Math.min( - ...data.map((item) => { - return item[parentId]; - }), - ) || - 0; - // 对源数据深度克隆 - const cloneData = structuredClone(data); - // 循环所有项 - const treeData = cloneData.filter((father) => { - const branchArr = cloneData.filter((child) => { - // 返回每一项的子级数组 - return father[id] === child[parentId]; - }); - branchArr.length > 0 ? (father.children = branchArr) : ''; - // 返回第一层 - return father[parentId] === rootId; - }); - return treeData === '' ? data : treeData; -}; - -/** - * 校验选中的节点,是否为指定 level - * - * @param tree 要操作的树结构数据 - * @param nodeId 需要判断在什么层级的数据 - * @param level 检查的级别, 默认检查到二级 - * @return true 是;false 否 - */ -export const checkSelectedNode = ( - tree: any[], - nodeId: any, - level = 2, -): boolean => { - if (tree === undefined || !Array.isArray(tree) || tree.length === 0) { - console.warn('tree must be an array'); - return false; - } - - // 校验是否是一级节点 - if (tree.some((item) => item.id === nodeId)) { - return false; - } - - // 递归计数 - let count = 1; - - // 深层次校验 - function performAThoroughValidation(arr: any[]): boolean { - count += 1; - for (const item of arr) { - if (item.id === nodeId) { - return true; - } else if ( - item.children !== undefined && - item.children.length > 0 && - performAThoroughValidation(item.children) - ) { - return true; - } - } - return false; - } - - for (const item of tree) { - count = 1; - if ( - performAThoroughValidation(item.children) && // 找到后对比是否是期望的层级 - count >= level - ) { - return true; - } - } - - return false; -}; - -/** - * 获取节点的完整结构 - * @param tree 树数据 - * @param nodeId 节点 id - */ -export const treeToString = (tree: any[], nodeId) => { - if (tree === undefined || !Array.isArray(tree) || tree.length === 0) { - console.warn('tree must be an array'); - return ''; - } - // 校验是否是一级节点 - const node = tree.find((item) => item.id === nodeId); - if (node !== undefined) { - return node.name; - } - let str = ''; - - function performAThoroughValidation(arr) { - if (arr === undefined || !Array.isArray(arr) || arr.length === 0) { - return false; - } - for (const item of arr) { - if (item.id === nodeId) { - str += ` / ${item.name}`; - return true; - } else if (item.children !== undefined && item.children.length > 0) { - str += ` / ${item.name}`; - if (performAThoroughValidation(item.children)) { - return true; - } - } - } - return false; - } - - for (const item of tree) { - str = `${item.name}`; - if (performAThoroughValidation(item.children)) { - break; - } - } - return str; -}; diff --git a/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue index e51dfe8f7..69f09011f 100644 --- a/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue @@ -10,7 +10,9 @@ import type { import { computed, nextTick, ref } from 'vue'; import { useVbenForm, useVbenModal } from '@vben/common-ui'; +import { PromotionDiscountTypeEnum } from '@vben/constants'; import { + cloneDeep, convertToInteger, erpCalculatePercentage, formatToFraction, @@ -37,12 +39,6 @@ import { useFormSchema } from '../data'; defineOptions({ name: 'DiscountActivityForm' }); -/** 折扣类型枚举 */ -const PromotionDiscountTypeEnum = { - PRICE: { type: 1 }, // 满减 - PERCENT: { type: 2 }, // 折扣 -}; - const emit = defineEmits(['success']); // ================= 表单相关 ================= @@ -243,7 +239,7 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); try { // 获取折扣商品配置 - const products = structuredClone( + const products = cloneDeep( spuAndSkuListRef.value?.getSkuConfigs('productConfig') || [], ) as MallDiscountActivityApi.DiscountProduct[]; @@ -253,7 +249,7 @@ const [Modal, modalApi] = useVbenModal({ item.discountPrice = convertToInteger(item.discountPrice); }); - const data = structuredClone( + const data = cloneDeep( await formApi.getValues(), ) as MallDiscountActivityApi.DiscountActivity; data.products = products; From e2433fc531c557e96be50b546ce37544ab0af656 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 12:06:10 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90antd/ele=E3=80=91?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20productSpuIds=20=E5=92=8C=20productCategor?= =?UTF-8?q?yIds=20=E8=87=AA=E5=AE=9A=E4=B9=89=E6=8F=92=E6=A7=BD=E7=9A=84?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E5=9C=A8=E9=AA=8C=E8=AF=81=E5=89=8D=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=20formData=20=E4=B8=AD=E7=9A=84=E5=80=BC=E5=88=B0?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/coupon/template/modules/form.vue | 13 +++++++++++++ .../promotion/rewardActivity/modules/form.vue | 16 ++++++++++++++-- .../promotion/coupon/template/modules/form.vue | 13 +++++++++++++ .../promotion/rewardActivity/modules/form.vue | 15 ++++++++++++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue b/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue index b015eb7f4..23bc87900 100644 --- a/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue +++ b/apps/web-antd/src/views/mall/promotion/coupon/template/modules/form.vue @@ -49,6 +49,19 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 同步商品/分类选择到表单,确保验证时能获取到值 + if (formData.value.productSpuIds) { + await formApi.setFieldValue( + 'productSpuIds', + formData.value.productSpuIds, + ); + } + if (formData.value.productCategoryIds) { + await formApi.setFieldValue( + 'productCategoryIds', + formData.value.productCategoryIds, + ); + } const { valid } = await formApi.validate(); if (!valid) { return; diff --git a/apps/web-antd/src/views/mall/promotion/rewardActivity/modules/form.vue b/apps/web-antd/src/views/mall/promotion/rewardActivity/modules/form.vue index 282d7ceea..4a35c0316 100644 --- a/apps/web-antd/src/views/mall/promotion/rewardActivity/modules/form.vue +++ b/apps/web-antd/src/views/mall/promotion/rewardActivity/modules/form.vue @@ -52,9 +52,21 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { - // 在验证前同步 formData.rules 到表单中 - // TODO @puhui999:选择了分类、或者商品,还是报没选择; + // 在验证前同步 formData 中的值到表单中 await formApi.setFieldValue('rules', formData.value.rules || []); + // 同步商品/分类选择到表单,确保验证时能获取到值 + if (formData.value.productSpuIds) { + await formApi.setFieldValue( + 'productSpuIds', + formData.value.productSpuIds, + ); + } + if (formData.value.productCategoryIds) { + await formApi.setFieldValue( + 'productCategoryIds', + formData.value.productCategoryIds, + ); + } const { valid } = await formApi.validate(); if (!valid) { return; diff --git a/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue b/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue index 13e4c7535..a7f4996da 100644 --- a/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue @@ -49,6 +49,19 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 同步商品/分类选择到表单,确保验证时能获取到值 + if (formData.value.productSpuIds) { + await formApi.setFieldValue( + 'productSpuIds', + formData.value.productSpuIds, + ); + } + if (formData.value.productCategoryIds) { + await formApi.setFieldValue( + 'productCategoryIds', + formData.value.productCategoryIds, + ); + } const { valid } = await formApi.validate(); if (!valid) { return; diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue index 4ade74808..d7074f2bb 100644 --- a/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue @@ -52,8 +52,21 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { - // 在验证前同步 formData.rules 到表单中 + // 在验证前同步 formData 中的值到表单中 await formApi.setFieldValue('rules', formData.value.rules || []); + // 同步商品/分类选择到表单,确保验证时能获取到值 + if (formData.value.productSpuIds) { + await formApi.setFieldValue( + 'productSpuIds', + formData.value.productSpuIds, + ); + } + if (formData.value.productCategoryIds) { + await formApi.setFieldValue( + 'productCategoryIds', + formData.value.productCategoryIds, + ); + } const { valid } = await formApi.validate(); if (!valid) { return; From 2426f891e79ac5cfd8bdeb2e6bc82545e74129f9 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 12:13:22 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90antd/ele=E3=80=91?= =?UTF-8?q?=E5=B0=86=E6=96=87=E7=AB=A0=E7=9A=84=E5=95=86=E5=93=81=E5=85=B3?= =?UTF-8?q?=E8=81=94=E5=AD=97=E6=AE=B5=E4=BB=8E=E6=89=8B=E5=8A=A8=E8=BE=93?= =?UTF-8?q?=E5=85=A5=20SPU=20=E7=BC=96=E5=8F=B7=E6=94=B9=E4=B8=BA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20SpuShowcase=20=E7=BB=84=E4=BB=B6=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=95=86=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/mall/promotion/article/data.ts | 4 ---- .../views/mall/promotion/article/modules/form.vue | 12 +++++++++++- .../web-ele/src/views/mall/promotion/article/data.ts | 4 ---- .../views/mall/promotion/article/modules/form.vue | 12 +++++++++++- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/apps/web-antd/src/views/mall/promotion/article/data.ts b/apps/web-antd/src/views/mall/promotion/article/data.ts index 9f10b9377..5af659775 100644 --- a/apps/web-antd/src/views/mall/promotion/article/data.ts +++ b/apps/web-antd/src/views/mall/promotion/article/data.ts @@ -96,14 +96,10 @@ export function useFormSchema(): VbenFormSchema[] { defaultValue: true, }, { - // TODO: @puhui999:商品关联 fieldName: 'spuId', label: '商品关联', component: 'Input', formItemClass: 'col-span-2', - componentProps: { - placeholder: '请输入商品 SPU 编号', - }, }, { fieldName: 'sort', diff --git a/apps/web-antd/src/views/mall/promotion/article/modules/form.vue b/apps/web-antd/src/views/mall/promotion/article/modules/form.vue index b8a8487b8..0e73e0eca 100644 --- a/apps/web-antd/src/views/mall/promotion/article/modules/form.vue +++ b/apps/web-antd/src/views/mall/promotion/article/modules/form.vue @@ -14,6 +14,7 @@ import { updateArticle, } from '#/api/mall/promotion/article'; import { $t } from '#/locales'; +import { SpuShowcase } from '#/views/mall/product/spu/components'; import { useFormSchema } from '../data'; @@ -41,6 +42,10 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 同步商品选择到表单,确保验证时能获取到值 + if (formData.value?.spuId) { + await formApi.setFieldValue('spuId', formData.value.spuId); + } const { valid } = await formApi.validate(); if (!valid) { return; @@ -82,6 +87,11 @@ const [Modal, modalApi] = useVbenModal({ diff --git a/apps/web-ele/src/views/mall/promotion/article/data.ts b/apps/web-ele/src/views/mall/promotion/article/data.ts index 487fa0e0f..b4e4bb614 100644 --- a/apps/web-ele/src/views/mall/promotion/article/data.ts +++ b/apps/web-ele/src/views/mall/promotion/article/data.ts @@ -92,14 +92,10 @@ export function useFormSchema(): VbenFormSchema[] { defaultValue: true, }, { - // TODO: @puhui999:商品关联 fieldName: 'spuId', label: '商品关联', component: 'Input', formItemClass: 'col-span-2', - componentProps: { - placeholder: '请输入商品 SPU 编号', - }, }, { fieldName: 'sort', diff --git a/apps/web-ele/src/views/mall/promotion/article/modules/form.vue b/apps/web-ele/src/views/mall/promotion/article/modules/form.vue index fd3aad79d..754ba967a 100644 --- a/apps/web-ele/src/views/mall/promotion/article/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/article/modules/form.vue @@ -14,6 +14,7 @@ import { updateArticle, } from '#/api/mall/promotion/article'; import { $t } from '#/locales'; +import { SpuShowcase } from '#/views/mall/product/spu/components'; import { useFormSchema } from '../data'; @@ -41,6 +42,10 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 同步商品选择到表单,确保验证时能获取到值 + if (formData.value?.spuId) { + await formApi.setFieldValue('spuId', formData.value.spuId); + } const { valid } = await formApi.validate(); if (!valid) { return; @@ -82,6 +87,11 @@ const [Modal, modalApi] = useVbenModal({ From 0302b70c48f2bcfed30ebce6faefa0f7d78a5707 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 12:25:27 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90antd/ele=E3=80=91Elem?= =?UTF-8?q?ent=20Plus=20=E7=9A=84=20value-format=3D"x"=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E7=9A=84=E5=80=BC=E5=8F=AF=E4=BB=A5=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=EF=BC=8C=E4=B8=8D=E9=9C=80=E8=A6=81=20Number?= =?UTF-8?q?()=20=E8=BD=AC=E6=8D=A2=EF=BC=8C=E4=B8=8E=20antd=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/mall/promotion/rewardActivity/modules/form.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue index d7074f2bb..f210f4953 100644 --- a/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue @@ -78,9 +78,8 @@ const [Modal, modalApi] = useVbenModal({ // 使用 formData.value 作为基础,确保 rules 来自 formData const data = { ...values, ...formData.value }; if (data.startAndEndTime && Array.isArray(data.startAndEndTime)) { - // TODO @puhui999:这里 ele 会告警;antd 不会告警,可能要看看; - data.startTime = Number(data.startAndEndTime[0]); - data.endTime = Number(data.startAndEndTime[1]); + data.startTime = data.startAndEndTime[0]; + data.endTime = data.startAndEndTime[1]; delete data.startAndEndTime; } // 深拷贝 rules 避免修改原始数据 From c478bef269edd95ba2e753bc7841f3d757375eba Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 13 Jan 2026 12:32:00 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele=E3=80=91cropper?= =?UTF-8?q?=20todo=20=E4=BC=98=E5=8C=96=EF=BC=8C=E5=AF=B9=E9=BD=90=20antd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/cropper/cropper-avatar.vue | 9 +-- .../src/components/cropper/cropper-modal.vue | 71 +++++-------------- 2 files changed, 22 insertions(+), 58 deletions(-) diff --git a/apps/web-ele/src/components/cropper/cropper-avatar.vue b/apps/web-ele/src/components/cropper/cropper-avatar.vue index 62bc20c42..0c537022b 100644 --- a/apps/web-ele/src/components/cropper/cropper-avatar.vue +++ b/apps/web-ele/src/components/cropper/cropper-avatar.vue @@ -6,6 +6,7 @@ import type { CropperAvatarProps } from './typing'; import { computed, ref, unref, watch, watchEffect } from 'vue'; import { useVbenModal } from '@vben/common-ui'; +import { IconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; import { ElButton, ElMessage } from 'element-plus'; @@ -83,16 +84,16 @@ defineExpose({ class="duration-400 absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black bg-opacity-40 opacity-0 transition-opacity group-hover:opacity-100" :style="getImageWrapperStyle" > - - + />