feat:【antd】mall 商品列表的优化代码

This commit is contained in:
YunaiV
2025-10-21 23:48:41 +08:00
parent 1286623863
commit a7b9292b8d
2 changed files with 110 additions and 113 deletions

View File

@@ -2,11 +2,17 @@ import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallSpuApi } from '#/api/mall/product/spu';
import { handleTree } from '@vben/utils';
import { fenToYuan, handleTree, treeToString } from '@vben/utils';
import { getCategoryList } from '#/api/mall/product/category';
import { getRangePickerDefaultProps } from '#/utils';
/** 关联数据 */
let categoryList: any[] = [];
getCategoryList({}).then((data) => {
categoryList = handleTree(data, 'id', 'parentId', 'children');
});
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
@@ -14,16 +20,19 @@ export function useGridFormSchema(): VbenFormSchema[] {
fieldName: 'name',
label: '商品名称',
component: 'Input',
componentProps: {
placeholder: '请输入商品名称',
allowClear: true,
},
},
{
fieldName: 'categoryId',
label: '商品分类',
component: 'ApiTreeSelect',
componentProps: {
api: async () => {
const res = await getCategoryList({});
return handleTree(res, 'id', 'parentId', 'children');
},
placeholder: '请选择商品分类',
allowClear: true,
options: categoryList,
fieldNames: { label: 'name', value: 'id', children: 'children' },
},
},
@@ -47,16 +56,11 @@ export function useGridColumns(
) => PromiseLike<boolean | undefined>,
): VxeTableGridOptions['columns'] {
return [
{
type: 'expand',
width: 80,
slots: { content: 'expand_content' },
fixed: 'left',
},
{
field: 'id',
title: '商品编号',
fixed: 'left',
minWidth: 100,
},
{
field: 'name',
@@ -67,30 +71,23 @@ export function useGridColumns(
{
field: 'picUrl',
title: '商品图片',
minWidth: 100,
cellRender: {
name: 'CellImage',
},
},
{
field: 'price',
title: '价格',
formatter: 'formatAmount2',
},
{
field: 'salesCount',
title: '销量',
},
{
field: 'stock',
title: '库存',
},
{
field: 'sort',
title: '排序',
field: 'categoryId',
title: '商品分类',
minWidth: 150,
formatter: ({ row }) => {
return treeToString(categoryList, row.categoryId);
},
},
{
field: 'status',
title: '销售状态',
minWidth: 100,
cellRender: {
attrs: { beforeChange: onStatusChange },
name: 'CellSwitch',
@@ -102,9 +99,57 @@ export function useGridColumns(
},
},
},
{
field: 'price',
title: '价格',
minWidth: 100,
formatter: 'formatAmount2',
},
{
field: 'marketPrice',
title: '市场价',
minWidth: 100,
formatter: ({ row }) => {
return `${fenToYuan(row.marketPrice)}`;
},
},
{
field: 'costPrice',
title: '成本价',
minWidth: 100,
formatter: ({ row }) => {
return `${fenToYuan(row.costPrice)}`;
},
},
{
field: 'salesCount',
title: '销量',
minWidth: 80,
},
{
field: 'virtualSalesCount',
title: '虚拟销量',
minWidth: 100,
},
{
field: 'stock',
title: '库存',
minWidth: 80,
},
{
field: 'browseCount',
title: '浏览量',
minWidth: 100,
},
{
field: 'sort',
title: '排序',
minWidth: 80,
},
{
field: 'createTime',
title: '创建时间',
minWidth: 160,
formatter: 'formatDateTime',
},
{

View File

@@ -7,17 +7,11 @@ import { useRoute, useRouter } from 'vue-router';
import { confirm, DocAlert, Page } from '@vben/common-ui';
import { ProductSpuStatusEnum } from '@vben/constants';
import {
downloadFileFromBlobPart,
fenToYuan,
handleTree,
treeToString,
} from '@vben/utils';
import { downloadFileFromBlobPart } from '@vben/utils';
import { Descriptions, message, Tabs } from 'ant-design-vue';
import { message, Tabs } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getCategoryList } from '#/api/mall/product/category';
import {
deleteSpu,
exportSpu,
@@ -32,10 +26,6 @@ import { useGridColumns, useGridFormSchema } from './data';
const { push } = useRouter();
const route = useRoute();
const tabType = ref(0);
// TODO @AI放到 data.ts 里;
const categoryList = ref();
const tabsData = ref([
{
name: '出售中',
@@ -62,7 +52,7 @@ const tabsData = ref([
type: 4,
count: 0,
},
]); // tabs 数据
]);
/** 刷新表格 */
async function handleRefresh() {
@@ -70,13 +60,19 @@ async function handleRefresh() {
await getTabCount();
}
/** 导出表格 */
async function handleExport() {
const data = await exportSpu(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '商品.xls', source: data });
}
/** 获得每个 Tab 的数量 */
async function getTabCount() {
const res = await getTabsCount();
for (const objName in res) {
const index = Number(objName);
if (tabsData.value[index]) {
tabsData.value[index].count = res[objName] as number;
tabsData.value[index].count = res[objName]!;
}
}
}
@@ -86,12 +82,6 @@ function handleCreate() {
push({ name: 'ProductSpuAdd' });
}
/** 导出表格 */
async function handleExport() {
const data = await exportSpu(await gridApi.formApi.getValues());
downloadFileFromBlobPart({ fileName: '商品.xls', source: data });
}
/** 编辑商品 */
function handleEdit(row: MallSpuApi.Spu) {
push({ name: 'ProductSpuEdit', params: { id: row.id } });
@@ -104,34 +94,14 @@ async function handleDelete(row: MallSpuApi.Spu) {
duration: 0,
});
try {
await deleteSpu(row.id as number);
message.success({
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
});
handleRefresh();
await deleteSpu(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
await handleRefresh();
} finally {
hideLoading();
}
}
/** 添加到仓库 / 回收站的状态 */
async function handleStatus02Change(row: MallSpuApi.Spu, newStatus: number) {
// 二次确认
const text =
newStatus === ProductSpuStatusEnum.RECYCLE.status
? '加入到回收站'
: '恢复到仓库';
confirm(`确认要"${row.name}"${text}吗?`)
.then(async () => {
await updateStatus({ id: row.id as number, status: newStatus });
message.success(`${text}成功`);
await handleRefresh();
})
.catch(() => {
message.error(`${text}失败`);
});
}
/** 更新状态 */
async function handleStatusChange(
newStatus: number,
@@ -139,14 +109,14 @@ async function handleStatusChange(
): Promise<boolean | undefined> {
return new Promise((resolve, reject) => {
// 二次确认
const text = row.status ? '上架' : '下架';
const text = newStatus ? '上架' : '下架';
confirm({
content: `确认要${text + row.name}吗?`,
})
.then(async () => {
// 更新状态
const res = await updateStatus({
id: row.id as number,
id: row.id!,
status: newStatus,
});
if (res) {
@@ -163,6 +133,28 @@ async function handleStatusChange(
});
}
/** 添加到仓库 / 回收站的状态 */
async function handleStatus02Change(row: MallSpuApi.Spu, newStatus: number) {
const text =
newStatus === ProductSpuStatusEnum.RECYCLE.status
? '加入到回收站'
: '恢复到仓库';
await confirm({
content: `确认要"${row.name}"${text}吗?`,
});
const hideLoading = message.loading({
content: `正在${text}中...`,
duration: 0,
});
try {
await updateStatus({ id: row.id!, status: newStatus });
message.success(`${text}成功`);
await handleRefresh();
} finally {
hideLoading();
}
}
/** 查看商品详情 */
function handleDetail(row: MallSpuApi.Spu) {
push({ name: 'ProductSpuDetail', params: { id: row.id } });
@@ -175,12 +167,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: {
columns: useGridColumns(handleStatusChange),
height: 'auto',
cellConfig: {
height: 80,
},
expandConfig: {
height: 100,
},
keepSource: true,
proxyConfig: {
ajax: {
@@ -196,7 +182,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
},
rowConfig: {
keyField: 'id',
resizable: true,
isHover: true,
},
toolbarConfig: {
refresh: true,
@@ -217,9 +203,8 @@ onMounted(async () => {
categoryId: Number(route.query.categoryId),
});
}
// 获得每个 Tab 的数量
await getTabCount();
const categoryRes = await getCategoryList({});
categoryList.value = handleTree(categoryRes, 'id', 'parentId', 'children');
});
</script>
@@ -262,39 +247,6 @@ onMounted(async () => {
]"
/>
</template>
<template #expand_content="{ row }">
<Descriptions
:column="4"
class="mx-32 my-4 w-1/2"
:label-style="{
width: '100px',
fontWeight: 'bold',
fontSize: '14px',
color: '#99a9bf',
}"
:content-style="{ width: '100px', fontSize: '14px' }"
>
<Descriptions.Item label="商品分类">
{{ treeToString(categoryList, row.categoryId!) }}
</Descriptions.Item>
<Descriptions.Item label="商品名称">
{{ row.name }}
</Descriptions.Item>
<Descriptions.Item label="市场价">
{{ fenToYuan(row.marketPrice as number) }}
</Descriptions.Item>
<Descriptions.Item label="成本价">
{{ fenToYuan(row.costPrice as number) }}
</Descriptions.Item>
<Descriptions.Item label="浏览量">
{{ row.browseCount }}
</Descriptions.Item>
<Descriptions.Item label="虚拟销量">
{{ row.virtualSalesCount }}
</Descriptions.Item>
</Descriptions>
</template>
<template #actions="{ row }">
<TableAction
:actions="[