fix: sku list

This commit is contained in:
xingyu4j
2025-10-21 17:55:23 +08:00
parent 80545f973a
commit 9ab51a137c
7 changed files with 388 additions and 390 deletions

View File

@@ -160,7 +160,6 @@ export function useSkuFormSchema(
fieldName: 'singleSkuList', fieldName: 'singleSkuList',
label: '', label: '',
component: 'Input', component: 'Input',
componentProps: {},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 false单规格时显示 // 当 specType 为 false单规格时显示
@@ -172,7 +171,6 @@ export function useSkuFormSchema(
fieldName: 'productAttributes', fieldName: 'productAttributes',
label: '商品属性', label: '商品属性',
component: 'Input', component: 'Input',
componentProps: {},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 true多规格时显示 // 当 specType 为 true多规格时显示
@@ -184,7 +182,6 @@ export function useSkuFormSchema(
fieldName: 'batchSkuList', fieldName: 'batchSkuList',
label: '批量设置', label: '批量设置',
component: 'Input', component: 'Input',
componentProps: {},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 true多规格且 propertyList 有数据时显示,且非详情模式 // 当 specType 为 true多规格且 propertyList 有数据时显示,且非详情模式
@@ -197,7 +194,6 @@ export function useSkuFormSchema(
fieldName: 'multiSkuList', fieldName: 'multiSkuList',
label: '规格列表', label: '规格列表',
component: 'Input', component: 'Input',
componentProps: {},
dependencies: { dependencies: {
triggerFields: ['specType'], triggerFields: ['specType'],
// 当 specType 为 true多规格且 propertyList 有数据时显示 // 当 specType 为 true多规格且 propertyList 有数据时显示

View File

@@ -313,67 +313,69 @@ onMounted(async () => {
</script> </script>
<template> <template>
<ProductPropertyAddFormModal :property-list="propertyList" /> <div>
<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">
<template #extra> <template #extra>
<Button type="primary" @click="onSubmit">保存</Button> <Button type="primary" @click="onSubmit">保存</Button>
</template> </template>
<Tabs v-model:active-key="activeTabName"> <Tabs v-model:active-key="activeTabName">
<Tabs.TabPane tab="基础设置" key="info"> <Tabs.TabPane tab="基础设置" key="info">
<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-full"> <SkuForm class="w-full">
<template #singleSkuList> <template #singleSkuList>
<SkuList <SkuList
ref="skuListRef" ref="skuListRef"
:prop-form-data="formData" :prop-form-data="formData"
:property-list="propertyList"
:rule-config="ruleConfig"
/>
</template>
<template #productAttributes>
<div>
<Button class="mb-10px mr-15px" @click="openPropertyAddForm">
添加属性
</Button>
<ProductAttributes
:is-detail="isDetail"
:property-list="propertyList" :property-list="propertyList"
@success="generateSkus" :rule-config="ruleConfig"
/> />
</div> </template>
</template> <template #productAttributes>
<template #batchSkuList> <div>
<SkuList <Button class="mb-10px mr-15px" @click="openPropertyAddForm">
:is-batch="true" 添加属性
:prop-form-data="formData" </Button>
:property-list="propertyList" <ProductAttributes
/> :is-detail="isDetail"
</template> :property-list="propertyList"
<template #multiSkuList> @success="generateSkus"
<SkuList />
ref="skuListRef" </div>
:is-detail="isDetail" </template>
:prop-form-data="formData" <template #batchSkuList>
:property-list="propertyList" <SkuList
:rule-config="ruleConfig" :is-batch="true"
/> :prop-form-data="formData"
</template> :property-list="propertyList"
</SkuForm> />
</Tabs.TabPane> </template>
<Tabs.TabPane tab="物流设置" key="delivery"> <template #multiSkuList>
<DeliveryForm class="w-3/5" /> <SkuList
</Tabs.TabPane> ref="skuListRef"
<Tabs.TabPane tab="商品详情" key="description"> :is-detail="isDetail"
<DescriptionForm class="w-3/5" /> :prop-form-data="formData"
</Tabs.TabPane> :property-list="propertyList"
<Tabs.TabPane tab="其它设置" key="other"> :rule-config="ruleConfig"
<OtherForm class="w-3/5" /> />
</Tabs.TabPane> </template>
</Tabs> </SkuForm>
</ContentWrap> </Tabs.TabPane>
</Page> <Tabs.TabPane tab="物流设置" key="delivery">
<DeliveryForm class="w-3/5" />
</Tabs.TabPane>
<Tabs.TabPane tab="商品详情" key="description">
<DescriptionForm class="w-3/5" />
</Tabs.TabPane>
<Tabs.TabPane tab="其它设置" key="other">
<OtherForm class="w-3/5" />
</Tabs.TabPane>
</Tabs>
</ContentWrap>
</Page>
</div>
</template> </template>

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { MallSpuApi } from '#/api/mall/product/spu'; import type { MallSpuApi } from '#/api/mall/product/spu';
export interface PropertyAndValues { export interface PropertyAndValues {
@@ -39,9 +40,7 @@ const getPropertyList = (spu: MallSpuApi.Spu): PropertyAndValues[] => {
// 添加属性 // 添加属性
if (!properties?.some((item) => item.id === propertyId)) { if (!properties?.some((item) => item.id === propertyId)) {
properties.push({ properties.push({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
id: propertyId!, id: propertyId!,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
name: propertyName!, name: propertyName!,
values: [], values: [],
}); });
@@ -51,7 +50,6 @@ const getPropertyList = (spu: MallSpuApi.Spu): PropertyAndValues[] => {
if ( if (
!properties[index]?.values?.some((value) => value.id === valueId) !properties[index]?.values?.some((value) => value.id === valueId)
) { ) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
properties[index]?.values?.push({ id: valueId!, name: valueName! }); properties[index]?.values?.push({ id: valueId!, name: valueName! });
} }
}, },

View File

@@ -48,7 +48,7 @@ interface InputRefItem {
const inputRef = ref<InputRefItem[]>([]); // 标签输入框Ref const inputRef = ref<InputRefItem[]>([]); // 标签输入框Ref
/** 解决 ref 在 v-for 中的获取问题*/ /** 解决 ref 在 v-for 中的获取问题*/
const setInputRef = (el: any) => { function setInputRef(el: any) {
if (el === null || el === undefined) return; if (el === null || el === undefined) return;
// 如果不存在 id 相同的元素才添加 // 如果不存在 id 相同的元素才添加
if ( if (
@@ -58,7 +58,7 @@ const setInputRef = (el: any) => {
) { ) {
inputRef.value.push(el); inputRef.value.push(el);
} }
}; }
const attributeList = ref<PropertyAndValues[]>([]); // 商品属性列表 const attributeList = ref<PropertyAndValues[]>([]); // 商品属性列表
const attributeOptions = ref<MallPropertyApi.PropertyValue[]>([]); // 商品属性值下拉框 const attributeOptions = ref<MallPropertyApi.PropertyValue[]>([]); // 商品属性值下拉框
@@ -75,26 +75,26 @@ watch(
); );
/** 删除属性值*/ /** 删除属性值*/
const handleCloseValue = (index: number, valueIndex: number) => { function handleCloseValue(index: number, valueIndex: number) {
attributeList.value?.[index]?.values?.splice(valueIndex, 1); attributeList.value?.[index]?.values?.splice(valueIndex, 1);
}; }
/** 删除属性*/ /** 删除属性*/
const handleCloseProperty = (index: number) => { function handleCloseProperty(index: number) {
attributeList.value?.splice(index, 1); attributeList.value?.splice(index, 1);
emit('success', attributeList.value); emit('success', attributeList.value);
}; }
/** 显示输入框并获取焦点 */ /** 显示输入框并获取焦点 */
const showInput = async (index: number) => { async function showInput(index: number) {
attributeIndex.value = index; attributeIndex.value = index;
inputRef.value?.[index]?.focus(); inputRef.value?.[index]?.focus();
// 获取属性下拉选项 // 获取属性下拉选项
await getAttributeOptions(attributeList.value?.[index]?.id!); await getAttributeOptions(attributeList.value?.[index]?.id!);
}; }
// 定义 success 事件,用于操作成功后的回调 // 定义 success 事件,用于操作成功后的回调
const handleInputConfirm = async (index: number, propertyId: number) => { async function handleInputConfirm(index: number, propertyId: number) {
// 从数组中取最后一个输入的值tags 模式下 inputValue 是数组) // 从数组中取最后一个输入的值tags 模式下 inputValue 是数组)
const currentValue = inputValue.value?.[inputValue.value.length - 1]?.trim(); const currentValue = inputValue.value?.[inputValue.value.length - 1]?.trim();
@@ -144,12 +144,12 @@ const handleInputConfirm = async (index: number, propertyId: number) => {
} }
attributeIndex.value = null; attributeIndex.value = null;
inputValue.value = []; inputValue.value = [];
}; }
/** 获取商品属性下拉选项 */ /** 获取商品属性下拉选项 */
const getAttributeOptions = async (propertyId: number) => { async function getAttributeOptions(propertyId: number) {
attributeOptions.value = await getPropertyValueSimpleList(propertyId); attributeOptions.value = await getPropertyValueSimpleList(propertyId);
}; }
</script> </script>
<template> <template>

View File

@@ -128,7 +128,7 @@ const [Modal, modalApi] = useVbenModal({
name, name,
values: [], values: [],
}); });
message.success($t('common.createSuccess')); message.success($t('ui.actionMessage.operationSuccess'));
await modalApi.close(); await modalApi.close();
emit('success'); emit('success');
} catch (error) { } catch (error) {

View File

@@ -60,15 +60,15 @@ const skuList = ref<MallSpuApi.Sku[]>([
]); // 批量添加时的临时数据 ]); // 批量添加时的临时数据
/** 批量添加 */ /** 批量添加 */
const batchAdd = () => { function batchAdd() {
validateProperty(); validateProperty();
formData.value!.skus!.forEach((item: MallSpuApi.Sku) => { formData.value!.skus!.forEach((item: MallSpuApi.Sku) => {
copyValueToTarget(item, skuList.value[0]); copyValueToTarget(item, skuList.value[0]);
}); });
}; }
/** 校验商品属性属性值 */ /** 校验商品属性属性值 */
const validateProperty = () => { function validateProperty() {
// 校验商品属性属性值是否为空,有一个为空都不给过 // 校验商品属性属性值是否为空,有一个为空都不给过
const warningInfo = '存在属性属性值为空,请先检查完善属性值后重试!!!'; const warningInfo = '存在属性属性值为空,请先检查完善属性值后重试!!!';
for (const item of props.propertyList as PropertyAndValues[]) { for (const item of props.propertyList as PropertyAndValues[]) {
@@ -77,24 +77,24 @@ const validateProperty = () => {
throw new Error(warningInfo); throw new Error(warningInfo);
} }
} }
}; }
/** 删除 sku */ /** 删除 sku */
const 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); formData.value!.skus!.splice(index, 1);
}; }
const tableHeaders = ref<{ label: string; prop: string }[]>([]); // 多属性表头 const tableHeaders = ref<{ label: string; prop: string }[]>([]); // 多属性表头
/** /**
* 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。 * 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。
*/ */
const validateSku = () => { function validateSku() {
validateProperty(); validateProperty();
let warningInfo = '请检查商品各行相关属性配置,'; let warningInfo = '请检查商品各行相关属性配置,';
let validate = true; // 默认通过 let validate = true; // 默认通过
@@ -114,9 +114,9 @@ const validateSku = () => {
throw new Error(warningInfo); throw new Error(warningInfo);
} }
} }
}; }
const getValue = (obj: any, arg: string): unknown => { function getValue(obj: any, arg: string): unknown {
const keys = arg.split('.'); const keys = arg.split('.');
let value: any = obj; let value: any = obj;
for (const key of keys) { for (const key of keys) {
@@ -128,15 +128,15 @@ const getValue = (obj: any, arg: string): unknown => {
} }
} }
return value; return value;
}; }
/** /**
* 选择时触发 * 选择时触发
* @param records 传递过来的选中的 sku 是一个数组 * @param records 传递过来的选中的 sku 是一个数组
*/ */
const handleSelectionChange = ({ records }: { records: MallSpuApi.Sku[] }) => { function handleSelectionChange({ records }: { records: MallSpuApi.Sku[] }) {
emit('selectionChange', records); emit('selectionChange', records);
}; }
/** /**
* 将传进来的值赋值给 skuList * 将传进来的值赋值给 skuList
@@ -154,7 +154,7 @@ watch(
); );
/** 生成表数据 */ /** 生成表数据 */
const 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 }) => ({
@@ -194,12 +194,12 @@ const generateTableData = (propertyList: PropertyAndValues[]) => {
} }
formData.value!.skus!.push(row); formData.value!.skus!.push(row);
} }
}; }
/** /**
* 生成 skus 前置校验 * 生成 skus 前置校验
*/ */
const validateData = (propertyList: PropertyAndValues[]): boolean => { function validateData(propertyList: PropertyAndValues[]): boolean {
const skuPropertyIds: number[] = []; const skuPropertyIds: number[] = [];
formData.value!.skus!.forEach((sku: MallSpuApi.Sku) => formData.value!.skus!.forEach((sku: MallSpuApi.Sku) =>
sku.properties sku.properties
@@ -212,12 +212,12 @@ const validateData = (propertyList: PropertyAndValues[]): boolean => {
); );
const propertyIds = propertyList.map((item: PropertyAndValues) => item.id); const propertyIds = propertyList.map((item: PropertyAndValues) => item.id);
return skuPropertyIds.length === propertyIds.length; return skuPropertyIds.length === propertyIds.length;
}; }
/** 构建所有排列组合 */ /** 构建所有排列组合 */
const build = ( function build(
propertyValuesList: MallSpuApi.Property[][], propertyValuesList: MallSpuApi.Property[][],
): (MallSpuApi.Property | MallSpuApi.Property[])[] => { ): (MallSpuApi.Property | MallSpuApi.Property[])[] {
if (propertyValuesList.length === 0) { if (propertyValuesList.length === 0) {
return []; return [];
} else if (propertyValuesList.length === 1) { } else if (propertyValuesList.length === 1) {
@@ -240,7 +240,7 @@ const build = (
} }
return result; return result;
} }
}; }
/** 监听属性列表,生成相关参数和表头 */ /** 监听属性列表,生成相关参数和表头 */
watch( watch(
@@ -298,307 +298,315 @@ watch(
const activitySkuListRef = ref(); const activitySkuListRef = ref();
const getSkuTableRef = () => { function getSkuTableRef() {
return activitySkuListRef.value; return activitySkuListRef.value;
}; }
// 暴露出生成 sku 方法,给添加属性成功时调用 // 暴露出生成 sku 方法,给添加属性成功时调用
defineExpose({ generateTableData, validateSku, getSkuTableRef }); defineExpose({ generateTableData, validateSku, getSkuTableRef });
</script> </script>
<template> <template>
<!-- 情况一添加/修改 --> <div>
<VxeTable <!-- 情况一添加/修改 -->
v-if="!isDetail && !isActivityComponent" <VxeTable
:data="isBatch ? skuList : formData?.skus || []" v-if="!isDetail && !isActivityComponent"
border :data="isBatch ? skuList : formData?.skus || []"
max-height="500" border
size="small" max-height="500"
class="w-full" size="small"
> class="w-full"
<VxeColumn align="center" title="图片" min-width="80">
<template #default="{ row }">
<ImageUpload
v-model:value="row.picUrl"
:max-number="1"
:max-size="2"
:show-description="false"
/>
</template>
</VxeColumn>
<template v-if="formData?.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
:title="item.label"
align="center"
min-width="120"
>
<template #default="{ row }">
<span class="font-bold text-[#40aaff]">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</VxeColumn>
</template>
<VxeColumn align="center" title="商品条码" min-width="168">
<template #default="{ row }">
<Input v-model:value="row.barCode" class="w-full" />
</template>
</VxeColumn>
<VxeColumn align="center" title="销售价" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.price"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.marketPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.costPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" min-width="168">
<template #default="{ row }">
<InputNumber v-model:value="row.stock" :min="0" class="w-full" />
</template>
</VxeColumn>
<VxeColumn align="center" title="重量(kg)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.weight"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="体积(m^3)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.volume"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.firstBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.secondBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
</template>
<VxeColumn
v-if="formData?.specType"
align="center"
fixed="right"
title="操作"
width="100"
> >
<template #default="{ row }"> <VxeColumn align="center" title="图片" min-width="120">
<Button v-if="isBatch" type="link" size="small" @click="batchAdd"> <template #default="{ row }">
批量添加 <ImageUpload
</Button> v-model:value="row.picUrl"
<Button v-else type="link" size="small" danger @click="deleteSku(row)"> :max-number="1"
删除 :max-size="2"
</Button> :show-description="false"
/>
</template>
</VxeColumn>
<template v-if="formData?.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
:title="item.label"
align="center"
min-width="120"
>
<template #default="{ row }">
<span class="font-bold text-[#40aaff]">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</VxeColumn>
</template> </template>
</VxeColumn> <VxeColumn align="center" title="商品条码" min-width="168">
</VxeTable> <template #default="{ row }">
<Input v-model:value="row.barCode" class="w-full" />
<!-- 情况二详情 --> </template>
<VxeTable </VxeColumn>
v-if="isDetail" <VxeColumn align="center" title="销售价" min-width="168">
ref="activitySkuListRef" <template #default="{ row }">
:data="formData?.skus || []" <InputNumber
border v-model:value="row.price"
max-height="500" :min="0"
size="small" :precision="2"
class="w-full" :step="0.1"
:checkbox-config="isComponent ? { reserve: true } : undefined" class="w-full"
@checkbox-change="handleSelectionChange" />
@checkbox-all="handleSelectionChange" </template>
> </VxeColumn>
<VxeColumn v-if="isComponent" type="checkbox" width="45" /> <VxeColumn align="center" title="市场价" min-width="168">
<VxeColumn align="center" title="图片" min-width="80"> <template #default="{ row }">
<template #default="{ row }"> <InputNumber
<Image v-model:value="row.marketPrice"
v-if="row.picUrl" :min="0"
:src="row.picUrl" :precision="2"
class="h-[50px] w-[50px] cursor-pointer" :step="0.1"
:preview="true" class="w-full"
/> />
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.costPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" min-width="168">
<template #default="{ row }">
<InputNumber v-model:value="row.stock" :min="0" class="w-full" />
</template>
</VxeColumn>
<VxeColumn align="center" title="重量(kg)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.weight"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="体积(m^3)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.volume"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.firstBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" min-width="168">
<template #default="{ row }">
<InputNumber
v-model:value="row.secondBrokeragePrice"
:min="0"
:precision="2"
:step="0.1"
class="w-full"
/>
</template>
</VxeColumn>
</template> </template>
</VxeColumn>
<template v-if="formData?.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<VxeColumn <VxeColumn
v-for="(item, index) in tableHeaders" v-if="formData?.specType"
:key="index"
:title="item.label"
align="center" align="center"
min-width="80" fixed="right"
title="操作"
width="100"
> >
<template #default="{ row }"> <template #default="{ row }">
<span class="font-bold text-[#40aaff]"> <Button v-if="isBatch" type="link" size="small" @click="batchAdd">
{{ row.properties?.[index]?.valueName }} 批量添加
</span> </Button>
<Button
v-else
type="link"
size="small"
danger
@click="deleteSku(row)"
>
删除
</Button>
</template> </template>
</VxeColumn> </VxeColumn>
</template> </VxeTable>
<VxeColumn align="center" title="商品条码" min-width="100">
<template #default="{ row }">
{{ row.barCode }}
</template>
</VxeColumn>
<VxeColumn align="center" title="销售价(元)" min-width="80">
<template #default="{ row }">
{{ row.price }}
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价(元)" min-width="80">
<template #default="{ row }">
{{ row.marketPrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" min-width="80">
<template #default="{ row }">
{{ row.costPrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" min-width="80">
<template #default="{ row }">
{{ row.stock }}
</template>
</VxeColumn>
<VxeColumn align="center" title="重量(kg)" min-width="80">
<template #default="{ row }">
{{ row.weight }}
</template>
</VxeColumn>
<VxeColumn align="center" title="体积(m^3)" min-width="80">
<template #default="{ row }">
{{ row.volume }}
</template>
</VxeColumn>
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" min-width="80">
<template #default="{ row }">
{{ row.firstBrokeragePrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" min-width="80">
<template #default="{ row }">
{{ row.secondBrokeragePrice }}
</template>
</VxeColumn>
</template>
</VxeTable>
<!-- 情况作为活动组件 --> <!-- 情况详情 -->
<VxeTable <VxeTable
v-if="isActivityComponent" v-if="isDetail"
:data="formData?.skus || []" ref="activitySkuListRef"
border :data="formData?.skus || []"
max-height="500" border
size="small" max-height="500"
class="w-full" size="small"
> class="w-full"
<VxeColumn v-if="isComponent" type="checkbox" width="45" /> :checkbox-config="isComponent ? { reserve: true } : undefined"
<VxeColumn align="center" title="图片" min-width="80"> @checkbox-change="handleSelectionChange"
<template #default="{ row }"> @checkbox-all="handleSelectionChange"
<Image >
:src="row.picUrl" <VxeColumn v-if="isComponent" type="checkbox" width="45" />
class="h-[60px] w-[60px] cursor-pointer" <VxeColumn align="center" title="图片" min-width="120">
:preview="true"
/>
</template>
</VxeColumn>
<template v-if="formData?.specType">
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
:title="item.label"
align="center"
min-width="80"
>
<template #default="{ row }"> <template #default="{ row }">
<span class="font-bold text-[#40aaff]"> <Image
{{ row.properties?.[index]?.valueName }} v-if="row.picUrl"
</span> :src="row.picUrl"
class="h-[50px] w-[50px] cursor-pointer"
:preview="true"
/>
</template> </template>
</VxeColumn> </VxeColumn>
</template> <template v-if="formData?.specType && !isBatch">
<VxeColumn align="center" title="商品条码" min-width="100"> <!-- 根据商品属性动态添加 -->
<template #default="{ row }"> <VxeColumn
{{ row.barCode }} v-for="(item, index) in tableHeaders"
:key="index"
:title="item.label"
align="center"
min-width="80"
>
<template #default="{ row }">
<span class="font-bold text-[#40aaff]">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</VxeColumn>
</template> </template>
</VxeColumn> <VxeColumn align="center" title="商品条码" min-width="100">
<VxeColumn align="center" title="销售价(元)" min-width="80"> <template #default="{ row }">
<template #default="{ row }"> {{ row.barCode }}
{{ formatToFraction(row.price) }} </template>
</VxeColumn>
<VxeColumn align="center" title="销售价(元)" min-width="80">
<template #default="{ row }">
{{ row.price }}
</template>
</VxeColumn>
<VxeColumn align="center" title="市场价(元)" min-width="80">
<template #default="{ row }">
{{ row.marketPrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" min-width="80">
<template #default="{ row }">
{{ row.costPrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" min-width="80">
<template #default="{ row }">
{{ row.stock }}
</template>
</VxeColumn>
<VxeColumn align="center" title="重量(kg)" min-width="80">
<template #default="{ row }">
{{ row.weight }}
</template>
</VxeColumn>
<VxeColumn align="center" title="体积(m^3)" min-width="80">
<template #default="{ row }">
{{ row.volume }}
</template>
</VxeColumn>
<template v-if="formData?.subCommissionType">
<VxeColumn align="center" title="一级返佣(元)" min-width="80">
<template #default="{ row }">
{{ row.firstBrokeragePrice }}
</template>
</VxeColumn>
<VxeColumn align="center" title="二级返佣(元)" min-width="80">
<template #default="{ row }">
{{ row.secondBrokeragePrice }}
</template>
</VxeColumn>
</template> </template>
</VxeColumn> </VxeTable>
<VxeColumn align="center" title="市场价(元)" min-width="80">
<template #default="{ row }"> <!-- 情况三作为活动组件 -->
{{ formatToFraction(row.marketPrice) }} <VxeTable
v-if="isActivityComponent"
:data="formData?.skus || []"
border
max-height="500"
size="small"
class="w-full"
>
<VxeColumn v-if="isComponent" type="checkbox" width="45" />
<VxeColumn align="center" title="图片" min-width="120">
<template #default="{ row }">
<Image
:src="row.picUrl"
class="h-[60px] w-[60px] cursor-pointer"
:preview="true"
/>
</template>
</VxeColumn>
<template v-if="formData?.specType">
<!-- 根据商品属性动态添加 -->
<VxeColumn
v-for="(item, index) in tableHeaders"
:key="index"
:title="item.label"
align="center"
min-width="80"
>
<template #default="{ row }">
<span class="font-bold text-[#40aaff]">
{{ row.properties?.[index]?.valueName }}
</span>
</template>
</VxeColumn>
</template> </template>
</VxeColumn> <VxeColumn align="center" title="商品条码" min-width="100">
<VxeColumn align="center" title="成本价(元)" min-width="80"> <template #default="{ row }">
<template #default="{ row }"> {{ row.barCode }}
{{ formatToFraction(row.costPrice) }} </template>
</template> </VxeColumn>
</VxeColumn> <VxeColumn align="center" title="销售价(元)" min-width="80">
<VxeColumn align="center" title="库存" min-width="80"> <template #default="{ row }">
<template #default="{ row }"> {{ formatToFraction(row.price) }}
{{ row.stock }} </template>
</template> </VxeColumn>
</VxeColumn> <VxeColumn align="center" title="市场价(元)" min-width="80">
<!-- 方便扩展每个活动配置的属性不一样 --> <template #default="{ row }">
<slot name="extension"></slot> {{ formatToFraction(row.marketPrice) }}
</VxeTable> </template>
</VxeColumn>
<VxeColumn align="center" title="成本价(元)" min-width="80">
<template #default="{ row }">
{{ formatToFraction(row.costPrice) }}
</template>
</VxeColumn>
<VxeColumn align="center" title="库存" min-width="80">
<template #default="{ row }">
{{ row.stock }}
</template>
</VxeColumn>
<!-- 方便扩展每个活动配置的属性不一样 -->
<slot name="extension"></slot>
</VxeTable>
</div>
</template> </template>

View File

@@ -6,8 +6,9 @@ import type { MallSpuApi } from '#/api/mall/product/spu';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { fenToYuan } from '@vben/utils';
import { message } from 'ant-design-vue'; import { Input, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSpu } from '#/api/mall/product/spu'; import { getSpu } from '#/api/mall/product/spu';
@@ -23,13 +24,6 @@ const emit = defineEmits<{
const selectedSkuId = ref<number>(); const selectedSkuId = ref<number>();
const spuId = ref<number>(); const spuId = ref<number>();
// 价格格式化:分转元
const fenToYuan = (price?: number | string) => {
const numPrice =
typeof price === 'string' ? Number.parseFloat(price) : price || 0;
return (numPrice / 100).toFixed(2);
};
// 配置列 // 配置列
const gridColumns = computed<VxeGridProps['columns']>(() => [ const gridColumns = computed<VxeGridProps['columns']>(() => [
{ {
@@ -102,11 +96,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
}); });
// 处理选中 // 处理选中
const handleSelected = (row: MallSpuApi.Sku) => { function handleSelected(row: MallSpuApi.Sku) {
emit('change', row); emit('change', row);
modalApi.close(); modalApi.close();
selectedSkuId.value = undefined; selectedSkuId.value = undefined;
}; }
// 初始化弹窗 // 初始化弹窗
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
@@ -133,7 +127,7 @@ const [Modal, modalApi] = useVbenModal({
<Grid> <Grid>
<!-- 单选列 --> <!-- 单选列 -->
<template #radio-column="{ row }"> <template #radio-column="{ row }">
<input <Input
v-model="selectedSkuId" v-model="selectedSkuId"
:value="row.id" :value="row.id"
class="cursor-pointer" class="cursor-pointer"