feat:【mall 商城】商品发布 - 库存价格【antd】80%: 完善 sku form 逻辑

This commit is contained in:
puhui999
2025-10-21 16:10:49 +08:00
parent 6ea2082e4b
commit 93149876e5
4 changed files with 97 additions and 53 deletions

View File

@@ -1,7 +1,5 @@
import type { VbenFormSchema } from '#/adapter/form'; import type { VbenFormSchema } from '#/adapter/form';
import { h } from 'vue';
import { DeliveryTypeEnum, DICT_TYPE } from '@vben/constants'; import { DeliveryTypeEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';
@@ -11,8 +9,6 @@ import { getSimpleBrandList } from '#/api/mall/product/brand';
import { getCategoryList } from '#/api/mall/product/category'; import { getCategoryList } from '#/api/mall/product/category';
import { getSimpleTemplateList } from '#/api/mall/trade/delivery/expressTemplate'; import { getSimpleTemplateList } from '#/api/mall/trade/delivery/expressTemplate';
import SkuList from './sku-list.vue';
/** 基础设置的表单 */ /** 基础设置的表单 */
export function useInfoFormSchema(): VbenFormSchema[] { export function useInfoFormSchema(): VbenFormSchema[] {
return [ return [
@@ -110,7 +106,6 @@ export function useInfoFormSchema(): VbenFormSchema[] {
/** 价格库存的表单 */ /** 价格库存的表单 */
export function useSkuFormSchema( export function useSkuFormSchema(
propertyList: any[] = [], propertyList: any[] = [],
ruleConfig: any[] = [],
isDetail: boolean = false, isDetail: boolean = false,
): VbenFormSchema[] { ): VbenFormSchema[] {
return [ return [
@@ -164,11 +159,8 @@ export function useSkuFormSchema(
{ {
fieldName: 'singleSkuList', fieldName: 'singleSkuList',
label: '', label: '',
component: h(SkuList), component: 'Input',
componentProps: { componentProps: {},
propertyList,
ruleConfig,
},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 false单规格时显示 // 当 specType 为 false单规格时显示
@@ -191,11 +183,8 @@ export function useSkuFormSchema(
{ {
fieldName: 'batchSkuList', fieldName: 'batchSkuList',
label: '批量设置', label: '批量设置',
component: h(SkuList), component: 'Input',
componentProps: { componentProps: {},
isBatch: true,
propertyList,
},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 true多规格且 propertyList 有数据时显示,且非详情模式 // 当 specType 为 true多规格且 propertyList 有数据时显示,且非详情模式
@@ -207,12 +196,8 @@ export function useSkuFormSchema(
{ {
fieldName: 'multiSkuList', fieldName: 'multiSkuList',
label: '规格列表', label: '规格列表',
component: h(SkuList), component: 'Input',
componentProps: { componentProps: {},
propertyList,
ruleConfig,
isDetail,
},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 true多规格且 propertyList 有数据时显示 // 当 specType 为 true多规格且 propertyList 有数据时显示

View File

@@ -3,7 +3,7 @@ import type { PropertyAndValues, RuleConfig } from './index';
import type { MallSpuApi } from '#/api/mall/product/spu'; import type { MallSpuApi } from '#/api/mall/product/spu';
import { computed, onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { ContentWrap, Page, useVbenModal } from '@vben/common-ui'; import { ContentWrap, Page, useVbenModal } from '@vben/common-ui';
@@ -24,6 +24,7 @@ import {
import { getPropertyList } from './index'; import { getPropertyList } from './index';
import ProductAttributes from './product-attributes.vue'; import ProductAttributes from './product-attributes.vue';
import ProductPropertyAddForm from './product-property-add-form.vue'; import ProductPropertyAddForm from './product-property-add-form.vue';
import SkuList from './sku-list.vue';
const spuId = ref<number>(); const spuId = ref<number>();
const { params, name } = useRoute(); const { params, name } = useRoute();
@@ -63,6 +64,7 @@ const formData = ref<MallSpuApi.Spu>({
const propertyList = ref<PropertyAndValues[]>([]); // 商品属性列表 const propertyList = ref<PropertyAndValues[]>([]); // 商品属性列表
const formLoading = ref(false); // 表单的加载中1修改时的数据加载2提交的按钮禁用 const formLoading = ref(false); // 表单的加载中1修改时的数据加载2提交的按钮禁用
const isDetail = ref(false); // 是否查看详情 const isDetail = ref(false); // 是否查看详情
const skuListRef = ref(); // 商品属性列表 Ref
// sku 相关属性校验规则 // sku 相关属性校验规则
const ruleConfig: RuleConfig[] = [ const ruleConfig: RuleConfig[] = [
@@ -88,15 +90,6 @@ const ruleConfig: RuleConfig[] = [
}, },
]; ];
// 动态生成 sku form schema
const skuFormSchema = computed(() => {
return useSkuFormSchema(
propertyList.value,
ruleConfig,
isDetail.value || false,
);
});
const [InfoForm, infoFormApi] = useVbenForm({ const [InfoForm, infoFormApi] = useVbenForm({
commonConfig: { commonConfig: {
componentProps: { componentProps: {
@@ -119,8 +112,18 @@ const [SkuForm, skuFormApi] = useVbenForm({
labelWidth: 120, labelWidth: 120,
}, },
layout: 'horizontal', layout: 'horizontal',
schema: skuFormSchema.value, schema: useSkuFormSchema(propertyList.value, isDetail.value),
showDefaultActions: false, showDefaultActions: false,
handleValuesChange: (values, fieldsChanged) => {
if (fieldsChanged.includes('subCommissionType')) {
formData.value.subCommissionType = values.subCommissionType;
changeSubCommissionType();
}
if (fieldsChanged.includes('specType')) {
formData.value.specType = values.specType;
onChangeSpec();
}
},
}); });
const [ProductPropertyAddFormModal, productPropertyAddFormApi] = useVbenModal({ const [ProductPropertyAddFormModal, productPropertyAddFormApi] = useVbenModal({
@@ -227,11 +230,8 @@ const getDetail = async () => {
} }
}); });
formData.value = res; formData.value = res;
// 将 SKU 的属性,整理成 PropertyAndValues 数组
propertyList.value = getPropertyList(res);
// 初始化各表单值(异步) // 初始化各表单值(异步)
infoFormApi.setValues(res); infoFormApi.setValues(res);
// 特殊处理 SpuSkuFormData
skuFormApi.setValues(res); skuFormApi.setValues(res);
deliveryFormApi.setValues(res); deliveryFormApi.setValues(res);
descriptionFormApi.setValues(res); descriptionFormApi.setValues(res);
@@ -240,18 +240,58 @@ const getDetail = async () => {
formLoading.value = false; formLoading.value = false;
} }
} }
// 将 SKU 的属性,整理成 PropertyAndValues 数组
propertyList.value = getPropertyList(formData.value);
}; };
// =========== sku form 逻辑 ===========
function openPropertyAddForm() {
productPropertyAddFormApi.open();
}
/** 调用 SkuList generateTableData 方法*/ /** 调用 SkuList generateTableData 方法*/
const generateSkus = (propertyList: any[]) => { const generateSkus = (propertyList: any[]) => {
// skuListRef.value.generateTableData(propertyList) skuListRef.value.generateTableData(propertyList);
};
/** 分销类型 */
const changeSubCommissionType = () => {
// 默认为零,类型切换后也要重置为零
for (const item of formData.value.skus!) {
item.firstBrokeragePrice = 0;
item.secondBrokeragePrice = 0;
}
};
/** 选择规格 */
const onChangeSpec = () => {
// 重置商品属性列表
propertyList.value = [];
// 重置sku列表
formData.value.skus = [
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
];
}; };
// 监听 sku form schema 变化,更新表单 // 监听 sku form schema 变化,更新表单
watch( watch(
skuFormSchema, propertyList,
(newSchema) => { () => {
skuFormApi.updateSchema(newSchema); skuFormApi.updateSchema(
useSkuFormSchema(propertyList.value, isDetail.value),
);
}, },
{ deep: true }, { deep: true },
); );
@@ -266,7 +306,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<ProductPropertyAddFormModal /> <ProductPropertyAddFormModal :property-list="propertyList" />
<Page auto-content-height> <Page auto-content-height>
<ContentWrap class="h-full w-full pb-8"> <ContentWrap class="h-full w-full pb-8">
@@ -278,13 +318,18 @@ onMounted(async () => {
<InfoForm class="w-3/5" /> <InfoForm class="w-3/5" />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="价格库存" key="sku"> <Tabs.TabPane tab="价格库存" key="sku">
<SkuForm class="w-3/5"> <SkuForm class="w-full">
<template #singleSkuList>
<SkuList
ref="skuListRef"
:prop-form-data="formData"
:property-list="propertyList"
:rule-config="ruleConfig"
/>
</template>
<template #productAttributes> <template #productAttributes>
<div> <div>
<Button <Button class="mb-10px mr-15px" @click="openPropertyAddForm">
class="mb-10px mr-15px"
@click="productPropertyAddFormApi.open"
>
添加属性 添加属性
</Button> </Button>
<ProductAttributes <ProductAttributes
@@ -294,6 +339,22 @@ onMounted(async () => {
/> />
</div> </div>
</template> </template>
<template #batchSkuList>
<SkuList
:is-batch="true"
:prop-form-data="formData"
:property-list="propertyList"
/>
</template>
<template #multiSkuList>
<SkuList
ref="skuListRef"
:is-detail="isDetail"
:prop-form-data="formData"
:property-list="propertyList"
:rule-config="ruleConfig"
/>
</template>
</SkuForm> </SkuForm>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="物流设置" key="delivery"> <Tabs.TabPane tab="物流设置" key="delivery">

View File

@@ -87,6 +87,7 @@ const [Form, formApi] = useVbenForm({
// 初始化弹窗 // 初始化弹窗
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
destroyOnClose: true, // 关键:关闭时销毁弹窗,确保每次打开都是全新状态
async onConfirm() { async onConfirm() {
const { valid } = await formApi.validate(); const { valid } = await formApi.validate();
if (!valid) return; if (!valid) return;
@@ -118,7 +119,6 @@ const [Modal, modalApi] = useVbenModal({
} }
// 情况二:如果是不存在的属性,则需要执行新增 // 情况二:如果是不存在的属性,则需要执行新增
modalApi.lock();
try { try {
const data = { name } as MallPropertyApi.Property; const data = { name } as MallPropertyApi.Property;
const propertyId = await createProperty(data); const propertyId = await createProperty(data);
@@ -131,8 +131,9 @@ const [Modal, modalApi] = useVbenModal({
message.success($t('common.createSuccess')); message.success($t('common.createSuccess'));
await modalApi.close(); await modalApi.close();
emit('success'); emit('success');
} finally { } catch (error) {
modalApi.unlock(); // 发生错误时不关闭弹窗
console.error('添加属性失败:', error);
} }
}, },
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {

View File

@@ -214,11 +214,8 @@ const validateData = (propertyList: PropertyAndValues[]): boolean => {
sku.properties sku.properties
?.map((property: MallSpuApi.Property) => property.propertyId) ?.map((property: MallSpuApi.Property) => property.propertyId)
?.forEach((propertyId?: number) => { ?.forEach((propertyId?: number) => {
if ( if (!skuPropertyIds.includes(propertyId!)) {
propertyId !== undefined && skuPropertyIds.push(propertyId!);
!skuPropertyIds.indexOf(propertyId) === -1
) {
skuPropertyIds.push(propertyId);
} }
}), }),
); );