feat:【mall 商城】sku-list 组件优化(antd)

pull/244/head
puhui999 2025-10-31 23:40:48 +08:00
parent 58a0636959
commit abe2c04171
1 changed files with 56 additions and 72 deletions

View File

@ -7,7 +7,12 @@ import type { MallSpuApi } from '#/api/mall/product/spu';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { copyValueToTarget, formatToFraction, isEmpty } from '@vben/utils'; import {
copyValueToTarget,
formatToFraction,
getNestedValue,
isEmpty,
} from '@vben/utils';
import { Button, Image, Input, InputNumber, message } from 'ant-design-vue'; import { Button, Image, Input, InputNumber, message } from 'ant-design-vue';
@ -43,9 +48,12 @@ const emit = defineEmits<{
const { isBatch, isDetail, isComponent, isActivityComponent } = props; const { isBatch, isDetail, isComponent, isActivityComponent } = props;
const formData: Ref<MallSpuApi.Spu | undefined> = ref<MallSpuApi.Spu>(); // const formData: Ref<MallSpuApi.Spu | undefined> = ref<MallSpuApi.Spu>();
const skuList = ref<MallSpuApi.Sku[]>([ const tableHeaders = ref<{ label: string; prop: string }[]>([]);
{
/** 创建空 SKU 数据 */
function createEmptySku(): MallSpuApi.Sku {
return {
price: 0, price: 0,
marketPrice: 0, marketPrice: 0,
costPrice: 0, costPrice: 0,
@ -56,8 +64,10 @@ const skuList = ref<MallSpuApi.Sku[]>([
volume: 0, volume: 0,
firstBrokeragePrice: 0, firstBrokeragePrice: 0,
secondBrokeragePrice: 0, secondBrokeragePrice: 0,
}, };
]); // }
const skuList = ref<MallSpuApi.Sku[]>([createEmptySku()]);
/** 批量添加 */ /** 批量添加 */
function batchAdd() { function batchAdd() {
@ -79,34 +89,33 @@ function validateProperty() {
} }
} }
/** 删除 sku */ /** 删除 SKU */
function deleteSku(row: MallSpuApi.Sku) { function deleteSku(row: MallSpuApi.Sku) {
const index = formData.value!.skus!.findIndex( const index = formData.value!.skus!.findIndex(
//
(sku: MallSpuApi.Sku) => (sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties), JSON.stringify(sku.properties) === JSON.stringify(row.properties),
); );
formData.value!.skus!.splice(index, 1); if (index !== -1) {
formData.value!.skus!.splice(index, 1);
}
} }
const tableHeaders = ref<{ label: string; prop: string }[]>([]); // /** 校验 SKU 数据:保存时,每个商品规格的表单要校验。例如:销售金额最低是 0.01 */
/** 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种 */
function validateSku() { function validateSku() {
validateProperty(); validateProperty();
let warningInfo = '请检查商品各行相关属性配置,'; let warningInfo = '请检查商品各行相关属性配置,';
let validate = true; // let validate = true;
for (const sku of formData.value!.skus!) { for (const sku of formData.value!.skus!) {
//
for (const rule of props?.ruleConfig as RuleConfig[]) { for (const rule of props?.ruleConfig as RuleConfig[]) {
const arg = getValue(sku, rule.name); const value = getNestedValue(sku, rule.name);
if (!rule.rule(arg)) { if (!rule.rule(value)) {
validate = false; // validate = false;
warningInfo += rule.message; warningInfo += rule.message;
break; break;
} }
} }
//
if (!validate) { if (!validate) {
message.warning(warningInfo); message.warning(warningInfo);
throw new Error(warningInfo); throw new Error(warningInfo);
@ -114,21 +123,6 @@ function validateSku() {
} }
} }
// TODO @puhui999 getNestedValue
function getValue(obj: any, arg: string): unknown {
const keys = arg.split('.');
let value: any = obj;
for (const key of keys) {
if (value && typeof value === 'object' && key in value) {
value = value[key];
} else {
value = undefined;
break;
}
}
return value;
}
/** /**
* 选择时触发 * 选择时触发
* *
@ -155,7 +149,6 @@ watch(
/** 生成表数据 */ /** 生成表数据 */
function generateTableData(propertyList: PropertyAndValues[]) { function generateTableData(propertyList: PropertyAndValues[]) {
//
const propertyValues = propertyList.map((item: PropertyAndValues) => const propertyValues = propertyList.map((item: PropertyAndValues) =>
(item.values || []).map((v: { id: number; name: string }) => ({ (item.values || []).map((v: { id: number; name: string }) => ({
propertyId: item.id, propertyId: item.id,
@ -164,35 +157,30 @@ function generateTableData(propertyList: PropertyAndValues[]) {
valueName: v.name, valueName: v.name,
})), })),
); );
const buildSkuList = build(propertyValues); const buildSkuList = build(propertyValues);
// sku skus // sku skus
if (!validateData(propertyList)) { if (!validateData(propertyList)) {
// sku
formData.value!.skus = []; formData.value!.skus = [];
} }
for (const item of buildSkuList) { for (const item of buildSkuList) {
const properties = Array.isArray(item) ? item : [item];
const row = { const row = {
properties: Array.isArray(item) ? item : [item], // property ...createEmptySku(),
price: 0, properties,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
}; };
// sku // sku
const index = formData.value!.skus!.findIndex( const exists = formData.value!.skus!.some(
(sku: MallSpuApi.Sku) => (sku: MallSpuApi.Sku) =>
JSON.stringify(sku.properties) === JSON.stringify(row.properties), JSON.stringify(sku.properties) === JSON.stringify(row.properties),
); );
if (index !== -1) {
continue; if (!exists) {
formData.value!.skus!.push(row);
} }
formData.value!.skus!.push(row);
} }
} }
@ -248,43 +236,33 @@ watch(
if (!formData.value!.specType) { if (!formData.value!.specType) {
return; return;
} }
// 使 // 使
if (props.isBatch) { if (props.isBatch) {
skuList.value = [ skuList.value = [createEmptySku()];
{
price: 0,
marketPrice: 0,
costPrice: 0,
barCode: '',
picUrl: '',
stock: 0,
weight: 0,
volume: 0,
firstBrokeragePrice: 0,
secondBrokeragePrice: 0,
},
];
} }
// //
if (JSON.stringify(propertyList) === '[]') { if (JSON.stringify(propertyList) === '[]') {
return; return;
} }
//
tableHeaders.value = []; //
// tableHeaders.value = propertyList.map((item, index) => ({
propertyList.forEach((item, index) => { prop: `name${index}`,
// nameindex label: item.name,
tableHeaders.value.push({ prop: `name${index}`, label: item.name }); }));
});
// sku // sku
if (validateData(propertyList)) { if (validateData(propertyList)) {
return; return;
} }
// //
if (propertyList.some((item) => !item.values || isEmpty(item.values))) { if (propertyList.some((item) => !item.values || isEmpty(item.values))) {
return; return;
} }
// table sku // table sku
generateTableData(propertyList); generateTableData(propertyList);
}, },
@ -296,17 +274,23 @@ watch(
const activitySkuListRef = ref(); const activitySkuListRef = ref();
/** 获取 SKU 表格引用 */
function getSkuTableRef() { function getSkuTableRef() {
return activitySkuListRef.value; return activitySkuListRef.value;
} }
defineExpose({ generateTableData, validateSku, getSkuTableRef }); defineExpose({
generateTableData,
validateSku,
getSkuTableRef,
});
</script> </script>
<template> <template>
<div> <div>
<!-- 情况一添加/修改 --> <!-- 情况一添加/修改 -->
<!-- TODO @puhui999有可以通过 grid 来做么主要考虑这样不直接使用 vxe 标签抽象程度更高 --> <!-- TODO @puhui999有可以通过 grid 来做么主要考虑这样不直接使用 vxe 标签抽象程度更高 -->
<!-- TODO 还是先这样吧 🤣🤣 -->
<VxeTable <VxeTable
v-if="!isDetail && !isActivityComponent" v-if="!isDetail && !isActivityComponent"
:data="isBatch ? skuList : formData?.skus || []" :data="isBatch ? skuList : formData?.skus || []"