fix(mall): 修复商品 SKU 编辑回显被重置

- 修复 SPU 编辑页详情回填时误触发规格/分销类型变更,导致 SKU 价格库存被重置为 0 的问题
- 三端同步处理 web-antd、web-ele、web-antdv-next
- SKU 金额回填保持 number 类型,避免影响 InputNumber 双向绑定
- 详情页金额展示补齐两位小数,避免 0.50 显示成 0.5
- 补充浏览器自动化验证详情展示与编辑页直接保存 payload
This commit is contained in:
YunaiV
2026-07-04 21:18:10 +08:00
parent 5023510612
commit 396ba7dcfd
6 changed files with 120 additions and 57 deletions

View File

@@ -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<MallSpuApi.Sku[]>([createEmptySku()]);
/** 批量添加 */
@@ -501,17 +506,17 @@ defineExpose({
</VxeColumn>
<VxeColumn align="center" title="销售价(元)" width="80">
<template #default="{ row }">
{{ row.price }}
{{ formatDetailMoney(row.price) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价(元)" width="80">
<template #default="{ row }">
{{ row.marketPrice }}
{{ formatDetailMoney(row.marketPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" width="80">
<template #default="{ row }">
{{ row.costPrice }}
{{ formatDetailMoney(row.costPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" width="80">
@@ -532,12 +537,12 @@ defineExpose({
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" width="80">
<template #default="{ row }">
{{ row.firstBrokeragePrice }}
{{ formatDetailMoney(row.firstBrokeragePrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" width="80">
<template #default="{ row }">
{{ row.secondBrokeragePrice }}
{{ formatDetailMoney(row.secondBrokeragePrice) }}
</template>
</VxeColumn>
</template>

View File

@@ -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<MallSpuApi.Spu>({
@@ -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;
}
}

View File

@@ -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<MallSpuApi.Sku[]>([createEmptySku()]);
/** 批量添加 */
@@ -501,17 +506,17 @@ defineExpose({
</VxeColumn>
<VxeColumn align="center" title="销售价(元)" width="80">
<template #default="{ row }">
{{ row.price }}
{{ formatDetailMoney(row.price) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价(元)" width="80">
<template #default="{ row }">
{{ row.marketPrice }}
{{ formatDetailMoney(row.marketPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" width="80">
<template #default="{ row }">
{{ row.costPrice }}
{{ formatDetailMoney(row.costPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" width="80">
@@ -532,12 +537,12 @@ defineExpose({
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" width="80">
<template #default="{ row }">
{{ row.firstBrokeragePrice }}
{{ formatDetailMoney(row.firstBrokeragePrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" width="80">
<template #default="{ row }">
{{ row.secondBrokeragePrice }}
{{ formatDetailMoney(row.secondBrokeragePrice) }}
</template>
</VxeColumn>
</template>

View File

@@ -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<MallSpuApi.Spu>({
@@ -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;
}
}

View File

@@ -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<MallSpuApi.Sku[]>([createEmptySku()]);
/** 批量添加 */
@@ -536,17 +541,17 @@ defineExpose({
</VxeColumn>
<VxeColumn align="center" title="销售价(元)" width="80">
<template #default="{ row }">
{{ row.price }}
{{ formatDetailMoney(row.price) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价(元)" width="80">
<template #default="{ row }">
{{ row.marketPrice }}
{{ formatDetailMoney(row.marketPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" width="80">
<template #default="{ row }">
{{ row.costPrice }}
{{ formatDetailMoney(row.costPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" width="80">
@@ -567,12 +572,12 @@ defineExpose({
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" width="80">
<template #default="{ row }">
{{ row.firstBrokeragePrice }}
{{ formatDetailMoney(row.firstBrokeragePrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" width="80">
<template #default="{ row }">
{{ row.secondBrokeragePrice }}
{{ formatDetailMoney(row.secondBrokeragePrice) }}
</template>
</VxeColumn>
</template>

View File

@@ -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<MallSpuApi.Spu>({
@@ -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;
}
}