feat:【antd】【mall】 优化积分商城活动表单

This commit is contained in:
puhui999
2025-11-25 18:22:00 +08:00
parent 26416b0acd
commit 46129c53af
3 changed files with 87 additions and 82 deletions

View File

@@ -5,7 +5,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallCategoryApi } from '#/api/mall/product/category'; import type { MallCategoryApi } from '#/api/mall/product/category';
import type { MallSpuApi } from '#/api/mall/product/spu'; import type { MallSpuApi } from '#/api/mall/product/spu';
import { computed, onMounted, ref } from 'vue'; import { computed, nextTick, onMounted, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';
@@ -86,34 +86,21 @@ function selectSku(val: MallSpuApi.Sku[]) {
} }
/** 处理 SPU 选择变化 */ /** 处理 SPU 选择变化 */
function selectSpu(val: MallSpuApi.Spu[]) { function selectSpu(row: MallSpuApi.Spu) {
if (val.length === 0) { if (!row) {
selectedSpuId.value = 0; selectedSpuId.value = 0;
return; return;
} }
// 只选择一个 selectedSpuId.value = row.id!;
const firstId = val[0]?.id;
if (firstId !== undefined) {
selectedSpuId.value = firstId;
}
// 切换选择 spu 如果有选择的 sku 则清空,确保选择的 sku 是对应的 spu 下面的 // 切换选择 spu 如果有选择的 sku 则清空,确保选择的 sku 是对应的 spu 下面的
if (selectedSkuIds.value.length > 0) { if (selectedSkuIds.value.length > 0) {
selectedSkuIds.value = []; selectedSkuIds.value = [];
} }
// 如果大于1个
if (val.length > 1) {
// 清空选择
gridApi.grid.clearCheckboxRow();
// 变更为最后一次选择的
const lastRow = val.pop();
if (lastRow) {
gridApi.grid.setCheckboxRow(lastRow, true);
}
return;
}
// 自动展开选中的 SPU // 自动展开选中的 SPU
if (props.isSelectSku && val[0]) { if (props.isSelectSku) {
expandChange(val[0], [val[0]]); expandChange(row, [row]);
} }
} }
@@ -181,33 +168,20 @@ async function expandChange(
isExpand.value = true; isExpand.value = true;
} }
/** 确认选择时的触发事件 */
function confirm() {
if (selectedSpuId.value === 0) {
message.warning('没有选择任何商品');
return;
}
if (props.isSelectSku && selectedSkuIds.value.length === 0) {
message.warning('没有选择任何商品属性');
return;
}
// 返回各自 id 列表
props.isSelectSku
? emit('confirm', selectedSpuId.value, selectedSkuIds.value)
: emit('confirm', selectedSpuId.value);
// 关闭弹窗
modalApi.close();
selectedSpuId.value = 0;
selectedSkuIds.value = [];
}
/** 搜索表单 Schema */ /** 搜索表单 Schema */
const formSchema = computed(() => useGridFormSchema(categoryTreeList)); const formSchema = computed(() => useGridFormSchema(categoryTreeList));
/** 表格列配置 */ /** 表格列配置 */
const gridColumns = computed<VxeTableGridOptions['columns']>(() => const gridColumns = computed<VxeTableGridOptions['columns']>(() => {
useGridColumns(props.isSelectSku), const columns = useGridColumns(props.isSelectSku);
); // 将 checkbox 替换为 radio
return columns?.map((col) => {
if (col.type === 'checkbox') {
return { ...col, type: 'radio' };
}
return col;
});
});
/** 初始化列表 */ /** 初始化列表 */
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
@@ -218,10 +192,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
gridOptions: { gridOptions: {
columns: gridColumns.value, columns: gridColumns.value,
height: 500, height: 800,
border: true, border: true,
checkboxConfig: { radioConfig: {
reserve: true, reserve: true,
highlight: true,
}, },
rowConfig: { rowConfig: {
keyField: 'id', keyField: 'id',
@@ -247,8 +222,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
}, },
gridEvents: { gridEvents: {
checkboxChange: ({ records }: { records: unknown[] }) => { radioChange: ({ row }: { row: MallSpuApi.Spu }) => {
selectSpu(records as MallSpuApi.Spu[]); selectSpu(row);
}, },
toggleRowExpand: ({ toggleRowExpand: ({
row, row,
@@ -269,15 +244,27 @@ const [Grid, gridApi] = useVbenVxeGrid({
/** 初始化弹窗 */ /** 初始化弹窗 */
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
destroyOnClose: true, destroyOnClose: true,
onConfirm: confirm, onConfirm: () => {
if (selectedSpuId.value === 0) {
message.warning('没有选择任何商品');
return;
}
if (props.isSelectSku && selectedSkuIds.value.length === 0) {
message.warning('没有选择任何商品属性');
return;
}
// 返回各自 id 列表
props.isSelectSku
? emit('confirm', selectedSpuId.value, selectedSkuIds.value)
: emit('confirm', selectedSpuId.value);
// 重置选中状态
selectedSpuId.value = 0;
selectedSkuIds.value = [];
modalApi.close();
},
async onOpenChange(isOpen: boolean) { async onOpenChange(isOpen: boolean) {
if (!isOpen) { if (!isOpen) {
await gridApi.grid.clearCheckboxRow();
// 关闭所有展开的行
const tableData = gridApi.grid.getTableData().fullData;
tableData.forEach((row: MallSpuApi.Spu) => {
gridApi.grid.setRowExpand(row, false);
});
selectedSpuId.value = 0; selectedSpuId.value = 0;
selectedSkuIds.value = []; selectedSkuIds.value = [];
spuData.value = undefined; spuData.value = undefined;
@@ -285,8 +272,11 @@ const [Modal, modalApi] = useVbenModal({
isExpand.value = false; isExpand.value = false;
return; return;
} }
// 打开时查询数据 // 等待 Grid 组件完全初始化后再查询数据
await gridApi.query(); await nextTick();
if (gridApi.grid) {
await gridApi.query();
}
}, },
}); });

View File

@@ -113,6 +113,7 @@ export function useFormSchema(): VbenFormSchema[] {
placeholder: '请输入排序', placeholder: '请输入排序',
class: '!w-full', class: '!w-full',
}, },
defaultValue: 0,
rules: 'required', rules: 'required',
}, },
{ {

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { MallSpuApi } from '#/api/mall/product/spu';
import type { MallPointActivityApi } from '#/api/mall/promotion/point'; import type { MallPointActivityApi } from '#/api/mall/promotion/point';
import type { import type {
RuleConfig, RuleConfig,
@@ -8,7 +9,7 @@ import type {
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui'; import { useVbenModal } from '@vben/common-ui';
import { convertToInteger, formatToFraction } from '@vben/utils'; import { cloneDeep, convertToInteger, formatToFraction } from '@vben/utils';
import { Button, InputNumber, message } from 'ant-design-vue'; import { Button, InputNumber, message } from 'ant-design-vue';
@@ -73,14 +74,12 @@ const ruleConfig: RuleConfig[] = [
}, },
]; // SKU 规则配置 ]; // SKU 规则配置
const spuList = ref<any[]>([]); // 选择的 SPU 列表 const spuList = ref<MallSpuApi.Spu[]>([]); // 选择的 SPU 列表
// TODO @puhui999有问题 const spuPropertyList = ref<SpuProperty<MallSpuApi.Spu>[]>([]); // SPU 属性列表
const spuPropertyList = ref<SpuProperty<any>[]>([]); // SPU 属性列表
/** 打开商品选择器 */ /** 打开商品选择器 */
// TODO @puhui999spuSkuSelectRef.value.open is not a function
function openSpuSelect() { function openSpuSelect() {
spuSkuSelectRef.value.open(); spuSkuSelectRef.value?.open();
} }
/** 选择商品后的回调 */ /** 选择商品后的回调 */
@@ -108,7 +107,7 @@ async function getSpuDetails(
? res.skus ? res.skus
: res.skus?.filter((sku) => skuIds.includes(sku.id!)); : res.skus?.filter((sku) => skuIds.includes(sku.id!));
// 为每个 SKU 配置积分商城相关的配置 // 为每个 SKU 配置积分商城相关的配置
selectSkus?.forEach((sku: any) => { selectSkus?.forEach((sku) => {
let config: MallPointActivityApi.PointProduct = { let config: MallPointActivityApi.PointProduct = {
skuId: sku.id!, skuId: sku.id!,
stock: 0, stock: 0,
@@ -124,22 +123,26 @@ async function getSpuDetails(
} }
config = product || config; config = product || config;
} }
sku.productConfig = config; // 动态添加 productConfig 属性到 SKU
(
sku as MallSpuApi.Sku & {
productConfig: MallPointActivityApi.PointProduct;
}
).productConfig = config;
}); });
res.skus = selectSkus; res.skus = selectSkus;
// TODO @puhui999有问题 // 构建 SPU 属性列表
// const spuProperties: SpuProperty[] = []; const spuProperties: SpuProperty<MallSpuApi.Spu>[] = [
const spuProperties: any[] = []; {
spuProperties.push({ spuId: res.id!,
spuId: res.id!, spuDetail: res,
spuDetail: res, propertyList: getPropertyList(res),
propertyList: getPropertyList(res), },
}); ];
// TODO @puhui999貌似直接 = 下面的,不用 push // 直接赋值,因为每次只选择一个 SPU
spuList.value.push(res); spuList.value = [res];
// TODO @puhui999貌似直接 = 下面的,不用 push
spuPropertyList.value = spuProperties; spuPropertyList.value = spuProperties;
} }
@@ -153,9 +156,10 @@ const [Modal, modalApi] = useVbenModal({
} }
modalApi.lock(); modalApi.lock();
try { try {
// 获取积分商城商品配置 // 获取积分商城商品配置(深拷贝避免直接修改原对象)
const products: MallPointActivityApi.PointProduct[] = const products: MallPointActivityApi.PointProduct[] = cloneDeep(
spuAndSkuListRef.value?.getSkuConfigs('productConfig') || []; spuAndSkuListRef.value?.getSkuConfigs('productConfig') || [],
);
// 价格需要转为分 // 价格需要转为分
products.forEach((item) => { products.forEach((item) => {
item.price = convertToInteger(item.price); item.price = convertToInteger(item.price);
@@ -182,11 +186,22 @@ const [Modal, modalApi] = useVbenModal({
spuPropertyList.value = []; spuPropertyList.value = [];
return; return;
} }
// 加载数据 // 重置表单数据(新增和编辑模式都需要)
formData.value = undefined;
spuList.value = [];
spuPropertyList.value = [];
// 加载数据(仅编辑模式)
const data = modalApi.getData<MallPointActivityApi.PointActivity>(); const data = modalApi.getData<MallPointActivityApi.PointActivity>();
if (!data || !data.id) { if (!data || !data.id) {
// 新增模式:重置表单字段
await formApi.setValues({
sort: 0,
remark: '',
spuId: undefined,
});
return; return;
} }
// 编辑模式:加载数据
modalApi.lock(); modalApi.lock();
try { try {
formData.value = await getPointActivity(data.id); formData.value = await getPointActivity(data.id);
@@ -216,11 +231,10 @@ const [Modal, modalApi] = useVbenModal({
<!-- SPU SKU 列表展示 --> <!-- SPU SKU 列表展示 -->
<SpuAndSkuList <SpuAndSkuList
v-if="spuList.length > 0"
ref="spuAndSkuListRef" ref="spuAndSkuListRef"
:rule-config="ruleConfig" :rule-config="ruleConfig"
:spu-list="spuList" :spu-list="spuList"
:spu-property-list="spuPropertyList" :spu-property-list-p="spuPropertyList"
class="mt-4" class="mt-4"
> >
<!-- 扩展列积分商城特有配置 --> <!-- 扩展列积分商城特有配置 -->