Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
3a954e1e
authored
Feb 23, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: extract binding modal and polish binding management UX
parent
62856666
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
32 additions
and
18 deletions
+32
-18
web/src/components/table/users/modals/EditUserModal.jsx
+3
-3
web/src/components/table/users/modals/UserBindingManagementModal.jsx
+29
-15
No files found.
web/src/components/table/users/modals/EditUserModal.jsx
View file @
3a954e1e
...
@@ -207,7 +207,7 @@ const EditUserModal = (props) => {
...
@@ -207,7 +207,7 @@ const EditUserModal = (props) => {
onSubmit=
{
submit
}
onSubmit=
{
submit
}
>
>
{
({
values
})
=>
(
{
({
values
})
=>
(
<
div
className=
'p-2'
>
<
div
className=
'p-2
space-y-3
'
>
{
/* 基本信息 */
}
{
/* 基本信息 */
}
<
Card
className=
'!rounded-2xl shadow-sm border-0'
>
<
Card
className=
'!rounded-2xl shadow-sm border-0'
>
<
div
className=
'flex items-center mb-2'
>
<
div
className=
'flex items-center mb-2'
>
...
@@ -344,7 +344,7 @@ const EditUserModal = (props) => {
...
@@ -344,7 +344,7 @@ const EditUserModal = (props) => {
{
t
(
'绑定信息'
)
}
{
t
(
'绑定信息'
)
}
</
Text
>
</
Text
>
<
div
className=
'text-xs text-gray-600'
>
<
div
className=
'text-xs text-gray-600'
>
{
t
(
'
第三方账户绑定状态(只读)
'
)
}
{
t
(
'
管理用户已绑定的第三方账户,支持筛选与解绑
'
)
}
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
...
@@ -353,7 +353,7 @@ const EditUserModal = (props) => {
...
@@ -353,7 +353,7 @@ const EditUserModal = (props) => {
theme=
'outline'
theme=
'outline'
onClick=
{
openBindingModal
}
onClick=
{
openBindingModal
}
>
>
{
t
(
'
修改
绑定'
)
}
{
t
(
'
管理
绑定'
)
}
</
Button
>
</
Button
>
</
div
>
</
div
>
</
Card
>
</
Card
>
...
...
web/src/components/table/users/modals/UserBindingManagementModal.jsx
View file @
3a954e1e
...
@@ -53,7 +53,7 @@ const UserBindingManagementModal = ({
...
@@ -53,7 +53,7 @@ const UserBindingManagementModal = ({
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
[
bindingLoading
,
setBindingLoading
]
=
React
.
useState
(
false
);
const
[
bindingLoading
,
setBindingLoading
]
=
React
.
useState
(
false
);
const
[
show
UnboundOnly
,
setShowUnboundOnly
]
=
React
.
useState
(
fals
e
);
const
[
show
BoundOnly
,
setShowBoundOnly
]
=
React
.
useState
(
tru
e
);
const
[
statusInfo
,
setStatusInfo
]
=
React
.
useState
({});
const
[
statusInfo
,
setStatusInfo
]
=
React
.
useState
({});
const
[
customOAuthBindings
,
setCustomOAuthBindings
]
=
React
.
useState
([]);
const
[
customOAuthBindings
,
setCustomOAuthBindings
]
=
React
.
useState
([]);
const
[
bindingActionLoading
,
setBindingActionLoading
]
=
React
.
useState
({});
const
[
bindingActionLoading
,
setBindingActionLoading
]
=
React
.
useState
({});
...
@@ -90,7 +90,7 @@ const UserBindingManagementModal = ({
...
@@ -90,7 +90,7 @@ const UserBindingManagementModal = ({
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
!
visible
)
return
;
if
(
!
visible
)
return
;
setShow
UnboundOnly
(
fals
e
);
setShow
BoundOnly
(
tru
e
);
setBindingActionLoading
({});
setBindingActionLoading
({});
loadBindingData
();
loadBindingData
();
},
[
visible
,
loadBindingData
]);
},
[
visible
,
loadBindingData
]);
...
@@ -294,8 +294,12 @@ const UserBindingManagementModal = ({
...
@@ -294,8 +294,12 @@ const UserBindingManagementModal = ({
...
customBindingItems
.
map
((
item
)
=>
({
...
item
,
type
:
'custom'
})),
...
customBindingItems
.
map
((
item
)
=>
({
...
item
,
type
:
'custom'
})),
];
];
const
visibleBindingItems
=
showUnboundOnly
const
boundCount
=
allBindingItems
.
filter
((
item
)
=>
?
allBindingItems
.
filter
((
item
)
=>
!
item
.
value
)
Boolean
(
item
.
value
),
).
length
;
const
visibleBindingItems
=
showBoundOnly
?
allBindingItems
.
filter
((
item
)
=>
Boolean
(
item
.
value
))
:
allBindingItems
;
:
allBindingItems
;
return
(
return
(
...
@@ -308,30 +312,31 @@ const UserBindingManagementModal = ({
...
@@ -308,30 +312,31 @@ const UserBindingManagementModal = ({
title=
{
title=
{
<
div
className=
'flex items-center'
>
<
div
className=
'flex items-center'
>
<
IconLink
className=
'mr-2'
/>
<
IconLink
className=
'mr-2'
/>
{
t
(
'
绑定信息
'
)
}
{
t
(
'
账户绑定管理
'
)
}
</
div
>
</
div
>
}
}
>
>
<
Spin
spinning=
{
bindingLoading
}
>
<
Spin
spinning=
{
bindingLoading
}
>
<
div
className=
'max-h-[68vh] overflow-y-auto pr-1 pb-2'
>
<
div
className=
'flex items-center justify-between mb-4 gap-3 flex-wrap'
>
<
div
className=
'flex items-center justify-between mb-4 gap-3 flex-wrap'
>
<
Checkbox
<
Checkbox
checked=
{
showUnb
oundOnly
}
checked=
{
showB
oundOnly
}
onChange=
{
(
e
)
=>
setShowUnb
oundOnly
(
Boolean
(
e
.
target
.
checked
))
}
onChange=
{
(
e
)
=>
setShowB
oundOnly
(
Boolean
(
e
.
target
.
checked
))
}
>
>
{
`${t('筛选')} ${t('未绑定')}`
}
{
t
(
'仅显示已绑定'
)
}
</
Checkbox
>
</
Checkbox
>
<
Text
type=
'tertiary'
>
<
Text
type=
'tertiary'
>
{
t
(
'筛选'
)
}
·
{
visible
BindingItems
.
length
}
{
t
(
'已绑定'
)
}
{
boundCount
}
/
{
all
BindingItems
.
length
}
</
Text
>
</
Text
>
</
div
>
</
div
>
{
visibleBindingItems
.
length
===
0
?
(
{
visibleBindingItems
.
length
===
0
?
(
<
Card
className=
'!rounded-xl border-dashed'
>
<
Card
className=
'!rounded-xl border-dashed'
>
<
Text
type=
'tertiary'
>
{
t
(
'暂无自定义 OAuth 提供商
'
)
}
</
Text
>
<
Text
type=
'tertiary'
>
{
t
(
'暂无已绑定项
'
)
}
</
Text
>
</
Card
>
</
Card
>
)
:
(
)
:
(
<
div
className=
'grid grid-cols-1 lg:grid-cols-2 gap-3
'
>
<
div
className=
'grid grid-cols-1 lg:grid-cols-2 gap-4
'
>
{
visibleBindingItems
.
map
((
item
)
=>
{
{
visibleBindingItems
.
map
((
item
,
index
)
=>
{
const
isBound
=
Boolean
(
item
.
value
);
const
isBound
=
Boolean
(
item
.
value
);
const
loadingKey
=
const
loadingKey
=
item
.
type
===
'builtin'
item
.
type
===
'builtin'
...
@@ -342,10 +347,16 @@ const UserBindingManagementModal = ({
...
@@ -342,10 +347,16 @@ const UserBindingManagementModal = ({
:
item
.
enabled
:
item
.
enabled
?
t
(
'未绑定'
)
?
t
(
'未绑定'
)
:
t
(
'未启用'
);
:
t
(
'未启用'
);
const
shouldSpanTwoColsOnDesktop
=
visibleBindingItems
.
length
%
2
===
1
&&
index
===
visibleBindingItems
.
length
-
1
;
return
(
return
(
<
Card
key=
{
item
.
key
}
className=
'!rounded-xl'
>
<
Card
<
div
className=
'flex items-center justify-between gap-3'
>
key=
{
item
.
key
}
className=
{
`!rounded-xl ${shouldSpanTwoColsOnDesktop ? 'lg:col-span-2' : ''}`
}
>
<
div
className=
'flex items-center justify-between gap-3 min-h-[92px]'
>
<
div
className=
'flex items-center flex-1 min-w-0'
>
<
div
className=
'flex items-center flex-1 min-w-0'
>
<
div
className=
'w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3 flex-shrink-0'
>
<
div
className=
'w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3 flex-shrink-0'
>
{
item
.
icon
}
{
item
.
icon
}
...
@@ -354,7 +365,9 @@ const UserBindingManagementModal = ({
...
@@ -354,7 +365,9 @@ const UserBindingManagementModal = ({
<
div
className=
'font-medium text-gray-900 flex items-center gap-2'
>
<
div
className=
'font-medium text-gray-900 flex items-center gap-2'
>
<
span
>
{
item
.
name
}
</
span
>
<
span
>
{
item
.
name
}
</
span
>
<
Tag
size=
'small'
color=
'white'
>
<
Tag
size=
'small'
color=
'white'
>
{
item
.
type
===
'builtin'
?
'Built-in'
:
'Custom'
}
{
item
.
type
===
'builtin'
?
t
(
'内置'
)
:
t
(
'自定义'
)
}
</
Tag
>
</
Tag
>
</
div
>
</
div
>
<
div
className=
'text-sm text-gray-500 truncate'
>
<
div
className=
'text-sm text-gray-500 truncate'
>
...
@@ -388,6 +401,7 @@ const UserBindingManagementModal = ({
...
@@ -388,6 +401,7 @@ const UserBindingManagementModal = ({
})
}
})
}
</
div
>
</
div
>
)
}
)
}
</
div
>
</
Spin
>
</
Spin
>
</
Modal
>
</
Modal
>
);
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment