fix: 切换角色&更新角色菜单回显

This commit is contained in:
zy7y 2022-09-16 17:37:24 +08:00
parent 7ba3f23684
commit 8e725d3f78
16 changed files with 201 additions and 141 deletions

View File

@ -1,18 +1,13 @@
from fastapi import Query
from core.utils import list_to_tree from core.utils import list_to_tree
from dbhelper.menu import del_menu, get_menus, get_tree_menu, insert_menu, put_menu from dbhelper.menu import del_menu, get_tree_menu, insert_menu, put_menu
from schemas import ListAll, MenuIn, MenuRead, Response from schemas import MenuIn, MenuRead, Response
async def menu_add(data: MenuIn) -> Response[MenuRead]: async def menu_add(data: MenuIn) -> Response[MenuRead]:
return Response(data=await insert_menu(data)) return Response(data=await insert_menu(data))
async def menu_arr( async def menu_arr() -> Response:
offset: int = Query(default=1, description="偏移量"),
limit: int = Query(default=10, description="数量"),
) -> Response:
menus = await get_tree_menu() menus = await get_tree_menu()
return Response(data=list_to_tree(menus)) return Response(data=list_to_tree(menus))

View File

@ -3,7 +3,7 @@ import json
from fastapi import Query from fastapi import Query
from core.utils import list_to_tree from core.utils import list_to_tree
from dbhelper.relation import role_assigned_menu from dbhelper.menu import get_menu
from dbhelper.role import ( from dbhelper.role import (
del_role, del_role,
get_role, get_role,
@ -12,7 +12,7 @@ from dbhelper.role import (
new_role, new_role,
put_role, put_role,
) )
from schemas import ListAll, Response, RoleIn, RoleInfo, RoleMenuIn, RoleQuery, RoleRead from schemas import ListAll, Response, RoleIn, RoleInfo, RoleQuery, RoleRead
async def role_add(data: RoleIn) -> Response[RoleInfo]: async def role_add(data: RoleIn) -> Response[RoleInfo]:
@ -40,15 +40,6 @@ async def role_arr(
return Response(data=ListAll(total=count, items=roles)) return Response(data=ListAll(total=count, items=roles))
async def assigned_menu(data: RoleMenuIn) -> Response:
"""分配菜单给角色"""
if await get_role({"id": data.rid, "status__not": 9}) is None:
return Response(code=400, msg="角色不存在")
if isinstance(await role_assigned_menu(data), int):
return Response(code=400, msg=f"菜单不存在")
return Response()
async def role_del(pk: int) -> Response: async def role_del(pk: int) -> Response:
if await del_role(pk) == 0: if await del_role(pk) == 0:
return Response(code=400, msg="角色不存在") return Response(code=400, msg="角色不存在")
@ -57,6 +48,14 @@ async def role_del(pk: int) -> Response:
async def role_put(pk: int, data: RoleIn) -> Response: async def role_put(pk: int, data: RoleIn) -> Response:
"""更新角色""" """更新角色"""
print(await get_role({"id": pk}))
if await get_role({"id": pk}) is None:
return Response(code=400, msg="角色不存在")
# 如果不为ture -> 有菜单id不存在
if not all([await get_menu({"id": mid}) for mid in data.menus]):
return Response(code=400, msg="菜单不存在")
if await put_role(pk, data) == 0: if await put_role(pk, data) == 0:
return Response(code=400, msg="角色不存在") return Response(code=400, msg="角色不存在")
return Response() return Response()

View File

@ -68,6 +68,11 @@ async def del_role(rid: int):
return await RoleModel.filter(id=rid).update(status=9) return await RoleModel.filter(id=rid).update(status=9)
async def put_role(pk: int, data): async def put_role(pk, data):
"""更新角色""" """更新角色 菜单"""
return await RoleModel.filter(id=pk).update(**data.dict()) await RoleModel.filter(id=pk).update(name=data.name, remark=data.remark)
await RoleMenuModel.filter(rid=pk).update(status=9)
await RoleMenuModel.bulk_create(
[RoleMenuModel(rid=pk, mid=mid) for mid in data.menus]
)

View File

@ -23,11 +23,11 @@ async def get_user_info(user: UserModel):
根据id查用户角色列表 按激活角色倒序显示 根据id查用户角色列表 按激活角色倒序显示
""" """
db = connections.get("default") db = connections.get("default")
# 查角色表 用户角色表中 角色状态 = 1 关联表中 状态 != 9 为有效角色
sql_result = await db.execute_query_dict( sql_result = await db.execute_query_dict(
""" """
select r.id, r.name, ur.status from sys_role as r select r.id, r.name, ur.status from sys_role as r , sys_user_role as ur where r.id = ur.rid and
left join sys_user_role as ur on r.id = ur.rid where ur.uid = (?) and r.status = 1 and ur.status !=9 order by ur.status desc
ur.uid = (?) and ur.status != 9 and r.status != 9 order by ur.status desc
""", """,
[user.id], [user.id],
) )
@ -65,7 +65,7 @@ async def insert_user(user, roles):
# 创建用户 # 创建用户
obj = await UserModel.create(**user.dict()) obj = await UserModel.create(**user.dict())
# 已有角色 关联 角色id 和是否选中状态
await UserRoleModel.bulk_create( await UserRoleModel.bulk_create(
[UserRoleModel(rid=role.rid, uid=obj.id, status=role.status) for role in roles] [UserRoleModel(rid=role.rid, uid=obj.id, status=role.status) for role in roles]
) )
@ -81,8 +81,8 @@ async def put_user(uid: int, data: UserPut):
"""更新用户""" """更新用户"""
from core.security import get_password_hash from core.security import get_password_hash
roles = data.rids roles = data.roles
del data.rids del data.roles
for role in roles: for role in roles:
if await get_role({"id": role.rid, "status__not": 9}) is None: if await get_role({"id": role.rid, "status__not": 9}) is None:
return role.rid return role.rid
@ -96,13 +96,27 @@ async def put_user(uid: int, data: UserPut):
# todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态 # todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态
db = connections.get("default") db = connections.get("default")
# 1. 先把所有数据做删除 # 1. 先把用户有的角色做删除
await db.execute_query_dict( has_roles = await db.execute_query_dict(
""" """
update sys_user_role set status = 9 where uid = (?) select r.id from sys_role as r , sys_user_role as ur where r.id = ur.rid and
ur.uid = (?) and r.status = 1 and ur.status !=9
""", """,
[uid], [uid],
) )
print(has_roles)
# 2. 将先有的数据标记 删除
[
await db.execute_query_dict(
"""
update sys_user_role set status = 9 where rid = (?)
""",
[role["id"]],
)
for role in has_roles
]
# 2. 新增次此更新的数据 # 2. 新增次此更新的数据
await UserRoleModel.bulk_create( await UserRoleModel.bulk_create(
[UserRoleModel(uid=uid, **role.dict()) for role in roles] [UserRoleModel(uid=uid, **role.dict()) for role in roles]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,7 +5,6 @@ from fastapi import Depends, routing
from controller.common import about, login from controller.common import about, login
from controller.menu import menu_add, menu_arr, menu_del, menu_put from controller.menu import menu_add, menu_arr, menu_del, menu_put
from controller.role import ( from controller.role import (
assigned_menu,
role_add, role_add,
role_arr, role_arr,
role_del, role_del,
@ -122,6 +121,7 @@ class Route(routing.APIRoute):
has_perm = {"dependencies": [Depends(check_permissions)]} has_perm = {"dependencies": [Depends(check_permissions)]}
# has_perm = {}
routes = [ routes = [
Route.post("/login", endpoint=login, tags=["公共"], summary="登录"), Route.post("/login", endpoint=login, tags=["公共"], summary="登录"),
@ -163,13 +163,6 @@ routes = [
Route.post( Route.post(
"/role/query", endpoint=role_query, tags=["角色管理"], summary="角色条件查询", **has_perm "/role/query", endpoint=role_query, tags=["角色管理"], summary="角色条件查询", **has_perm
), ),
Route.post(
"/role/assigned/menu",
endpoint=assigned_menu,
tags=["角色管理"],
summary="角色分配菜单",
**has_perm
),
# 菜单新增 # 菜单新增
Route.get("/menu", endpoint=menu_arr, tags=["菜单管理"], summary="菜单列表", **has_perm), Route.get("/menu", endpoint=menu_arr, tags=["菜单管理"], summary="菜单列表", **has_perm),
Route.post("/menu", endpoint=menu_add, tags=["菜单管理"], summary="菜单新增", **has_perm), Route.post("/menu", endpoint=menu_add, tags=["菜单管理"], summary="菜单新增", **has_perm),

View File

@ -66,4 +66,4 @@ class UserPut(BaseModel):
nickname: str = Field(..., description="用户昵称") nickname: str = Field(..., description="用户昵称")
password: str = Field(..., description="密码") password: str = Field(..., description="密码")
rids: list[RoleActive] = Field(..., description="选择角色列表") roles: list[RoleActive] = Field(..., description="选择角色列表")

View File

@ -3,42 +3,13 @@ import requests as client
from core.log import logger from core.log import logger
from schemas.menu import MenuIn from schemas.menu import MenuIn
from schemas.role import RoleIn, RoleMenuIn from schemas.role import RoleIn
from schemas.user import RoleActive, UserAdd from schemas.user import RoleActive, UserAdd
base = "http://localhost:8000" base = "http://localhost:8000"
params = [ params = [
# 创建角色
("/role", RoleIn(name="super", remark="全部权限").dict()),
("/role", RoleIn(name="user", remark="用户权限").dict()),
# 创建用户
(
"/user",
UserAdd(
username="admin",
nickname="管理员",
password="123456",
rids=[
RoleActive(rid=1, status=5),
RoleActive(rid=2),
],
).dict(),
),
(
"/user",
UserAdd(
username="tester",
nickname="测试员",
password="123456",
rids=[
RoleActive(rid=2, status=5),
],
).dict(),
),
# 创建菜单 # 创建菜单
# 目录
( (
"/menu", "/menu",
MenuIn( # id 1 MenuIn( # id 1
@ -252,20 +223,6 @@ params = [
method="POST", method="POST",
).dict(), ).dict(),
), ),
(
"/menu",
MenuIn(
name="分配权限",
meta={"icon": "Delete"},
path=None,
type=2,
component=None,
pid=4,
identifier="role:assign",
api="/role/assigned/menu",
method="POST",
).dict(),
),
( (
"/menu", "/menu",
MenuIn( MenuIn(
@ -309,17 +266,41 @@ params = [
method="DELETE", method="DELETE",
).dict(), ).dict(),
), ),
# 分配权限 ]
datas = [
( (
"/role/assigned/menu", "/role",
RoleMenuIn(rid=1, menus=[num for num in range(1, 20)]).dict(), RoleIn(
name="super",
remark="全部权限",
menus=[num for num in range(1, len(params) + 1)],
).dict(),
),
# 创建用户
(
"/user",
UserAdd(
username="admin",
nickname="管理员",
password="123456",
roles=[RoleActive(rid=1, status=5)],
).dict(),
), ),
("/role/assigned/menu", RoleMenuIn(rid=2, menus=[1, 3, 7, 8, 9, 11]).dict()),
] ]
@pytest.mark.parametrize("path, data", params) @pytest.mark.parametrize("path, data", params)
def test_add_data(path, data): def test_add_data(path, data):
"""注册菜单"""
res = client.post(url=base + path, json=data)
logger.info(res.json())
assert res.status_code == 200
@pytest.mark.parametrize("path, data", datas)
def test_add_user(path, data):
"""添加账号"""
res = client.post(url=base + path, json=data) res = client.post(url=base + path, json=data)
logger.info(res.json()) logger.info(res.json())
assert res.status_code == 200 assert res.status_code == 200

View File

@ -11,9 +11,11 @@ import '@/assets/css/base.css'
import 'ant-design-vue/dist/antd.css' import 'ant-design-vue/dist/antd.css'
import hasPermisson from '@/utils/directive' import hasPermisson from '@/utils/directive'
import { formatTime } from './utils/format'
const app = createApp(App) const app = createApp(App)
hasPermisson(app) hasPermisson(app)
app.config.globalProperties.$formatTime = (value) => formatTime(value)
app.use(store) app.use(store)
userStore().loadRoleRouter() userStore().loadRoleRouter()

View File

@ -65,3 +65,18 @@ export const columns = [
width: 120 width: 120
} }
] ]
// 菜单类型映射
export const menuType = {
0: '目录',
1: '菜单',
2: '按钮'
}
// 请求方法颜色映射
export const methodColor = {
GET: '#61AFFE',
POST: '#49CC90',
DELETE: '#F93E3E',
PUT: '#FCA130'
}

View File

@ -2,8 +2,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue' import { PlusOutlined } from '@ant-design/icons-vue'
import { columns } from './conf' import { columns, menuType, methodColor } from './conf'
import { formatTime } from '@/utils/format'
import { loadIconCpn } from '@/utils/loadCpn' import { loadIconCpn } from '@/utils/loadCpn'
import { getMenus } from '@/service/menu' import { getMenus } from '@/service/menu'
@ -14,23 +13,36 @@ getMenus().then((res) => (dataSource.value = res.data))
// //
const menuType = { // https://blog.csdn.net/weixin_52691965/article/details/120494451
0: '目录', const expandedRowKeys = ref([])
1: '菜单',
2: '按钮'
}
const methodColor = { const zi = (expanded, record) => {
GET: '#61AFFE', if (expandedRowKeys.value.length > 0) {
POST: '#49CC90', let index = expandedRowKeys.value.indexOf(record.id)
DELETE: '#F93E3E', if (index > -1) {
PUT: '#FCA130' expandedRowKeys.value.splice(index, 1)
} else {
expandedRowKeys.value.splice(0, expandedRowKeys.value.length)
expandedRowKeys.value.push(record.id)
}
} else {
expandedRowKeys.value.push(record.id)
}
} }
// //
const addClick = () => { const addClick = () => {
console.log('点击') console.log('点击')
} }
//
const putClick = () => {
console.log('点击')
}
const delClick = () => {
console.log('点击')
}
</script> </script>
<template> <template>
@ -50,6 +62,12 @@ const addClick = () => {
:columns="columns" :columns="columns"
:scroll="{ x: 1600, y: 'calc(100vh - 380px)' }" :scroll="{ x: 1600, y: 'calc(100vh - 380px)' }"
:data-source="dataSource" :data-source="dataSource"
:pagination="{
hideOnSinglePage: true
}"
:row-key="(record) => record.id"
@expand="zi"
:expandedRowKeys="expandRowKeys"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'meta'"> <template v-if="column.key === 'meta'">
@ -59,13 +77,15 @@ const addClick = () => {
{{ menuType[record.type] }} {{ menuType[record.type] }}
</template> </template>
<template v-if="column.key === 'method'"> <template v-if="column.key === 'method'">
<a-tag :color="methodColor[record.method]">{{ record.method }}</a-tag> <template v-if="record.method">
<a-tag :color="methodColor[record.method]">{{ record.method }}</a-tag>
</template>
</template> </template>
<template v-else-if="column.key === 'created'"> <template v-else-if="column.key === 'created'">
{{ formatTime(record.created) }} {{ $formatTime(record.created) }}
</template> </template>
<template v-else-if="column.key === 'modified'"> <template v-else-if="column.key === 'modified'">
{{ formatTime(record.modified) }} {{ $formatTime(record.modified) }}
</template> </template>
<template v-else-if="column.key === 'action'"> <template v-else-if="column.key === 'action'">
<span> <span>

View File

@ -35,16 +35,21 @@ export const columns = [
} }
] ]
export const addRoleRules = [ export const rules = {
{ name: [
name: [ { required: true, message: '请输入名称', trigger: 'blur' },
{ required: true, message: '请输入名称', trigger: 'blur' }, { min: 3, max: 12, message: '3-12', trigger: 'blur' }
{ min: 3, max: 12, message: '3-12', trigger: 'blur' } ],
], remark: [
remark: [ { required: true, message: '请输入描述', trigger: 'blur' },
{ required: true, message: '请输入描述', trigger: 'blur' }, { min: 1, max: 20, message: '1~20', trigger: 'blur' }
{ min: 1, max: 20, message: '1~20', trigger: 'blur' } ],
], menus: [{ required: true, message: '请选择菜单', trigger: 'blur' }]
menus: [{ required: true, message: '请选择菜单', trigger: 'blur' }] }
}
] // a-tree组件 字段替换 适配接口返回数据
export const treeFieldNames = {
key: 'id',
title: 'name',
children: 'children'
}

View File

@ -1,13 +1,14 @@
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue' import { ref, reactive, onMounted, watch } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue' import { PlusOutlined } from '@ant-design/icons-vue'
import { getRoles, queryRole, delRole, putRole, addRole } from '@/service/role' import { getRoles, queryRole, delRole, putRole, addRole } from '@/service/role'
import { getMenus } from '@/service/menu' import { getMenus } from '@/service/menu'
import { columns, addRoleRules } from './conf' import { columns, rules, treeFieldNames } from './conf'
import { formatTime } from '@/utils/format' import { formatTime } from '@/utils/format'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { getMenus as getRoleMenu } from '@/service/user'
/**查询表单响应式数据 */ /**查询表单响应式数据 */
const queryFormRef = ref() const queryFormRef = ref()
@ -96,8 +97,6 @@ const addClick = () => {
const onOk = () => { const onOk = () => {
formRef.value.validateFields().then(() => { formRef.value.validateFields().then(() => {
// //
console.log(newRoleForm.menus)
newRoleForm.menus = newRoleForm.menus.map((e) => e.id)
addRole(newRoleForm).then((res) => { addRole(newRoleForm).then((res) => {
if (res.msg === '请求成功') { if (res.msg === '请求成功') {
message.success('新增成功') message.success('新增成功')
@ -120,26 +119,50 @@ const onCancel = () => {
/**更新 */ /**更新 */
/**
* 编辑时获取数据 回显到put modal
* @param {} record 行数据
*/
// modal
const putClick = (record) => { const putClick = (record) => {
putVisible.value = !putVisible.value
getPutModalData(record.id)
}
const getPutModalData = (record) => {
// modal // modal
getMenus().then((res) => (treeData.value = res.data)) getMenus().then((res) => (treeData.value = res.data))
//
putVisible.value = !putVisible.value getRoleMenu(record.id).then((res) => {
function _mids(menus) {
for (const menu of menus) {
if (menu.childer) {
_mids(menu.childer)
} else {
putRoleForm.menus.push(menu.id)
}
}
}
_mids(res.data)
})
putId.value = record.id putId.value = record.id
putRoleForm.name = record.name putRoleForm.name = record.name
putRoleForm.remark = record.remark putRoleForm.remark = record.remark
console.log(record)
} }
// modal
const putVisible = ref(false) const putVisible = ref(false)
// modal
const putRoleFormRef = ref() const putRoleFormRef = ref()
// modal
const putRoleForm = reactive({ const putRoleForm = reactive({
name: '', name: '',
remark: '', remark: '',
menus: [] menus: []
}) })
// id 便
const putId = ref() const putId = ref()
//modal //modal
@ -168,11 +191,13 @@ const onCancelPut = () => {
// tree // tree
const treeData = ref() const treeData = ref()
// // menus
const check = (key, { checkedNodesPositions }) => { watch(
// console.log(checkedNodesPositions) () => newRoleForm.menus,
newRoleForm.menus = checkedNodesPositions.map((e) => ({ key: e.pos, id: e.node.id })) (newValue, oldvalue) => {
} console.log('menu', newValue, oldvalue)
}
)
</script> </script>
<template> <template>
@ -242,7 +267,7 @@ const check = (key, { checkedNodesPositions }) => {
@ok="onOk" @ok="onOk"
@cancel="onCancel" @cancel="onCancel"
> >
<a-form ref="formRef" :model="newRoleForm" :rules="addRoleRules"> <a-form ref="formRef" :model="newRoleForm" :rules="rules">
<a-form-item name="name" label="名称"> <a-form-item name="name" label="名称">
<a-input v-model:value="newRoleForm.name" /> <a-input v-model:value="newRoleForm.name" />
</a-form-item> </a-form-item>
@ -250,14 +275,18 @@ const check = (key, { checkedNodesPositions }) => {
<a-input v-model:value="newRoleForm.remark" /> <a-input v-model:value="newRoleForm.remark" />
</a-form-item> </a-form-item>
<a-form-item name="menus" label="菜单"> <a-form-item name="menus" label="菜单">
<a-tree checkable :tree-data="treeData" @check="check"> <a-tree
<template #title="{ name }"> {{ name }} </template> checkable
:tree-data="treeData"
:fieldNames="treeFieldNames"
v-model:checkedKeys="newRoleForm.menus"
>
</a-tree> </a-tree>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
<!-- 修改用户 --> <!-- 修改 -->
<a-modal <a-modal
v-model:visible="putVisible" v-model:visible="putVisible"
title="编辑角色" title="编辑角色"
@ -266,7 +295,7 @@ const check = (key, { checkedNodesPositions }) => {
@ok="onOkPut" @ok="onOkPut"
@cancel="onCancelPut" @cancel="onCancelPut"
> >
<a-form ref="putRoleFormRef" :model="putRoleForm" :rules="putUserRules"> <a-form ref="putRoleFormRef" :model="putRoleForm" :rules="rules">
<a-form-item name="name" label="名称"> <a-form-item name="name" label="名称">
<a-input v-model:value="putRoleForm.name" /> <a-input v-model:value="putRoleForm.name" />
</a-form-item> </a-form-item>
@ -274,9 +303,12 @@ const check = (key, { checkedNodesPositions }) => {
<a-input v-model:value="putRoleForm.remark" /> <a-input v-model:value="putRoleForm.remark" />
</a-form-item> </a-form-item>
<a-form-item name="menus" label="菜单"> <a-form-item name="menus" label="菜单">
<a-tree checkable :tree-data="treeData" @check="check"> <a-tree
<template #title="{ name }"> {{ name }} </template> checkable
</a-tree> :tree-data="treeData"
:fieldNames="treeFieldNames"
v-model:checkedKeys="putRoleForm.menus"
></a-tree>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>

View File

@ -6,7 +6,6 @@ import { PlusOutlined } from '@ant-design/icons-vue'
import { getUsers, queryUser, delUser, addUser, getUserInfo, putUser } from '@/service/user' import { getUsers, queryUser, delUser, addUser, getUserInfo, putUser } from '@/service/user'
import { getRoles } from '@/service/role' import { getRoles } from '@/service/role'
import { columns, addUserRules, putUserRules } from './conf' import { columns, addUserRules, putUserRules } from './conf'
import { formatTime } from '@/utils/format'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { userStore } from '@/stores/user' import { userStore } from '@/stores/user'
import router from '@/router' import router from '@/router'
@ -164,7 +163,7 @@ const onOkPut = () => {
// //
const { nickname, password, roles } = putUserForm const { nickname, password, roles } = putUserForm
let rids = roles.map((e, i) => ({ rid: e, status: i === 0 ? 5 : 1 })) let rids = roles.map((e, i) => ({ rid: e, status: i === 0 ? 5 : 1 }))
putUser(putId.value, { nickname, password, rids }).then((res) => { putUser(putId.value, { nickname, password, roles: rids }).then((res) => {
if (res.msg === '请求成功') { if (res.msg === '请求成功') {
message.success('修改成功') message.success('修改成功')
// 1. modal // 1. modal
@ -232,10 +231,10 @@ const onCancelPut = () => {
</a-tag> </a-tag>
</template> </template>
<template v-else-if="column.key === 'created'"> <template v-else-if="column.key === 'created'">
{{ formatTime(record.created) }} {{ $formatTime(record.created) }}
</template> </template>
<template v-else-if="column.key === 'modified'"> <template v-else-if="column.key === 'modified'">
{{ formatTime(record.modified) }} {{ $formatTime(record.modified) }}
</template> </template>
<template v-else-if="column.key === 'action'"> <template v-else-if="column.key === 'action'">
<span> <span>