feat: 角色权限的分配

This commit is contained in:
戴业伟 2024-01-08 16:02:52 +08:00
parent 5b5f0638b3
commit 47d72c9f21
21 changed files with 891 additions and 40 deletions

View File

@ -9,4 +9,4 @@ VITE_APP_PORT = 8090
VITE_APP_BASE_API = '/api'
# proxy代理配置
VITE_APP_API_URL = ' http://192.168.1.7:12500/'
VITE_APP_API_URL = 'http://192.168.1.7:12500/'

View File

@ -6,4 +6,4 @@ VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/zsqy'
# proxy代理配置
VITE_APP_API_URL = ""
VITE_APP_API_URL = "http://175.27.240.186:12500"

View File

@ -23,10 +23,7 @@ axiosInstance.interceptors.request.use(
// 响应拦截器
axiosInstance.interceptors.response.use(
(res: AxiosResponse) => {
const { message, success } = res.data as {
success: boolean;
message: string;
};
const { message, success } = res.data as IResponse;
// 如果是文件流,直接过
if (res.config.responseType === "blob") return Promise.resolve(res.data);

View File

@ -1 +0,0 @@
export * from "./menu";

25
src/api/system/role.ts Normal file
View File

@ -0,0 +1,25 @@
import { get, post } from "@/api/http";
const fix = "/role";
const url = {
insert: `${fix}/insert`,
page: `${fix}/page`,
update: `${fix}/update`,
delete: `${fix}/delete`,
};
export const insertRule = (params: Object) => {
return post(url.insert, params);
};
export const getRuleList = (params: Object) => {
return get(url.page, params);
};
export const updateRule = (params: Object) => {
return post(url.update, params);
};
export const deleteRule = (params: Object) => {
return post(url.delete, params);
};

View File

@ -0,0 +1,15 @@
import { get, post } from "@/api/http";
const fix = "/roleMenu";
const url = {
queryMenuByRole: `${fix}/queryMenuByRole`, // 根据角色查询菜单
save: `${fix}/save`, // 保存角色菜单
};
export const queryMenuByRole = (params: Object) => {
return get(url.queryMenuByRole, params);
};
export const saveRoleMenu = (params: Object) => {
return post(url.save, params);
};

26
src/api/system/user.ts Normal file
View File

@ -0,0 +1,26 @@
import { get, post } from "@/api/http";
const fix = "/user";
const url = {
insert: `${fix}/insert`,
page: `${fix}/page`,
update: `${fix}/update`,
delete: `${fix}/delete`,
};
export const insertUser = (params: Object) => {
return post(url.insert, params);
};
export const getUserList = (params: Object) => {
return get(url.page, params);
};
export const updateUser = (params: Object) => {
return post(url.update, params);
};
export const deleteUser = (params: Object) => {
return post(url.delete, params);
};

View File

@ -0,0 +1,13 @@
// 用户角色关系
import { get, post } from "@/api/http";
const fix = "/userRole";
const url = {
save: `${fix}/save`,
queryMenuTree: `${fix}/queryMenuTree `,
};
export const saveUserRole = (params: Object) => {
return post(url.save, params);
};
export const queryUserRole = (params: Object) => {
return get(url.queryMenuTree, params);
};

View File

@ -47,12 +47,15 @@ export function useDataSource(
setLoading(true);
const { request, pagination, beforeRequest, afterRequest }: any =
unref(propsRef);
if (!request) return;
//组装分页信息
const pageField = APISETTING.pageField;
const sizeField = APISETTING.sizeField;
const totalField = APISETTING.totalField;
const listField = APISETTING.listField;
console.log(listField);
const itemCount = APISETTING.countField;
let pageParams = {};
const { page = 1, pageSize = 10 } = unref(
@ -71,7 +74,7 @@ export function useDataSource(
let params = {
...pageParams,
// ...opt,
...opt,
};
// console.log(opt);
@ -80,6 +83,7 @@ export function useDataSource(
params = (await beforeRequest(params)) || params;
}
const res = await request(params);
const resultTotal = res[totalField];
const currentPage = res[pageField];
const total = res[itemCount];

View File

@ -6,6 +6,8 @@ import { OptionsSharp } from "@vicons/ionicons5";
// 引入路径
const importPath = {
USER: () => import("@/views/system/user/user.vue"),
ROLE: () => import("@/views/system/role/role.vue"),
MENU: () => import("@/views/system/menu/menu.vue"),
};
@ -20,22 +22,30 @@ const systemRoutes: RouteRecordRaw = {
sort: 1,
},
children: [
{
path: "menu",
name: "system_menu",
meta: {
title: "菜单权限管理",
},
component: importPath["MENU"],
},
{
path: "role",
name: "system_role",
meta: {
title: "角色权限管理",
title: "角色管理",
},
component: () => import("@/views/system/role/role.vue"),
},
{
path: "user",
name: "system_user",
meta: {
title: "用户管理",
},
component: () => import("@/views/system/user/user.vue"),
},
{
path: "menu",
name: "system_menu",
meta: {
title: "菜单管理",
},
component: importPath["MENU"],
},
],
};

View File

@ -97,7 +97,7 @@
</template>
<script lang="ts" setup>
import { insertMenu } from "@/api/system";
import { insertMenu } from "@/api/system/menu";
const props = defineProps({
title: {
type: String,

View File

@ -340,7 +340,7 @@ const selectedTree = (keys) => {
const handleDel = async () => {
const res = await deleteMenu({ id: formParams.id });
if (res.status) window["$message"].success("删除成功!");
if (res.status === 200) window["$message"].success("删除成功!");
getMenuTreeApi();
};
@ -353,7 +353,7 @@ const formSubmit = () => {
formRef.value.validate(async (errors: boolean) => {
if (!errors) {
const res = await updateMenu(formParams);
if (res.status) window["$message"].success("修改成功");
if (res.status === 200) window["$message"].success("修改成功");
getMenuTreeApi();
}
});

View File

@ -1,36 +1,32 @@
import { h } from 'vue';
import { NTag } from 'naive-ui';
import { h } from "vue";
import { NTag } from "naive-ui";
export const columns = [
{
title: 'id',
key: 'id',
title: "ID",
key: "id",
},
{
title: '角色名称',
key: 'name',
title: "角色名称",
key: "roleName",
},
{
title: '说明',
key: 'explain',
title: "说明",
key: "description",
},
{
title: '是否默认角色',
key: 'isDefault',
title: "状态",
key: "status",
render(row) {
return h(
NTag,
{
type: row.isDefault ? 'success' : 'error',
type: row.status ? "success" : "error",
},
{
default: () => (row.isDefault ? '是' : '否'),
default: () => (row.status ? "正常" : "冻结"),
}
);
},
},
{
title: '创建时间',
key: 'create_date',
},
];

View File

@ -0,0 +1,108 @@
<script setup lang="ts">
import { insertRule } from "@/api/system/role";
const props = defineProps({
formRoleValue: {
type: Object,
default: () => ({}),
},
});
const formRef = ref();
const { formRoleValue } = toRefs(props);
const roleTitle = ref("新增角色");
const formBtnLoading = ref(false);
const showRoleModal = ref(false);
const rules = {
roleName: {
required: true,
message: "请输入角色名称",
trigger: "blur",
},
};
//
const confirmForm = () => {
formRef.value.validate(async (errors: any) => {
if (!errors) {
formRoleValue.value.id ? updateRuleApi() : insertRuleApi();
}
});
};
//
const updateRuleApi = () => {};
//
const insertRuleApi = async () => {
const { roleName, description, status } = formRoleValue.value;
const params = {
roleName,
description,
status,
};
const res = await insertRule(params);
if (res.status === 200) window["$message"].success("新增成功!");
};
const init = (str: string) => {
if (str === "add") {
roleTitle.value = "新增角色";
} else {
roleTitle.value = "编辑角色";
}
showRoleModal.value = true;
};
const close = () => {
showRoleModal.value = false;
};
defineExpose({
init,
close,
});
</script>
<template>
<!-- 新增角色 -->
<n-modal
v-model:show="showRoleModal"
:show-icon="false"
preset="dialog"
:title="roleTitle"
>
<n-form
ref="formRef"
:rules="rules"
label-placement="left"
label-width="auto"
:model="formRoleValue"
require-mark-placement="right-hanging"
>
<n-form-item label="角色名称" path="roleName">
<n-input
v-model:value="formRoleValue.roleName"
placeholder="输入角色名称"
/>
</n-form-item>
<n-form-item label="状态">
<n-radio-group v-model:value="formRoleValue.status" name="radiogroup1">
<n-space>
<n-radio :value="0"> 冻结 </n-radio>
<n-radio :value="1"> 启用 </n-radio>
</n-space>
</n-radio-group>
</n-form-item>
<n-form-item label="描述">
<n-input
v-model:value="formRoleValue.description"
type="textarea"
placeholder="请输入描述"
/>
</n-form-item>
</n-form>
<template #action>
<n-space>
<n-button type="primary" :loading="formBtnLoading" @click="confirmForm"
>提交</n-button
>
</n-space>
</template>
</n-modal>
</template>

View File

@ -1,6 +1,247 @@
<script setup lang="ts"></script>
<template>
<div>345</div>
<div>
<n-card :bordered="false" class="mt-4 proCard">
<BasicTable
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
>
<template #tableTitle>
<n-button type="primary" @click="addRole">
<template #icon>
<n-icon>
<PlusOutlined />
</n-icon>
</template>
添加角色
</n-button>
</template>
<template #action>
<TableAction />
</template>
</BasicTable>
</n-card>
<!-- 添加角色 -->
<RoleModal ref="formRole" :formRoleValue="formRoleValue" />
<!-- 菜单权限 -->
<n-modal
v-model:show="showModal"
:show-icon="false"
preset="dialog"
:title="editRoleTitle"
>
<div class="py-3 menu-list">
<n-tree
block-line
cascade
checkable
:virtual-scroll="true"
:data="treeData"
:expanded-keys="expandedKeys"
:checked-keys="checkedKeys"
style="max-height: 950px; overflow: hidden"
@update:checked-keys="checkedTree"
@update:expanded-keys="onExpandedKeys"
/>
</div>
<template #action>
<n-space>
<n-button type="info" ghost icon-placement="left" @click="packHandle">
全部{{ expandedKeys.length ? "收起" : "展开" }}
</n-button>
<n-button
type="info"
ghost
icon-placement="left"
@click="checkedAllHandle"
>
全部{{ checkedAll ? "取消" : "选择" }}
</n-button>
<n-button
type="primary"
:loading="formBtnLoading"
@click="confirmForm"
>提交</n-button
>
</n-space>
</template>
</n-modal>
</div>
</template>
<style scoped lang="scss"></style>
<script lang="ts" setup>
import { BasicTable, TableAction } from "@/components/Table";
import { columns } from "./columns";
import { PlusOutlined } from "@vicons/antd";
import { getTreeAll } from "@/utils";
import { getRuleList } from "@/api/system/role";
import { getMenuTree } from "@/api/system/menu";
import { queryMenuByRole, saveRoleMenu } from "@/api/system/roleMenu";
const actionRef = ref();
const formRole = ref();
const formRoleValue = reactive({
id: null,
roleName: "",
roleCode: null,
status: 1,
description: "",
});
const lastMenuIds = ref([]);
const showModal = ref(false);
const formBtnLoading = ref(false);
const checkedAll = ref(false);
const editRoleTitle = ref("");
const treeData = ref([]);
const expandedKeys = ref([]);
const checkedKeys: Ref<any[]> = ref([]);
const roleId = ref(null);
const params = reactive({
roleName: "",
});
const actionColumn = reactive({
width: 250,
title: "操作",
key: "action",
fixed: "right",
render(record) {
return h(TableAction, {
style: "button",
actions: [
{
label: "菜单权限",
onClick: handleMenuAuth.bind(null, record),
// isShow auth
ifShow: () => {
return true;
},
},
{
label: "编辑",
onClick: handleEdit.bind(null, record),
ifShow: () => {
return true;
},
},
{
label: "删除",
onClick: handleDelete.bind(null, record),
// isShow auth
ifShow: () => {
return true;
},
},
],
});
},
});
const mapTreeData = (data) => {
return data.map((ele) => ({
...ele,
key: ele.id,
label: ele.meta.title,
children: ele.children ? mapTreeData(ele.children) : undefined,
}));
};
//
const addRole = () => {
formRole.value.init("add");
};
const loadDataTable = async (res: any) => {
let _params = {
...unref(params),
...res,
};
const { data } = await getRuleList(_params);
return data;
};
function onCheckedRow(rowKeys: any[]) {
console.log(rowKeys);
}
function reloadTable() {
actionRef.value.reload();
}
async function confirmForm(e: any) {
e.preventDefault();
formBtnLoading.value = true;
const res = await saveRoleMenu({
roleId: roleId.value,
menuIds: checkedKeys.value,
lastMenuIds: lastMenuIds.value,
});
if (res.status === 200) {
showModal.value = false;
reloadTable();
window["$message"].success("保存成功");
formBtnLoading.value = false;
}
}
function handleEdit(record: Recordable) {
console.log("点击了编辑", record);
}
function handleDelete(record: Recordable) {
console.log("点击了删除", record);
}
async function handleMenuAuth(record: Recordable) {
editRoleTitle.value = `分配 ${record.roleName} 的菜单权限`;
roleId.value = record.id;
const res = await queryMenuByRole({ roleId: record.id });
lastMenuIds.value = res.data;
checkedKeys.value = res.data; // roleid
showModal.value = true;
}
function checkedTree(keys) {
checkedKeys.value = keys;
}
function onExpandedKeys(keys) {
expandedKeys.value = keys;
}
function packHandle() {
if (expandedKeys.value.length) {
expandedKeys.value = [];
} else {
expandedKeys.value = treeData.value.map((item: any) => item.key) as [];
}
}
function checkedAllHandle() {
if (!checkedAll.value) {
checkedKeys.value = getTreeAll(treeData.value);
checkedAll.value = true;
} else {
checkedKeys.value = [];
checkedAll.value = false;
}
}
onMounted(async () => {
const treeMenuList = await getMenuTree();
const res = mapTreeData(treeMenuList.data);
expandedKeys.value = treeMenuList.data.map((item) => item.key);
treeData.value = res;
});
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,51 @@
import { h } from "vue";
import { NTag } from "naive-ui";
export const columns = [
{
title: "用户名",
key: "username",
},
{
title: "真实姓名",
key: "realname",
},
{
title: "手机号",
key: "phone",
},
{
title: "电子邮箱",
key: "email",
},
{
title: "用户类型",
key: "userType",
render(row) {
return h(
NTag,
{
type: row.userType === 1 ? "success" : "info",
},
{
default: () => (row.userType === 1 ? "员工" : "用户"),
}
);
},
},
{
title: "性别",
key: "sex",
render(row) {
return h(
NTag,
{
type: row.sex === 1 ? "success" : "info",
},
{
default: () => (row.sex === 1 ? "男" : "女"),
}
);
},
},
];

View File

@ -0,0 +1,135 @@
<script setup lang="ts">
import { insertUser } from "@/api/system/user";
const props = defineProps({
formRoleValue: {
type: Object,
default: () => ({}),
},
});
const formRef = ref();
const { formRoleValue } = toRefs(props);
const roleTitle = ref("新增角色");
const formBtnLoading = ref(false);
const showRoleModal = ref(false);
const rules = {
roleName: {
required: true,
message: "请输入角色名称",
trigger: "blur",
},
};
//
const confirmForm = () => {
formRef.value.validate(async (errors: any) => {
if (!errors) {
formRoleValue.value.id ? updateUserApi() : insertUserApi();
} else {
}
});
};
//
const updateUserApi = () => {};
//
const insertUserApi = async () => {
const res = await insertUser(formRoleValue.value);
if (res.status === 200) window["$message"].success("新增成功!");
};
const init = (str: string) => {
if (str === "add") {
roleTitle.value = "新增用户";
} else {
roleTitle.value = "编辑用户";
}
showRoleModal.value = true;
};
const close = () => {
showRoleModal.value = false;
};
defineExpose({
init,
close,
});
</script>
<template>
<!-- 新增角色 -->
<n-modal
v-model:show="showRoleModal"
:show-icon="false"
preset="dialog"
:title="roleTitle"
>
<n-form
ref="formRef"
:rules="rules"
label-placement="left"
label-width="auto"
:model="formRoleValue"
require-mark-placement="right-hanging"
>
<n-form-item label="用户名" path="username">
<n-input
v-model:value="formRoleValue.username"
placeholder="请输入用户名"
/>
</n-form-item>
<n-form-item label="真实姓名">
<n-input
v-model:value="formRoleValue.realname"
placeholder="请输入真实姓名"
/>
</n-form-item>
<n-form-item label="电子邮箱">
<n-input
v-model:value="formRoleValue.email"
placeholder="请输入电子邮箱"
/>
</n-form-item>
<n-form-item label="手机号">
<n-input
v-model:value="formRoleValue.phone"
placeholder="请输入手机号"
/>
</n-form-item>
<n-form-item label="密码">
<n-input
type="password"
v-model:value="formRoleValue.password"
placeholder="请输入密码"
/>
</n-form-item>
<n-form-item label="性别">
<n-radio-group v-model:value="formRoleValue.sex" name="radiogroup1">
<n-space>
<n-radio :value="0"> </n-radio>
<n-radio :value="1"> </n-radio>
</n-space>
</n-radio-group>
</n-form-item>
<n-form-item label="用户类型">
<n-radio-group
v-model:value="formRoleValue.userType"
name="radiogroup1"
>
<n-space>
<n-radio :value="2"> 用户 </n-radio>
<n-radio :value="1"> 员工 </n-radio>
</n-space>
</n-radio-group>
</n-form-item>
</n-form>
<template #action>
<n-space>
<n-button type="primary" :loading="formBtnLoading" @click="confirmForm"
>提交</n-button
>
</n-space>
</template>
</n-modal>
</template>

View File

@ -0,0 +1,228 @@
<template>
<div>
<n-card :bordered="false" class="mt-4 proCard">
<BasicTable
:columns="columns"
:request="loadDataTable"
:row-key="(row) => row.id"
ref="actionRef"
:actionColumn="actionColumn"
@update:checked-row-keys="onCheckedRow"
>
<template #tableTitle>
<n-button type="primary" @click="addRole">
<template #icon>
<n-icon>
<PlusOutlined />
</n-icon>
</template>
添加用户
</n-button>
</template>
<template #action>
<TableAction />
</template>
</BasicTable>
</n-card>
<!-- 添加角色 -->
<UserModal ref="formRole" :formRoleValue="formRoleValue" />
<!-- 分配角色 -->
<n-modal
v-model:show="showModal"
:show-icon="false"
preset="dialog"
:title="editRoleTitle"
>
<n-form
ref="formRef"
:rules="rules"
label-placement="left"
label-width="auto"
:model="formRoleValue"
require-mark-placement="right-hanging"
>
<n-form-item label="用户名">
<n-input
disabled
v-model:value="formRoleValue.username"
placeholder="请输入用户名"
/>
</n-form-item>
<n-form-item label="真实姓名">
<n-input
disabled
v-model:value="formRoleValue.realname"
placeholder="请输入真实姓名"
/>
</n-form-item>
<n-form-item label="角色" path="roleIds">
<n-select
label-field="roleName"
value-field="id"
multiple
clearable
v-model:value="formRoleValue.roleIds"
:options="roleOptions"
/>
</n-form-item>
</n-form>
<template #action>
<n-space>
<n-button
type="primary"
:loading="formBtnLoading"
@click="confirmForm"
>提交</n-button
>
</n-space>
</template>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import { BasicTable, TableAction } from "@/components/Table";
import { columns } from "./columns";
import { PlusOutlined } from "@vicons/antd";
import { getUserList, updateUser } from "@/api/system/user";
import { getRuleList } from "@/api/system/role";
import { saveUserRole } from "@/api/system/userrole";
const actionRef = ref();
const formRef = ref();
const formRole = ref();
const rules = {
// roleIds: {
// type: "array",
// required: true,
// message: "",
// trigger: ["blur", "change"],
// },
};
const roleIds = ref([]);
const formRoleValue = reactive({
id: null,
username: "",
realname: null,
password: "123456",
userType: 2,
sex: 1,
email: "",
phone: "",
status: 1,
skinColor: "white",
roleIds: [],
});
const roleOptions = ref([]);
const showModal = ref(false);
const formBtnLoading = ref(false);
const editRoleTitle = ref("");
const params = reactive({
username: "",
});
const actionColumn = reactive({
width: 250,
title: "操作",
key: "action",
fixed: "right",
render(record) {
return h(TableAction, {
style: "button",
actions: [
{
label: "配置角色",
onClick: handleMenuAuth.bind(null, record),
// isShow auth
ifShow: () => {
return true;
},
},
{
label: "编辑",
onClick: handleEdit.bind(null, record),
ifShow: () => {
return true;
},
},
{
label: "删除",
onClick: handleDelete.bind(null, record),
// isShow auth
ifShow: () => {
return true;
},
},
],
});
},
});
//
const addRole = () => {
formRole.value.init("add");
};
const loadDataTable = async (res: any) => {
let _params = {
...unref(params),
...res,
};
const { data } = await getUserList(_params);
return data;
};
function onCheckedRow(rowKeys: any[]) {
console.log(rowKeys);
}
function reloadTable() {
actionRef.value.reload();
}
async function confirmForm() {
formRef.value.validate(async (errors: any) => {
if (!errors) {
formBtnLoading.value = true;
const res = await saveUserRole({
userId: formRoleValue.id,
roleIds: formRoleValue.roleIds,
});
if (res.status === 200) {
showModal.value = false;
window["$message"].success("保存成功");
reloadTable();
formBtnLoading.value = false;
}
}
});
}
function handleEdit(record: Recordable) {
Object.assign(formRoleValue, record);
formRole.value.init("edit");
}
function handleDelete(record: Recordable) {}
async function handleMenuAuth(record: Recordable) {
editRoleTitle.value = `分配 ${record.realname} 的角色权限`;
Object.assign(formRoleValue, record);
showModal.value = true;
}
onMounted(async () => {
const { data } = await getRuleList({ current: 1, size: 100 });
roleOptions.value = data.records;
});
</script>
<style lang="less" scoped></style>

View File

@ -17,7 +17,9 @@ declare module '@vue/runtime-core' {
Histogram: typeof import('./../src/components/Charts/Histogram.vue')['default']
Line: typeof import('./../src/components/Charts/Line.vue')['default']
Main: typeof import('./../src/layout/components/Main/index.vue')['default']
MenuModal: typeof import('./../src/views/system/role/components/MenuModal.vue')['default']
Ring: typeof import('./../src/components/Charts/Ring.vue')['default']
RoleModal: typeof import('./../src/views/system/role/components/RoleModal.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sider: typeof import('./../src/layout/components/Sider/index.vue')['default']
@ -25,5 +27,6 @@ declare module '@vue/runtime-core' {
Table: typeof import('./../src/components/Table/src/Table.vue')['default']
TableAction: typeof import('./../src/components/Table/src/components/TableAction.vue')['default']
TransitionMain: typeof import('./../src/layout/components/TransitionMain/index.vue')['default']
UserModal: typeof import('./../src/views/system/user/components/UserModal.vue')['default']
}
}