From 396ba7dcfd3c6fca4bc6cab39cdf54039575c0dc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 4 Jul 2026 21:18:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(mall):=20=E4=BF=AE=E5=A4=8D=E5=95=86?= =?UTF-8?q?=E5=93=81=20SKU=20=E7=BC=96=E8=BE=91=E5=9B=9E=E6=98=BE=E8=A2=AB?= =?UTF-8?q?=E9=87=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 SPU 编辑页详情回填时误触发规格/分销类型变更,导致 SKU 价格库存被重置为 0 的问题 - 三端同步处理 web-antd、web-ele、web-antdv-next - SKU 金额回填保持 number 类型,避免影响 InputNumber 双向绑定 - 详情页金额展示补齐两位小数,避免 0.50 显示成 0.5 - 补充浏览器自动化验证详情展示与编辑页直接保存 payload --- .../mall/product/spu/components/sku-list.vue | 15 ++++--- .../src/views/mall/product/spu/form/index.vue | 44 +++++++++++++------ .../mall/product/spu/components/sku-list.vue | 15 ++++--- .../src/views/mall/product/spu/form/index.vue | 44 +++++++++++++------ .../mall/product/spu/components/sku-list.vue | 15 ++++--- .../src/views/mall/product/spu/form/index.vue | 44 +++++++++++++------ 6 files changed, 120 insertions(+), 57 deletions(-) diff --git a/apps/web-antd/src/views/mall/product/spu/components/sku-list.vue b/apps/web-antd/src/views/mall/product/spu/components/sku-list.vue index f64a981f1..a899c63ce 100644 --- a/apps/web-antd/src/views/mall/product/spu/components/sku-list.vue +++ b/apps/web-antd/src/views/mall/product/spu/components/sku-list.vue @@ -70,6 +70,11 @@ function createEmptySku(): MallSpuApi.Sku { }; } +function formatDetailMoney(value: null | number | string | undefined) { + const amount = Number(value ?? 0); + return Number.isFinite(amount) ? amount.toFixed(2) : '0.00'; +} + const skuList = ref([createEmptySku()]); /** 批量添加 */ @@ -501,17 +506,17 @@ defineExpose({ @@ -532,12 +537,12 @@ defineExpose({ diff --git a/apps/web-antd/src/views/mall/product/spu/form/index.vue b/apps/web-antd/src/views/mall/product/spu/form/index.vue index 8e9e4bd52..7a45e75f9 100644 --- a/apps/web-antd/src/views/mall/product/spu/form/index.vue +++ b/apps/web-antd/src/views/mall/product/spu/form/index.vue @@ -5,7 +5,7 @@ import type { RuleConfig, } from '#/views/mall/product/spu/components'; -import { onMounted, ref, watch } from 'vue'; +import { nextTick, onMounted, ref, watch } from 'vue'; import { useRoute } from 'vue-router'; import { Page, useVbenModal } from '@vben/common-ui'; @@ -34,6 +34,7 @@ const { closeCurrentTab } = useTabs(); const activeTabName = ref('info'); const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const isDetail = ref(name === 'ProductSpuDetail'); // 是否查看详情 +const initializingForm = ref(false); // 详情回填时不触发 SKU 重置逻辑 const skuListRef = ref(); // 商品属性列表 Ref const formData = ref({ @@ -113,11 +114,20 @@ const [SkuForm, skuFormApi] = useVbenForm({ schema: useSkuFormSchema(propertyList.value, isDetail.value), showDefaultActions: false, handleValuesChange: (values, fieldsChanged) => { - if (fieldsChanged.includes('subCommissionType')) { + if (initializingForm.value) { + return; + } + if ( + fieldsChanged.includes('subCommissionType') && + values.subCommissionType !== formData.value.subCommissionType + ) { formData.value.subCommissionType = values.subCommissionType; handleChangeSubCommissionType(); } - if (fieldsChanged.includes('specType')) { + if ( + fieldsChanged.includes('specType') && + values.specType !== formData.value.specType + ) { formData.value.specType = values.specType; handleChangeSpec(); } @@ -235,22 +245,28 @@ async function getDetail() { // 金额转换:分转元 res.skus = res.skus?.map((item) => ({ ...item, - price: formatToFraction(item.price), - marketPrice: formatToFraction(item.marketPrice), - costPrice: formatToFraction(item.costPrice), - firstBrokeragePrice: formatToFraction(item.firstBrokeragePrice), - secondBrokeragePrice: formatToFraction(item.secondBrokeragePrice), + price: Number(formatToFraction(item.price)), + marketPrice: Number(formatToFraction(item.marketPrice)), + costPrice: Number(formatToFraction(item.costPrice)), + firstBrokeragePrice: Number(formatToFraction(item.firstBrokeragePrice)), + secondBrokeragePrice: Number( + formatToFraction(item.secondBrokeragePrice), + ), })); + initializingForm.value = true; formData.value = res; - // 初始化各表单值 - infoFormApi.setValues(res).then(); - skuFormApi.setValues(res).then(); - deliveryFormApi.setValues(res).then(); - descriptionFormApi.setValues(res).then(); - otherFormApi.setValues(res).then(); // 将 SKU 的属性,整理成 PropertyAndValues 数组 propertyList.value = getPropertyList(formData.value); + // 初始化各表单值 + void infoFormApi.setValues(res); + void skuFormApi.setValues(res); + void deliveryFormApi.setValues(res); + void descriptionFormApi.setValues(res); + void otherFormApi.setValues(res); + await nextTick(); + await nextTick(); } finally { + initializingForm.value = false; formLoading.value = false; } } diff --git a/apps/web-antdv-next/src/views/mall/product/spu/components/sku-list.vue b/apps/web-antdv-next/src/views/mall/product/spu/components/sku-list.vue index f812c0fe1..cd0939e5a 100644 --- a/apps/web-antdv-next/src/views/mall/product/spu/components/sku-list.vue +++ b/apps/web-antdv-next/src/views/mall/product/spu/components/sku-list.vue @@ -70,6 +70,11 @@ function createEmptySku(): MallSpuApi.Sku { }; } +function formatDetailMoney(value: null | number | string | undefined) { + const amount = Number(value ?? 0); + return Number.isFinite(amount) ? amount.toFixed(2) : '0.00'; +} + const skuList = ref([createEmptySku()]); /** 批量添加 */ @@ -501,17 +506,17 @@ defineExpose({ @@ -532,12 +537,12 @@ defineExpose({ diff --git a/apps/web-antdv-next/src/views/mall/product/spu/form/index.vue b/apps/web-antdv-next/src/views/mall/product/spu/form/index.vue index b3d9cc178..a52d403a0 100644 --- a/apps/web-antdv-next/src/views/mall/product/spu/form/index.vue +++ b/apps/web-antdv-next/src/views/mall/product/spu/form/index.vue @@ -5,7 +5,7 @@ import type { RuleConfig, } from '#/views/mall/product/spu/components'; -import { onMounted, ref, watch } from 'vue'; +import { nextTick, onMounted, ref, watch } from 'vue'; import { useRoute } from 'vue-router'; import { Page, useVbenModal } from '@vben/common-ui'; @@ -34,6 +34,7 @@ const { closeCurrentTab } = useTabs(); const activeTabName = ref('info'); const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const isDetail = ref(name === 'ProductSpuDetail'); // 是否查看详情 +const initializingForm = ref(false); // 详情回填时不触发 SKU 重置逻辑 const skuListRef = ref(); // 商品属性列表 Ref const formData = ref({ @@ -113,11 +114,20 @@ const [SkuForm, skuFormApi] = useVbenForm({ schema: useSkuFormSchema(propertyList.value, isDetail.value), showDefaultActions: false, handleValuesChange: (values, fieldsChanged) => { - if (fieldsChanged.includes('subCommissionType')) { + if (initializingForm.value) { + return; + } + if ( + fieldsChanged.includes('subCommissionType') && + values.subCommissionType !== formData.value.subCommissionType + ) { formData.value.subCommissionType = values.subCommissionType; handleChangeSubCommissionType(); } - if (fieldsChanged.includes('specType')) { + if ( + fieldsChanged.includes('specType') && + values.specType !== formData.value.specType + ) { formData.value.specType = values.specType; handleChangeSpec(); } @@ -235,22 +245,28 @@ async function getDetail() { // 金额转换:分转元 res.skus = res.skus?.map((item) => ({ ...item, - price: formatToFraction(item.price), - marketPrice: formatToFraction(item.marketPrice), - costPrice: formatToFraction(item.costPrice), - firstBrokeragePrice: formatToFraction(item.firstBrokeragePrice), - secondBrokeragePrice: formatToFraction(item.secondBrokeragePrice), + price: Number(formatToFraction(item.price)), + marketPrice: Number(formatToFraction(item.marketPrice)), + costPrice: Number(formatToFraction(item.costPrice)), + firstBrokeragePrice: Number(formatToFraction(item.firstBrokeragePrice)), + secondBrokeragePrice: Number( + formatToFraction(item.secondBrokeragePrice), + ), })); + initializingForm.value = true; formData.value = res; - // 初始化各表单值 - infoFormApi.setValues(res).then(); - skuFormApi.setValues(res).then(); - deliveryFormApi.setValues(res).then(); - descriptionFormApi.setValues(res).then(); - otherFormApi.setValues(res).then(); // 将 SKU 的属性,整理成 PropertyAndValues 数组 propertyList.value = getPropertyList(formData.value); + // 初始化各表单值 + void infoFormApi.setValues(res); + void skuFormApi.setValues(res); + void deliveryFormApi.setValues(res); + void descriptionFormApi.setValues(res); + void otherFormApi.setValues(res); + await nextTick(); + await nextTick(); } finally { + initializingForm.value = false; formLoading.value = false; } } 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 5dfd77b27..0ba73e543 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 @@ -76,6 +76,11 @@ function createEmptySku(): MallSpuApi.Sku { }; } +function formatDetailMoney(value: null | number | string | undefined) { + const amount = Number(value ?? 0); + return Number.isFinite(amount) ? amount.toFixed(2) : '0.00'; +} + const skuList = ref([createEmptySku()]); /** 批量添加 */ @@ -536,17 +541,17 @@ defineExpose({ @@ -567,12 +572,12 @@ defineExpose({ diff --git a/apps/web-ele/src/views/mall/product/spu/form/index.vue b/apps/web-ele/src/views/mall/product/spu/form/index.vue index 720a90321..f42173548 100644 --- a/apps/web-ele/src/views/mall/product/spu/form/index.vue +++ b/apps/web-ele/src/views/mall/product/spu/form/index.vue @@ -5,7 +5,7 @@ import type { RuleConfig, } from '#/views/mall/product/spu/components'; -import { onMounted, ref, watch } from 'vue'; +import { nextTick, onMounted, ref, watch } from 'vue'; import { useRoute } from 'vue-router'; import { Page, useVbenModal } from '@vben/common-ui'; @@ -34,6 +34,7 @@ const { closeCurrentTab } = useTabs(); const activeTabName = ref('info'); const formLoading = ref(false); // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const isDetail = ref(name === 'ProductSpuDetail'); // 是否查看详情 +const initializingForm = ref(false); // 详情回填时不触发 SKU 重置逻辑 const skuListRef = ref(); // 商品属性列表 Ref const formData = ref({ @@ -113,11 +114,20 @@ const [SkuForm, skuFormApi] = useVbenForm({ schema: useSkuFormSchema(propertyList.value, isDetail.value), showDefaultActions: false, handleValuesChange: (values, fieldsChanged) => { - if (fieldsChanged.includes('subCommissionType')) { + if (initializingForm.value) { + return; + } + if ( + fieldsChanged.includes('subCommissionType') && + values.subCommissionType !== formData.value.subCommissionType + ) { formData.value.subCommissionType = values.subCommissionType; handleChangeSubCommissionType(); } - if (fieldsChanged.includes('specType')) { + if ( + fieldsChanged.includes('specType') && + values.specType !== formData.value.specType + ) { formData.value.specType = values.specType; handleChangeSpec(); } @@ -235,22 +245,28 @@ async function getDetail() { // 金额转换:分转元 res.skus = res.skus?.map((item) => ({ ...item, - price: formatToFraction(item.price), - marketPrice: formatToFraction(item.marketPrice), - costPrice: formatToFraction(item.costPrice), - firstBrokeragePrice: formatToFraction(item.firstBrokeragePrice), - secondBrokeragePrice: formatToFraction(item.secondBrokeragePrice), + price: Number(formatToFraction(item.price)), + marketPrice: Number(formatToFraction(item.marketPrice)), + costPrice: Number(formatToFraction(item.costPrice)), + firstBrokeragePrice: Number(formatToFraction(item.firstBrokeragePrice)), + secondBrokeragePrice: Number( + formatToFraction(item.secondBrokeragePrice), + ), })); + initializingForm.value = true; formData.value = res; - // 初始化各表单值 - infoFormApi.setValues(res).then(); - skuFormApi.setValues(res).then(); - deliveryFormApi.setValues(res).then(); - descriptionFormApi.setValues(res).then(); - otherFormApi.setValues(res).then(); // 将 SKU 的属性,整理成 PropertyAndValues 数组 propertyList.value = getPropertyList(formData.value); + // 初始化各表单值 + void infoFormApi.setValues(res); + void skuFormApi.setValues(res); + void deliveryFormApi.setValues(res); + void descriptionFormApi.setValues(res); + void otherFormApi.setValues(res); + await nextTick(); + await nextTick(); } finally { + initializingForm.value = false; formLoading.value = false; } }