feat(special-zone): 优化专区白名单弹窗及添加/移除功能

- 修复白名单弹窗复选框不显示的问题,替换 ele-pro-table 为 antd a-table 实现
- 实现弹窗打开即加载已选用户并勾选,完善分页及搜索功能
- 解决弹窗搜索事件传参错误导致后端参数异常的问题
- 修复保存白名单时报唯一键冲突异常,改用物理删除规避逻辑删除死锁
- 重构白名单弹窗为商品式交互,支持即时单条添加和移除,无需覆盖式保存
- 后端新增接口支持白名单用户追加和删除,前端调用对应新增接口
- 统一用户模型字段,优化查询及展示,提升用户体验
- 清理前端无用代码,简化白名单弹窗调用逻辑,减少页面复杂度
This commit is contained in:
2026-08-17 03:10:30 +08:00
parent 6e57fba192
commit 4ba0427417
4 changed files with 246 additions and 151 deletions
+67
View File
@@ -40,6 +40,28 @@
- 改动文件:`src/views/special/zone/components/UserSelectModal.vue`(仅模板加 1 个 prop - 改动文件:`src/views/special/zone/components/UserSelectModal.vue`(仅模板加 1 个 prop
- 部署:重新构建前端即可(`npm run dev` 热更新或 `npm run build` 部署)。 - 部署:重新构建前端即可(`npm run dev` 热更新或 `npm run build` 部署)。
## 专区白名单弹窗 checkbox 修复(8-17 纠正:8-16 修复实际无效)
- 反馈:用户截图仍无勾选框,8-16 加的 `selection-type="checkbox"` 未生效。
- **重读 ele-pro-table 源码确认 8-16 根因判断错误**(`node_modules/ele-admin-pro/es/ele-pro-table/index.js` 94-103 行):
```js
const noSelection = typeof props2.selection === "undefined";
const noCurrent = typeof props2.current === "undefined";
if (noSelection && noCurrent && props2.selectionType !== "radio") {
return; // 不渲染
}
```
三个条件**同时满足**才 return。即 `noSelection && noCurrent && selectionType !== "radio"`。我传的 `selection-type="checkbox"` 让 `selectionType !== "radio"` 为 true,反而**满足**了 early return 条件 → 不渲染。这正是 8-16 修复无效的真因。
- 另外发现 `tableRowSelection.selectedRowKeys` 强制用内部 ref(源码 111 行),**不支持外部初始化已选**——即使绕过 selection 限制,弹窗打开时已选的人也不会显示勾选。
- **正确修复**:弃用 ele-pro-table 的 selection 机制,将 `UserSelectModal.vue` 重写为 antd `a-table`
- 受控 `selectedRowKeys`watch props.visible 时用 `props.selectedUserIds` 初始化 → 打开弹窗已选即勾选
- `rowSelection` computed`{ columnWidth:48, selectedRowKeys, onChange }`
- datasource 用 `pageUsers` 包成 Promise,分页用 antd `pagination` reactive`@change` 调 reload
- search `a-input-search`@search/@pressEnter reload
- confirm emit `selectedRowKeys`
- 字段已与 User 模型对齐(userId/realName/mobile/nickname/organizationName),`PageResult<T>={list,count}`。
- 改动文件:`src/views/special/zone/components/UserSelectModal.vue`(整体重写约 130 行)。
- 部署:刷新页面(`npm run dev` 热更新)或重新构建 admin 前端即可。无需后端改动。
## 专区小程序码(扫码直接进入专区) ## 专区小程序码(扫码直接进入专区)
- 需求:/special/zone 专区管理加「二维码」入口,生成该专区的小程序码,用户微信扫码直接进入小程序对应专区。 - 需求:/special/zone 专区管理加「二维码」入口,生成该专区的小程序码,用户微信扫码直接进入小程序对应专区。
- 后端(guilixu-java `ShopHomeSectionController.java`):新增 `GET /api/shop/shop-home-section/{id}/qrcode` - 后端(guilixu-java `ShopHomeSectionController.java`):新增 `GET /api/shop/shop-home-section/{id}/qrcode`
@@ -62,3 +84,48 @@
2. 扫码能正确进专区,还要求把「能解析 scene 的 section-detail.tsx」发布到**同一版本**:正式版需发版、体验版需上传体验版、开发版需开发者工具预览。 2. 扫码能正确进专区,还要求把「能解析 scene 的 section-detail.tsx」发布到**同一版本**:正式版需发版、体验版需上传体验版、开发版需开发者工具预览。
3. 若默认正式版仍 41030,说明线上版本也没有此页面 → 需先发布小程序(该页面虽在 app.config,但可能未上线)。 3. 若默认正式版仍 41030,说明线上版本也没有此页面 → 需先发布小程序(该页面虽在 app.config,但可能未上线)。
- 部署:需重新编译部署 guilixu-java + 重新构建发布 taro 小程序(到目标版本)。 - 部署:需重新编译部署 guilixu-java + 重新构建发布 taro 小程序(到目标版本)。
## 专区白名单「添加/移除」功能确认 + tenantId 修复
- 现状确认:`shop_home_section_user` 白名单的「添加/移除」功能**此前已完整实现**,本次只是排查确认:
- 后端 `ShopHomeSectionController``GET /{id}/users`(查)、`PUT /{id}/users`(覆盖式保存);`ShopHomeSectionUserService.listBySectionId` → `selectBySectionId`(XML)。
- 前端 `index.vue`:列表「白名单」按钮 → `openUsers` 调 `listSectionUsers` 加载已选 → 弹窗;`onSaveUsers` 调 `setSectionUsers` 保存。
- `UserSelectModal.vue`:用 `pageUsers`(sys_user) 选人,勾选=添加、取消=移除、保存=覆盖提交;columns 与 User 模型字段(userId/realName/mobile/nickname/organizationName)已核对匹配。
- **真 bug 修复**:后端 `setUsers` 新建 `ShopHomeSectionUser` 时**未设 tenantId**(实体有该字段,本项目手动多租户)。若表 `tenant_id` 有 NOT NULL 约束会插入报错;即便不报错也缺租户归属。
- 修复:从专区 `homeSectionService.getById(id).getTenantId()` 取租户,set 到每条白名单记录(`ShopHomeSectionController.setUsers`line ~119)。
- 查询侧(`selectBySectionId`)按 `section_id` 隔离,未动;下单强校验 `checkPermission` 也按 section_id 隔离,一致。
- 用户体系统一结论(沿用 01:50 澄清):买家=sys_user`pageUsers` 选人正确,白名单 user_id 与下单校验 userId 同体系。
- 部署:重新编译部署 guilixu-javashop-api)生效后,后台 /special/zone 每条专区「白名单」即可勾选添加、取消移除并保存。
## 白名单弹窗搜索报错修复([object PointerEvent] 误作 limit 传给后端)
- 现象:白名单弹窗点搜索触发 400:`BindException ... Field error in object 'userParam' on field 'limit': rejected value [[object PointerEvent]]`。
- 根因:重写后的 `UserSelectModal.vue` 模板 `@search="reload"` / `@pressEnter="reload"`。antd `a-input-search` 的 `search` 事件签名是 `(value, event)`,故 `reload` 实际收到 `reload(搜索词, PointerEvent)``reload(page, limit)` 把第二个参数当 `limit` 传给 `pageUsers``pageSize` 变成 PointerEvent → 序列化 `[object PointerEvent]` 传给后端 `limit` 字段 → 类型转换失败。
- 修复:模板改为 `@search="() => reload(1)"` / `@pressEnter="() => reload(1)"`,只传页码、不传事件对象;`reload(1)` 时 `limit=undefined` → `pageSize` 回退 `pagination.pageSize`。
- 改动:`UserSelectModal.vue` 两行模板。
- 部署:刷新 admin 前端(`npm run dev` 热更新或重新构建)即可。
## 白名单保存报 Duplicate entry(逻辑删除×唯一索引死局)
- 现象:保存专区白名单 `PUT /{id}/users` 报 `SQLIntegrityConstraintViolationException: Duplicate entry '1-35771-1' for key 'shop_home_section_user.uk_section_user'`,失败 SQL 是 `UPDATE shop_home_section_user SET deleted=1 WHERE tenant_id=10606 AND deleted=0 AND section_id=?`MP 逻辑删除)。
- 根因(已确证):`add_shop_home_section.sql:40` 定义 `UNIQUE KEY uk_section_user (section_id, user_id, deleted)`——唯一索引**包含 deleted 列**;而实体 `ShopHomeSectionUser` 上有 `@TableLogic`(逻辑删除)。`setUsers` 用「`remove` 全部 + `saveBatch`」覆盖式保存,`remove` 被 MP 转成 `UPDATE SET deleted=1`;历史多次保存已囤积 `deleted=1` 旧行,下次再把某条 `deleted=0` 行改成 `deleted=1` 就与旧 `deleted=1` 行撞唯一键。
- 修复(绕过逻辑删除,用物理删除):
- `ShopHomeSectionUserMapper.java` 新增 `int physicsDeleteBySection(@Param sectionId, @Param tenantId)`
- `ShopHomeSectionUserMapper.xml` 新增 `<delete id="physicsDeleteBySection"> DELETE FROM shop_home_section_user WHERE section_id=? <if tenantId not null>AND tenant_id=?</if> </delete>`(自定义 SQL,MP 不会注入逻辑删除 → 真物理删除);
- `ShopHomeSectionController.setUsers` 把 `homeSectionUserService.remove(new QueryWrapper...eq("section_id", id))` 换成 `homeSectionUserMapper.physicsDeleteBySection(id, currentTenantId)`,再 `saveBatch`。每次保存前把该专区所有行(含 deleted=1 残留)真正删掉再插新行,不再囤积,不撞键。
- `selectBySectionId` 已带 `deleted=0` 过滤,查询侧无影响。
- 说明:此表使用逻辑删除 + 含 deleted 的唯一索引本就是反模式;本修复用物理删除规避,安全且低风险(本机无 maven 未编译)。前端无需改动。
- 遗留(无害):其他未再保存过的专区可能仍有历史 deleted=1 重复行,因 `selectBySectionId` 过滤 deleted=0 且保存已改物理删除,不影响业务;如需彻底清可用 `DELETE FROM shop_home_section_user WHERE deleted=1 GROUP BY section_id,user_id HAVING COUNT(*)>1` 之类语句(谨慎,先备份)。
## 白名单弹窗改为商品式(打开列已加、搜索追加、单条即时增删)
- 用户反馈:旧版覆盖式勾选弹窗「勾了别的用户后前面勾过的又不见了」。`UserSelectModal.vue` 重写,统一成与专区「商品」抽屉一致的交互:打开只列已添加的白名单用户;搜索手机/姓名/昵称出候选,点「+添加」即时入库、点「移除」即时删。
- 根因(旧交互):覆盖式保存依赖 `selectedRowKeys` 跨打开/重渲染保持,易丢;且 `setUsers` 覆盖写遇到唯一键冲突会整体失败。商品式用「单条即时增删」彻底规避。
- 后端(guilixu-java `ShopHomeSectionController`):
- `GET /{id}/users` 改为**联表返回用户详情** `List<User>`realName/mobile/nickname/organizationName/userId),不再只返回关系行(旧返回 ShopHomeSectionUser 仅含 userId,前端展示空白)。新增注入 `UserService`,先取关系行再 `userService.listByIds` 取详情。
- 新增 `POST /{id}/users` `addUsers`:追加用户,先查已存在 userIds 跳过(防 uk_section_user 唯一键冲突),再 `saveBatch`,带 tenantId。
- 新增 `DELETE /{id}/users` `removeUsers`:物理删除指定用户,调 `homeSectionUserMapper.physicsDeleteBySectionAndUsers(id, userIds, tenantId)`(新增 mapper/XML 方法,foreach user_id IN,绕过 @TableLogic)。
- 旧 `PUT /{id}/users` `setUsers`(覆盖式)保留兼容,前端不再使用。
- 原 `users` 用的 `homeSectionUserService.listBySectionId` 不再调用(改用 `list(new QueryWrapper...)`)。
- 前端:
- `api/shop/shopZone/index.ts``listSectionUsers` 返回类型改 `User[]`import system `User`,移除 `SectionUser`);新增 `addSectionUsers(id, userIds)`(POST)、`removeSectionUsers(id, userIds)`(DELETE)。
- `components/UserSelectModal.vue` 整体重写:props(`visible`,`sectionId`),内部 watch(visible) 调 `listSectionUsers` 加载已加名单;`pageUsers({keywords})` 搜索候选(排除已加);`addOne`/`removeOne` 即时调接口并重载;移除 footer 与 `@confirm`,改用 `:footer="null"`。
- `index.vue`:弹窗调用去掉 `:selectedUserIds` / `@confirm``openUsers` 简化为只置 `currentSectionId`+开弹窗;删除 `onSaveUsers`、`currentUserIds`、未用 import`setSectionUsers`/`listSectionUsers` 在 index 内已无引用)。
- 后端 `users` 关键字搜索:sys_user `UserParam.keywords` 已覆盖 username/user_id/nickname/real_name/alias/phone(含手机、姓名、昵称),满足需求。
- 部署:重新编译部署 guilixu-javashop-api+ 重新构建 admin 前端。本机无 maven 未编译;改动为标准 MP/MyBatis 用法,风险低。
+33 -5
View File
@@ -4,9 +4,9 @@ import type {
HomeSection, HomeSection,
HomeSectionParam, HomeSectionParam,
SectionPermission, SectionPermission,
SectionGoods, SectionGoods
SectionUser
} from './model'; } from './model';
import type { User } from '@/api/system/user/model';
import { MODULES_API_URL } from '@/config/setting'; import { MODULES_API_URL } from '@/config/setting';
/** /**
@@ -96,10 +96,10 @@ export async function removeHomeSection(id?: number) {
} }
/** /**
* 查询专区白名单用户 * 查询专区白名单用户(返回用户详情:userId/realName/mobile/nickname/organizationName
*/ */
export async function listSectionUsers(id: number) { export async function listSectionUsers(id: number) {
const res = await request.get<ApiResult<SectionUser[]>>( const res = await request.get<ApiResult<User[]>>(
MODULES_API_URL + '/shop/shop-home-section/' + id + '/users' MODULES_API_URL + '/shop/shop-home-section/' + id + '/users'
); );
if (res.data.code === 0 && res.data.data) { if (res.data.code === 0 && res.data.data) {
@@ -109,7 +109,7 @@ export async function listSectionUsers(id: number) {
} }
/** /**
* 保存专区白名单用户(覆盖式) * 保存专区白名单用户(覆盖式,保留以备兼容
*/ */
export async function setSectionUsers(id: number, userIds: number[]) { export async function setSectionUsers(id: number, userIds: number[]) {
const res = await request.put<ApiResult<unknown>>( const res = await request.put<ApiResult<unknown>>(
@@ -122,6 +122,34 @@ export async function setSectionUsers(id: number, userIds: number[]) {
return Promise.reject(new Error(res.data.message)); return Promise.reject(new Error(res.data.message));
} }
/**
* 添加白名单用户到专区(单条/批量追加)
*/
export async function addSectionUsers(id: number, userIds: number[]) {
const res = await request.post<ApiResult<unknown>>(
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 removeSectionUsers(id: number, userIds: number[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/shop/shop-home-section/' + id + '/users',
{ data: userIds }
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/** /**
* 查询专区商品分页 * 查询专区商品分页
*/ */
@@ -1,161 +1,185 @@
<!-- 白名单用户多选弹窗 --> <!-- 白名单用户管理弹窗商品式打开只列已添加搜索手机/姓名昵称追加单条即时增删 -->
<template> <template>
<ele-modal <a-modal
:width="820" :width="820"
:visible="visible" :visible="visible"
:maskClosable="false" :maskClosable="false"
title="设置专区白名单用户" title="专区白名单用户"
:body-style="{ paddingBottom: '28px' }" :footer="null"
:body-style="{ paddingBottom: '16px' }"
@update:visible="updateVisible" @update:visible="updateVisible"
@ok="confirm"
> >
<ele-pro-table <!-- 搜索可添加的用户 -->
ref="tableRef" <div style="margin-bottom: 12px; display: flex; gap: 8px; align-items: center;">
row-key="userId" <a-input-search
:datasource="datasource" v-model:value="searchText"
:columns="columns" placeholder="搜索手机号码 / 姓名 / 昵称"
:row-selection="rowSelection" allow-clear
selection-type="checkbox" style="flex: 1; max-width: 360px;"
:pagination="true" @search="searchToAdd"
:page-size="10" @pressEnter="searchToAdd"
> />
<template #toolbar> <a-button type="primary" :loading="searchLoading" @click="searchToAdd">
<a-space> 搜索
<a-input-search </a-button>
allow-clear
v-model:value="searchText"
placeholder="姓名/手机号/昵称"
style="width: 220px"
@search="reload"
@pressEnter="reload"
/>
</a-space>
</template>
</ele-pro-table>
<div class="mt-2 ele-text-secondary">
已选 {{ selectedRowKeys.length }}
</div> </div>
</ele-modal>
<!-- 搜索结果点击 + 添加 -->
<div
v-if="candidates.length > 0"
style="margin-bottom: 12px; border: 1px dashed #d9d9d9; border-radius: 6px; padding: 8px;"
>
<div style="font-size: 12px; color: #999; margin-bottom: 6px;">搜索结果点击 + 添加</div>
<div
v-for="u in candidates"
:key="u.userId"
style="display: flex; align-items: center; padding: 4px 0; gap: 8px; border-bottom: 1px solid #f0f0f0;"
>
<span style="flex: 1; font-size: 13px;">{{ u.realName || '-' }}</span>
<span style="color: #999; font-size: 12px;">{{ u.mobile || '-' }}</span>
<span style="color: #999; font-size: 12px;">{{ u.nickname || '' }}</span>
<a-button type="link" size="small" style="padding: 0;" @click="addOne(u)">+ 添加</a-button>
</div>
</div>
<!-- 已添加的白名单用户 -->
<a-divider style="margin: 8px 0;">已添加{{ addedUsers.length }}</a-divider>
<a-spin :spinning="addedLoading">
<a-table
v-if="addedUsers.length > 0"
row-key="userId"
:columns="columns"
:data-source="addedUsers"
:pagination="false"
:scroll="{ y: 320 }"
size="middle"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-popconfirm title="确定移出该白名单用户吗?" @confirm="removeOne(record)">
<a-button type="link" danger size="small">移除</a-button>
</a-popconfirm>
</template>
</template>
</a-table>
<a-empty v-else description="暂无白名单用户,请用上方搜索添加" />
</a-spin>
</a-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch } from 'vue'; import { ref, watch } from 'vue';
import { import { message } from 'ant-design-vue';
ColumnItem, import { listSectionUsers, addSectionUsers, removeSectionUsers } from '@/api/shop/shopZone';
DatasourceFunction
} from 'ele-admin-pro/es/ele-pro-table/types';
import { pageUsers } from '@/api/system/user'; import { pageUsers } from '@/api/system/user';
import { EleProTable } from 'ele-admin-pro'; import type { User } from '@/api/system/user/model';
const props = defineProps<{ const props = defineProps<{
// 弹窗是否打开
visible: boolean; visible: boolean;
// 专区ID
sectionId?: number; sectionId?: number;
// 已选用户ID
selectedUserIds?: number[];
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'confirm', userIds: number[]): void;
(e: 'update:visible', visible: boolean): void; (e: 'update:visible', visible: boolean): void;
}>(); }>();
/* 更新visible */
const updateVisible = (value: boolean) => { const updateVisible = (value: boolean) => {
emit('update:visible', value); emit('update:visible', value);
}; };
// 搜索内容 // 已添加的白名单用户
const searchText = ref<string>(''); const addedUsers = ref<User[]>([]);
const addedLoading = ref(false);
// 表格实例 // 搜索结果(候选)
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const searchText = ref('');
const candidates = ref<User[]>([]);
const searchLoading = ref(false);
// 表格配置 const columns = [
const columns = ref<ColumnItem[]>([ { title: 'ID', dataIndex: 'userId', key: 'userId', align: 'center', width: 80 },
{ { title: '姓名', dataIndex: 'realName', key: 'realName', align: 'center' },
title: 'ID', { title: '手机号码', dataIndex: 'mobile', key: 'mobile', align: 'center' },
dataIndex: 'userId', { title: '昵称', dataIndex: 'nickname', key: 'nickname', align: 'center' },
key: 'userId', { title: '所属部门', dataIndex: 'organizationName', key: 'organizationName', align: 'center' },
align: 'center', { title: '操作', key: 'action', align: 'center', width: 90 }
width: 80 ];
},
{
title: '姓名',
dataIndex: 'realName',
key: 'realName',
align: 'center'
},
{
title: '手机号码',
dataIndex: 'mobile',
key: 'mobile',
align: 'center'
},
{
title: '昵称',
dataIndex: 'nickname',
key: 'nickname',
align: 'center'
},
{
title: '所属部门',
dataIndex: 'organizationName',
key: 'organizationName',
align: 'center'
}
]);
// 已选用户ID /* 加载已添加的白名单 */
const selectedRowKeys = ref<number[]>([]); const loadAdded = () => {
if (!props.sectionId) return;
// 多选配置(与 shopCoupon 一致:computed + columnWidth addedLoading.value = true;
const rowSelection = computed(() => ({ listSectionUsers(props.sectionId)
columnWidth: 48, .then((list: User[]) => {
selectedRowKeys: selectedRowKeys.value, addedUsers.value = list || [];
onChange: (keys: (string | number)[]) => { })
selectedRowKeys.value = keys.map((k) => Number(k)); .catch((e: Error) => {
} message.error(e.message || '加载白名单失败');
})); })
.finally(() => {
// 表格数据源 addedLoading.value = false;
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => { });
if (searchText.value) {
where.keywords = searchText.value;
}
return pageUsers({
keywords: searchText.value,
...where,
...orders,
page,
limit
});
}; };
/* 搜索 */ /* 搜索可添加的用户(排除已添加的) */
const reload = () => { const searchToAdd = () => {
tableRef?.value?.reload({ page: 1 }); const kw = searchText.value.trim();
if (!kw) {
message.warning('请输入手机号码 / 姓名 / 昵称');
return;
}
searchLoading.value = true;
pageUsers({ keywords: kw, page: 1, limit: 20 } as any)
.then((res: any) => {
const addedIds = new Set(addedUsers.value.map((u) => u.userId));
candidates.value = (res?.list || []).filter((u: User) => !addedIds.has(u.userId));
if (candidates.value.length === 0) {
message.info('没有可添加的新用户');
}
})
.catch((e: Error) => {
message.error(e.message || '搜索失败');
})
.finally(() => {
searchLoading.value = false;
});
}; };
/* 确认保存 */ /* 添加单个用户到白名单 */
const confirm = () => { const addOne = (u: User) => {
emit('confirm', [...selectedRowKeys.value]); if (!props.sectionId) return;
updateVisible(false); addSectionUsers(props.sectionId, [u.userId as number])
.then((msg: string) => {
message.success(msg || '添加成功');
candidates.value = candidates.value.filter((c) => c.userId !== u.userId);
loadAdded();
})
.catch((e: Error) => {
message.error(e.message || '添加失败');
});
}; };
// 打开弹窗时初始化已选 /* 从白名单移除单个用户 */
const removeOne = (u: User) => {
if (!props.sectionId) return;
removeSectionUsers(props.sectionId, [u.userId as number])
.then((msg: string) => {
message.success(msg || '移除成功');
loadAdded();
})
.catch((e: Error) => {
message.error(e.message || '移除失败');
});
};
// 打开弹窗时加载已添加名单
watch( watch(
() => props.visible, () => props.visible,
(visible) => { (visible) => {
if (visible) { if (visible) {
selectedRowKeys.value = props.selectedUserIds searchText.value = '';
? [...props.selectedUserIds] candidates.value = [];
: []; loadAdded();
if (tableRef.value) {
// 等待表格挂载后刷新
setTimeout(() => reload(), 0);
}
} }
}, },
{ immediate: true } { immediate: true }
+2 -26
View File
@@ -87,12 +87,10 @@
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<ZoneEdit v-model:visible="showEdit" :data="current" @done="reload" /> <ZoneEdit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 白名单用户选择弹窗 --> <!-- 白名单用户管理弹窗 -->
<UserSelectModal <UserSelectModal
v-model:visible="showUser" v-model:visible="showUser"
:sectionId="currentSectionId" :sectionId="currentSectionId"
:selectedUserIds="currentUserIds"
@confirm="onSaveUsers"
/> />
<!-- 专区商品抽屉 --> <!-- 专区商品抽屉 -->
@@ -230,8 +228,6 @@
pageHomeSections, pageHomeSections,
removeHomeSection, removeHomeSection,
updateHomeSection, updateHomeSection,
listSectionUsers,
setSectionUsers,
listSectionGoods, listSectionGoods,
addSectionGoods, addSectionGoods,
removeSectionGoods, removeSectionGoods,
@@ -254,7 +250,6 @@
// 白名单弹窗 // 白名单弹窗
const showUser = ref(false); const showUser = ref(false);
const currentSectionId = ref<number>(0); const currentSectionId = ref<number>(0);
const currentUserIds = ref<number[]>([]);
// 商品抽屉 // 商品抽屉
const showGoods = ref(false); const showGoods = ref(false);
@@ -420,26 +415,7 @@
/* 打开白名单弹窗 */ /* 打开白名单弹窗 */
const openUsers = (row: HomeSection) => { const openUsers = (row: HomeSection) => {
currentSectionId.value = row.sectionId || 0; currentSectionId.value = row.sectionId || 0;
listSectionUsers(row.sectionId || 0) showUser.value = true;
.then((list) => {
currentUserIds.value = (list || []).map((u) => u.userId || 0).filter(Boolean);
showUser.value = true;
})
.catch((e) => {
message.error(e.message);
});
};
/* 保存白名单 */
const onSaveUsers = (userIds: number[]) => {
setSectionUsers(currentSectionId.value, userIds)
.then((msg) => {
message.success(msg || '白名单已保存');
showUser.value = false;
})
.catch((e) => {
message.error(e.message);
});
}; };
/* 打开商品抽屉 */ /* 打开商品抽屉 */