优化用户和角色管理

This commit is contained in:
李志强 2026-02-26 10:05:41 +08:00
parent 86ea16ec4b
commit 0b268c65d6
2 changed files with 156 additions and 155 deletions

View File

@ -1,18 +1,8 @@
<template>
<el-dialog
v-model="visible"
:title="isEdit ? '编辑角色' : '添加角色'"
width="600px"
@close="handleClose"
>
<el-dialog v-model="visible" :title="isEdit ? '编辑角色' : '添加角色'" width="600px" @close="handleClose">
<el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
<el-form-item label="角色名称" prop="name">
<el-input
v-model="form.name"
placeholder="请输入角色名称"
maxlength="50"
show-word-limit
/>
<el-input v-model="form.name" placeholder="请输入角色名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="状态" prop="status">
@ -23,15 +13,16 @@
</el-form-item>
<el-form-item label="权限设置" prop="rights">
<el-tree
ref="treeRef"
:data="menuTree"
show-checkbox
node-key="id"
:props="{ children: 'children', label: 'title' }"
:default-checked-keys="form.rights"
style="width: 100%; border: 1px solid var(--el-border-color); border-radius: 4px; padding: 10px; max-height: 400px; overflow-y: auto;"
/>
<div style="margin-bottom: 10px;">
<el-button size="small" @click="toggleSelectAll">
{{ isAllSelected ? '全不选' : '全选' }}
</el-button>
<el-button size="small" @click="handleExpandAll">展开/折叠</el-button>
</div>
<el-tree ref="treeRef" :data="menuTree" show-checkbox node-key="id"
:props="{ children: 'children', label: 'title' }" @check="updateSelectStatus"
style="width: 100%; border: 1px solid var(--el-border-color); border-radius: 4px; padding: 10px; max-height: 400px; overflow-y: auto;" />
</el-form-item>
</el-form>
@ -62,12 +53,15 @@ const props = withDefaults(defineProps<Props>(), {
const emit = defineEmits(["update:modelValue", "success"]);
// --- ---
const visible = ref(false);
const isEdit = ref(false);
const submitting = ref(false);
const formRef = ref();
const treeRef = ref();
const menuTree = ref<any[]>([]);
const isAllSelected = ref(false);
const isExpandAll = ref(false);
const form = ref({
name: "",
@ -83,40 +77,82 @@ const rules = {
status: [{ required: true, message: "请选择状态", trigger: "change" }],
};
// modelValue
watch(
() => props.modelValue,
(val) => {
visible.value = val;
if (val) {
loadMenus();
if (props.role) {
isEdit.value = true;
form.value = {
name: props.role.name,
status: props.role.status,
rights: parseRights(props.role.rights),
// --- ---
// ID
const getAllMenuIds = (data: any[]): number[] => {
const ids: number[] = [];
const traverse = (list: any[]) => {
list.forEach((item) => {
ids.push(item.id);
if (item.children && item.children.length > 0) traverse(item.children);
});
};
//
nextTick(() => {
if (treeRef.value) {
treeRef.value.setCheckedKeys(form.value.rights);
traverse(data);
return ids;
};
//
const handleExpandAll = () => {
if (!treeRef.value) return;
//
isExpandAll.value = !isExpandAll.value;
//
// nodesMap
const nodes = treeRef.value.store.nodesMap;
for (let id in nodes) {
// expanded
nodes[id].expanded = isExpandAll.value;
}
});
};
// /
const updateSelectStatus = () => {
if (!treeRef.value || menuTree.value.length === 0) {
isAllSelected.value = false;
return;
}
const checkedKeys = treeRef.value.getCheckedKeys();
const allIds = getAllMenuIds(menuTree.value);
//
isAllSelected.value = allIds.length > 0 && checkedKeys.length === allIds.length;
};
// /
const toggleSelectAll = () => {
if (!treeRef.value) return;
if (isAllSelected.value) {
// ->
treeRef.value.setCheckedKeys([]);
isAllSelected.value = false;
} else {
isEdit.value = false;
resetForm();
// ->
const allIds = getAllMenuIds(menuTree.value);
treeRef.value.setCheckedKeys(allIds);
isAllSelected.value = true;
}
}
}
);
};
// visible
watch(visible, (val) => {
emit("update:modelValue", val);
});
// --- ---
//
//
const loadMenus = async () => {
try {
const res = await getAllMenus();
if (res.code === 200) {
menuTree.value = res.data || [];
}
} catch (error) {
console.error("加载菜单失败:", error);
ElMessage.error("加载菜单失败");
}
};
//
const parseRights = (rights: any): number[] => {
if (!rights) return [];
if (Array.isArray(rights)) return rights;
@ -131,48 +167,67 @@ const parseRights = (rights: any): number[] => {
return [];
};
//
const loadMenus = async () => {
try {
const res = await getAllMenus();
if (res.code === 200) {
menuTree.value = res.data || [];
}
} catch (error) {
console.error("加载菜单失败:", error);
ElMessage.error("加载菜单失败");
}
};
//
watch(
() => props.modelValue,
async (val) => {
visible.value = val;
if (val) {
//
await loadMenus();
//
if (props.role) {
isEdit.value = true;
form.value = {
name: props.role.name,
status: props.role.status,
rights: parseRights(props.role.rights),
};
//
nextTick(() => {
if (treeRef.value) {
treeRef.value.setCheckedKeys(form.value.rights);
updateSelectStatus(); //
}
});
} else {
isEdit.value = false;
isAllSelected.value = false;
resetForm();
}
}
}
);
watch(visible, (val) => {
emit("update:modelValue", val);
});
// --- ---
//
const resetForm = () => {
form.value = {
name: "",
status: 1,
rights: [],
};
if (formRef.value) {
formRef.value.clearValidate();
}
if (treeRef.value) {
treeRef.value.setCheckedKeys([]);
}
if (formRef.value) formRef.value.clearValidate();
if (treeRef.value) treeRef.value.setCheckedKeys([]);
};
//
const handleClose = () => {
visible.value = false;
resetForm();
};
//
const handleSubmit = async () => {
if (!formRef.value) return;
try {
await formRef.value.validate();
// ID
// +
const checkedKeys = treeRef.value.getCheckedKeys();
const halfCheckedKeys = treeRef.value.getHalfCheckedKeys();
const allCheckedKeys = [...checkedKeys, ...halfCheckedKeys];
@ -201,7 +256,6 @@ const handleSubmit = async () => {
}
} catch (error: any) {
if (error !== false) {
//
ElMessage.error(error.message || "操作失败");
}
} finally {

View File

@ -4,11 +4,15 @@
<h2>用户管理</h2>
<div class="header-actions">
<el-button type="primary" @click="handleAddUser">
<el-icon><Plus /></el-icon>
<el-icon>
<Plus />
</el-icon>
添加用户
</el-button>
<el-button @click="refresh">
<el-icon><Refresh /></el-icon>
<el-icon>
<Refresh />
</el-icon>
刷新
</el-button>
</div>
@ -18,50 +22,24 @@
<!-- 用户列表表格 -->
<el-table :data="users" style="width: 100%" v-loading="loading">
<el-table-column
prop="id"
label="ID"
align="center"
fixed="left"
/>
<el-table-column prop="account" label="账号" align="center" />
<el-table-column
prop="name"
label="姓名"
align="center"
>
<el-table-column prop="id" label="ID" align="center" fixed="left" />
<el-table-column prop="name" label="姓名" min-width="120" align="center">
<template #default="scope">
<span class="name-link" @click="handlePreview(scope.row)">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="group_id" label="角色" align="center">
<template #default="scope">
<el-tag
:type="getRoleTagType(scope.row.group_id)"
size="small"
>
<el-tag :type="getRoleTagType(scope.row.group_id)" size="small">
{{ getRoleTagText(roles, scope.row.group_id) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="phone"
label="手机号"
align="center"
/>
<el-table-column prop="qq" label="QQ" align="center" />
<el-table-column
prop="last_login_ip"
label="最后登录IP"
width="120"
align="center"
/>
<el-table-column
prop="login_count"
label="登陆次数"
width="120"
align="center"
/>
<el-table-column prop="account" label="账号" min-width="180" align="center" />
<el-table-column prop="phone" label="手机号" min-width="180" align="center" />
<el-table-column prop="qq" label="QQ" min-width="180" align="center" />
<el-table-column prop="last_login_ip" label="最后登录IP" width="120" align="center" />
<el-table-column prop="login_count" label="登陆次数" width="120" align="center" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">{{
@ -71,65 +49,34 @@
</el-table-column>
<el-table-column label="操作" width="240" align="center" fixed="right">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)"
>编辑</el-button
>
<el-button
size="small"
type="warning"
@click="handleChangePassword(scope.row)"
>
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="warning" @click="handleChangePassword(scope.row)">
修改密码
</el-button>
<el-button
v-if="scope.row.username !== 'admin' && scope.row.id !== 1"
size="small"
type="danger"
@click="handleDelete(scope.row)"
>删除</el-button
>
<el-button v-if="scope.row.username !== 'admin' && scope.row.id !== 1" size="small" type="danger"
@click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-bar">
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
layout="total, prev, pager, next"
/>
<el-pagination :current-page="page" :page-size="pageSize" :total="total" @current-change="handlePageChange"
layout="total, prev, pager, next" />
</div>
<!-- 编辑用户对话框组件 -->
<UserEditDialog
ref="userEditRef"
:modelValue="editDialogVisible"
@update:modelValue="editDialogVisible = $event"
:is-edit="isEdit"
@submit="handleEditSuccess"
@close="editDialogVisible = false"
/>
<UserEditDialog ref="userEditRef" :modelValue="editDialogVisible" @update:modelValue="editDialogVisible = $event"
:is-edit="isEdit" @submit="handleEditSuccess" @close="editDialogVisible = false" />
<!-- 修改密码对话框组件 -->
<ChangePasswordDialog
ref="changePasswordRef"
:modelValue="passwordDialogVisible"
@update:modelValue="passwordDialogVisible = $event"
:user-id="currentUserId"
@submit="handlePasswordChangeSuccess"
@close="passwordDialogVisible = false"
/>
<ChangePasswordDialog ref="changePasswordRef" :modelValue="passwordDialogVisible"
@update:modelValue="passwordDialogVisible = $event" :user-id="currentUserId" @submit="handlePasswordChangeSuccess"
@close="passwordDialogVisible = false" />
<!-- 预览用户对话框组件 -->
<PreviewDialog
ref="previewDialogRef"
:modelValue="previewDialogVisible"
@update:modelValue="previewDialogVisible = $event"
:user="currentUser"
/>
<PreviewDialog ref="previewDialogRef" :modelValue="previewDialogVisible"
@update:modelValue="previewDialogVisible = $event" :user="currentUser" />
</div>
</template>