feat:【mall 商城】SKU 选择弹窗组件【antd】100%: 迁移完成

This commit is contained in:
puhui999
2025-10-21 17:18:06 +08:00
parent 5a4ecd4ad7
commit 65e9aeed7f
2 changed files with 147 additions and 1 deletions

View File

@@ -87,7 +87,7 @@ const [Form, formApi] = useVbenForm({
// 初始化弹窗 // 初始化弹窗
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
destroyOnClose: true, // 关键:关闭时销毁弹窗,确保每次打开都是全新状态 destroyOnClose: true,
async onConfirm() { async onConfirm() {
const { valid } = await formApi.validate(); const { valid } = await formApi.validate();
if (!valid) return; if (!valid) return;

View File

@@ -0,0 +1,146 @@
<!-- SKU 选择弹窗组件 -->
<script lang="ts" setup>
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getSpu } from '#/api/mall/product/spu';
interface SpuData {
spuId: number;
}
const emit = defineEmits<{
change: [sku: MallSpuApi.Sku];
}>();
const selectedSkuId = 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']>(() => [
{
field: 'id',
title: '#',
width: 60,
align: 'center',
slots: { default: 'radio-column' },
},
{
field: 'picUrl',
title: '图片',
width: 100,
align: 'center',
cellRender: {
name: 'CellImage',
},
},
{
field: 'properties',
title: '规格',
minWidth: 120,
align: 'center',
formatter: ({ cellValue }) => {
return (
cellValue?.map((p: MallSpuApi.Property) => p.valueName)?.join(' ') ||
'-'
);
},
},
{
field: 'price',
title: '销售价(元)',
width: 120,
align: 'center',
formatter: ({ cellValue }) => {
return fenToYuan(cellValue);
},
},
]);
// 初始化表格
const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: gridColumns.value,
height: 400,
border: true,
showOverflow: true,
proxyConfig: {
ajax: {
query: async () => {
if (!spuId.value) {
return { items: [], total: 0 };
}
try {
const spu = await getSpu(spuId.value);
return {
items: spu.skus || [],
total: spu.skus?.length || 0,
};
} catch (error) {
message.error('加载 SKU 数据失败');
console.error(error);
return { items: [], total: 0 };
}
},
},
},
},
});
// 处理选中
const handleSelected = (row: MallSpuApi.Sku) => {
emit('change', row);
modalApi.close();
selectedSkuId.value = undefined;
};
// 初始化弹窗
const [Modal, modalApi] = useVbenModal({
destroyOnClose: true,
onOpenChange: async (isOpen: boolean) => {
if (!isOpen) {
selectedSkuId.value = undefined;
spuId.value = undefined;
return;
}
const data = modalApi.getData<SpuData>();
if (data?.spuId) {
spuId.value = data.spuId;
// 触发数据查询
await gridApi.query();
}
},
});
</script>
<template>
<Modal class="w-[700px]" title="选择规格">
<Grid>
<!-- 单选列 -->
<template #radio-column="{ row }">
<input
v-model="selectedSkuId"
:value="row.id"
class="cursor-pointer"
type="radio"
@change="handleSelected(row)"
/>
</template>
</Grid>
</Modal>
</template>