From c023ebbdb9f70a4599389fb0df81ca87f93ac021 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 28 Dec 2025 15:44:21 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele=E3=80=91cropper?= =?UTF-8?q?=E3=80=81form-create\rules=20todo=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/cropper/cropper-avatar.vue | 80 +++--------- .../src/components/cropper/cropper-modal.vue | 117 ++++++------------ .../src/components/cropper/cropper.vue | 6 +- .../src/components/form-create/rules/data.ts | 4 +- .../form-create/rules/use-dict-select.ts | 2 +- 5 files changed, 58 insertions(+), 151 deletions(-) diff --git a/apps/web-ele/src/components/cropper/cropper-avatar.vue b/apps/web-ele/src/components/cropper/cropper-avatar.vue index e50085f3c..894169844 100644 --- a/apps/web-ele/src/components/cropper/cropper-avatar.vue +++ b/apps/web-ele/src/components/cropper/cropper-avatar.vue @@ -18,7 +18,7 @@ const props = withDefaults(defineProps(), { width: 200, value: '', showBtn: true, - btnProps: () => ({}), + btnProps: () => ({}) as any, btnText: '', uploadApi: () => Promise.resolve(), size: 5, @@ -27,14 +27,10 @@ const props = withDefaults(defineProps(), { const emit = defineEmits(['update:value', 'change']); const sourceValue = ref(props.value || ''); -// TODO @puhui999:这个有办法去掉么? -const prefixCls = 'cropper-avatar'; const [CropperModal, modalApi] = useVbenModal({ connectedComponent: cropperModal, }); -const getClass = computed(() => [prefixCls]); - const getWidth = computed(() => `${`${props.width}`.replace(/px/, '')}px`); const getIconWidth = computed( @@ -74,34 +70,41 @@ defineExpose({ - - diff --git a/apps/web-ele/src/components/cropper/cropper-modal.vue b/apps/web-ele/src/components/cropper/cropper-modal.vue index 1d949c0aa..2c631185c 100644 --- a/apps/web-ele/src/components/cropper/cropper-modal.vue +++ b/apps/web-ele/src/components/cropper/cropper-modal.vue @@ -36,7 +36,6 @@ const cropper = ref(); let scaleX = 1; let scaleY = 1; -const prefixCls = 'cropper-am'; const [Modal, modalApi] = useVbenModal({ onConfirm: handleOk, onOpenChange(isOpen) { @@ -120,11 +119,34 @@ async function handleOk() { :title="$t('ui.cropper.modalTitle')" class="w-[800px]" > -
+
-
+
-
+
-
+
-
+
-
+
- - diff --git a/apps/web-ele/src/components/cropper/cropper.vue b/apps/web-ele/src/components/cropper/cropper.vue index d371e4a45..5cdf1bc36 100644 --- a/apps/web-ele/src/components/cropper/cropper.vue +++ b/apps/web-ele/src/components/cropper/cropper.vue @@ -33,8 +33,6 @@ const imgElRef = ref>(); const cropper = ref(); const isReady = ref(false); -// TODO @puhui999:这个有办法去掉么? -const prefixCls = 'cropper-image'; const debounceRealTimeCropped = useDebounceFn(realTimeCropped, 80); const getImageStyle = computed((): CSSProperties => { @@ -47,10 +45,9 @@ const getImageStyle = computed((): CSSProperties => { const getClass = computed(() => { return [ - prefixCls, attrs.class, { - [`${prefixCls}--circled`]: props.circled, + 'cropper-image--circled': props.circled, }, ]; }); @@ -158,6 +155,7 @@ function getRoundedCanvas() { :crossorigin="crossorigin" :src="src" :style="getImageStyle" + class="h-auto max-w-full" />
diff --git a/apps/web-ele/src/components/form-create/rules/data.ts b/apps/web-ele/src/components/form-create/rules/data.ts index fa89ec654..2c6cee2ce 100644 --- a/apps/web-ele/src/components/form-create/rules/data.ts +++ b/apps/web-ele/src/components/form-create/rules/data.ts @@ -121,7 +121,7 @@ const apiSelectRule = [ field: 'data', title: '请求参数 JSON 格式', props: { - autosize: true, // TODO @puhui999:这里时 autoSize 还是 autosize 哈?和 antd 不同 + autosize: true, type: 'textarea', placeholder: '{"type": 1}', }, @@ -155,7 +155,7 @@ const apiSelectRule = [ info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表 (data: any)=>{ label: string; value: any }[]`, props: { - autosize: true, // TODO @puhui999:这里时 autoSize 还是 autosize 哈?和 antd 不同 + autosize: true, rows: { minRows: 2, maxRows: 6 }, type: 'textarea', placeholder: ` diff --git a/apps/web-ele/src/components/form-create/rules/use-dict-select.ts b/apps/web-ele/src/components/form-create/rules/use-dict-select.ts index b77af4464..e9eb46e9d 100644 --- a/apps/web-ele/src/components/form-create/rules/use-dict-select.ts +++ b/apps/web-ele/src/components/form-create/rules/use-dict-select.ts @@ -39,7 +39,7 @@ export function useDictSelectRule() { title: label, info: '', $required: false, - // TODO @puhui999:vben 版本里,这里有个 modelField: 'value', 需要添加么? + modelField: 'model-value', }; }, props(_: any, { t }: any) { From 4395353c223c5f47258e41ae4b62b846cf36231c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 28 Dec 2025 17:35:43 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele/antd=E3=80=91rewa?= =?UTF-8?q?rdActivity=20todo=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/promotion/rewardActivity/data.ts | 7 ++++-- .../promotion/rewardActivity/modules/form.vue | 20 +++++++++++----- .../mall/promotion/rewardActivity/data.ts | 6 ++++- .../promotion/rewardActivity/modules/form.vue | 24 ++++++++++++++----- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/apps/web-antd/src/views/mall/promotion/rewardActivity/data.ts b/apps/web-antd/src/views/mall/promotion/rewardActivity/data.ts index 613a4a7e7..c1232e43f 100644 --- a/apps/web-antd/src/views/mall/promotion/rewardActivity/data.ts +++ b/apps/web-antd/src/views/mall/promotion/rewardActivity/data.ts @@ -138,6 +138,7 @@ export function useFormSchema(): VbenFormSchema[] { componentProps: { showTime: true, format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', placeholder: [ $t('utils.rangePicker.beginTime'), $t('utils.rangePicker.endTime'), @@ -217,13 +218,15 @@ export function useFormSchema(): VbenFormSchema[] { }, rules: 'required', }, - // TODO @puhui999:1)新增时:一直报:“请输入优惠设置”;2)修改老数据,出现报“请求参数类型错误:50.00”; { fieldName: 'rules', label: '优惠设置', component: 'Input', formItemClass: 'items-start', - rules: 'required', + rules: z + .array(z.any()) + .min(1, { message: '请添加至少一条优惠规则' }) + .default([]), }, { fieldName: 'productScopeValues', // 隐藏字段:用于自动同步 productScopeValues 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 99e323193..de911c629 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 @@ -8,10 +8,9 @@ import { PromotionConditionTypeEnum, PromotionProductScopeEnum, } from '@vben/constants'; -import { convertToInteger, formatToFraction } from '@vben/utils'; +import { cloneDeep, convertToInteger, formatToFraction } from '@vben/utils'; import { message } from 'ant-design-vue'; -import dayjs from 'dayjs'; import { useVbenForm } from '#/adapter/form'; import { @@ -53,6 +52,8 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 在验证前同步 formData.rules 到表单中 + await formApi.setFieldValue('rules', formData.value.rules || []); const { valid } = await formApi.validate(); if (!valid) { return; @@ -61,18 +62,24 @@ const [Modal, modalApi] = useVbenModal({ // 提交表单 try { const values = await formApi.getValues(); - const data = { ...formData.value, ...values }; + // 使用 formData.value 作为基础,确保 rules 来自 formData + const data = { ...values, ...formData.value }; if (data.startAndEndTime && Array.isArray(data.startAndEndTime)) { data.startTime = data.startAndEndTime[0]; data.endTime = data.startAndEndTime[1]; delete data.startAndEndTime; } - data.rules?.forEach((item: any) => { + // 深拷贝 rules 避免修改原始数据 + const rules = cloneDeep( + data.rules, + ) as unknown as MallRewardActivityApi.RewardRule[]; + rules.forEach((item: any) => { item.discountPrice = convertToInteger(item.discountPrice || 0); if (data.conditionType === PromotionConditionTypeEnum.PRICE.type) { item.limit = convertToInteger(item.limit || 0); } }); + data.rules = rules; await (data.id ? updateRewardActivity(data as MallRewardActivityApi.RewardActivity) : createRewardActivity(data as MallRewardActivityApi.RewardActivity)); @@ -97,9 +104,10 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); try { const result = await getReward(data.id); + // valueFormat: 'x' 配置下,直接使用时间戳 result.startAndEndTime = [ - result.startTime ? dayjs(result.startTime) : undefined, - result.endTime ? dayjs(result.endTime) : undefined, + result.startTime ? String(result.startTime) : undefined, + result.endTime ? String(result.endTime) : undefined, ] as any[]; result.rules?.forEach((item: any) => { item.discountPrice = formatToFraction(item.discountPrice || 0); diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts index 57ebd0acb..c1232e43f 100644 --- a/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts @@ -138,6 +138,7 @@ export function useFormSchema(): VbenFormSchema[] { componentProps: { showTime: true, format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', placeholder: [ $t('utils.rangePicker.beginTime'), $t('utils.rangePicker.endTime'), @@ -222,7 +223,10 @@ export function useFormSchema(): VbenFormSchema[] { label: '优惠设置', component: 'Input', formItemClass: 'items-start', - rules: 'required', + rules: z + .array(z.any()) + .min(1, { message: '请添加至少一条优惠规则' }) + .default([]), }, { fieldName: 'productScopeValues', // 隐藏字段:用于自动同步 productScopeValues 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 b2f3bec94..199242efb 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 @@ -8,7 +8,7 @@ import { PromotionConditionTypeEnum, PromotionProductScopeEnum, } from '@vben/constants'; -import { convertToInteger, formatToFraction } from '@vben/utils'; +import { cloneDeep, convertToInteger, formatToFraction } from '@vben/utils'; import { ElMessage } from 'element-plus'; @@ -52,6 +52,8 @@ const [Form, formApi] = useVbenForm({ const [Modal, modalApi] = useVbenModal({ async onConfirm() { + // 在验证前同步 formData.rules 到表单中 + await formApi.setFieldValue('rules', formData.value.rules || []); const { valid } = await formApi.validate(); if (!valid) { return; @@ -60,18 +62,24 @@ const [Modal, modalApi] = useVbenModal({ // 提交表单 try { const values = await formApi.getValues(); - const data = { ...formData.value, ...values }; + // 使用 formData.value 作为基础,确保 rules 来自 formData + const data = { ...values, ...formData.value }; if (data.startAndEndTime && Array.isArray(data.startAndEndTime)) { - data.startTime = data.startAndEndTime[0]; - data.endTime = data.startAndEndTime[1]; + data.startTime = Number(data.startAndEndTime[0]); + data.endTime = Number(data.startAndEndTime[1]); delete data.startAndEndTime; } - data.rules?.forEach((item: any) => { + // 深拷贝 rules 避免修改原始数据 + const rules = cloneDeep( + data.rules, + ) as unknown as MallRewardActivityApi.RewardRule[]; + rules.forEach((item: any) => { item.discountPrice = convertToInteger(item.discountPrice || 0); if (data.conditionType === PromotionConditionTypeEnum.PRICE.type) { item.limit = convertToInteger(item.limit || 0); } }); + data.rules = rules; await (data.id ? updateRewardActivity(data as MallRewardActivityApi.RewardActivity) : createRewardActivity(data as MallRewardActivityApi.RewardActivity)); @@ -96,7 +104,11 @@ const [Modal, modalApi] = useVbenModal({ modalApi.lock(); try { const result = await getReward(data.id); - result.startAndEndTime = [result.startTime, result.endTime] as any[]; + // valueFormat: 'x' 配置下,直接使用时间戳字符串 + result.startAndEndTime = [ + result.startTime ? String(result.startTime) : undefined, + result.endTime ? String(result.endTime) : undefined, + ] as any[]; result.rules?.forEach((item: any) => { item.discountPrice = formatToFraction(item.discountPrice || 0); if (result.conditionType === PromotionConditionTypeEnum.PRICE.type) { From e6327ae9da5da7d6ac1bed214a50a44085ee8a9d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 28 Dec 2025 17:57:10 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele=E3=80=91spu=20tod?= =?UTF-8?q?o=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/product/spu/components/sku-list.vue | 52 +++++++++++++------ .../spu/components/spu-and-sku-list.vue | 3 +- .../src/views/mall/product/spu/form/index.vue | 1 - 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/apps/web-ele/src/views/mall/product/spu/components/sku-list.vue b/apps/web-ele/src/views/mall/product/spu/components/sku-list.vue index d92eeb16a..06dc595b9 100644 --- a/apps/web-ele/src/views/mall/product/spu/components/sku-list.vue +++ b/apps/web-ele/src/views/mall/product/spu/components/sku-list.vue @@ -490,16 +490,25 @@ defineExpose({ @checkbox-all="handleSelectionChange" > - - +