diff --git a/src/api/shop/shopGoods/model/index.ts b/src/api/shop/shopGoods/model/index.ts index 24a265d..3ee5b72 100644 --- a/src/api/shop/shopGoods/model/index.ts +++ b/src/api/shop/shopGoods/model/index.ts @@ -151,6 +151,8 @@ export interface ShopGoods { activityType?: number; // 配送方式:0送上门 1限自提 deliveryMode?: number; + // 所属专区(逗号分隔的 sectionId 字符串,对应 shop_goods.section_ids 列) + sectionIds?: string; } export interface BathSet { diff --git a/src/api/shop/shopZone/index.ts b/src/api/shop/shopZone/index.ts new file mode 100644 index 0000000..4dd6f32 --- /dev/null +++ b/src/api/shop/shopZone/index.ts @@ -0,0 +1,156 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { + HomeSection, + HomeSectionParam, + SectionPermission, + SectionGoods, + SectionUser +} from './model'; +import { MODULES_API_URL } from '@/config/setting'; + +/** + * 分页查询首页专区 + */ +export async function pageHomeSections(params: HomeSectionParam) { + const res = await request.get>>( + MODULES_API_URL + '/shop/shop-home-section/page', + { + params + } + ); + if (res.data.code === 0) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 查询首页专区列表 + */ +export async function listHomeSections(params?: HomeSectionParam) { + const res = await request.get>( + MODULES_API_URL + '/shop/shop-home-section', + { + params + } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 根据id查询首页专区 + */ +export async function getHomeSection(id: number) { + const res = await request.get>( + MODULES_API_URL + '/shop/shop-home-section/' + id + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 添加首页专区 + */ +export async function addHomeSection(data: HomeSection) { + const res = await request.post>( + MODULES_API_URL + '/shop/shop-home-section', + data + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 修改首页专区 + */ +export async function updateHomeSection(data: HomeSection) { + const res = await request.put>( + MODULES_API_URL + '/shop/shop-home-section', + data + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 删除首页专区 + */ +export async function removeHomeSection(id?: number) { + const res = await request.delete>( + MODULES_API_URL + '/shop/shop-home-section/' + id + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 查询专区白名单用户 + */ +export async function listSectionUsers(id: number) { + const res = await request.get>( + MODULES_API_URL + '/shop/shop-home-section/' + id + '/users' + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 保存专区白名单用户(覆盖式) + */ +export async function setSectionUsers(id: number, userIds: number[]) { + const res = await request.put>( + MODULES_API_URL + '/shop/shop-home-section/' + id + '/users', + userIds + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 查询专区商品分页 + */ +export async function listSectionGoods( + id: number, + params?: { page?: number; limit?: number } +) { + const res = await request.get>>( + MODULES_API_URL + '/shop/shop-home-section/' + id + '/goods', + { + params: { page: 1, limit: 10, ...params } + } + ); + if (res.data.code === 0) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 校验专区访问权限 + */ +export async function checkSectionPermission(sectionIds: number[]) { + const res = await request.post>( + MODULES_API_URL + '/shop/shop-home-section/check-permission', + { sectionIds } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/shop/shopZone/model/index.ts b/src/api/shop/shopZone/model/index.ts new file mode 100644 index 0000000..6cd8a4d --- /dev/null +++ b/src/api/shop/shopZone/model/index.ts @@ -0,0 +1,88 @@ +import type { PageParam } from '@/api'; + +/** + * 首页专区 + */ +export interface HomeSection { + // 专区ID + sectionId?: number; + // 租户id + tenantId?: number; + // 专区标题 + title?: string; + // 专区副标题 + subtitle?: string; + // banner 图片URL + banner?: string; + // 关联分类ID(逗号分隔字符串,可选) + categoryIds?: string; + // 样式类型 0默认 + styleType?: number; + // 是否受限专区 0否 1是 + restricted?: number; + // 排序号(数字越小越靠前) + sortNumber?: number; + // 状态 0正常 1禁用 + status?: number; + // 生效开始时间 + startTime?: string; + // 生效结束时间 + endTime?: string; + // 用户ID + userId?: number; + // 是否删除 0否 1是 + deleted?: number; + // 创建时间 + createTime?: string; + // 修改时间 + updateTime?: string; +} + +/** + * 首页专区搜索条件 + */ +export interface HomeSectionParam extends PageParam { + // 专区标题(模糊) + title?: string; + // 状态 0正常 1禁用 + status?: number; + // 是否受限专区 0/1 + restricted?: number; + // 排序号 + sortNumber?: number; +} + +/** + * 专区权限校验结果 + */ +export interface SectionPermission { + // 专区ID + sectionId?: number; + // 是否受限专区 + restricted?: boolean; + // 当前用户是否允许访问 + allowed?: boolean; +} + +/** + * 专区商品(只读展示,字段按需取用) + */ +export interface SectionGoods { + goodsId?: number; + name?: string; + image?: string; + price?: string; + salePrice?: string; + stock?: number; + status?: number; + [key: string]: any; +} + +/** + * 专区白名单用户 + */ +export interface SectionUser { + id?: number; + sectionId?: number; + userId?: number; +} diff --git a/src/views/shop/shopGoods/components/shopGoodsEdit.vue b/src/views/shop/shopGoods/components/shopGoodsEdit.vue index fdefebe..6b53c97 100644 --- a/src/views/shop/shopGoods/components/shopGoodsEdit.vue +++ b/src/views/shop/shopGoods/components/shopGoodsEdit.vue @@ -48,6 +48,17 @@ @change="onCategoryIds" /> + + + ([]); const files = ref([]); const goodsSpec = ref(); const category = ref([]); +// 所属专区(多选) +const sectionIdList = ref([]); +const zoneOptions = ref<{ label: string; value: number }[]>([]); // 批量设置 const batchPrice = ref(undefined); @@ -1803,6 +1818,10 @@ const save = () => { const formData: any = { ...form, + // 所属专区:数字数组 -> 逗号分隔字符串 + sectionIds: sectionIdList.value.length + ? sectionIdList.value.join(',') + : '', content: content.value, category: JSON.stringify(category.value), files: JSON.stringify(files.value), @@ -1966,8 +1985,20 @@ watch( async (visible) => { if (visible) { await getExpressTemplateList(); + // 加载所属专区选项 + listHomeSections({}) + .then((list) => { + zoneOptions.value = (list || []).map((z) => ({ + label: z.title || '', + value: z.sectionId as number + })); + }) + .catch(() => { + zoneOptions.value = []; + }); images.value = []; + sectionIdList.value = []; category.value = []; files.value = []; videos.value = []; @@ -1976,6 +2007,13 @@ watch( ensureTagItem.value = ''; if (props.data) { assignObject(form, props.data); + // 所属专区:逗号分隔字符串 -> 数字数组 + sectionIdList.value = form.sectionIds + ? form.sectionIds + .split(',') + .map((s) => Number(s.trim())) + .filter((n) => !Number.isNaN(n)) + : []; if (form.commissionType === undefined || form.commissionType === null) { form.commissionType = 10; } diff --git a/src/views/special/zone/components/UserSelectModal.vue b/src/views/special/zone/components/UserSelectModal.vue new file mode 100644 index 0000000..8d26788 --- /dev/null +++ b/src/views/special/zone/components/UserSelectModal.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/views/special/zone/components/zoneEdit.vue b/src/views/special/zone/components/zoneEdit.vue new file mode 100644 index 0000000..8a1abee --- /dev/null +++ b/src/views/special/zone/components/zoneEdit.vue @@ -0,0 +1,251 @@ + + + + diff --git a/src/views/special/zone/index.vue b/src/views/special/zone/index.vue new file mode 100644 index 0000000..0845267 --- /dev/null +++ b/src/views/special/zone/index.vue @@ -0,0 +1,380 @@ + + + +