Files
guilixu-admin/src/views/special/zone/components/UserSelectModal.vue
T
gxwebsoft 47fb8d1bd0 feat(zone): 将专区白名单弹窗改为右侧抽屉
- 用户要求专区白名单入口交互与商品一致,改为右侧抽屉弹出
- UserSelectModal 组件外层由 a-modal 改为 a-drawer,保持内部逻辑不变
- 组件 props 和 emits 完全兼容,调用方式无需更改
- 前端重新构建即可,无需后端改动
2026-08-17 03:23:45 +08:00

189 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 白名单用户管理抽屉右侧弹出打开只列已添加搜索手机/姓名昵称追加单条即时增删 -->
<template>
<a-drawer
:width="820"
:visible="visible"
:maskClosable="false"
title="专区白名单用户"
placement="right"
:body-style="{ paddingBottom: '16px' }"
@update:visible="updateVisible"
>
<!-- 搜索可添加的用户 -->
<div style="margin-bottom: 12px; display: flex; gap: 8px; align-items: center;">
<a-input-search
v-model:value="searchText"
placeholder="搜索手机号码 / 姓名 / 昵称"
allow-clear
style="flex: 1; max-width: 360px;"
@search="searchToAdd"
@pressEnter="searchToAdd"
/>
<a-button type="primary" :loading="searchLoading" @click="searchToAdd">
搜索
</a-button>
</div>
<!-- 搜索结果点击 + 添加 -->
<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-drawer>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { message } from 'ant-design-vue';
import { listSectionUsers, addSectionUsers, removeSectionUsers } from '@/api/shop/shopZone';
import { pageUsers } from '@/api/system/user';
import type { User } from '@/api/system/user/model';
const props = defineProps<{
visible: boolean;
sectionId?: number;
}>();
const emit = defineEmits<{
(e: 'update:visible', visible: boolean): void;
}>();
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 已添加的白名单用户
const addedUsers = ref<User[]>([]);
const addedLoading = ref(false);
// 搜索结果(候选)
const searchText = ref('');
const candidates = ref<User[]>([]);
const searchLoading = ref(false);
const columns = [
{ title: 'ID', dataIndex: 'userId', key: 'userId', align: 'center', 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' },
{ title: '操作', key: 'action', align: 'center', width: 90 }
];
/* 加载已添加的白名单 */
const loadAdded = () => {
if (!props.sectionId) return;
addedLoading.value = true;
listSectionUsers(props.sectionId)
.then((list: User[]) => {
addedUsers.value = list || [];
})
.catch((e: Error) => {
message.error(e.message || '加载白名单失败');
})
.finally(() => {
addedLoading.value = false;
});
};
/* 搜索可添加的用户(排除已添加的) */
const searchToAdd = () => {
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 addOne = (u: User) => {
if (!props.sectionId) return;
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(
() => props.visible,
(visible) => {
if (visible) {
searchText.value = '';
candidates.value = [];
loadAdded();
}
},
{ immediate: true }
);
</script>
<style lang="less"></style>