feat(shopGoods): 支持商品分类多选及列表树形高度优化
- 商品模型增加 categoryIds 字段支持多分类,搜索参数同步支持多分类查询 - 商品列表新增分类列,展示商品所属分类名称 - 商品编辑及搜索组件商品分类选择改为多选,保持兼容单选主分类逻辑 - 商品分类树形列表SVG连线高度调小,提升界面紧凑度 - 优化分类图片尺寸,缩小预览图大小,提升视觉效果 - 更新用户推荐接口方法参数类型声明,增加类型安全保障
This commit is contained in:
@@ -31,8 +31,10 @@ export interface ShopGoods {
|
||||
canExpress?: number;
|
||||
// 商品分类
|
||||
category?: string;
|
||||
// 商品分类ID
|
||||
// 商品分类ID(主分类,向后兼容)
|
||||
categoryId?: number;
|
||||
// 商品分类ID列表(方案B:可归属多个分类)
|
||||
categoryIds?: number[];
|
||||
parentName?: string;
|
||||
categoryName?: string;
|
||||
// 一级分类
|
||||
@@ -174,6 +176,8 @@ export interface ShopGoodsCollectParam {
|
||||
export interface ShopGoodsParam extends PageParam {
|
||||
parentId?: number;
|
||||
categoryId?: number;
|
||||
// 方案B:按多个分类查询,与单值 categoryId 二选一,多值时优先
|
||||
categoryIds?: number[];
|
||||
goodsId?: number;
|
||||
status?: number;
|
||||
goodsName?: string;
|
||||
|
||||
@@ -170,7 +170,7 @@ export async function updateUserStatus(userId?: number, status?: number) {
|
||||
/**
|
||||
* 修改推荐状态
|
||||
*/
|
||||
export async function updateUserRecommend(form) {
|
||||
export async function updateUserRecommend(form: { userId: number; recommend: number }) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
SERVER_API_URL + '/system/user/recommend',
|
||||
form
|
||||
|
||||
@@ -52,15 +52,17 @@
|
||||
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
multiple
|
||||
:max-tag-count="2"
|
||||
:tree-data="navigationList"
|
||||
tree-default-expand-all
|
||||
style="width: 180px"
|
||||
style="width: 200px"
|
||||
:listHeight="700"
|
||||
placeholder="请选择分类"
|
||||
:value="where.categoryId || undefined"
|
||||
placeholder="请选择分类(可多选)"
|
||||
:value="where.categoryIds && where.categoryIds.length ? where.categoryIds : undefined"
|
||||
:dropdown-style="{ overflow: 'auto' }"
|
||||
@update:value="(value?: number) => (where.categoryId = value)"
|
||||
@change="onCategoryId"
|
||||
@update:value="(value?: number[]) => (where.categoryIds = value || [])"
|
||||
@change="onCategoryIds"
|
||||
/>
|
||||
<a-input-search
|
||||
allow-clear
|
||||
@@ -149,6 +151,7 @@
|
||||
status: undefined,
|
||||
stock: undefined,
|
||||
categoryId: undefined,
|
||||
categoryIds: [],
|
||||
merchantId: undefined,
|
||||
keywords: ''
|
||||
});
|
||||
@@ -238,9 +241,10 @@
|
||||
reload();
|
||||
};
|
||||
|
||||
// 按分类查询
|
||||
const onCategoryId = (id: number) => {
|
||||
where.categoryId = id;
|
||||
// 按分类查询(方案B:支持多选,主分类取首个)
|
||||
const onCategoryIds = (ids: number[]) => {
|
||||
where.categoryIds = ids || [];
|
||||
where.categoryId = ids && ids.length ? ids[0] : undefined;
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
|
||||
@@ -32,18 +32,20 @@
|
||||
v-model:value="form.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商品分类" name="categoryId">
|
||||
<a-form-item label="商品分类" name="categoryIds">
|
||||
<a-tree-select
|
||||
allow-clear
|
||||
multiple
|
||||
:max-tag-count="3"
|
||||
:tree-data="navigationList"
|
||||
tree-default-expand-all
|
||||
style="width: 320px"
|
||||
placeholder="请选择分类"
|
||||
:value="form.categoryId || undefined"
|
||||
placeholder="请选择分类(可多选)"
|
||||
:value="form.categoryIds && form.categoryIds.length ? form.categoryIds : undefined"
|
||||
:listHeight="700"
|
||||
:dropdown-style="{ overflow: 'auto' }"
|
||||
@update:value="(value?: number) => (form.categoryId = value)"
|
||||
@change="onCategoryId"
|
||||
@update:value="(value?: number[]) => (form.categoryIds = value || [])"
|
||||
@change="onCategoryIds"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="商品编码" name="code">
|
||||
@@ -928,12 +930,13 @@ const addEnsureTag = (item = '') => {
|
||||
ensureTagItem.value = '';
|
||||
};
|
||||
|
||||
// 选择栏目
|
||||
const onCategoryId = (id: number) => {
|
||||
form.categoryId = id;
|
||||
// 选择栏目(方案B:支持多选,主分类取列表首个,向后兼容旧字段)
|
||||
const onCategoryIds = (ids: number[]) => {
|
||||
form.categoryIds = ids || [];
|
||||
form.categoryId = ids && ids.length ? ids[0] : undefined;
|
||||
// 💾 在新增模式下,用户手动选择栏目时也保存到本地存储
|
||||
if (!isUpdate.value && id) {
|
||||
saveLastCategory(id);
|
||||
if (!isUpdate.value && ids && ids.length) {
|
||||
saveLastCategory(ids[0]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1204,6 +1207,7 @@ const form = reactive<ShopGoods>({
|
||||
canExpress: 1,
|
||||
unitName: '',
|
||||
categoryId: undefined,
|
||||
categoryIds: [],
|
||||
parentName: undefined,
|
||||
categoryName: undefined,
|
||||
specs: 0,
|
||||
@@ -1449,14 +1453,18 @@ const chooseSupplier = (item: ShopMerchant) => {
|
||||
};
|
||||
|
||||
const chooseGoodsCategory = (item: ShopGoodsCategory, value: any) => {
|
||||
form.categoryId = value[1].value;
|
||||
const cid = value[1].value as number;
|
||||
form.categoryId = cid;
|
||||
form.categoryIds = [cid];
|
||||
form.parentName = value[0].label;
|
||||
form.categoryName = value[1].label;
|
||||
};
|
||||
const chooseTakeawayCategory = (item: ShopGoodsCategory, value: any) => {
|
||||
const cid = item[0] as number;
|
||||
form.categoryParent = '店铺分类';
|
||||
form.categoryChildren = value[0].label;
|
||||
form.categoryId = item[0];
|
||||
form.categoryId = cid;
|
||||
form.categoryIds = [cid];
|
||||
};
|
||||
|
||||
const chooseImage = (data: FileRecord) => {
|
||||
@@ -1788,6 +1796,11 @@ const save = () => {
|
||||
if (form.specs === 1 && goodsSpec.value && spec.value.length) {
|
||||
goodsSpec.value.specValue = JSON.stringify(spec.value);
|
||||
}
|
||||
// 方案B:主分类兜底取多分类第一个,保证旧字段/旧逻辑兼容
|
||||
if (form.categoryIds && form.categoryIds.length && !form.categoryId) {
|
||||
form.categoryId = form.categoryIds[0];
|
||||
}
|
||||
|
||||
const formData: any = {
|
||||
...form,
|
||||
content: content.value,
|
||||
@@ -2050,15 +2063,21 @@ watch(
|
||||
if (form.ensureTag) {
|
||||
ensureTag.value = form.ensureTag.split(',');
|
||||
}
|
||||
// 🎯 优先级设置栏目:
|
||||
// 1. 如果传入了 categoryId(从栏目页面点击添加),使用传入的
|
||||
// 2. 否则使用上次保存的栏目
|
||||
if (props.data.categoryId) {
|
||||
// 🎯 优先级设置栏目(方案B:支持多分类回填):
|
||||
// 1. 如果传入了 categoryIds,使用多分类
|
||||
// 2. 否则如果传入了 categoryId,兼容单分类
|
||||
// 3. 否则使用上次保存的栏目
|
||||
if (props.data.categoryIds && props.data.categoryIds.length) {
|
||||
form.categoryIds = props.data.categoryIds;
|
||||
form.categoryId = props.data.categoryId ?? props.data.categoryIds[0];
|
||||
} else if (props.data.categoryId) {
|
||||
form.categoryId = props.data?.categoryId;
|
||||
form.categoryIds = [props.data.categoryId];
|
||||
} else {
|
||||
const lastCategory = getLastCategory();
|
||||
if (lastCategory) {
|
||||
form.categoryId = lastCategory;
|
||||
form.categoryIds = [lastCategory];
|
||||
}
|
||||
}
|
||||
await getRoleList();
|
||||
|
||||
@@ -269,6 +269,15 @@
|
||||
key: 'name',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'categoryName',
|
||||
key: 'categoryName',
|
||||
align: 'center',
|
||||
width: 160,
|
||||
ellipsis: true,
|
||||
customRender: ({ text }) => text || '--'
|
||||
},
|
||||
{
|
||||
title: '价格',
|
||||
key: 'priceGroup',
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<img
|
||||
v-if="record.adImage"
|
||||
:src="getCompressedImageUrl(record.adImage, { width: 144, quality: 85 })"
|
||||
style="width: 72px; height: 40px; object-fit: cover; border-radius: 4px;"
|
||||
style="max-width: 56px; height: 28px; object-fit: cover; border-radius: 4px;"
|
||||
/>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
@@ -176,7 +176,7 @@
|
||||
const depth = depthMap.value.get(record.categoryId || 0) || 0;
|
||||
if (depth === 0) return '';
|
||||
const W = 20;
|
||||
const H = 40;
|
||||
const H = 20;
|
||||
const mid = H / 2;
|
||||
// 回溯祖先链:chain[0]=顶级 ... chain[depth]=当前节点
|
||||
const chain: number[] = [];
|
||||
|
||||
Reference in New Issue
Block a user