feat:【ele】【mall】spu 选择组件优化
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
<!-- SKU 选择弹窗组件 -->
|
<!-- SKU 选择弹窗组件 -->
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
|
||||||
import type { MallSpuApi } from '#/api/mall/product/spu';
|
import type { MallSpuApi } from '#/api/mall/product/spu';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { ElDialog } from 'element-plus';
|
||||||
import { fenToYuan } from '@vben/utils';
|
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
import { useSkuGridColumns } from './spu-select-data';
|
||||||
|
|
||||||
interface SpuData {
|
interface SpuData {
|
||||||
spuId: number;
|
spuId: number;
|
||||||
}
|
}
|
||||||
@@ -19,78 +19,28 @@ const emit = defineEmits<{
|
|||||||
change: [sku: MallSpuApi.Sku];
|
change: [sku: MallSpuApi.Sku];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
const spuId = ref<number>();
|
const spuId = ref<number>();
|
||||||
|
|
||||||
/** 表格列配置 */
|
|
||||||
const gridColumns = computed<VxeGridProps['columns']>(() => [
|
|
||||||
{
|
|
||||||
type: 'radio',
|
|
||||||
width: 55,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
/** 处理选中 */
|
/** 处理选中 */
|
||||||
function handleRadioChange() {
|
function handleRadioChange() {
|
||||||
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Sku;
|
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Sku;
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
emit('change', selectedRow);
|
emit('change', selectedRow);
|
||||||
modalApi.close();
|
closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:要不要直接非 pager?
|
|
||||||
const [Grid, gridApi] = useVbenVxeGrid({
|
const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
gridOptions: {
|
gridOptions: {
|
||||||
columns: gridColumns.value,
|
columns: useSkuGridColumns(),
|
||||||
height: 400,
|
height: 400,
|
||||||
border: true,
|
border: true,
|
||||||
showOverflow: true,
|
|
||||||
radioConfig: {
|
radioConfig: {
|
||||||
reserve: true,
|
reserve: true,
|
||||||
},
|
},
|
||||||
proxyConfig: {
|
pagerConfig: {
|
||||||
ajax: {
|
enabled: false,
|
||||||
query: async () => {
|
|
||||||
if (!spuId.value) {
|
|
||||||
return { list: [], total: 0 };
|
|
||||||
}
|
|
||||||
const spu = await getSpu(spuId.value);
|
|
||||||
return {
|
|
||||||
list: spu.skus || [],
|
|
||||||
total: spu.skus?.length || 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gridEvents: {
|
gridEvents: {
|
||||||
@@ -98,26 +48,43 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
/** 关闭弹窗 */
|
||||||
destroyOnClose: true,
|
function closeModal() {
|
||||||
onOpenChange: async (isOpen: boolean) => {
|
visible.value = false;
|
||||||
if (!isOpen) {
|
gridApi.grid.clearRadioRow();
|
||||||
gridApi.grid.clearRadioRow();
|
spuId.value = undefined;
|
||||||
spuId.value = undefined;
|
}
|
||||||
return;
|
|
||||||
}
|
/** 打开弹窗 */
|
||||||
const data = modalApi.getData<SpuData>();
|
async function openModal(data?: SpuData) {
|
||||||
if (!data?.spuId) {
|
if (!data?.spuId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
spuId.value = data.spuId;
|
spuId.value = data.spuId;
|
||||||
await gridApi.query();
|
visible.value = true;
|
||||||
},
|
if (!spuId.value) {
|
||||||
|
gridApi.grid?.reloadData([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const spu = await getSpu(spuId.value);
|
||||||
|
gridApi.grid?.reloadData(spu.skus || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 对外暴露的方法 */
|
||||||
|
defineExpose({
|
||||||
|
open: openModal,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-[700px]" title="选择规格">
|
<ElDialog
|
||||||
|
v-model="visible"
|
||||||
|
title="选择规格"
|
||||||
|
width="700px"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:append-to-body="true"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
<Grid />
|
<Grid />
|
||||||
</Modal>
|
</ElDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import type { VxeGridProps } 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 { handleTree } from '@vben/utils';
|
import { handleTree } from '@vben/utils';
|
||||||
|
|
||||||
|
import { ElDialog } from 'element-plus';
|
||||||
|
|
||||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getCategoryList } from '#/api/mall/product/category';
|
import { getCategoryList } from '#/api/mall/product/category';
|
||||||
import { getSpuPage } from '#/api/mall/product/spu';
|
import { getSpuPage } from '#/api/mall/product/spu';
|
||||||
@@ -30,12 +31,16 @@ const emit = defineEmits<{
|
|||||||
const categoryList = ref<MallCategoryApi.Category[]>([]); // 分类列表
|
const categoryList = ref<MallCategoryApi.Category[]>([]); // 分类列表
|
||||||
const categoryTreeList = ref<any[]>([]); // 分类树
|
const categoryTreeList = ref<any[]>([]); // 分类树
|
||||||
|
|
||||||
|
/** 弹窗显示状态 */
|
||||||
|
const visible = ref(false);
|
||||||
|
const initData = ref<MallSpuApi.Spu | MallSpuApi.Spu[]>();
|
||||||
|
|
||||||
/** 单选:处理选中变化 */
|
/** 单选:处理选中变化 */
|
||||||
function handleRadioChange() {
|
function handleRadioChange() {
|
||||||
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Spu;
|
const selectedRow = gridApi.grid.getRadioRecord() as MallSpuApi.Spu;
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
emit('change', selectedRow);
|
emit('change', selectedRow);
|
||||||
modalApi.close();
|
closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,56 +158,65 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
/** 打开弹窗 */
|
||||||
destroyOnClose: true,
|
async function openModal(data?: MallSpuApi.Spu | MallSpuApi.Spu[]) {
|
||||||
showConfirmButton: props.multiple, // 特殊:radio 单选情况下,走 handleRadioChange 处理。
|
initData.value = data;
|
||||||
onConfirm: () => {
|
visible.value = true;
|
||||||
const selectedRows = gridApi.grid.getCheckboxRecords() as MallSpuApi.Spu[];
|
await nextTick();
|
||||||
emit('change', selectedRows);
|
// 1. 查询数据
|
||||||
modalApi.close();
|
await gridApi.query();
|
||||||
},
|
// 2. 设置已选中行
|
||||||
async onOpenChange(isOpen: boolean) {
|
const tableData = gridApi.grid.getTableData().fullData;
|
||||||
if (!isOpen) {
|
if (
|
||||||
await gridApi.grid.clearCheckboxRow();
|
props.multiple &&
|
||||||
await gridApi.grid.clearRadioRow();
|
Array.isArray(initData.value) &&
|
||||||
return;
|
initData.value.length > 0
|
||||||
}
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
// 1. 先查询数据
|
(initData.value as unknown as MallSpuApi.Spu[])!.forEach((spu) => {
|
||||||
await gridApi.query();
|
|
||||||
// 2. 设置已选中行
|
|
||||||
const data = modalApi.getData<MallSpuApi.Spu | MallSpuApi.Spu[]>();
|
|
||||||
if (props.multiple && Array.isArray(data) && data.length > 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const tableData = gridApi.grid.getTableData().fullData;
|
|
||||||
data.forEach((spu) => {
|
|
||||||
const row = tableData.find(
|
|
||||||
(item: MallSpuApi.Spu) => item.id === spu.id,
|
|
||||||
);
|
|
||||||
if (row) {
|
|
||||||
gridApi.grid.setCheckboxRow(row, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 300);
|
|
||||||
} else if (!props.multiple && data && !Array.isArray(data)) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const tableData = gridApi.grid.getTableData().fullData;
|
|
||||||
const row = tableData.find(
|
const row = tableData.find(
|
||||||
(item: MallSpuApi.Spu) => item.id === data.id,
|
(item: MallSpuApi.Spu) => item.id === spu.id,
|
||||||
);
|
);
|
||||||
if (row) {
|
if (row) {
|
||||||
gridApi.grid.setRadioRow(row);
|
gridApi.grid.setCheckboxRow(row, true);
|
||||||
}
|
}
|
||||||
}, 300);
|
});
|
||||||
}
|
}, 300);
|
||||||
},
|
} else if (
|
||||||
});
|
!props.multiple &&
|
||||||
|
initData.value &&
|
||||||
|
!Array.isArray(initData.value)
|
||||||
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const row = tableData.find(
|
||||||
|
(item: MallSpuApi.Spu) =>
|
||||||
|
item.id === (initData.value as MallSpuApi.Spu).id,
|
||||||
|
);
|
||||||
|
if (row) {
|
||||||
|
gridApi.grid.setRadioRow(row);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭弹窗 */
|
||||||
|
async function closeModal() {
|
||||||
|
visible.value = false;
|
||||||
|
await gridApi.grid.clearCheckboxRow();
|
||||||
|
await gridApi.grid.clearRadioRow();
|
||||||
|
initData.value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认选择(多选模式) */
|
||||||
|
function handleConfirm() {
|
||||||
|
const selectedRows = gridApi.grid.getCheckboxRecords() as MallSpuApi.Spu[];
|
||||||
|
emit('change', selectedRows);
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
/** 对外暴露的方法 */
|
/** 对外暴露的方法 */
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open: (data?: MallSpuApi.Spu | MallSpuApi.Spu[]) => {
|
open: openModal,
|
||||||
modalApi.setData(data).open();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 初始化分类数据 */
|
/** 初始化分类数据 */
|
||||||
@@ -213,7 +227,18 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal title="选择商品" class="w-[950px]">
|
<ElDialog
|
||||||
|
v-model="visible"
|
||||||
|
title="选择商品"
|
||||||
|
width="950px"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:append-to-body="true"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
<Grid />
|
<Grid />
|
||||||
</Modal>
|
<template v-if="props.multiple" #footer>
|
||||||
|
<el-button @click="closeModal">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</ElDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user