634e1c12af
- 角色页新增「成员」动作,弹窗展示姓名/账号/所属组织/状态,支持搜索添加启用用户与移除 - api/system/role 增加 pageRoleMembers/addRoleMember/removeRoleMember,走 THINK_API_URL - api/system/user 的 updateUser 改走 THINK_API_URL,空角色由后端清空 - 用户编辑表单允许角色为空,保存后可删除该用户全部角色关联
231 lines
6.1 KiB
Vue
231 lines
6.1 KiB
Vue
<!-- 角色成员维护弹窗 -->
|
||
<template>
|
||
<ele-modal
|
||
:width="720"
|
||
title="角色成员"
|
||
:visible="visible"
|
||
:body-style="{ paddingBottom: '8px' }"
|
||
:footer="null"
|
||
@update:visible="updateVisible"
|
||
>
|
||
<div style="margin-bottom: 12px">
|
||
<a-space>
|
||
<a-select
|
||
v-model:value="selectedUserId"
|
||
show-search
|
||
allow-clear
|
||
:filter-option="false"
|
||
:options="userOptions"
|
||
:loading="userLoading"
|
||
placeholder="输入姓名或账号搜索启用用户"
|
||
style="width: 320px"
|
||
@search="searchUsers"
|
||
/>
|
||
<a-button
|
||
type="primary"
|
||
:loading="adding"
|
||
:disabled="!selectedUserId"
|
||
@click="addOne"
|
||
>
|
||
添加成员
|
||
</a-button>
|
||
</a-space>
|
||
<div class="ele-text-secondary" style="margin-top: 6px">
|
||
角色:{{ data?.roleName }}({{ data?.roleCode }})
|
||
</div>
|
||
</div>
|
||
<a-table
|
||
size="small"
|
||
row-key="userId"
|
||
:columns="columns"
|
||
:data-source="members"
|
||
:loading="loading"
|
||
:pagination="false"
|
||
:scroll="{ y: '46vh' }"
|
||
>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.key === 'userName'">
|
||
{{ record.realName || record.nickname || '-' }}
|
||
</template>
|
||
<template v-else-if="column.key === 'organizationName'">
|
||
{{ record.organizationName || '-' }}
|
||
</template>
|
||
<template v-else-if="column.key === 'status'">
|
||
<a-tag :color="record.status === 0 ? 'green' : 'red'">
|
||
{{ record.status === 0 ? '启用' : '停用' }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else-if="column.key === 'action'">
|
||
<a-popconfirm
|
||
placement="topRight"
|
||
title="确定要移除该成员吗?"
|
||
@confirm="removeOne(record)"
|
||
>
|
||
<a class="ele-text-danger">移除</a>
|
||
</a-popconfirm>
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
</ele-modal>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { computed, ref, watch } from 'vue';
|
||
import { message } from 'ant-design-vue/es';
|
||
import type { ColumnItem } from 'ele-admin-pro/es/ele-pro-table/types';
|
||
import {
|
||
addRoleMember,
|
||
pageRoleMembers,
|
||
removeRoleMember
|
||
} from '@/api/system/role';
|
||
import type { Role, RoleMember } from '@/api/system/role/model';
|
||
import { pageUsers } from '@/api/system/user';
|
||
|
||
const emit = defineEmits<{
|
||
(e: 'update:visible', visible: boolean): void;
|
||
}>();
|
||
|
||
const props = defineProps<{
|
||
// 弹窗是否打开
|
||
visible: boolean;
|
||
// 当前角色数据
|
||
data?: Role | null;
|
||
}>();
|
||
|
||
// 表格列配置
|
||
const columns = ref<ColumnItem[]>([
|
||
{ title: '姓名', key: 'userName', width: 120 },
|
||
{ title: '账号', dataIndex: 'username', width: 150 },
|
||
{ title: '所属组织', key: 'organizationName' },
|
||
{ title: '状态', key: 'status', width: 90, align: 'center' },
|
||
{ title: '操作', key: 'action', width: 90, align: 'center' }
|
||
]);
|
||
|
||
// 成员列表
|
||
const members = ref<RoleMember[]>([]);
|
||
|
||
// 成员请求状态
|
||
const loading = ref(false);
|
||
|
||
// 添加状态
|
||
const adding = ref(false);
|
||
|
||
// 候选用户请求状态
|
||
const userLoading = ref(false);
|
||
|
||
// 候选用户下拉选项
|
||
const userOptions = ref<{ label: string; value: number }[]>([]);
|
||
|
||
// 选中的候选用户
|
||
const selectedUserId = ref<number | undefined>(undefined);
|
||
|
||
// 用户搜索关键词
|
||
const keyword = ref('');
|
||
|
||
// 当前角色id
|
||
const roleId = computed(() => props.data?.roleId);
|
||
|
||
// 已是成员的候选用户id
|
||
const memberIds = computed(() => new Set(members.value.map((d) => d.userId)));
|
||
|
||
/* 查询角色成员 */
|
||
const queryMembers = () => {
|
||
if (!roleId.value) {
|
||
members.value = [];
|
||
return;
|
||
}
|
||
loading.value = true;
|
||
pageRoleMembers({ roleId: roleId.value, page: 1, limit: 2000 })
|
||
.then((res) => {
|
||
loading.value = false;
|
||
members.value = res?.list ?? [];
|
||
})
|
||
.catch((e) => {
|
||
loading.value = false;
|
||
members.value = [];
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
/* 搜索启用用户作为候选 */
|
||
const searchUsers = (value?: string) => {
|
||
keyword.value = value ?? '';
|
||
userLoading.value = true;
|
||
pageUsers({
|
||
keywords: keyword.value || undefined,
|
||
status: 0,
|
||
page: 1,
|
||
limit: 20
|
||
})
|
||
.then((res) => {
|
||
userLoading.value = false;
|
||
userOptions.value = (res?.list ?? [])
|
||
.filter((d) => d.userId != null && !memberIds.value.has(d.userId))
|
||
.map((d) => ({
|
||
label: `${d.realName || d.nickname || d.username}(${d.username})`,
|
||
value: d.userId as number
|
||
}));
|
||
})
|
||
.catch((e) => {
|
||
userLoading.value = false;
|
||
userOptions.value = [];
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
/* 添加成员 */
|
||
const addOne = () => {
|
||
if (!roleId.value || selectedUserId.value == null) {
|
||
return;
|
||
}
|
||
adding.value = true;
|
||
addRoleMember({ roleId: roleId.value, userId: selectedUserId.value })
|
||
.then((msg) => {
|
||
adding.value = false;
|
||
selectedUserId.value = undefined;
|
||
message.success(msg);
|
||
queryMembers();
|
||
searchUsers(keyword.value);
|
||
})
|
||
.catch((e) => {
|
||
adding.value = false;
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
/* 移除成员 */
|
||
const removeOne = (row: RoleMember) => {
|
||
if (!roleId.value || row.userId == null) {
|
||
return;
|
||
}
|
||
removeRoleMember(roleId.value, row.userId)
|
||
.then((msg) => {
|
||
message.success(msg);
|
||
queryMembers();
|
||
searchUsers(keyword.value);
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
/* 更新visible */
|
||
const updateVisible = (value: boolean) => {
|
||
emit('update:visible', value);
|
||
};
|
||
|
||
watch(
|
||
() => props.visible,
|
||
(visible) => {
|
||
if (visible) {
|
||
members.value = [];
|
||
userOptions.value = [];
|
||
selectedUserId.value = undefined;
|
||
keyword.value = '';
|
||
queryMembers();
|
||
searchUsers('');
|
||
}
|
||
}
|
||
);
|
||
</script>
|