feat: 切换角色
This commit is contained in:
parent
9766950106
commit
d79b39b7eb
@ -28,4 +28,3 @@ async def menu_put(pk: int, data: MenuIn) -> Response:
|
||||
if await put_menu(pk, data) == 0:
|
||||
return Response(code=400, msg="菜单不存在")
|
||||
return Response()
|
||||
|
||||
|
@ -4,10 +4,15 @@ from fastapi import Query
|
||||
|
||||
from core.utils import list_to_tree
|
||||
from dbhelper.relation import role_assigned_menu
|
||||
from dbhelper.role import (del_role, get_role, get_role_menus, get_roles,
|
||||
new_role, put_role)
|
||||
from schemas import (ListAll, Response, RoleIn, RoleInfo, RoleMenuIn,
|
||||
RoleQuery, RoleRead)
|
||||
from dbhelper.role import (
|
||||
del_role,
|
||||
get_role,
|
||||
get_role_menus,
|
||||
get_roles,
|
||||
new_role,
|
||||
put_role,
|
||||
)
|
||||
from schemas import ListAll, Response, RoleIn, RoleInfo, RoleMenuIn, RoleQuery, RoleRead
|
||||
|
||||
|
||||
async def role_add(data: RoleIn) -> Response[RoleInfo]:
|
||||
|
@ -1,8 +1,16 @@
|
||||
from fastapi import Query
|
||||
from fastapi import Depends, Query
|
||||
from starlette.requests import Request
|
||||
|
||||
from core.security import get_password_hash
|
||||
from dbhelper.user import (del_user, get_user, get_user_info, get_users,
|
||||
insert_user, put_user)
|
||||
from core.security import check_token, get_password_hash
|
||||
from dbhelper.user import (
|
||||
del_user,
|
||||
get_user,
|
||||
get_user_info,
|
||||
get_users,
|
||||
insert_user,
|
||||
put_user,
|
||||
select_role,
|
||||
)
|
||||
from schemas import Response, UserAdd, UserInfo, UserPut, UserQuery, UserRead
|
||||
from schemas.common import ListAll
|
||||
|
||||
@ -63,3 +71,11 @@ async def user_put(pk: int, data: UserPut) -> Response:
|
||||
if isinstance(result, int):
|
||||
return Response(code=400, msg=f"角色不存在{result}")
|
||||
return Response()
|
||||
|
||||
|
||||
async def user_select_role(rid: int, user=Depends(check_token)):
|
||||
"""用户切换角色"""
|
||||
res = await select_role(user.id, rid)
|
||||
if res == 0:
|
||||
return Response(code=400, msg=f"角色不存在{res}")
|
||||
return Response()
|
||||
|
@ -74,7 +74,7 @@ async def check_permissions(request: Request, user: UserModel = Depends(check_to
|
||||
whitelist = [f"/user/{user.id}", f"/role/{active_rid}/menu"]
|
||||
flag = request.url.path in whitelist and request.method == "GET"
|
||||
if flag:
|
||||
return
|
||||
return user
|
||||
|
||||
api = request.url.path
|
||||
for k, v in request.path_params.items():
|
||||
|
@ -71,4 +71,4 @@ async def get_apis(pk: int):
|
||||
|
||||
async def put_menu(pk: int, data):
|
||||
"""更新菜单"""
|
||||
return await MenuModel.filter(id=pk).update(**data.dict())
|
||||
return await MenuModel.filter(id=pk).update(**data.dict())
|
||||
|
@ -104,3 +104,9 @@ async def put_user(uid: int, data: UserPut):
|
||||
await UserRoleModel.bulk_create(
|
||||
[UserRoleModel(uid=uid, **role.dict()) for role in roles]
|
||||
)
|
||||
|
||||
|
||||
async def select_role(uid: int, rid: int):
|
||||
"""用户切换角色"""
|
||||
await UserRoleModel.filter(uid=uid, rid__not=rid).update(status=1)
|
||||
return await UserRoleModel.filter(uid=uid, rid=rid).update(status=5)
|
||||
|
Binary file not shown.
Binary file not shown.
@ -4,10 +4,24 @@ from fastapi import Depends, routing
|
||||
|
||||
from controller.common import about, login
|
||||
from controller.menu import menu_add, menu_arr, menu_del, menu_put
|
||||
from controller.role import (assigned_menu, role_add, role_arr, role_del,
|
||||
role_has_menu, role_put, role_query)
|
||||
from controller.user import (user_add, user_arr, user_del, user_info,
|
||||
user_list, user_put)
|
||||
from controller.role import (
|
||||
assigned_menu,
|
||||
role_add,
|
||||
role_arr,
|
||||
role_del,
|
||||
role_has_menu,
|
||||
role_put,
|
||||
role_query,
|
||||
)
|
||||
from controller.user import (
|
||||
user_add,
|
||||
user_arr,
|
||||
user_del,
|
||||
user_info,
|
||||
user_list,
|
||||
user_put,
|
||||
user_select_role,
|
||||
)
|
||||
from core.security import check_permissions
|
||||
|
||||
|
||||
@ -107,9 +121,7 @@ class Route(routing.APIRoute):
|
||||
)
|
||||
|
||||
|
||||
has_perm = {
|
||||
# "dependencies": [Depends(check_permissions)]
|
||||
}
|
||||
has_perm = {"dependencies": [Depends(check_permissions)]}
|
||||
|
||||
routes = [
|
||||
Route.post("/login", endpoint=login, tags=["公共"], summary="登录"),
|
||||
@ -129,6 +141,9 @@ routes = [
|
||||
Route.post(
|
||||
"/user/query", endpoint=user_list, tags=["用户管理"], summary="用户列表查询", **has_perm
|
||||
),
|
||||
Route.put(
|
||||
"/user/role/{rid}", endpoint=user_select_role, tags=["用户管理"], summary="用户切换角色"
|
||||
),
|
||||
# 角色管理,
|
||||
Route.get("/role", endpoint=role_arr, tags=["角色管理"], summary="角色列表", **has_perm),
|
||||
Route.post("/role", endpoint=role_add, tags=["角色管理"], summary="角色新增", **has_perm),
|
||||
|
@ -309,20 +309,6 @@ params = [
|
||||
method="DELETE",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="修改菜单",
|
||||
meta={"icon": "Update"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=5,
|
||||
identifier="menu:update",
|
||||
api="/menu/{pk}",
|
||||
method="PUT",
|
||||
).dict(),
|
||||
),
|
||||
# 分配权限
|
||||
(
|
||||
"/role/assigned/menu",
|
||||
|
42
frontend/src/components/layout/layout-header.vue
Normal file
42
frontend/src/components/layout/layout-header.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import UserInfo from "@/components/layout/layout-info/layout-info.vue";
|
||||
import { MenuUnfoldOutlined, MenuFoldOutlined } from "@ant-design/icons-vue";
|
||||
|
||||
// 记录图标状态
|
||||
const collapsed = ref(false);
|
||||
|
||||
const emits = defineEmits(["changeFold"]);
|
||||
|
||||
// 修改图标状态同时传递参数给父组件让其变更菜单收缩
|
||||
const clickMenuFold = () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
// 父组件需要绑定这个事件
|
||||
emits("changeFold", collapsed.value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="header">
|
||||
<menu-unfold-outlined
|
||||
v-if="collapsed"
|
||||
class="trigger"
|
||||
@click="clickMenuFold"
|
||||
/>
|
||||
<menu-fold-outlined v-else class="trigger" @click="clickMenuFold" />
|
||||
<UserInfo />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.trigger {
|
||||
margin-left: 16px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
margin-right: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
41
frontend/src/components/layout/layout-info/layout-info.vue
Normal file
41
frontend/src/components/layout/layout-info/layout-info.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { userStore } from "@/stores/user";
|
||||
|
||||
import SelectRole from "./select-role.vue";
|
||||
|
||||
const store = userStore();
|
||||
const router = useRouter();
|
||||
|
||||
const roleChangeRef = ref();
|
||||
|
||||
const onClick = ({ key }) => {
|
||||
if (key === "1") {
|
||||
// 点击切换角色
|
||||
roleChangeRef.value?.showModal();
|
||||
} else {
|
||||
store.$reset();
|
||||
router.push("/login");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="right">
|
||||
<a-dropdown>
|
||||
<a class="ant-dropdown-link" @click.prevent>
|
||||
{{ store.userInfo.nickname }} - {{ store.userInfo.roles[0].name }}
|
||||
</a>
|
||||
<template #overlay>
|
||||
<a-menu @click="onClick">
|
||||
<a-menu-item key="1">切换角色</a-menu-item>
|
||||
<a-menu-item key="2">退出登录</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<SelectRole ref="roleChangeRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
69
frontend/src/components/layout/layout-info/select-role.vue
Normal file
69
frontend/src/components/layout/layout-info/select-role.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { userStore } from "@/stores/user";
|
||||
|
||||
const store = userStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const visible = ref(false);
|
||||
|
||||
const currentRoleId = ref(store.userInfo.roles[0].id);
|
||||
|
||||
// 角色列表选项
|
||||
const options = computed(() => {
|
||||
return store.userInfo.roles.map((role) => ({
|
||||
label: role.name,
|
||||
value: role.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const showModal = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
loading.value = true;
|
||||
store.userSelectRole(currentRoleId.value);
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
visible.value = false;
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
showModal,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="select-role">
|
||||
<a-modal v-model:visible="visible" title="切换角色" @ok="handleOk">
|
||||
<template #footer>
|
||||
<a-button key="back" @click="handleCancel">取消</a-button>
|
||||
<a-button
|
||||
key="submit"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleOk"
|
||||
>确定</a-button
|
||||
>
|
||||
</template>
|
||||
<span>选择角色:</span>
|
||||
|
||||
<a-space direction="vertical">
|
||||
<a-select
|
||||
v-model:value="currentRoleId"
|
||||
size="default"
|
||||
style="width: 400px"
|
||||
:options="options"
|
||||
></a-select>
|
||||
</a-space>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -1,7 +1,6 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { message } from "ant-design-vue";
|
||||
import { userStore } from "@/stores/user";
|
||||
import { loadRouter } from "@/utils/loadCpn";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
@ -21,3 +21,11 @@ export function getMenus(rid) {
|
||||
url: `/role/${rid}/menu`,
|
||||
});
|
||||
}
|
||||
|
||||
// 修改用户信息
|
||||
export function selectRole(rid) {
|
||||
return request({
|
||||
url: `/user/role/${rid}`,
|
||||
method: "put",
|
||||
});
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { message } from "ant-design-vue";
|
||||
|
||||
import router from "@/router";
|
||||
import { loadRouter, loadDefaultMenu } from "@/utils/loadCpn";
|
||||
import { getMenus, getUserInfo, login } from "@/service/user";
|
||||
import { getMenus, getUserInfo, login, selectRole } from "@/service/user";
|
||||
|
||||
export const userStore = defineStore(
|
||||
"user",
|
||||
@ -28,14 +28,13 @@ export const userStore = defineStore(
|
||||
userMenus.value = [];
|
||||
};
|
||||
|
||||
// 非setup语法时的actions
|
||||
const loginAction = async (data) => {
|
||||
// 1. 登录
|
||||
const res = await login(data);
|
||||
token.value = res.data.token;
|
||||
|
||||
/**
|
||||
* 获取用户信息 & 菜单路由
|
||||
* @param {*} uid 用户id
|
||||
*/
|
||||
const getUserData = async (uid) => {
|
||||
// 2. 获取用户信息
|
||||
const info = await getUserInfo(res.data.id);
|
||||
const info = await getUserInfo(uid);
|
||||
userInfo.value = info.data;
|
||||
|
||||
// 3. 获取权限信息
|
||||
@ -55,7 +54,13 @@ export const userStore = defineStore(
|
||||
} else {
|
||||
router.push("/main");
|
||||
}
|
||||
};
|
||||
|
||||
const loginAction = async (data) => {
|
||||
// 1. 登录
|
||||
const res = await login(data);
|
||||
token.value = res.data.token;
|
||||
await getUserData(res.data.id);
|
||||
// 弹框提示登录成功
|
||||
message.success("登录成功.");
|
||||
};
|
||||
@ -65,6 +70,13 @@ export const userStore = defineStore(
|
||||
loadRouter(userMenus.value);
|
||||
};
|
||||
|
||||
// 切换角色
|
||||
const userSelectRole = async (rid) => {
|
||||
await selectRole(rid);
|
||||
// 重新拿用户信息
|
||||
await getUserData(userInfo.value.id);
|
||||
};
|
||||
|
||||
return {
|
||||
token,
|
||||
accessToken,
|
||||
@ -75,6 +87,7 @@ export const userStore = defineStore(
|
||||
$reset,
|
||||
loginAction,
|
||||
loadRoleRouter,
|
||||
userSelectRole,
|
||||
};
|
||||
},
|
||||
{
|
||||
|
@ -25,7 +25,15 @@ export default (config) => {
|
||||
},
|
||||
(err) => {
|
||||
userStore().isLoading = !userStore().isLoading;
|
||||
message.error(err);
|
||||
if (err.response.data?.msg) {
|
||||
message.error(err.response.data.msg);
|
||||
} else if (err.response.data?.detail) {
|
||||
// 请求参数缺失
|
||||
message.error(err.response.data?.detail[0].msg);
|
||||
} else {
|
||||
message.error(err.message);
|
||||
}
|
||||
|
||||
return Promise.reject(err);
|
||||
}
|
||||
);
|
||||
|
@ -1,17 +1,15 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import router from "@/router";
|
||||
|
||||
import { userStore } from "@/stores/user";
|
||||
|
||||
import SiderMenu from "@/components/layout/sider-menu.vue";
|
||||
const store = userStore();
|
||||
import LayoutHeader from "@/components/layout/layout-header.vue";
|
||||
|
||||
// a-ayout-sider 折叠状态响应式数据
|
||||
const collapsed = ref(false);
|
||||
|
||||
const logout = () => {
|
||||
store.$reset();
|
||||
router.push("/login");
|
||||
// header组件 折叠按钮事件 触发a-layout-sider折叠
|
||||
const changeSiderFold = (subValue) => {
|
||||
collapsed.value = subValue;
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -25,7 +23,7 @@ const logout = () => {
|
||||
<a-layout>
|
||||
<a-layout-header style="background: #fff; padding: 0">
|
||||
<!-- 页头 -->
|
||||
<a-button @click="logout">退出</a-button>
|
||||
<LayoutHeader @changeFold="changeSiderFold" />
|
||||
</a-layout-header>
|
||||
<!-- 面包屑 -->
|
||||
<a-layout-content
|
||||
|
Loading…
Reference in New Issue
Block a user