mirror of
http://183.237.82.114:10086/kilo/leaser_front.git
synced 2026-10-23 19:35:25 +00:00
feat: 完成角色权限的功能权限和用户分配
This commit is contained in:
parent
293beb9f5d
commit
c5588374ba
12 changed files with 1562 additions and 133 deletions
|
|
@ -1,6 +1,30 @@
|
|||
import axios from 'axios';
|
||||
import type { UserPermission } from '@/types/api';
|
||||
import { HttpResponse } from './interceptor';
|
||||
|
||||
// ==================== 通用类型定义 ====================
|
||||
export interface BaseEntity {
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface PaginationParams {
|
||||
role_id?: number | null;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
|
||||
export interface PaginationResult {
|
||||
total?: number;
|
||||
total_pages?: number;
|
||||
}
|
||||
|
||||
export type UserStatus = 1 | 2; // 1: 启用, 2: 禁用
|
||||
export type ManagerStatus = 1 | 2; // 1: 是主管, 2: 否
|
||||
export type DataType = 1 | 2 | 3; // 1: 全部数据权限, 2: 自定义数据权限, 3: 仅本人数据
|
||||
|
||||
// ==================== 部门相关类型 ====================
|
||||
export interface DepartmentFormParams {
|
||||
parent_id: number | null;
|
||||
name: string;
|
||||
|
|
@ -12,21 +36,8 @@ export interface DepartmentFormParams {
|
|||
export interface DepartmentIdData {
|
||||
departmentId: number | string;
|
||||
}
|
||||
type DepartmentFormRes = HttpResponse<DepartmentIdData>;
|
||||
// 创建部门
|
||||
export function createDepartment(
|
||||
params: DepartmentFormParams
|
||||
): Promise<DepartmentFormRes> {
|
||||
return axios.post('/department/createDepartment', params);
|
||||
}
|
||||
// 更新(编辑)部门
|
||||
export function updateDepartment(
|
||||
params: DepartmentFormParams
|
||||
): Promise<DepartmentFormRes> {
|
||||
return axios.post('/department/updateDepartment', params);
|
||||
}
|
||||
export interface DepartmentItem {
|
||||
id: number;
|
||||
|
||||
export interface DepartmentItem extends BaseEntity {
|
||||
systemTenantId: number;
|
||||
name: string;
|
||||
parentId: number;
|
||||
|
|
@ -35,24 +46,127 @@ export interface DepartmentItem {
|
|||
path: string;
|
||||
remark: string;
|
||||
isDelete: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
sort: number;
|
||||
children?: DepartmentItem[]; // 递归结构
|
||||
isShowEditIcon?: boolean; // 是否展示编辑icon
|
||||
username?: string;
|
||||
parentName?: string;
|
||||
}
|
||||
|
||||
export interface DepartmentDetailData {
|
||||
department: DepartmentItem;
|
||||
}
|
||||
|
||||
// ==================== 用户相关类型 ====================
|
||||
export interface UserItem {
|
||||
id: number;
|
||||
username: string;
|
||||
realname: string;
|
||||
email: string;
|
||||
tel: string;
|
||||
status: UserStatus;
|
||||
roleId: number;
|
||||
departmentId: number;
|
||||
roleName: string;
|
||||
deptName: string;
|
||||
lastLoginTime: string;
|
||||
}
|
||||
|
||||
export interface FormUserItem {
|
||||
username: string;
|
||||
realname: string;
|
||||
tel: string | number;
|
||||
password: string;
|
||||
repassword: string;
|
||||
role_id: number | null;
|
||||
department_id: number | null;
|
||||
email: string;
|
||||
is_manager: ManagerStatus;
|
||||
}
|
||||
|
||||
export interface DisableUserParam {
|
||||
id: number;
|
||||
status: UserStatus;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
user: FormUserItem;
|
||||
}
|
||||
|
||||
export interface AllUserList {
|
||||
page: PaginationParams & PaginationResult;
|
||||
user?: UserItem[];
|
||||
userList?: UserItem[];
|
||||
}
|
||||
|
||||
// ==================== 角色相关类型 ====================
|
||||
export interface RoleItem extends BaseEntity {
|
||||
systemTenantId: number;
|
||||
name: string;
|
||||
createUserId?: number;
|
||||
isDelete?: number;
|
||||
remark?: string;
|
||||
dataType: DataType;
|
||||
}
|
||||
|
||||
export interface RoleFormParam {
|
||||
id?: number;
|
||||
name: string;
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export interface RoleList {
|
||||
rolesList: RoleItem[];
|
||||
}
|
||||
|
||||
export interface UserPermissionItem {
|
||||
permission: UserPermission;
|
||||
}
|
||||
|
||||
export interface UserPermissionItemList {
|
||||
permission: number[];
|
||||
}
|
||||
|
||||
export interface SaveRolePermissionParam {
|
||||
id: number;
|
||||
children?: Array<{
|
||||
id: number;
|
||||
path: string;
|
||||
}>;
|
||||
}
|
||||
export interface UpdateUserRoleParam {
|
||||
role_id: number | null;
|
||||
user_id: number | null;
|
||||
}
|
||||
|
||||
// ==================== 响应类型别名 ====================
|
||||
export type DepartmentFormRes = HttpResponse<DepartmentIdData>;
|
||||
export type DepartmentList = DepartmentItem[];
|
||||
export type DepartmentListRes = HttpResponse<{
|
||||
departmentList: DepartmentItem[];
|
||||
}>;
|
||||
export interface DepartmentDetailData {
|
||||
department: DepartmentItem;
|
||||
}
|
||||
export type DepartmentDetailRes = HttpResponse<DepartmentDetailData>;
|
||||
export type UserList = UserItem[];
|
||||
export type UserListRes = HttpResponse<UserList>;
|
||||
export type AllUserListRes = HttpResponse<AllUserList>;
|
||||
export type UserInfoRes = HttpResponse<UserInfo>;
|
||||
export type RoleListRes = HttpResponse<RoleList>;
|
||||
export type PermissionRes = HttpResponse<UserPermissionItem>;
|
||||
export type RolePermissionRes = HttpResponse<UserPermissionItemList>;
|
||||
|
||||
// ==================== 部门相关 API ====================
|
||||
export function createDepartment(
|
||||
params: DepartmentFormParams
|
||||
): Promise<DepartmentFormRes> {
|
||||
return axios.post('/department/createDepartment', params);
|
||||
}
|
||||
|
||||
export function updateDepartment(
|
||||
params: DepartmentFormParams
|
||||
): Promise<DepartmentFormRes> {
|
||||
return axios.post('/department/updateDepartment', params);
|
||||
}
|
||||
|
||||
// 获取部门列表
|
||||
export function getDepartmentList(): Promise<DepartmentListRes> {
|
||||
return axios.get('/department/getDepartmentList');
|
||||
}
|
||||
|
|
@ -61,137 +175,89 @@ export function getDepartmentDetail(id: number): Promise<DepartmentDetailRes> {
|
|||
return axios.get(`/department/getDepartment?id=${id}`);
|
||||
}
|
||||
|
||||
export interface UserItem {
|
||||
id: number; // 用户id
|
||||
username: string; // 用户名称
|
||||
realname: string; // 姓名
|
||||
email: string; // 邮箱
|
||||
tel: string; // 手机号
|
||||
status: 1 | 2; // 状态:1 启用,2 禁用
|
||||
roleId: number; // 角色id
|
||||
departmentId: number; // 部门id
|
||||
roleName: string; // 角色名称
|
||||
deptName: string; // 部门名称
|
||||
lastLoginTime: string; // 最后登录时间
|
||||
}
|
||||
export type UserList = UserItem[];
|
||||
export type UserListRes = HttpResponse<UserList>;
|
||||
export interface Pagination {
|
||||
page: number;
|
||||
page_size: number;
|
||||
total?: number;
|
||||
total_pages?: number;
|
||||
}
|
||||
export interface AllUserList {
|
||||
page: Pagination;
|
||||
user: UserListRes;
|
||||
}
|
||||
export type AllUserListRes = HttpResponse<AllUserList>;
|
||||
// 后台-用户列表(除主管外所有用户)
|
||||
export function getAllUserList(params): Promise<AllUserListRes> {
|
||||
return axios.post('/user/getAllUserList', params);
|
||||
}
|
||||
// 后台-用户列表(所有用户)
|
||||
export function getUserList(params): Promise<AllUserListRes> {
|
||||
return axios.post('/user/getUserList', params);
|
||||
}
|
||||
export interface FormUserItem {
|
||||
username: string; // 用户名
|
||||
realname: string; // 姓名
|
||||
tel: string | number; // 手机号
|
||||
password: string; // 密码
|
||||
repassword: string; // 确认密码
|
||||
role_id: number | null; // 角色id
|
||||
department_id: number | null; // 部门id
|
||||
email: string; // 邮件
|
||||
is_manager: 1 | 2; // 是否为主管:1是,2否
|
||||
}
|
||||
// 创建用户
|
||||
export function createUser(params: FormUserItem): Promise<HttpResponse> {
|
||||
return axios.post('/user/createUser', params);
|
||||
}
|
||||
// 更新用户信息
|
||||
export function updateUser(params: FormUserItem): Promise<HttpResponse> {
|
||||
return axios.post('/user/updateUser', params);
|
||||
}
|
||||
interface UserInfo {
|
||||
user: FormUserItem;
|
||||
}
|
||||
export type UserInfoRES = HttpResponse<UserInfo>;
|
||||
// 获取用户信息
|
||||
export function getUserInfo(id: number): Promise<UserInfoRES> {
|
||||
return axios.get(`/user/getUser?id=${id}`);
|
||||
}
|
||||
// 删除用户
|
||||
export function delUser(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/user/delUser?id=${id}`);
|
||||
}
|
||||
export interface DisableUserParam {
|
||||
id: number;
|
||||
status: number;
|
||||
}
|
||||
// 禁用启用用户
|
||||
export function disableUser(params: DisableUserParam): Promise<HttpResponse> {
|
||||
return axios.post('/user/disableUser', params);
|
||||
}
|
||||
|
||||
// 部门-向上移动
|
||||
export function departmentSetUp(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/department/setUp?id=${id}`);
|
||||
}
|
||||
// 部门-向下移动
|
||||
|
||||
export function departmentSetDown(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/department/setDown?id=${id}`);
|
||||
}
|
||||
// 删除部门
|
||||
|
||||
export function delDepartment(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/department/delDepartment?id=${id}`);
|
||||
}
|
||||
|
||||
export interface RoleItem {
|
||||
id: number; // 角色id
|
||||
systemTenantId: number; // 租户名称
|
||||
name: string; // 角色名称
|
||||
createUserId?: number; // 创建人id
|
||||
isDelete?: number; // 是否删除
|
||||
remark?: string;
|
||||
dataType: number; // 数据权限类型:1全部数据权限,2自定义数据权限,3仅本人数据
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
// ==================== 用户相关 API ====================
|
||||
export function getAllUserList(
|
||||
params: PaginationParams
|
||||
): Promise<AllUserListRes> {
|
||||
return axios.post('/user/getAllUserList', params);
|
||||
}
|
||||
// 角色列表
|
||||
export interface RoleList {
|
||||
rolesList: RoleItem[];
|
||||
|
||||
export function getUserList(params: PaginationParams): Promise<AllUserListRes> {
|
||||
return axios.post('/user/getUserList', params);
|
||||
}
|
||||
export type RoleListRes = HttpResponse<RoleList>;
|
||||
// 角色列表
|
||||
|
||||
export function createUser(params: FormUserItem): Promise<HttpResponse> {
|
||||
return axios.post('/user/createUser', params);
|
||||
}
|
||||
|
||||
export function updateUser(params: FormUserItem): Promise<HttpResponse> {
|
||||
return axios.post('/user/updateUser', params);
|
||||
}
|
||||
|
||||
export function getUserInfo(id: number): Promise<UserInfoRes> {
|
||||
return axios.get(`/user/getUser?id=${id}`);
|
||||
}
|
||||
|
||||
export function delUser(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/user/delUser?id=${id}`);
|
||||
}
|
||||
|
||||
export function disableUser(params: DisableUserParam): Promise<HttpResponse> {
|
||||
return axios.post('/user/disableUser', params);
|
||||
}
|
||||
|
||||
// ==================== 角色相关 API ====================
|
||||
export function getRoleList(): Promise<RoleListRes> {
|
||||
return axios.get('/role/getRoleList');
|
||||
}
|
||||
|
||||
// 创建角色
|
||||
export interface RoleFormParam {
|
||||
id?: number; // 角色id
|
||||
name: string; // 姓名
|
||||
remark?: string; // 备注
|
||||
}
|
||||
export function createRole(params: RoleFormParam): Promise<HttpResponse> {
|
||||
return axios.post('/role/createRole', params);
|
||||
}
|
||||
// 更新角色
|
||||
|
||||
export function updateRole(params: RoleFormParam): Promise<HttpResponse> {
|
||||
return axios.post('/role/updateRole', params);
|
||||
}
|
||||
// 删除角色
|
||||
|
||||
export function delRole(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/role/delRole?id=${id}`);
|
||||
}
|
||||
|
||||
// // 项目信息
|
||||
// export function getProjectDetail(id: string) {
|
||||
// return axios.get<any>(`/project/getProject?id=${id}`);
|
||||
// }
|
||||
// // 项目列表(分页)
|
||||
// export function queryPolicyList(params: ProjectParams) {
|
||||
// return axios.post<any>('/project/getProjectList', { params });
|
||||
// }
|
||||
export function getPermissionList(): Promise<PermissionRes> {
|
||||
return axios.get('/role/getPermissionList');
|
||||
}
|
||||
|
||||
export function getRolePermission(id: number): Promise<RolePermissionRes> {
|
||||
return axios.get(`role/getRolePermission?id=${id}`);
|
||||
}
|
||||
|
||||
export function saveRolePermission(
|
||||
params: SaveRolePermissionParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post('role/saveRolePermission', params);
|
||||
}
|
||||
|
||||
// 角色下的用户列表
|
||||
export function getUserListFromRole(
|
||||
params: PaginationParams
|
||||
): Promise<AllUserListRes> {
|
||||
return axios.post('role/getUserList', params);
|
||||
}
|
||||
// 角色下的用户列表
|
||||
export function updateUserRole(
|
||||
params: UpdateUserRoleParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post('role/updateUserRole', params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ export default {
|
|||
'messageBox.switchRoles': '切换角色',
|
||||
'messageBox.userCenter': '用户中心',
|
||||
'messageBox.userSettings': '用户设置',
|
||||
'messageBox.logout': '登出登录',
|
||||
'messageBox.logout': '退出登录',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -97,6 +97,22 @@ export default {
|
|||
'edit': '编辑',
|
||||
'more': '更多',
|
||||
'user': '用户',
|
||||
'role.list': '角色列表',
|
||||
'role.add': '新增角色',
|
||||
'role.edit': '编辑角色',
|
||||
'roleName': '角色名称',
|
||||
'roleDesc': '角色描述',
|
||||
'permission': '权限',
|
||||
'function.permission': '功能权限',
|
||||
'data.permission': '数据权限',
|
||||
'user.assignment': '用户分配',
|
||||
'submit': '保存',
|
||||
'last.updated': '最后更新',
|
||||
'all.checked': '全部选中',
|
||||
'all.clear': '全部清空',
|
||||
'function.permission.setting': '功能权限设置',
|
||||
'user.assignment.setting': '用户分配设置',
|
||||
'change.role': '变更角色',
|
||||
|
||||
...localeSettings,
|
||||
...localeMessageBox,
|
||||
|
|
|
|||
|
|
@ -72,4 +72,7 @@ export interface UserPermission {
|
|||
visible: 1 | 2; // 是否可见:1显示、2隐藏
|
||||
createdAt: string; // 创建时间
|
||||
updatedAt: string; // 更新时间
|
||||
system_tenant_permission_id?: number; // 租客权限id
|
||||
tenant_id?: number; // 租客id
|
||||
children?: UserPermission[] | []; // 子集
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<template>
|
||||
<div>house-view</div>
|
||||
<div>house-view</div>
|
||||
</template>
|
||||
|
|
|
|||
98
src/views/setting/user/components/change-role-drawer.vue
Normal file
98
src/views/setting/user/components/change-role-drawer.vue
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="t('change.role')"
|
||||
unmount-on-close
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
layout="vertical"
|
||||
>
|
||||
<!-- 角色 -->
|
||||
<a-form-item :label="$t('change.role')" field="role_id" required>
|
||||
<a-select
|
||||
v-model="form.role_id"
|
||||
:placeholder="$t('please.select', { placeholder: $t('role') })"
|
||||
>
|
||||
<a-option
|
||||
v-for="role in props.roleList"
|
||||
:key="Number(role.id)"
|
||||
:value="Number(role.id)"
|
||||
>
|
||||
{{ role.name }}</a-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { UpdateUserRoleParam, updateUserRole, RoleItem } from '@/api/setting';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
roleId: number | null;
|
||||
userId: number | null;
|
||||
roleList: RoleItem[] | null;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'updateUser'): void;
|
||||
}>();
|
||||
// 表单数据
|
||||
const formRef = ref<FormInstance>();
|
||||
const defaultForm: UpdateUserRoleParam = {
|
||||
role_id: props.roleId,
|
||||
user_id: props.userId,
|
||||
};
|
||||
const form = reactive<UpdateUserRoleParam>({ ...defaultForm });
|
||||
const { t } = useI18n();
|
||||
// 校验规则
|
||||
const rules = reactive({
|
||||
role_id: [
|
||||
{
|
||||
required: true,
|
||||
message: t('please.select', { placeholder: t('role') }),
|
||||
},
|
||||
],
|
||||
});
|
||||
watch(
|
||||
() => [props.roleId, props.userId],
|
||||
([newRoleId, newUserId]) => {
|
||||
form.role_id = newRoleId;
|
||||
form.user_id = newUserId;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const valid = await formRef.value.validate();
|
||||
if (valid) return;
|
||||
const params = { ...form };
|
||||
const res = await updateUserRole(params);
|
||||
if (res?.code === 200) {
|
||||
Message.success(res?.message);
|
||||
emit('updateUser');
|
||||
emit('update:visible', false);
|
||||
}
|
||||
} catch (error) {
|
||||
// 捕获异常,暂不处理
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
268
src/views/setting/user/components/mock.json
Normal file
268
src/views/setting/user/components/mock.json
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
{
|
||||
"permission": [
|
||||
{
|
||||
"system_tenant_permission_id": 100,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 1,
|
||||
"id": 1,
|
||||
"parent_id": 0,
|
||||
"prem_name": "业务",
|
||||
"level": 1,
|
||||
"perm_key": "business",
|
||||
"prem_type": 1,
|
||||
"path": "/1/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:00:00",
|
||||
"updatedAt": "2024-06-20 10:00:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 101,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 2,
|
||||
"id": 2,
|
||||
"parent_id": 1,
|
||||
"prem_name": "项目管理",
|
||||
"level": 2,
|
||||
"perm_key": "project_manage",
|
||||
"prem_type": 1,
|
||||
"path": "/1/2/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:01:00",
|
||||
"updatedAt": "2024-06-20 10:01:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 102,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 3,
|
||||
"id": 3,
|
||||
"parent_id": 2,
|
||||
"prem_name": "查看项目",
|
||||
"level": 3,
|
||||
"perm_key": "view_project",
|
||||
"prem_type": 4,
|
||||
"path": "/1/2/3/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:02:00",
|
||||
"updatedAt": "2024-06-20 10:02:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 103,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 4,
|
||||
"id": 4,
|
||||
"parent_id": 3,
|
||||
"prem_name": "添加收入目标",
|
||||
"level": 4,
|
||||
"perm_key": "add_income_target",
|
||||
"prem_type": 2,
|
||||
"path": "/1/2/3/4/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:03:00",
|
||||
"updatedAt": "2024-06-20 10:03:00",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"system_tenant_permission_id": 104,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 5,
|
||||
"id": 5,
|
||||
"parent_id": 3,
|
||||
"prem_name": "编辑收入目标",
|
||||
"level": 4,
|
||||
"perm_key": "edit_income_target",
|
||||
"prem_type": 2,
|
||||
"path": "/1/2/3/5/",
|
||||
"sort": 2,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:04:00",
|
||||
"updatedAt": "2024-06-20 10:04:00",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"system_tenant_permission_id": 105,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 6,
|
||||
"id": 6,
|
||||
"parent_id": 2,
|
||||
"prem_name": "编辑项目",
|
||||
"level": 3,
|
||||
"perm_key": "edit_project",
|
||||
"prem_type": 4,
|
||||
"path": "/1/2/6/",
|
||||
"sort": 2,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:05:00",
|
||||
"updatedAt": "2024-06-20 10:05:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 106,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 7,
|
||||
"id": 7,
|
||||
"parent_id": 6,
|
||||
"prem_name": "删除收入目标",
|
||||
"level": 4,
|
||||
"perm_key": "delete_income_target",
|
||||
"prem_type": 2,
|
||||
"path": "/1/2/6/7/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:06:00",
|
||||
"updatedAt": "2024-06-20 10:06:00",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"system_tenant_permission_id": 107,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 8,
|
||||
"id": 8,
|
||||
"parent_id": 1,
|
||||
"prem_name": "合同管理",
|
||||
"level": 2,
|
||||
"perm_key": "contract_manage",
|
||||
"prem_type": 1,
|
||||
"path": "/1/8/",
|
||||
"sort": 2,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:07:00",
|
||||
"updatedAt": "2024-06-20 10:07:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 108,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 9,
|
||||
"id": 9,
|
||||
"parent_id": 8,
|
||||
"prem_name": "查看合同",
|
||||
"level": 3,
|
||||
"perm_key": "view_contract",
|
||||
"prem_type": 4,
|
||||
"path": "/1/8/9/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:08:00",
|
||||
"updatedAt": "2024-06-20 10:08:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 109,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 10,
|
||||
"id": 10,
|
||||
"parent_id": 9,
|
||||
"prem_name": "导出合同",
|
||||
"level": 4,
|
||||
"perm_key": "export_contract",
|
||||
"prem_type": 2,
|
||||
"path": "/1/8/9/10/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 10:09:00",
|
||||
"updatedAt": "2024-06-20 10:09:00",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"system_tenant_permission_id": 200,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 20,
|
||||
"id": 20,
|
||||
"parent_id": 0,
|
||||
"prem_name": "房源视图",
|
||||
"level": 1,
|
||||
"perm_key": "house_view",
|
||||
"prem_type": 1,
|
||||
"path": "/20/",
|
||||
"sort": 2,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 11:00:00",
|
||||
"updatedAt": "2024-06-20 11:00:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 201,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 21,
|
||||
"id": 21,
|
||||
"parent_id": 20,
|
||||
"prem_name": "新建房源",
|
||||
"level": 2,
|
||||
"perm_key": "add_house",
|
||||
"prem_type": 2,
|
||||
"path": "/20/21/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 11:01:00",
|
||||
"updatedAt": "2024-06-20 11:01:00",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"system_tenant_permission_id": 202,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 22,
|
||||
"id": 22,
|
||||
"parent_id": 20,
|
||||
"prem_name": "导入房源",
|
||||
"level": 2,
|
||||
"perm_key": "import_house",
|
||||
"prem_type": 3,
|
||||
"path": "/20/22/",
|
||||
"sort": 2,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 11:02:00",
|
||||
"updatedAt": "2024-06-20 11:02:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 203,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 23,
|
||||
"id": 23,
|
||||
"parent_id": 22,
|
||||
"prem_name": "批量导入",
|
||||
"level": 3,
|
||||
"perm_key": "batch_import",
|
||||
"prem_type": 3,
|
||||
"path": "/20/22/23/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 11:03:00",
|
||||
"updatedAt": "2024-06-20 11:03:00",
|
||||
"children": [
|
||||
{
|
||||
"system_tenant_permission_id": 204,
|
||||
"tenant_id": 1,
|
||||
"perm_id": 24,
|
||||
"id": 24,
|
||||
"parent_id": 23,
|
||||
"prem_name": "导入校验",
|
||||
"level": 4,
|
||||
"perm_key": "import_check",
|
||||
"prem_type": 2,
|
||||
"path": "/20/22/23/24/",
|
||||
"sort": 1,
|
||||
"visible": 1,
|
||||
"createdAt": "2024-06-20 11:04:00",
|
||||
"updatedAt": "2024-06-20 11:04:00",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
145
src/views/setting/user/components/permission-collapse.vue
Normal file
145
src/views/setting/user/components/permission-collapse.vue
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
<div class="function_permission_collapse">
|
||||
<div class="top">
|
||||
<h4 class="title">{{ $t('function.permission.setting') }}</h4>
|
||||
<a-space>
|
||||
<a-button @click="selectAll">全部选中</a-button>
|
||||
<a-button @click="clearAll">全部清空</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
<div class="collapse_wrap">
|
||||
<a-collapse accordion :active-key="defaultActiveKey">
|
||||
<a-collapse-item
|
||||
v-for="item in props.permissionList"
|
||||
:key="item.id"
|
||||
:header="item.prem_name"
|
||||
>
|
||||
<div class="collapse-item-body">
|
||||
<PermissionNode
|
||||
v-for="child in item.children"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
:parent-node="item"
|
||||
:checked-keys="checkedKeys"
|
||||
@update:checked-keys="(val) => (checkedKeys = val)"
|
||||
/>
|
||||
</div>
|
||||
</a-collapse-item>
|
||||
</a-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { getRolePermission } from '@/api/setting';
|
||||
import PermissionNode from './permission-node.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
roleId: number | null;
|
||||
permissionList: any;
|
||||
}>();
|
||||
const emit = defineEmits(['update:checkedKeys']);
|
||||
|
||||
const checkedKeys = ref<number[]>([]);
|
||||
const defaultActiveKey = ref<number[]>([]);
|
||||
|
||||
const getRolePermissionData = async (id: number) => {
|
||||
try {
|
||||
const { code, data } = await getRolePermission(id);
|
||||
if (code === 200 && data) {
|
||||
const { permission } = data;
|
||||
if (!permission || permission.length === 0) {
|
||||
checkedKeys.value = [];
|
||||
return;
|
||||
}
|
||||
// 如果返回的是权限对象数组,提取ID
|
||||
checkedKeys.value = Array.isArray(permission)
|
||||
? permission.map((p: any) => (typeof p === 'object' ? p.id : p))
|
||||
: [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取角色权限失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 递归收集所有权限ID(支持无限层级)
|
||||
function collectAllIds(list: any[]): number[] {
|
||||
const ids: number[] = [];
|
||||
|
||||
const traverse = (items: any[]) => {
|
||||
items.forEach((item: any) => {
|
||||
ids.push(item.id);
|
||||
if (item.children && item.children.length > 0) {
|
||||
traverse(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
traverse(list);
|
||||
return ids;
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
if (!props.permissionList || props.permissionList.length === 0) {
|
||||
return;
|
||||
}
|
||||
checkedKeys.value = collectAllIds(props.permissionList);
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
checkedKeys.value = [];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => checkedKeys.value,
|
||||
(val) => {
|
||||
emit('update:checkedKeys', checkedKeys.value);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.roleId,
|
||||
(val, oldval) => {
|
||||
if (val && val !== oldval) {
|
||||
getRolePermissionData(val);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听权限列表变化,设置默认展开项
|
||||
watch(
|
||||
() => props.permissionList,
|
||||
(newList) => {
|
||||
if (newList && newList.length > 0) {
|
||||
defaultActiveKey.value = [newList[0].id];
|
||||
console.log(defaultActiveKey.value, 'defaultActiveKey');
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.function_permission_collapse {
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse_wrap {
|
||||
padding-bottom: 20px;
|
||||
margin-top: 10px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
237
src/views/setting/user/components/permission-node.vue
Normal file
237
src/views/setting/user/components/permission-node.vue
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
<template>
|
||||
<div class="permission-node" :class="'level-' + node.level">
|
||||
<!-- 非按钮权限 -->
|
||||
<a-checkbox
|
||||
v-if="node.prem_type !== 2"
|
||||
:indeterminate="isIndeterminate"
|
||||
:model-value="isChecked"
|
||||
@change="onCheck($event)"
|
||||
>
|
||||
{{ node.prem_name }}
|
||||
</a-checkbox>
|
||||
|
||||
<!-- 递归渲染所有子节点(非按钮权限) -->
|
||||
<div v-if="normalChildren.length" class="children">
|
||||
<PermissionNode
|
||||
v-for="child in normalChildren"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
:parent-node="node"
|
||||
:checked-keys="checkedKeys"
|
||||
@update:checked-keys="updateCheckedKeys"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 按钮权限一行展示 -->
|
||||
<div v-if="buttonChildren.length" class="btn-permission-row">
|
||||
<a-button
|
||||
v-for="btn in buttonChildren"
|
||||
:key="btn.id"
|
||||
size="mini"
|
||||
:type="checkedKeys.includes(btn.id) ? 'outline' : undefined"
|
||||
@click="toggleButtonPermission(btn.id)"
|
||||
>
|
||||
{{ btn.prem_name }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
node: any;
|
||||
checkedKeys: number[];
|
||||
parentNode?: any;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:checkedKeys']);
|
||||
|
||||
// 递归获取所有子节点的ID(包括子节点的子节点)
|
||||
function getAllChildrenIds(node: any): number[] {
|
||||
let ids: number[] = [node.id];
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach((child: any) => {
|
||||
ids = ids.concat(getAllChildrenIds(child));
|
||||
});
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// 递归获取所有父节点的ID
|
||||
function getAllParentIds(node: any, parentNode: any): number[] {
|
||||
const ids: number[] = [];
|
||||
let currentParent = parentNode;
|
||||
|
||||
while (currentParent) {
|
||||
if (!ids.includes(currentParent.id)) {
|
||||
ids.push(currentParent.id);
|
||||
}
|
||||
currentParent = currentParent.parentNode;
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
// 移除空的父节点(当所有子节点都未选中时)
|
||||
function removeEmptyParents(node: any, keys: number[]) {
|
||||
const parent = props.parentNode;
|
||||
if (!parent || !parent.children) return;
|
||||
|
||||
// 检查当前父节点的所有子节点是否都没有被选中
|
||||
const hasCheckedChildren = parent.children.some((child: any) => {
|
||||
const childIds = getAllChildrenIds(child);
|
||||
return childIds.some((id) => keys.includes(id));
|
||||
});
|
||||
|
||||
if (!hasCheckedChildren) {
|
||||
// 移除父节点
|
||||
const parentIndex = keys.indexOf(parent.id);
|
||||
if (parentIndex !== -1) {
|
||||
keys.splice(parentIndex, 1);
|
||||
}
|
||||
|
||||
// 递归检查上级父节点
|
||||
if (parent.parentNode) {
|
||||
removeEmptyParents(parent, keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查当前节点是否被选中
|
||||
const isChecked = computed(() => {
|
||||
if (!props.node.children || props.node.children.length === 0) {
|
||||
return props.checkedKeys.includes(props.node.id);
|
||||
}
|
||||
// 对于有子节点的节点,只有当所有子节点都被选中时才认为被选中
|
||||
const allChildrenIds = getAllChildrenIds(props.node);
|
||||
return allChildrenIds.every((id) => props.checkedKeys.includes(id));
|
||||
});
|
||||
|
||||
// 检查是否为半选状态
|
||||
const isIndeterminate = computed(() => {
|
||||
if (!props.node.children || props.node.children.length === 0) return false;
|
||||
|
||||
const allChildrenIds = getAllChildrenIds(props.node);
|
||||
const checkedCount = allChildrenIds.filter((id) =>
|
||||
props.checkedKeys.includes(id)
|
||||
).length;
|
||||
|
||||
return checkedCount > 0 && checkedCount < allChildrenIds.length;
|
||||
});
|
||||
|
||||
// 过滤出非按钮权限的子节点
|
||||
const normalChildren = computed(() =>
|
||||
(props.node.children || []).filter((child: any) => child.prem_type !== 2)
|
||||
);
|
||||
|
||||
// 过滤出按钮权限的子节点
|
||||
const buttonChildren = computed(() =>
|
||||
(props.node.children || []).filter((child: any) => child.prem_type === 2)
|
||||
);
|
||||
|
||||
// 处理复选框变化
|
||||
function onCheck(checked: boolean) {
|
||||
let newKeys = [...props.checkedKeys];
|
||||
const allChildrenIds = getAllChildrenIds(props.node);
|
||||
|
||||
if (checked) {
|
||||
// 选中当前节点及其所有子节点
|
||||
allChildrenIds.forEach((id) => {
|
||||
if (!newKeys.includes(id)) {
|
||||
newKeys.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
// 选中所有父节点
|
||||
const parentIds = getAllParentIds(props.node, props.parentNode);
|
||||
parentIds.forEach((id) => {
|
||||
if (!newKeys.includes(id)) {
|
||||
newKeys.push(id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 取消选中当前节点及其所有子节点
|
||||
newKeys = newKeys.filter((id) => !allChildrenIds.includes(id));
|
||||
|
||||
// 检查并移除空的父节点
|
||||
removeEmptyParents(props.node, newKeys);
|
||||
}
|
||||
|
||||
emit('update:checkedKeys', newKeys);
|
||||
}
|
||||
|
||||
// 切换按钮权限
|
||||
function toggleButtonPermission(id: number) {
|
||||
const newKeys = [...props.checkedKeys];
|
||||
const index = newKeys.indexOf(id);
|
||||
|
||||
if (index > -1) {
|
||||
// 取消选中
|
||||
newKeys.splice(index, 1);
|
||||
removeEmptyParents(props.node, newKeys);
|
||||
} else {
|
||||
// 选中
|
||||
newKeys.push(id);
|
||||
|
||||
// 选中所有父节点
|
||||
const parentIds = getAllParentIds(props.node, props.parentNode);
|
||||
parentIds.forEach((parentId) => {
|
||||
if (!newKeys.includes(parentId)) {
|
||||
newKeys.push(parentId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
emit('update:checkedKeys', newKeys);
|
||||
}
|
||||
|
||||
// 更新选中的权限
|
||||
function updateCheckedKeys(newKeys: number[]) {
|
||||
emit('update:checkedKeys', newKeys);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.permission-node {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.level-1 {
|
||||
margin-left: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.level-2 {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.level-3 {
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.level-4 {
|
||||
margin-left: 48px;
|
||||
}
|
||||
|
||||
.level-5 {
|
||||
margin-left: 64px;
|
||||
}
|
||||
|
||||
.level-6 {
|
||||
margin-left: 80px;
|
||||
}
|
||||
|
||||
.children {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.btn-permission-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-left: 32px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
150
src/views/setting/user/components/role-form-drawer.vue
Normal file
150
src/views/setting/user/components/role-form-drawer.vue
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="isEdit ? $t('role.edit') : $t('role.add')"
|
||||
unmount-on-close
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
layout="vertical"
|
||||
>
|
||||
<!-- 角色名称 -->
|
||||
<a-form-item :label="$t('roleName')" field="name" required>
|
||||
<a-input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('please.enter', { placeholder: $t('roleName') })"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 角色描述 -->
|
||||
<a-form-item :label="$t('roleDesc')" field="remark">
|
||||
<a-textarea
|
||||
v-model="form.remark"
|
||||
:auto-size="{ minRows: 3, maxRows: 8 }"
|
||||
:placeholder="$t('please.enter', { placeholder: $t('roleDesc') })"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import {
|
||||
RoleFormParam,
|
||||
RoleItem,
|
||||
updateRole,
|
||||
createRole,
|
||||
} from '@/api/setting';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
isEdit: boolean;
|
||||
record?: RoleItem | null;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'submitted'): void;
|
||||
}>();
|
||||
// 表单数据
|
||||
const formRef = ref<FormInstance>();
|
||||
const defaultForm: RoleFormParam = {
|
||||
name: '',
|
||||
remark: '',
|
||||
};
|
||||
const form = reactive<RoleFormParam>({ ...defaultForm });
|
||||
const { t } = useI18n();
|
||||
// 校验规则
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('please.enter', { placeholder: t('roleName') }),
|
||||
},
|
||||
],
|
||||
remark: [
|
||||
{
|
||||
message: t('please.enter', { placeholder: t('roleDesc') }),
|
||||
},
|
||||
],
|
||||
});
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (props.isEdit && props.record) {
|
||||
const record = props.record as RoleItem & {
|
||||
roleId?: number;
|
||||
departmentId?: number;
|
||||
isManager?: number;
|
||||
};
|
||||
Object.assign(form, {
|
||||
...record,
|
||||
});
|
||||
} else {
|
||||
Object.assign(form, defaultForm);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
const valid = await formRef.value.validate();
|
||||
if (valid) return;
|
||||
const params = { ...form };
|
||||
const res = props?.isEdit
|
||||
? await updateRole(params)
|
||||
: await createRole(params);
|
||||
if (res?.code === 200) {
|
||||
Message.success(res?.message);
|
||||
emit('submitted');
|
||||
emit('update:visible', false);
|
||||
}
|
||||
} catch (err) {
|
||||
// 捕获异常,暂不处理
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(label.arco-form-item-label) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.label_wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.required {
|
||||
color: #f53f3f;
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -133,6 +133,7 @@
|
|||
FormUserItem,
|
||||
disableUser,
|
||||
delUser,
|
||||
UserStatus,
|
||||
} from '@/api/setting';
|
||||
import useDepartmentList from '@/composables/useDepartmentList';
|
||||
import useDataTable from '@/composables/useDataTable';
|
||||
|
|
@ -329,7 +330,8 @@
|
|||
};
|
||||
const handleDisableUser = async (id: number, currentStatus: number) => {
|
||||
try {
|
||||
const param = { id, status: currentStatus === 1 ? 2 : 1 };
|
||||
const status: UserStatus = currentStatus === 1 ? 2 : 1;
|
||||
const param = { id, status };
|
||||
const { code, message } = await disableUser(param);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,447 @@
|
|||
<template>
|
||||
<div>帅哥kilo</div>
|
||||
<div class="setting_role_list">
|
||||
<Breadcrumb
|
||||
:items="['menu.setting', 'menu.setting.user', 'menu.setting.role']"
|
||||
/>
|
||||
<a-row :gutter="12" :wrap="false">
|
||||
<a-col flex="310px">
|
||||
<a-card class="full_height" :bordered="false">
|
||||
<template #title>
|
||||
<div class="card-title_between">
|
||||
<p class="title">{{ $t('role.list') }}</p>
|
||||
<a-button type="primary" @click="handleAddRole">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
{{ $t('role.add') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<a-spin style="display: block" :loading="loading">
|
||||
<ul class="role_list">
|
||||
<li
|
||||
v-for="role in roleList"
|
||||
:key="role.id"
|
||||
:class="[
|
||||
'role_item',
|
||||
{ active: currentRoleActiveId === role.id },
|
||||
]"
|
||||
@click="handleClickRoleItem(role.id)"
|
||||
>
|
||||
<p class="name">{{ role.name }}</p>
|
||||
<div v-if="currentRoleActiveId === role.id" class="actions">
|
||||
<span @click="handleEditRole(role)"><icon-edit /></span>
|
||||
<span>
|
||||
<a-popconfirm
|
||||
:ok-text="$t('cancel')"
|
||||
:cancel-text="$t('delete')"
|
||||
:ok-button-props="{ type: 'outline' }"
|
||||
:cancel-button-props="{
|
||||
type: 'primary',
|
||||
status: 'danger',
|
||||
}"
|
||||
:content="
|
||||
$t('please.confirm.delete.some', { name: $t('role') })
|
||||
"
|
||||
type="error"
|
||||
@cancel="handleDelRole(role.id)"
|
||||
>
|
||||
<icon-delete />
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</a-spin>
|
||||
<roleFormDrawer
|
||||
v-model:visible="showDrawer"
|
||||
:record="roleItemInfo"
|
||||
:is-edit="isEdit"
|
||||
@submitted="onRoleSubmit"
|
||||
@update:checked-keys="(val) => (checkedKeys = val)"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col flex="auto" class="full_height">
|
||||
<a-tabs class="tabs_card" @change="handleTabChange">
|
||||
<template #extra>
|
||||
<p style="margin-right: 10px">
|
||||
<span>{{ $t('last.updated') }}</span>
|
||||
<span>{{ roleItemInfo ? roleItemInfo.updatedAt : '' }}</span>
|
||||
</p>
|
||||
<a-button type="primary" @click="handleSubmit">{{
|
||||
$t('submit')
|
||||
}}</a-button>
|
||||
</template>
|
||||
<a-tab-pane key="1" :title="$t('function.permission')">
|
||||
<permissionCollapse
|
||||
:role-id="currentRoleActiveId"
|
||||
:permission-list="permissionList"
|
||||
@update:checked-keys="(val) => (checkedKeys = val)"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :title="$t('data.permission')">
|
||||
Content of Tab Panel 2
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" :title="$t('user.assignment')">
|
||||
<h4>{{ $t('user.assignment.setting') }}</h4>
|
||||
<DataTable
|
||||
style="margin-top: 20px"
|
||||
:data="renderData"
|
||||
:columns="projectColumn"
|
||||
:loading="loading"
|
||||
:total="total"
|
||||
:current="current"
|
||||
:page-size="pageSize"
|
||||
pagination
|
||||
@page-change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
>
|
||||
<template #realname="{ record }">
|
||||
<span>{{ record.realname }}</span>
|
||||
<icon-idcard v-if="record.isManager === 1" />
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="record.status == 1 ? 'green' : 'red'">
|
||||
{{ record.status == 1 ? $t('enable') : $t('disable') }}</a-tag
|
||||
>
|
||||
</template>
|
||||
<template #operate="{ record }">
|
||||
<a-button @click="changeRole(record?.id)">{{
|
||||
$t('change.role')
|
||||
}}</a-button>
|
||||
</template>
|
||||
</DataTable>
|
||||
<changeRoleDrawer
|
||||
v-model:visible="changeRoleDrawerVisible"
|
||||
:role-id="currentRoleActiveId"
|
||||
:user-id="selectedUserId"
|
||||
:role-list="roleList"
|
||||
@update-user="onChangeRole"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {
|
||||
getRoleList,
|
||||
RoleItem,
|
||||
delRole,
|
||||
saveRolePermission,
|
||||
getPermissionList,
|
||||
getUserListFromRole,
|
||||
UserList,
|
||||
} from '@/api/setting';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useDataTable from '@/composables/useDataTable';
|
||||
import type { TableColumnData } from '@arco-design/web-vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import permissionCollapse from './components/permission-collapse.vue';
|
||||
import roleFormDrawer from './components/role-form-drawer.vue';
|
||||
import changeRoleDrawer from './components/change-role-drawer.vue';
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const { t } = useI18n();
|
||||
const roleList = ref<RoleItem[]>([]);
|
||||
const currentRoleActiveId = ref<number | null>(null);
|
||||
const showDrawer = ref<boolean>(false);
|
||||
const isEdit = ref<boolean>(false);
|
||||
const roleItemInfo = ref<RoleItem | null>(null);
|
||||
const checkedKeys = ref<number[]>([]);
|
||||
const permissionList = ref<any[]>([]);
|
||||
const changeRoleDrawerVisible = ref<boolean>(false);
|
||||
const selectedUserId = ref<number | null>(null);
|
||||
// 使用 DataTable composable
|
||||
const {
|
||||
data: renderData,
|
||||
columns: projectColumn,
|
||||
// loading,
|
||||
total,
|
||||
current,
|
||||
pageSize,
|
||||
setData,
|
||||
setColumns,
|
||||
// setLoading,
|
||||
setTotal,
|
||||
setCurrent,
|
||||
setPageSize,
|
||||
} = useDataTable({
|
||||
initialPage: 1,
|
||||
initialPageSize: 10,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
});
|
||||
const userListData = ref<UserList>([]);
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: t('username'),
|
||||
dataIndex: 'username',
|
||||
key: 'username',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('name'),
|
||||
dataIndex: 'realname',
|
||||
key: 'realname',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
slotName: 'realname',
|
||||
},
|
||||
{
|
||||
title: t('department'),
|
||||
dataIndex: 'deptName',
|
||||
key: 'departmentId',
|
||||
},
|
||||
{
|
||||
title: t('role'),
|
||||
dataIndex: 'roleName',
|
||||
key: 'roleName',
|
||||
},
|
||||
{
|
||||
title: t('status'),
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: t('lastLogin'),
|
||||
dataIndex: 'lastLoginTime',
|
||||
key: 'lastLoginTime',
|
||||
render: ({ record }) => record.lastLoginTime || '-',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'operate',
|
||||
slotName: 'operate',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
// 获取权限列表数据
|
||||
const getPermissionListData = async () => {
|
||||
try {
|
||||
const { code, data } = await getPermissionList();
|
||||
if (code === 200 && data) {
|
||||
// 根据 permission-collapse 组件的使用,API 返回的是权限数组
|
||||
permissionList.value = Array.isArray(data?.permission)
|
||||
? data.permission
|
||||
: [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取权限列表失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 根据权限ID获取权限信息(包括路径)
|
||||
const getPermissionById = (id: number): any => {
|
||||
const findPermission = (list: any[]): any => {
|
||||
// 用数组方法替换 for...of 循环
|
||||
return list.reduce((found, item) => {
|
||||
if (found) return found;
|
||||
if (item.id === id) {
|
||||
return item;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
return findPermission(item.children) || found;
|
||||
}
|
||||
return found;
|
||||
}, null);
|
||||
};
|
||||
return findPermission(permissionList.value);
|
||||
};
|
||||
const getUserListFromRoleData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const params = {
|
||||
role_id: currentRoleActiveId.value,
|
||||
page: current.value,
|
||||
page_size: pageSize.value,
|
||||
};
|
||||
const { code, data } = await getUserListFromRole(params);
|
||||
if (code === 200) {
|
||||
const { page, userList } = data;
|
||||
userListData.value = Array.isArray(userList) ? userList : [];
|
||||
setData(userListData.value);
|
||||
setTotal(typeof page.total === 'number' ? page.total : 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const getRoleListData = async (isInit: boolean) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { code, data } = await getRoleList();
|
||||
if (code === 200) {
|
||||
roleList.value = Array.isArray(data?.rolesList) ? data.rolesList : [];
|
||||
if (isInit) {
|
||||
roleItemInfo.value = roleList.value[0];
|
||||
currentRoleActiveId.value = roleItemInfo.value
|
||||
? roleItemInfo.value.id
|
||||
: null;
|
||||
getUserListFromRoleData();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 错误处理(保留注释,避免空代码块)
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const handleAddRole = () => {
|
||||
isEdit.value = false;
|
||||
showDrawer.value = true;
|
||||
roleItemInfo.value = null;
|
||||
};
|
||||
const handleClickRoleItem = (id: number) => {
|
||||
if (id === currentRoleActiveId.value) return;
|
||||
currentRoleActiveId.value = id;
|
||||
getUserListFromRoleData();
|
||||
};
|
||||
const handleEditRole = (role: RoleItem) => {
|
||||
roleItemInfo.value = role;
|
||||
isEdit.value = true;
|
||||
showDrawer.value = true;
|
||||
};
|
||||
const handleDelRole = async (id: number) => {
|
||||
try {
|
||||
const { code, message } = await delRole(Number(id));
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
getRoleListData(false);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
const onRoleSubmit = () => {
|
||||
getRoleListData(false);
|
||||
};
|
||||
|
||||
function handleSubmit() {
|
||||
if (!currentRoleActiveId.value) {
|
||||
Message.warning('请先选择一个角色');
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkedKeys.value.length === 0) {
|
||||
Message.warning('请至少选择一个权限');
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据选中的权限ID获取完整的权限信息
|
||||
const permissionData = checkedKeys.value
|
||||
.map((key) => {
|
||||
const permission = getPermissionById(key);
|
||||
return {
|
||||
id: key,
|
||||
path: permission?.path || '',
|
||||
};
|
||||
})
|
||||
.filter((item) => item.id);
|
||||
|
||||
// 调用保存角色权限的API
|
||||
saveRolePermission({
|
||||
id: currentRoleActiveId.value,
|
||||
children: permissionData,
|
||||
})
|
||||
.then(({ code, message }) => {
|
||||
if (code === 200) {
|
||||
Message.success(message || '权限保存成功');
|
||||
} else {
|
||||
Message.error(message || '权限保存失败');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
Message.error('权限保存失败');
|
||||
console.error('保存角色权限失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
if (key === '3') {
|
||||
// 占位注释,避免空代码块
|
||||
}
|
||||
};
|
||||
// 分页事件处理
|
||||
const onPageChange = (page: number) => {
|
||||
setCurrent(page);
|
||||
current.value = page;
|
||||
getUserListFromRoleData();
|
||||
};
|
||||
const onPageSizeChange = (size: number) => {
|
||||
setPageSize(size);
|
||||
pageSize.value = size;
|
||||
getUserListFromRoleData();
|
||||
};
|
||||
const changeRole = (id: number) => {
|
||||
selectedUserId.value = id;
|
||||
changeRoleDrawerVisible.value = true;
|
||||
};
|
||||
|
||||
const onChangeRole = () => {
|
||||
getUserListFromRoleData();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getRoleListData(true);
|
||||
getPermissionListData();
|
||||
setColumns(columns);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tabs_card {
|
||||
background-color: #fff;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.role_list {
|
||||
.role_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #efefef;
|
||||
|
||||
&.active {
|
||||
background-color: #efefef;
|
||||
|
||||
.name {
|
||||
cursor: pointer;
|
||||
color: #2d5cf6;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actions {
|
||||
& > span {
|
||||
width: 16px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue