Files
vue3-element-admin/src/views/system/role/index.vue
2025-02-24 17:15:05 +08:00

452 lines
12 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>
<div class="app-container">
<div class="search-bar">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item prop="keywords" label="关键字">
<el-input
v-model="queryParams.keywords"
placeholder="角色名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
<el-button icon="refresh" @click="handleResetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<el-card shadow="never">
<div class="mb-10px">
<el-button type="success" icon="plus" @click="handleOpenDialog()">新增</el-button>
<el-button type="danger" :disabled="ids.length === 0" icon="delete" @click="handleDelete()">
删除
</el-button>
</div>
<el-table
ref="dataTableRef"
v-loading="loading"
:data="roleList"
highlight-current-row
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色名称" prop="name" min-width="100" />
<el-table-column label="角色编码" prop="code" width="150" />
<el-table-column label="状态" align="center" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
<el-tag v-else type="info">禁用</el-tag>
</template>
</el-table-column>
<el-table-column label="排序" align="center" width="80" prop="sort" />
<el-table-column fixed="right" label="操作" width="220">
<template #default="scope">
<el-button
type="primary"
size="small"
link
icon="position"
@click="handleOpenAssignPermDialog(scope.row)"
>
分配权限
</el-button>
<el-button
type="primary"
size="small"
link
icon="edit"
@click="handleOpenDialog(scope.row.id)"
>
编辑
</el-button>
<el-button
type="danger"
size="small"
link
icon="delete"
@click="handleDelete(scope.row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-if="total > 0"
v-model:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
</el-card>
<!-- 角色表单弹窗 -->
<el-dialog
v-model="dialog.visible"
:title="dialog.title"
width="500px"
@close="handleCloseDialog"
>
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item label="角色编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入角色编码" />
</el-form-item>
<el-form-item label="数据权限" prop="dataScope">
<el-select v-model="formData.dataScope">
<el-option :key="0" label="全部数据" :value="0" />
<el-option :key="1" label="部门及子部门数据" :value="1" />
<el-option :key="2" label="本部门数据" :value="2" />
<el-option :key="3" label="本人数据" :value="3" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :value="1">正常</el-radio>
<el-radio :value="0">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number
v-model="formData.sort"
controls-position="right"
:min="0"
style="width: 100px"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleSubmit"> </el-button>
<el-button @click="handleCloseDialog"> </el-button>
</div>
</template>
</el-dialog>
<!-- 分配权限弹窗 -->
<el-drawer
v-model="assignPermDialogVisible"
:title="'【' + checkedRole.name + '】权限分配'"
size="500"
>
<div class="flex-x-between">
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单权限名称">
<template #prefix>
<Search />
</template>
</el-input>
<div class="flex-center ml-5">
<el-button type="primary" size="small" plain @click="togglePermTree">
<template #icon>
<Switch />
</template>
{{ isExpanded ? "收缩" : "展开" }}
</el-button>
<el-checkbox
v-model="parentChildLinked"
class="ml-5"
@change="handleparentChildLinkedChange"
>
父子联动
</el-checkbox>
<el-tooltip placement="bottom">
<template #content>
如果只需勾选菜单权限不需要勾选子菜单或者按钮权限请关闭父子联动
</template>
<el-icon class="ml-1 color-[--el-color-primary] inline-block cursor-pointer">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</div>
<el-tree
ref="permTreeRef"
node-key="value"
show-checkbox
:data="menuPermOptions"
:filter-node-method="handlePermFilter"
:default-expand-all="true"
:check-strictly="!parentChildLinked"
class="mt-5"
>
<template #default="{ data }">
{{ data.label }}
</template>
</el-tree>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="handleAssignPermSubmit"> </el-button>
<el-button @click="assignPermDialogVisible = false"> </el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "Role",
inheritAttrs: false,
});
import RoleAPI, { RolePageVO, RoleForm, RolePageQuery } from "@/api/system/role";
import MenuAPI from "@/api/system/menu";
const queryFormRef = ref();
const roleFormRef = ref();
const permTreeRef = ref();
const loading = ref(false);
const ids = ref<number[]>([]);
const total = ref(0);
const queryParams = reactive<RolePageQuery>({
pageNum: 1,
pageSize: 10,
});
// 角色表格数据
const roleList = ref<RolePageVO[]>();
// 菜单权限下拉
const menuPermOptions = ref<OptionType[]>([]);
// 弹窗
const dialog = reactive({
title: "",
visible: false,
});
// 角色表单
const formData = reactive<RoleForm>({
sort: 1,
status: 1,
});
const rules = reactive({
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
code: [{ required: true, message: "请输入角色编码", trigger: "blur" }],
dataScope: [{ required: true, message: "请选择数据权限", trigger: "blur" }],
status: [{ required: true, message: "请选择状态", trigger: "blur" }],
});
// 选中的角色
interface CheckedRole {
id?: number;
name?: string;
}
const checkedRole = ref<CheckedRole>({});
const assignPermDialogVisible = ref(false);
const permKeywords = ref("");
const isExpanded = ref(true);
const parentChildLinked = ref(true);
// 查询
function handleQuery() {
loading.value = true;
RoleAPI.getPage(queryParams)
.then((data) => {
roleList.value = data.list;
total.value = data.total;
})
.finally(() => {
loading.value = false;
});
}
// 重置查询
function handleResetQuery() {
queryFormRef.value.resetFields();
queryParams.pageNum = 1;
handleQuery();
}
// 行复选框选中
function handleSelectionChange(selection: any) {
ids.value = selection.map((item: any) => item.id);
}
// 打开角色弹窗
function handleOpenDialog(roleId?: number) {
dialog.visible = true;
if (roleId) {
dialog.title = "修改角色";
RoleAPI.getFormData(roleId).then((data) => {
Object.assign(formData, data);
});
} else {
dialog.title = "新增角色";
}
}
// 提交角色表单
function handleSubmit() {
roleFormRef.value.validate((valid: any) => {
if (valid) {
loading.value = true;
const roleId = formData.id;
if (roleId) {
RoleAPI.update(roleId, formData)
.then(() => {
ElMessage.success("修改成功");
handleCloseDialog();
handleResetQuery();
})
.finally(() => (loading.value = false));
} else {
RoleAPI.add(formData)
.then(() => {
ElMessage.success("新增成功");
handleCloseDialog();
handleResetQuery();
})
.finally(() => (loading.value = false));
}
}
});
}
// 关闭弹窗
function handleCloseDialog() {
dialog.visible = false;
roleFormRef.value.resetFields();
roleFormRef.value.clearValidate();
formData.id = undefined;
formData.sort = 1;
formData.status = 1;
}
// 删除角色
function handleDelete(roleId?: number) {
const roleIds = [roleId || ids.value].join(",");
if (!roleIds) {
ElMessage.warning("请勾选删除项");
return;
}
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(
() => {
loading.value = true;
RoleAPI.deleteByIds(roleIds)
.then(() => {
ElMessage.success("删除成功");
handleResetQuery();
})
.finally(() => (loading.value = false));
},
() => {
ElMessage.info("已取消删除");
}
);
}
// 打开分配菜单权限弹窗
async function handleOpenAssignPermDialog(row: RolePageVO) {
const roleId = row.id;
if (roleId) {
assignPermDialogVisible.value = true;
loading.value = true;
checkedRole.value.id = roleId;
checkedRole.value.name = row.name;
// 获取所有的菜单
menuPermOptions.value = await MenuAPI.getOptions();
// 回显角色已拥有的菜单
RoleAPI.getRoleMenuIds(roleId)
.then((data) => {
const checkedMenuIds = data;
checkedMenuIds.forEach((menuId) => permTreeRef.value!.setChecked(menuId, true, false));
})
.finally(() => {
loading.value = false;
});
}
}
// 分配菜单权限提交
function handleAssignPermSubmit() {
const roleId = checkedRole.value.id;
if (roleId) {
const checkedMenuIds: number[] = permTreeRef
.value!.getCheckedNodes(false, true)
.map((node: any) => node.value);
loading.value = true;
RoleAPI.updateRoleMenus(roleId, checkedMenuIds)
.then(() => {
ElMessage.success("分配权限成功");
assignPermDialogVisible.value = false;
handleResetQuery();
})
.finally(() => {
loading.value = false;
});
}
}
// 展开/收缩 菜单权限树
function togglePermTree() {
isExpanded.value = !isExpanded.value;
if (permTreeRef.value) {
Object.values(permTreeRef.value.store.nodesMap).forEach((node: any) => {
if (isExpanded.value) {
node.expand();
} else {
node.collapse();
}
});
}
}
// 权限筛选
watch(permKeywords, (val) => {
permTreeRef.value!.filter(val);
});
function handlePermFilter(
value: string,
data: {
[key: string]: any;
}
) {
if (!value) return true;
return data.label.includes(value);
}
// 父子菜单节点是否联动
function handleparentChildLinkedChange(val: any) {
parentChildLinked.value = val;
}
onMounted(() => {
handleQuery();
});
</script>