Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
2c95eb73
提交
2c95eb73
authored
7月 20, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refactor(admin): migrate access role module
上级
163696c5
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
831 行增加
和
2 行删除
+831
-2
access-role-report.md
.superpowers/sdd/access-role-report.md
+60
-0
api.ts
apps/admin/src/modules/access/role/api.ts
+102
-0
DeleteRoleModal.tsx
...in/src/modules/access/role/components/DeleteRoleModal.tsx
+32
-0
PermissionDrawer.tsx
...n/src/modules/access/role/components/PermissionDrawer.tsx
+82
-0
RoleFormDrawer.tsx
...min/src/modules/access/role/components/RoleFormDrawer.tsx
+79
-0
query.ts
apps/admin/src/modules/access/role/query.ts
+30
-0
routes.tsx
apps/admin/src/modules/access/role/routes.tsx
+10
-0
useRoleList.ts
apps/admin/src/modules/access/role/useRoleList.ts
+135
-0
RoleListView.tsx
apps/admin/src/modules/access/role/views/RoleListView.tsx
+299
-0
routes.tsx
apps/admin/src/modules/access/routes.tsx
+2
-2
index.jsx
apps/admin/src/pages/jurisdiction/role/index.jsx
+0
-0
没有找到文件。
.superpowers/sdd/access-role-report.md
0 → 100644
浏览文件 @
2c95eb73
# access/role migration report
## Status
Migrated
`/jurisdiction/role`
into
`modules/access/role`
. The active access route
now lazy-loads the module route and view. The old role page was removed after a
successful module build; its request file remains as a compatibility dependency
for the separately owned teachers module.
## Preserved contract
-
List API remains
`POST /user/role/getList`
through
`utils/request.js`
with a
`qs.stringify`
body and
`page`
,
`page_size`
,
`name`
,
`start_time`
,
`end_time`
.
-
Add, detail, edit, delete, status, selected-permission, permission-save, and
all-permissions endpoints are unchanged.
-
Status mutation still sends string
`"1"`
for enabled and
`"2"`
for disabled.
-
Permission save still sends
`{ role_id, power_list }`
.
-
Existing business success/error messages and false-return behavior remain in
the API adapter.
-
Filters remain role name and creation time; date values use local-day second
timestamps. Pagination remains 1/10 with 10/20/30/40 choices.
-
All seven columns remain in order. The displayed ID remains the page-relative
sequence number, and remarks retain the 300px three-line Popover treatment.
-
Permissions remain exact:
`changeStatus`
,
`getInfoById`
,
`getPowerByRoleId`
,
`del`
, and
`add`
under
`/jurisdiction/role/*`
.
-
Add/edit validation, deletion confirmation, role types, and mutation refresh
behavior are retained.
-
The authorization tree remains a module-private component with strict parent/
child checking, descendant selection, ancestor selection, and the original
fixed submit footer. It is not part of AppList.
## Architecture
-
`api.ts`
: legacy request protocol and awaited response handling.
-
`query.ts`
: role filter mapping.
-
`useRoleList.ts`
: controlled rows, filters, pagination, loading, error, retry,
and stale-response protection.
-
`views/RoleListView.tsx`
: permissions, mutations, AppList, and drawer control.
-
`components/`
: role form, delete confirmation, and permission tree drawer.
-
`routes.tsx`
: module-owned leaf route with unchanged public URL.
## Verification
-
`modules/access/role`
oxlint and oxfmt checks pass.
-
`pnpm test`
passed all 11 global Node tests.
-
Post-delete
`pnpm build`
passed with only the existing formula-editor and
large-chunk warnings.
-
`pnpm exec tsc --noEmit`
contains no access/role diagnostics; unrelated
existing/concurrent workspace diagnostics remain.
## Concerns
1.
No API/query/Hook tests were added, per instruction.
2.
`apps/admin/src/modules/teachers/list/views/TeacherListView.jsx`
still imports
`pages/jurisdiction/role/request.js`
for
`getAllList`
. That request file is
therefore retained; removing the cross-module dependency requires a teachers
task and is outside this scope.
3.
Deprecated
`src/routes/routes.jsx`
and
`src/config/component.js`
still contain
the old role page path as inactive metadata. Active routes and builds use the
new module.
apps/admin/src/modules/access/role/api.ts
0 → 100644
浏览文件 @
2c95eb73
import
{
message
}
from
"antd"
;
// @ts-expect-error The legacy workspace does not install the qs declaration package.
import
qs
from
"qs"
;
import
request
from
"@/utils/request.js"
;
import
type
{
RoleQuery
}
from
"./query"
;
export
interface
RoleRow
{
id
:
number
;
name
?:
string
;
type
?:
number
|
string
;
remarks
?:
string
;
status
?:
number
|
string
;
create_time
?:
string
;
}
export
interface
PermissionRecord
{
id
:
number
;
label
?:
string
;
children
?:
PermissionRecord
[];
[
key
:
string
]:
unknown
;
}
export
interface
RoleListResult
{
rows
:
RoleRow
[];
total
:
number
;
}
type
ApiParams
=
Record
<
string
,
unknown
>
;
async
function
getPayload
<
T
>
(
url
:
string
,
params
:
ApiParams
):
Promise
<
T
|
false
>
{
try
{
const
response
=
await
request
.
post
(
url
,
qs
.
stringify
(
params
));
const
body
=
response
.
data
as
{
code
?:
number
;
data
?:
T
;
message
?:
string
};
const
code
=
body
?.
code
??
0
;
const
data
=
(
body
?.
data
??
[])
as
T
;
const
text
=
body
?.
message
??
"数据获取失败!"
;
if
(
code
===
200
)
return
data
;
message
.
error
(
text
);
return
false
;
}
catch
{
return
false
;
}
}
async
function
mutate
(
url
:
string
,
params
:
ApiParams
)
{
try
{
const
response
=
await
request
.
post
(
url
,
qs
.
stringify
(
params
));
const
body
=
response
.
data
as
{
code
?:
number
;
message
?:
string
};
const
code
=
body
?.
code
??
0
;
const
text
=
body
?.
message
??
"数据获取失败!"
;
if
(
code
===
200
)
{
message
.
success
(
text
);
return
true
;
}
message
.
error
(
text
);
return
false
;
}
catch
{
return
false
;
}
}
export
async
function
getRoleList
(
params
:
RoleQuery
):
Promise
<
RoleListResult
|
false
>
{
const
data
=
await
getPayload
<
{
list
?:
RoleRow
[];
total
?:
number
}
>
(
"/user/role/getList"
,
{
...
params
,
});
if
(
data
===
false
)
return
false
;
return
{
rows
:
data
.
list
??
[],
total
:
data
.
total
??
0
};
}
export
function
getRoleInfo
(
id
:
number
)
{
return
getPayload
<
RoleRow
>
(
"/user/role/getInfoById"
,
{
id
});
}
export
function
addRole
(
params
:
ApiParams
)
{
return
mutate
(
"/user/role/add"
,
params
);
}
export
function
editRole
(
params
:
ApiParams
)
{
return
mutate
(
"/user/role/edit"
,
params
);
}
export
function
deleteRole
(
id
:
number
)
{
return
mutate
(
"/user/role/del"
,
{
id
});
}
export
function
changeRoleStatus
(
id
:
number
,
status
:
"1"
|
"2"
)
{
return
mutate
(
"/user/role/changeStatus"
,
{
status
,
id
});
}
export
function
getRolePermissionIds
(
roleId
:
number
)
{
return
getPayload
<
Array
<
string
|
number
>>
(
"/user/role/getPowerByRoleId"
,
{
role_id
:
roleId
});
}
export
function
setRolePermissions
(
roleId
:
number
,
powerList
:
Array
<
string
|
number
>
)
{
return
mutate
(
"/user/role/setRolePower"
,
{
role_id
:
roleId
,
power_list
:
powerList
});
}
export
function
getAllPermissions
()
{
return
getPayload
<
PermissionRecord
[]
>
(
"/system/power/getListAll"
,
{});
}
apps/admin/src/modules/access/role/components/DeleteRoleModal.tsx
0 → 100644
浏览文件 @
2c95eb73
import
{
Modal
,
Row
,
Space
}
from
"antd"
;
interface
DeleteRoleModalProps
{
open
:
boolean
;
onCancel
:
()
=>
void
;
onConfirm
:
()
=>
void
|
Promise
<
void
>
;
}
export
default
function
DeleteRoleModal
({
open
,
onCancel
,
onConfirm
}:
DeleteRoleModalProps
)
{
return
(
<
Modal
mask=
{
false
}
centered
open=
{
open
}
onOk=
{
onConfirm
}
onCancel=
{
onCancel
}
footer=
{
(
_
,
{
OkBtn
,
CancelBtn
})
=>
(
<
Row
justify=
"center"
className=
"delModal"
style=
{
{
marginBottom
:
"30px"
}
}
>
<
Space
size=
{
20
}
>
<
CancelBtn
/>
<
OkBtn
/>
</
Space
>
</
Row
>
)
}
okText=
"确认"
>
<
p
style=
{
{
textAlign
:
"center"
,
padding
:
"30px 0 18px"
,
fontSize
:
16
}
}
>
角色将永久删除,是否继续?
</
p
>
</
Modal
>
);
}
apps/admin/src/modules/access/role/components/PermissionDrawer.tsx
0 → 100644
浏览文件 @
2c95eb73
import
{
Button
,
Drawer
,
Form
,
Space
,
Tree
}
from
"antd"
;
import
type
{
DataNode
}
from
"antd/es/tree"
;
import
type
{
Key
}
from
"react"
;
import
{
findParents
}
from
"@/utils/common.js"
;
export
type
PermissionTreeNode
=
DataNode
&
{
key
:
Key
;
children
?:
PermissionTreeNode
[];
};
interface
PermissionDrawerProps
{
open
:
boolean
;
treeData
:
PermissionTreeNode
[];
checkedKeys
:
Key
[];
onCheckedKeysChange
:
(
keys
:
Key
[])
=>
void
;
onClose
:
()
=>
void
;
onSubmit
:
()
=>
void
|
Promise
<
void
>
;
}
function
collectNodeKeys
(
node
:
PermissionTreeNode
)
{
const
keys
:
Key
[]
=
[
node
.
key
];
node
.
children
?.
forEach
((
child
)
=>
keys
.
push
(...
collectNodeKeys
(
child
)));
return
keys
;
}
export
default
function
PermissionDrawer
({
open
,
treeData
,
checkedKeys
,
onCheckedKeysChange
,
onClose
,
onSubmit
,
}:
PermissionDrawerProps
)
{
return
(
<
Drawer
placement=
"right"
open=
{
open
}
onClose=
{
onClose
}
mask=
{
false
}
>
<
Form
labelCol=
{
{
span
:
6
}
}
>
<
Form
.
Item
name=
"power_list"
style=
{
{
padding
:
10
,
paddingTop
:
0
}
}
>
<
Tree
checkable
checkedKeys=
{
checkedKeys
}
defaultExpandAll=
{
false
}
treeData=
{
treeData
}
checkStrictly
onCheck=
{
(
nextCheckedKeys
,
info
)
=>
{
const
nextKeys
=
Array
.
isArray
(
nextCheckedKeys
)
?
nextCheckedKeys
:
nextCheckedKeys
.
checked
;
const
node
=
info
.
node
as
PermissionTreeNode
;
const
nodeAndChildren
=
collectNodeKeys
(
node
);
if
(
info
.
checked
)
{
const
parents
=
(
findParents
(
treeData
,
"key"
,
node
.
key
)
??
[])
as
Key
[];
onCheckedKeysChange
([...
nextKeys
,
...
nodeAndChildren
,
...
parents
]);
return
;
}
onCheckedKeysChange
(
nextKeys
.
filter
((
key
)
=>
!
nodeAndChildren
.
includes
(
key
)));
}
}
/>
</
Form
.
Item
>
<
Form
.
Item
wrapperCol=
{
{
offset
:
7
,
span
:
16
}
}
style=
{
{
position
:
"fixed"
,
bottom
:
0
,
width
:
"30%"
,
background
:
"#fff"
,
paddingBottom
:
20
,
margin
:
0
,
}
}
>
<
Space
size=
{
20
}
>
<
Button
onClick=
{
onClose
}
>
取消
</
Button
>
<
Button
type=
"primary"
htmlType=
"submit"
onClick=
{
onSubmit
}
>
提交
</
Button
>
</
Space
>
</
Form
.
Item
>
</
Form
>
</
Drawer
>
);
}
apps/admin/src/modules/access/role/components/RoleFormDrawer.tsx
0 → 100644
浏览文件 @
2c95eb73
import
{
Button
,
Drawer
,
Form
,
Input
,
Radio
,
Space
}
from
"antd"
;
import
{
useEffect
}
from
"react"
;
const
{
TextArea
}
=
Input
;
export
interface
RoleFormValues
{
name
:
string
;
type
:
string
;
remarks
?:
string
;
}
interface
RoleFormDrawerProps
{
open
:
boolean
;
initialRole
:
Partial
<
RoleFormValues
>
|
null
;
onClose
:
()
=>
void
;
onSubmit
:
(
values
:
RoleFormValues
)
=>
void
|
Promise
<
void
>
;
}
export
default
function
RoleFormDrawer
({
open
,
initialRole
,
onClose
,
onSubmit
,
}:
RoleFormDrawerProps
)
{
const
[
form
]
=
Form
.
useForm
<
RoleFormValues
>
();
useEffect
(()
=>
{
if
(
!
open
)
{
form
.
resetFields
();
return
;
}
if
(
initialRole
)
form
.
setFieldsValue
(
initialRole
);
},
[
form
,
initialRole
,
open
]);
return
(
<
Drawer
placement=
"right"
onClose=
{
onClose
}
open=
{
open
}
mask=
{
false
}
>
<
Form
form=
{
form
}
onFinish=
{
onSubmit
}
labelCol=
{
{
span
:
6
}
}
>
<
Form
.
Item
name=
"name"
label=
"角色名称"
rules=
{
[
{
required
:
true
,
message
:
"请输入角色名称"
},
{
max
:
50
,
message
:
"角色名称最多可输入50个字符"
},
]
}
>
<
Input
autoComplete=
"off"
placeholder=
"请输入角色名称"
/>
</
Form
.
Item
>
<
Form
.
Item
name=
"type"
label=
"角色类型"
rules=
{
[{
required
:
true
,
message
:
"请选择角色类型"
}]
}
>
<
Radio
.
Group
style=
{
{
textAlign
:
"center"
}
}
>
<
Radio
value=
"1"
>
管理员
</
Radio
>
<
Radio
value=
"2"
>
老师
</
Radio
>
</
Radio
.
Group
>
</
Form
.
Item
>
<
Form
.
Item
name=
"remarks"
label=
"备注"
rules=
{
[
{
required
:
false
,
message
:
"请输入备注"
},
{
max
:
100
,
message
:
"备注最多可输入100个字符"
},
]
}
>
<
TextArea
autoSize=
{
{
minRows
:
5
,
maxRows
:
8
}
}
placeholder=
"请输入备注"
/>
</
Form
.
Item
>
<
Form
.
Item
wrapperCol=
{
{
offset
:
12
,
span
:
16
}
}
>
<
Space
size=
{
20
}
>
<
Button
onClick=
{
onClose
}
>
取消
</
Button
>
<
Button
type=
"primary"
htmlType=
"submit"
>
提交
</
Button
>
</
Space
>
</
Form
.
Item
>
</
Form
>
</
Drawer
>
);
}
apps/admin/src/modules/access/role/query.ts
0 → 100644
浏览文件 @
2c95eb73
import
type
{
Dayjs
}
from
"dayjs"
;
export
interface
RoleFilters
{
name
?:
string
;
time
?:
[
Dayjs
|
null
,
Dayjs
|
null
]
|
null
;
}
export
interface
RoleQuery
{
page
:
number
;
page_size
:
number
;
name
?:
string
;
start_time
?:
number
;
end_time
?:
number
;
}
interface
BuildRoleQueryInput
{
page
:
number
;
pageSize
:
number
;
filters
:
RoleFilters
;
}
export
function
buildRoleQuery
({
page
,
pageSize
,
filters
}:
BuildRoleQueryInput
):
RoleQuery
{
const
query
:
RoleQuery
=
{
page
,
page_size
:
pageSize
};
if
(
filters
.
name
)
query
.
name
=
filters
.
name
;
if
(
filters
.
time
?.[
0
]
&&
filters
.
time
[
1
])
{
query
.
start_time
=
filters
.
time
[
0
].
startOf
(
"day"
).
unix
();
query
.
end_time
=
filters
.
time
[
1
].
endOf
(
"day"
).
unix
();
}
return
query
;
}
apps/admin/src/modules/access/role/routes.tsx
0 → 100644
浏览文件 @
2c95eb73
import
{
Navigate
,
useRoutes
}
from
"react-router"
;
import
RoleListView
from
"./views/RoleListView"
;
export
default
function
RoleRoutes
()
{
return
useRoutes
([
{
index
:
true
,
Component
:
RoleListView
},
{
path
:
"*"
,
element
:
<
Navigate
to=
"/jurisdiction/role"
/>
},
]);
}
apps/admin/src/modules/access/role/useRoleList.ts
0 → 100644
浏览文件 @
2c95eb73
import
{
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
getRoleList
,
type
RoleListResult
,
type
RoleRow
}
from
"./api"
;
import
{
buildRoleQuery
,
type
RoleFilters
,
type
RoleQuery
}
from
"./query"
;
const
INITIAL_PAGE
=
1
;
const
INITIAL_PAGE_SIZE
=
10
;
function
isCleared
(
previous
:
RoleFilters
,
next
:
RoleFilters
)
{
const
keys
=
new
Set
([...
Object
.
keys
(
previous
),
...
Object
.
keys
(
next
)])
as
Set
<
keyof
RoleFilters
>
;
return
[...
keys
].
some
((
key
)
=>
previous
[
key
]
!==
next
[
key
]
&&
!
next
[
key
]);
}
export
function
useRoleList
()
{
const
[
filters
,
setFilterState
]
=
useState
<
RoleFilters
>
({});
const
[
appliedFilters
,
setAppliedFilters
]
=
useState
<
RoleFilters
>
({});
const
[
page
,
setPage
]
=
useState
(
INITIAL_PAGE
);
const
[
pageSize
,
setPageSize
]
=
useState
(
INITIAL_PAGE_SIZE
);
const
[
rows
,
setRows
]
=
useState
<
RoleRow
[]
>
([]);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
error
,
setError
]
=
useState
<
Error
|
null
>
(
null
);
const
requestId
=
useRef
(
0
);
const
lastQuery
=
useRef
<
RoleQuery
>
(
buildRoleQuery
({
page
:
INITIAL_PAGE
,
pageSize
:
INITIAL_PAGE_SIZE
,
filters
:
{}
}),
);
const
load
=
useCallback
(
async
(
query
:
RoleQuery
)
=>
{
const
currentRequest
=
++
requestId
.
current
;
lastQuery
.
current
=
query
;
setLoading
(
true
);
setError
(
null
);
try
{
const
result
:
RoleListResult
|
false
=
await
getRoleList
(
query
);
if
(
currentRequest
!==
requestId
.
current
)
return
;
if
(
result
===
false
)
{
setError
(
new
Error
(
"数据获取失败!"
));
return
;
}
setRows
(
result
.
rows
);
setTotal
(
result
.
total
);
}
catch
(
reason
)
{
if
(
currentRequest
===
requestId
.
current
)
{
setError
(
reason
instanceof
Error
?
reason
:
new
Error
(
"数据获取失败!"
));
}
}
finally
{
if
(
currentRequest
===
requestId
.
current
)
setLoading
(
false
);
}
},
[]);
const
loadFor
=
useCallback
(
(
nextPage
:
number
,
nextPageSize
:
number
,
nextFilters
:
RoleFilters
)
=>
load
(
buildRoleQuery
({
page
:
nextPage
,
pageSize
:
nextPageSize
,
filters
:
nextFilters
})),
[
load
],
);
useEffect
(()
=>
{
void
load
(
lastQuery
.
current
);
return
()
=>
{
requestId
.
current
+=
1
;
};
},
[
load
]);
const
setFilters
=
useCallback
(
(
nextFilters
:
RoleFilters
)
=>
{
const
shouldReload
=
isCleared
(
filters
,
nextFilters
);
setFilterState
(
nextFilters
);
if
(
shouldReload
)
{
setAppliedFilters
(
nextFilters
);
void
loadFor
(
page
,
pageSize
,
nextFilters
);
}
},
[
filters
,
loadFor
,
page
,
pageSize
],
);
const
search
=
useCallback
(()
=>
{
setAppliedFilters
(
filters
);
setPage
(
INITIAL_PAGE
);
void
loadFor
(
INITIAL_PAGE
,
pageSize
,
filters
);
},
[
filters
,
loadFor
,
pageSize
]);
const
reset
=
useCallback
(()
=>
{
const
emptyFilters
:
RoleFilters
=
{};
setFilterState
(
emptyFilters
);
setAppliedFilters
(
emptyFilters
);
setPage
(
INITIAL_PAGE
);
void
loadFor
(
INITIAL_PAGE
,
pageSize
,
emptyFilters
);
},
[
loadFor
,
pageSize
]);
const
refresh
=
useCallback
(()
=>
{
setAppliedFilters
(
filters
);
void
loadFor
(
page
,
pageSize
,
filters
);
},
[
filters
,
loadFor
,
page
,
pageSize
]);
const
refreshUnfiltered
=
useCallback
(()
=>
{
void
loadFor
(
page
,
pageSize
,
{});
},
[
loadFor
,
page
,
pageSize
]);
const
changePage
=
useCallback
(
(
nextPage
:
number
)
=>
{
setPage
(
nextPage
);
void
loadFor
(
nextPage
,
pageSize
,
appliedFilters
);
},
[
appliedFilters
,
loadFor
,
pageSize
],
);
const
changePageSize
=
useCallback
(
(
nextPageSize
:
number
)
=>
{
setPage
(
INITIAL_PAGE
);
setPageSize
(
nextPageSize
);
void
loadFor
(
INITIAL_PAGE
,
nextPageSize
,
appliedFilters
);
},
[
appliedFilters
,
loadFor
],
);
const
retry
=
useCallback
(()
=>
void
load
(
lastQuery
.
current
),
[
load
]);
return
{
filters
,
setFilters
,
page
,
pageSize
,
rows
,
total
,
loading
,
error
,
search
,
reset
,
refresh
,
refreshUnfiltered
,
changePage
,
changePageSize
,
retry
,
};
}
apps/admin/src/modules/access/role/views/RoleListView.tsx
0 → 100644
浏览文件 @
2c95eb73
import
{
Button
,
DatePicker
,
Input
,
Popover
,
Space
,
Switch
}
from
"antd"
;
import
type
{
ColumnsType
}
from
"antd/es/table"
;
import
type
{
Key
}
from
"react"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
AppList
,
type
AppListFilterItem
}
from
"@/shared/components/AppList"
;
import
{
convertToAntdTreeData
}
from
"@/utils/common.js"
;
import
{
addRole
,
changeRoleStatus
,
deleteRole
,
editRole
,
getAllPermissions
,
getRoleInfo
,
getRolePermissionIds
,
setRolePermissions
,
type
RoleRow
,
}
from
"../api"
;
import
DeleteRoleModal
from
"../components/DeleteRoleModal"
;
import
PermissionDrawer
,
{
type
PermissionTreeNode
}
from
"../components/PermissionDrawer"
;
import
RoleFormDrawer
,
{
type
RoleFormValues
}
from
"../components/RoleFormDrawer"
;
import
type
{
RoleFilters
}
from
"../query"
;
import
{
useRoleList
}
from
"../useRoleList"
;
interface
RootState
{
user
:
{
operationPermissionsList
?:
string
[];
};
}
const
{
RangePicker
}
=
DatePicker
;
export
default
function
RoleListView
()
{
const
list
=
useRoleList
();
const
permissions
=
useSelector
((
state
:
RootState
)
=>
state
.
user
.
operationPermissionsList
??
[]);
const
[
selectedId
,
setSelectedId
]
=
useState
<
number
|
null
>
(
null
);
const
[
formOpen
,
setFormOpen
]
=
useState
(
false
);
const
[
isAdd
,
setIsAdd
]
=
useState
(
false
);
const
[
initialRole
,
setInitialRole
]
=
useState
<
Partial
<
RoleFormValues
>
|
null
>
(
null
);
const
[
deleteOpen
,
setDeleteOpen
]
=
useState
(
false
);
const
[
permissionOpen
,
setPermissionOpen
]
=
useState
(
false
);
const
[
permissionTree
,
setPermissionTree
]
=
useState
<
PermissionTreeNode
[]
>
([]);
const
[
checkedKeys
,
setCheckedKeys
]
=
useState
<
Key
[]
>
([]);
useEffect
(()
=>
{
let
active
=
true
;
void
getAllPermissions
().
then
((
result
)
=>
{
if
(
active
&&
result
!==
false
)
{
setPermissionTree
(
convertToAntdTreeData
(
result
)
as
PermissionTreeNode
[]);
}
});
return
()
=>
{
active
=
false
;
};
},
[]);
const
handleEdit
=
async
(
id
:
number
)
=>
{
const
role
=
await
getRoleInfo
(
id
);
if
(
role
===
false
)
return
;
setSelectedId
(
id
);
setIsAdd
(
false
);
setInitialRole
({
name
:
role
.
name
,
remarks
:
role
.
remarks
,
type
:
String
(
role
.
type
),
});
setFormOpen
(
true
);
};
const
handleFormSubmit
=
async
(
values
:
RoleFormValues
)
=>
{
const
params
:
Record
<
string
,
unknown
>
=
{
...
values
};
if
(
!
params
.
remarks
)
delete
params
.
remarks
;
const
success
=
isAdd
?
await
addRole
(
params
)
:
await
editRole
({
...
params
,
id
:
selectedId
});
if
(
!
success
)
return
;
list
.
refreshUnfiltered
();
setFormOpen
(
false
);
};
const
handleDelete
=
async
()
=>
{
if
(
selectedId
===
null
)
return
;
const
success
=
await
deleteRole
(
selectedId
);
if
(
success
)
list
.
refreshUnfiltered
();
setDeleteOpen
(
false
);
};
const
handleStatus
=
async
(
checked
:
boolean
,
id
:
number
)
=>
{
const
success
=
await
changeRoleStatus
(
id
,
checked
?
"1"
:
"2"
);
if
(
success
)
list
.
refreshUnfiltered
();
};
const
handleEmpower
=
async
(
id
:
number
)
=>
{
setSelectedId
(
id
);
setPermissionOpen
(
true
);
const
result
=
await
getRolePermissionIds
(
id
);
setCheckedKeys
(
result
===
false
?
[]
:
result
);
};
const
submitPermissions
=
async
()
=>
{
if
(
selectedId
===
null
)
return
;
const
success
=
await
setRolePermissions
(
selectedId
,
checkedKeys
as
Array
<
string
|
number
>
);
if
(
!
success
)
return
;
list
.
refreshUnfiltered
();
setPermissionOpen
(
false
);
};
const
columns
:
ColumnsType
<
RoleRow
>
=
[
{
title
:
"ID"
,
key
:
"id"
,
dataIndex
:
"id"
,
render
:
(
_
,
__
,
index
)
=>
list
.
pageSize
*
(
list
.
page
-
1
)
+
index
+
1
,
},
{
title
:
"角色名称"
,
key
:
"name"
,
align
:
"center"
,
dataIndex
:
"name"
,
width
:
300
,
},
{
title
:
"角色类型"
,
key
:
"type"
,
align
:
"center"
,
dataIndex
:
"type"
,
width
:
300
,
render
:
(
value
)
=>
{
if
(
Number
(
value
)
===
1
)
return
"管理员"
;
if
(
Number
(
value
)
===
2
)
return
"老师"
;
return
"未知"
;
},
},
{
title
:
"备注"
,
key
:
"remarks"
,
align
:
"center"
,
dataIndex
:
"remarks"
,
width
:
300
,
render
:
(
text
:
string
)
=>
(
<
Popover
content=
{
text
}
overlayStyle=
{
{
maxWidth
:
"400px"
}
}
>
<
div
style=
{
{
display
:
"-webkit-box"
,
WebkitLineClamp
:
"3"
,
WebkitBoxOrient
:
"vertical"
,
overflow
:
"hidden"
,
textOverflow
:
"ellipsis"
,
textAlign
:
"center"
,
}
}
>
{
text
}
</
div
>
</
Popover
>
),
},
{
title
:
"状态"
,
align
:
"center"
,
key
:
"status"
,
dataIndex
:
"status"
,
width
:
300
,
render
:
(
_
,
record
)
=>
(
<
Switch
checked=
{
Number
(
record
.
status
)
===
1
}
onChange=
{
(
checked
)
=>
void
handleStatus
(
checked
,
record
.
id
)
}
disabled=
{
!
permissions
.
includes
(
"/jurisdiction/role/changeStatus"
)
}
/>
),
},
{
title
:
"创建时间"
,
key
:
"create_time"
,
align
:
"center"
,
dataIndex
:
"create_time"
,
},
{
title
:
"操作"
,
key
:
"handle"
,
align
:
"center"
,
dataIndex
:
"handle"
,
width
:
400
,
render
:
(
_
,
record
)
=>
(
<
Space
>
{
permissions
.
includes
(
"/jurisdiction/role/getInfoById"
)
?
(
<
Button
onClick=
{
()
=>
void
handleEdit
(
record
.
id
)
}
>
编辑
</
Button
>
)
:
null
}
{
permissions
.
includes
(
"/jurisdiction/role/getPowerByRoleId"
)
?
(
<
Button
onClick=
{
()
=>
void
handleEmpower
(
record
.
id
)
}
>
授权
</
Button
>
)
:
null
}
{
permissions
.
includes
(
"/jurisdiction/role/del"
)
?
(
<
Button
onClick=
{
()
=>
{
setSelectedId
(
record
.
id
);
setDeleteOpen
(
true
);
}
}
>
删除
</
Button
>
)
:
null
}
</
Space
>
),
},
];
const
filterItems
:
AppListFilterItem
<
RoleFilters
>
[]
=
[
{
name
:
"name"
,
label
:
"角色名称"
,
render
:
(
field
)
=>
(
<
Input
autoComplete=
"off"
allowClear
value=
{
field
.
value
??
""
}
onChange=
{
(
event
)
=>
field
.
onChange
(
event
.
target
.
value
)
}
placeholder=
"请输入角色名称"
id=
"name"
/>
),
},
{
name
:
"time"
,
label
:
"创建时间"
,
render
:
(
field
)
=>
(
<
RangePicker
value=
{
field
.
value
}
onChange=
{
(
value
)
=>
field
.
onChange
(
value
)
}
id=
"createTime"
/>
),
},
];
const
toolbar
=
permissions
.
includes
(
"/jurisdiction/role/add"
)
?
(
<
Button
type=
"primary"
ghost
onClick=
{
()
=>
{
setSelectedId
(
null
);
setInitialRole
(
null
);
setIsAdd
(
true
);
setFormOpen
(
true
);
}
}
>
添加
</
Button
>
)
:
undefined
;
return
(
<>
<
AppList
<
RoleRow
,
RoleFilters
>
rowKey="id"
columns=
{
columns
}
data=
{
{
rows
:
list
.
rows
,
total
:
list
.
total
,
loading
:
list
.
loading
,
error
:
list
.
error
,
}
}
filters=
{
{
value
:
list
.
filters
,
items
:
filterItems
,
onChange
:
list
.
setFilters
,
onSearch
:
list
.
search
,
onReset
:
list
.
reset
,
onRefresh
:
list
.
refresh
,
}
}
pagination=
{
{
page
:
list
.
page
,
pageSize
:
list
.
pageSize
,
pageSizeOptions
:
[
10
,
20
,
30
,
40
],
onChange
:
list
.
changePage
,
onPageSizeChange
:
list
.
changePageSize
,
}
}
toolbar=
{
toolbar
}
onRetry=
{
list
.
retry
}
/
>
<
RoleFormDrawer
open=
{
formOpen
}
initialRole=
{
initialRole
}
onClose=
{
()
=>
setFormOpen
(
false
)
}
onSubmit=
{
handleFormSubmit
}
/>
<
DeleteRoleModal
open=
{
deleteOpen
}
onCancel=
{
()
=>
setDeleteOpen
(
false
)
}
onConfirm=
{
handleDelete
}
/>
<
PermissionDrawer
open=
{
permissionOpen
}
treeData=
{
permissionTree
}
checkedKeys=
{
checkedKeys
}
onCheckedKeysChange=
{
setCheckedKeys
}
onClose=
{
()
=>
setPermissionOpen
(
false
)
}
onSubmit=
{
submitPermissions
}
/>
</>
);
}
apps/admin/src/modules/access/routes.tsx
浏览文件 @
2c95eb73
...
...
@@ -4,7 +4,7 @@ import ModuleBoundary from "@/app/ModuleBoundary";
const
AccessLayout
=
lazy
(()
=>
import
(
"@/pages/jurisdiction/index"
));
const
AdminRoutes
=
lazy
(()
=>
import
(
"./admin/routes"
));
const
Role
=
lazy
(()
=>
import
(
"@/pages/jurisdiction/role/index
"
));
const
Role
Routes
=
lazy
(()
=>
import
(
"./role/routes
"
));
export
default
function
AccessRoutes
()
{
const
routes
=
useRoutes
([
...
...
@@ -13,7 +13,7 @@ export default function AccessRoutes() {
children
:
[
{
index
:
true
,
element
:
<
Navigate
to=
"/jurisdiction/role"
/>
},
{
path
:
"admin/*"
,
Component
:
AdminRoutes
},
{
path
:
"role
"
,
Component
:
Role
},
{
path
:
"role
/*"
,
Component
:
RoleRoutes
},
],
},
{
path
:
"*"
,
element
:
<
Navigate
to=
"/"
/>
},
...
...
apps/admin/src/pages/jurisdiction/role/index.jsx
deleted
100644 → 0
浏览文件 @
163696c5
差异被折叠。
点击展开。
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论