优化用户和角色管理

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

View File

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