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
b7ac0b18
authored
Nov 27, 2023
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
更新用户管理界面UI
parent
41e461f3
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
532 additions
and
542 deletions
+532
-542
web/src/App.js
+0
-43
web/src/components/RedemptionsTable.js
+0
-2
web/src/components/UsersTable.js
+294
-313
web/src/helpers/render.js
+9
-4
web/src/pages/Token/EditToken.js
+1
-2
web/src/pages/User/AddUser.js
+90
-69
web/src/pages/User/EditUser.js
+129
-105
web/src/pages/User/index.js
+9
-4
No files found.
web/src/App.js
View file @
b7ac0b18
...
@@ -8,7 +8,6 @@ import LoginForm from './components/LoginForm';
...
@@ -8,7 +8,6 @@ import LoginForm from './components/LoginForm';
import
NotFound
from
'./pages/NotFound'
;
import
NotFound
from
'./pages/NotFound'
;
import
Setting
from
'./pages/Setting'
;
import
Setting
from
'./pages/Setting'
;
import
EditUser
from
'./pages/User/EditUser'
;
import
EditUser
from
'./pages/User/EditUser'
;
import
AddUser
from
'./pages/User/AddUser'
;
import
{
API
,
getLogo
,
getSystemName
,
showError
,
showNotice
}
from
'./helpers'
;
import
{
API
,
getLogo
,
getSystemName
,
showError
,
showNotice
}
from
'./helpers'
;
import
PasswordResetForm
from
'./components/PasswordResetForm'
;
import
PasswordResetForm
from
'./components/PasswordResetForm'
;
import
GitHubOAuth
from
'./components/GitHubOAuth'
;
import
GitHubOAuth
from
'./components/GitHubOAuth'
;
...
@@ -17,10 +16,8 @@ import { UserContext } from './context/User';
...
@@ -17,10 +16,8 @@ import { UserContext } from './context/User';
import
{
StatusContext
}
from
'./context/Status'
;
import
{
StatusContext
}
from
'./context/Status'
;
import
Channel
from
'./pages/Channel'
;
import
Channel
from
'./pages/Channel'
;
import
Token
from
'./pages/Token'
;
import
Token
from
'./pages/Token'
;
import
EditToken
from
'./pages/Token/EditToken'
;
import
EditChannel
from
'./pages/Channel/EditChannel'
;
import
EditChannel
from
'./pages/Channel/EditChannel'
;
import
Redemption
from
'./pages/Redemption'
;
import
Redemption
from
'./pages/Redemption'
;
import
EditRedemption
from
'./pages/Redemption/EditRedemption'
;
import
TopUp
from
'./pages/TopUp'
;
import
TopUp
from
'./pages/TopUp'
;
import
Log
from
'./pages/Log'
;
import
Log
from
'./pages/Log'
;
import
Chat
from
'./pages/Chat'
;
import
Chat
from
'./pages/Chat'
;
...
@@ -132,22 +129,6 @@ function App() {
...
@@ -132,22 +129,6 @@ function App() {
}
}
/
>
/
>
<
Route
<
Route
path
=
'/token/edit/:id'
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
EditToken
/>
<
/Suspense
>
}
/
>
<
Route
path
=
'/token/add'
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
EditToken
/>
<
/Suspense
>
}
/
>
<
Route
path
=
'/redemption'
path
=
'/redemption'
element
=
{
element
=
{
<
PrivateRoute
>
<
PrivateRoute
>
...
@@ -156,22 +137,6 @@ function App() {
...
@@ -156,22 +137,6 @@ function App() {
}
}
/
>
/
>
<
Route
<
Route
path
=
'/redemption/edit/:id'
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
EditRedemption
/>
<
/Suspense
>
}
/
>
<
Route
path
=
'/redemption/add'
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
EditRedemption
/>
<
/Suspense
>
}
/
>
<
Route
path
=
'/user'
path
=
'/user'
element
=
{
element
=
{
<
PrivateRoute
>
<
PrivateRoute
>
...
@@ -196,14 +161,6 @@ function App() {
...
@@ -196,14 +161,6 @@ function App() {
}
}
/
>
/
>
<
Route
<
Route
path
=
'/user/add'
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
AddUser
/>
<
/Suspense
>
}
/
>
<
Route
path
=
'/user/reset'
path
=
'/user/reset'
element
=
{
element
=
{
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
<
Suspense
fallback
=
{
<
Loading
><
/Loading>}
>
...
...
web/src/components/RedemptionsTable.js
View file @
b7ac0b18
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Link
}
from
'react-router-dom'
;
import
{
API
,
copy
,
showError
,
showInfo
,
showSuccess
,
showWarning
,
timestamp2string
}
from
'../helpers'
;
import
{
API
,
copy
,
showError
,
showInfo
,
showSuccess
,
showWarning
,
timestamp2string
}
from
'../helpers'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
renderQuota
}
from
'../helpers/render'
;
import
{
renderQuota
}
from
'../helpers/render'
;
import
{
Button
,
Modal
,
Popconfirm
,
Popover
,
Table
,
Tag
,
Form
}
from
"@douyinfe/semi-ui"
;
import
{
Button
,
Modal
,
Popconfirm
,
Popover
,
Table
,
Tag
,
Form
}
from
"@douyinfe/semi-ui"
;
import
EditRedemption
from
"../pages/Redemption/EditRedemption"
;
import
EditRedemption
from
"../pages/Redemption/EditRedemption"
;
import
editRedemption
from
"../pages/Redemption/EditRedemption"
;
function
renderTimestamp
(
timestamp
)
{
function
renderTimestamp
(
timestamp
)
{
return
(
return
(
...
...
web/src/components/UsersTable.js
View file @
b7ac0b18
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Button
,
Form
,
Label
,
Pagination
,
Popup
,
Table
}
from
'semantic-ui-react
'
;
import
{
API
,
isAdmin
,
showError
,
showSuccess
}
from
'../helpers
'
;
import
{
Link
}
from
'react-router-dom'
;
import
{
Button
,
Modal
,
Popconfirm
,
Popover
,
Table
,
Tag
,
Form
,
Tooltip
,
Space
}
from
"@douyinfe/semi-ui"
;
import
{
API
,
showError
,
showSuccess
}
from
'../helper
s'
;
import
{
ITEMS_PER_PAGE
}
from
'../constant
s'
;
import
{
renderGroup
,
renderNumber
,
renderQuota
,
renderText
,
stringToColor
}
from
'../helpers/render'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
import
AddUser
from
"../pages/User/AddUser"
;
import
{
renderGroup
,
renderNumber
,
renderQuota
,
renderText
}
from
'../helpers/render'
;
import
EditUser
from
"../pages/User/EditUser"
;
function
renderRole
(
role
)
{
function
renderRole
(
role
)
{
switch
(
role
)
{
switch
(
role
)
{
case
1
:
case
1
:
return
<
Label
>
普通用户
<
/Label
>
;
return
<
Tag
size
=
'large'
>
普通用户
<
/Tag
>
;
case
10
:
case
10
:
return
<
Label
color
=
'yellow'
>
管理员
<
/Label
>
;
return
<
Tag
color
=
'yellow'
size
=
'large'
>
管理员
<
/Tag
>
;
case
100
:
case
100
:
return
<
Label
color
=
'orange'
>
超级管理员
<
/Label
>
;
return
<
Tag
color
=
'orange'
size
=
'large'
>
超级管理员
<
/Tag
>
;
default
:
default
:
return
<
Label
color
=
'red'
>
未知身份
<
/Label
>
;
return
<
Tag
color
=
'red'
size
=
'large'
>
未知身份
<
/Tag
>
;
}
}
}
}
const
UsersTable
=
()
=>
{
const
UsersTable
=
()
=>
{
const
[
users
,
setUsers
]
=
useState
([]);
const
columns
=
[{
const
[
loading
,
setLoading
]
=
useState
(
true
);
title
:
'ID'
,
dataIndex
:
'id'
,
const
[
activePage
,
setActivePage
]
=
useState
(
1
);
},
{
const
[
searchKeyword
,
setSearchKeyword
]
=
useState
(
''
);
title
:
'用户名'
,
dataIndex
:
'username'
,
const
[
searching
,
setSearching
]
=
useState
(
false
);
},
{
title
:
'分组'
,
dataIndex
:
'group'
,
render
:
(
text
,
record
,
index
)
=>
{
return
(
<
div
>
{
renderGroup
(
text
)}
<
/div>
)
;
},
},
{
title
:
'统计信息'
,
dataIndex
:
'info'
,
render
:
(
text
,
record
,
index
)
=>
{
return
(
<
div
>
<
Space
spacing
=
{
1
}
>
<
Tooltip
content
=
{
'剩余额度'
}
>
<
Tag
color
=
'white'
size
=
'large'
>
{
renderQuota
(
record
.
quota
)}
<
/Tag
>
<
/Tooltip
>
<
Tooltip
content
=
{
'已用额度'
}
>
<
Tag
color
=
'white'
size
=
'large'
>
{
renderQuota
(
record
.
used_quota
)}
<
/Tag
>
<
/Tooltip
>
<
Tooltip
content
=
{
'调用次数'
}
>
<
Tag
color
=
'white'
size
=
'large'
>
{
renderNumber
(
record
.
request_count
)}
<
/Tag
>
<
/Tooltip
>
<
/Space
>
<
/div>
)
;
}
},
{
title
:
'邀请信息'
,
dataIndex
:
'invite'
,
render
:
(
text
,
record
,
index
)
=>
{
return
(
<
div
>
<
Space
spacing
=
{
1
}
>
<
Tooltip
content
=
{
'邀请人数'
}
>
<
Tag
color
=
'white'
size
=
'large'
>
{
renderNumber
(
record
.
aff_count
)}
<
/Tag
>
<
/Tooltip
>
<
Tooltip
content
=
{
'邀请总收益'
}
>
<
Tag
color
=
'white'
size
=
'large'
>
{
renderQuota
(
record
.
aff_history_quota
)}
<
/Tag
>
<
/Tooltip
>
<
Tooltip
content
=
{
'邀请人ID'
}
>
{
record
.
inviter_id
===
0
?
<
Tag
color
=
'white'
size
=
'large'
>
无
<
/Tag>
:
<
Tag
color
=
'white'
size
=
'large'
>
{
record
.
inviter_id
}
<
/Tag>
}
<
/Tooltip
>
<
/Space
>
<
/div>
)
;
}
},
{
title
:
'角色'
,
dataIndex
:
'role'
,
render
:
(
text
,
record
,
index
)
=>
{
return
(
<
div
>
{
renderRole
(
text
)}
<
/div>
)
;
},
},
{
title
:
'状态'
,
dataIndex
:
'status'
,
render
:
(
text
,
record
,
index
)
=>
{
return
(
<
div
>
{
renderStatus
(
text
)}
<
/div>
)
;
},
},
{
title
:
''
,
dataIndex
:
'operate'
,
render
:
(
text
,
record
,
index
)
=>
(
<
div
>
<
Popconfirm
title
=
"确定?"
okType
=
{
'warning'
}
position
=
{
'left'
}
onConfirm
=
{()
=>
{
manageUser
(
record
.
username
,
'promote'
,
record
)
}}
>
<
Button
theme
=
'light'
type
=
'warning'
style
=
{{
marginRight
:
1
}}
>
提升
<
/Button
>
<
/Popconfirm
>
<
Popconfirm
title
=
"确定?"
okType
=
{
'warning'
}
position
=
{
'left'
}
onConfirm
=
{()
=>
{
manageUser
(
record
.
username
,
'demote'
,
record
)
}}
>
<
Button
theme
=
'light'
type
=
'secondary'
style
=
{{
marginRight
:
1
}}
>
降级
<
/Button
>
<
/Popconfirm
>
<
Popconfirm
title
=
"确定是否要删除此用户?"
content
=
"此修改将不可逆"
okType
=
{
'danger'
}
position
=
{
'left'
}
onConfirm
=
{()
=>
{
manageUser
(
record
.
username
,
'delete'
,
record
).
then
(()
=>
{
removeRecord
(
record
.
id
);
})
}}
>
<
Button
theme
=
'light'
type
=
'danger'
style
=
{{
marginRight
:
1
}}
>
删除
<
/Button
>
<
/Popconfirm
>
{
record
.
status
===
1
?
<
Button
theme
=
'light'
type
=
'warning'
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
manageUser
(
record
.
username
,
'disable'
,
record
)
}}
>
禁用
<
/Button>
:
<
Button
theme
=
'light'
type
=
'secondary'
style
=
{{
marginRight
:
1
}}
onClick
=
{
async
()
=>
{
manageUser
(
record
.
username
,
'enable'
,
record
);
}}
disabled
=
{
record
.
status
===
3
}
>
启用
<
/Button>
}
<
Button
theme
=
'light'
type
=
'tertiary'
style
=
{{
marginRight
:
1
}}
onClick
=
{()
=>
{
setEditingUser
(
record
);
setShowEditUser
(
true
);
}}
>
编辑
<
/Button
>
<
/div>
)
,
},];
const
loadUsers
=
async
(
startIdx
)
=>
{
const
[
users
,
setUsers
]
=
useState
([]);
const
res
=
await
API
.
get
(
`/api/user/?p=
${
startIdx
}
`
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
{
success
,
message
,
data
}
=
res
.
data
;
const
[
activePage
,
setActivePage
]
=
useState
(
1
);
if
(
success
)
{
const
[
searchKeyword
,
setSearchKeyword
]
=
useState
(
''
);
if
(
startIdx
===
0
)
{
const
[
searching
,
setSearching
]
=
useState
(
false
);
setUsers
(
data
);
const
[
userCount
,
setUserCount
]
=
useState
(
ITEMS_PER_PAGE
);
}
else
{
const
[
showAddUser
,
setShowAddUser
]
=
useState
(
false
);
let
newUsers
=
users
;
const
[
showEditUser
,
setShowEditUser
]
=
useState
(
false
);
newUsers
.
push
(...
data
);
const
[
editingUser
,
setEditingUser
]
=
useState
({
setUsers
(
newUsers
);
id
:
undefined
,
}
});
}
else
{
showError
(
message
);
const
setCount
=
(
data
)
=>
{
if
(
data
.
length
>=
(
activePage
)
*
ITEMS_PER_PAGE
)
{
setUserCount
(
data
.
length
+
1
);
}
else
{
setUserCount
(
data
.
length
);
}
}
}
setLoading
(
false
);
};
const
onPaginationChange
=
(
e
,
{
activePage
})
=>
{
const
removeRecord
=
key
=>
{
(
async
()
=>
{
console
.
log
(
key
);
if
(
activePage
===
Math
.
ceil
(
users
.
length
/
ITEMS_PER_PAGE
)
+
1
)
{
let
newDataSource
=
[...
users
];
// In this case we have to load more data and then append them.
if
(
key
!=
null
)
{
await
loadUsers
(
activePage
-
1
);
let
idx
=
newDataSource
.
findIndex
(
data
=>
data
.
id
===
key
);
}
setActivePage
(
activePage
);
})();
};
useEffect
(()
=>
{
if
(
idx
>
-
1
)
{
loadUsers
(
0
)
newDataSource
.
splice
(
idx
,
1
);
.
then
()
setUsers
(
newDataSource
);
.
catch
((
reason
)
=>
{
}
showError
(
reason
);
}
});
};
},
[]);
const
manageUser
=
(
username
,
action
,
idx
)
=>
{
const
loadUsers
=
async
(
startIdx
)
=>
{
(
async
()
=>
{
const
res
=
await
API
.
get
(
`/api/user/?p=
${
startIdx
}
`
);
const
res
=
await
API
.
post
(
'/api/user/manage'
,
{
const
{
success
,
message
,
data
}
=
res
.
data
;
username
,
if
(
success
)
{
action
if
(
startIdx
===
0
)
{
});
setUsers
(
data
);
const
{
success
,
message
}
=
res
.
data
;
setCount
(
data
);
if
(
success
)
{
}
else
{
showSuccess
(
'操作成功完成!'
);
let
newUsers
=
users
;
let
user
=
res
.
data
.
data
;
newUsers
.
push
(...
data
);
let
newUsers
=
[...
users
];
setUsers
(
newUsers
);
let
realIdx
=
(
activePage
-
1
)
*
ITEMS_PER_PAGE
+
idx
;
setCount
(
newUsers
);
if
(
action
===
'delete'
)
{
}
newUsers
[
realIdx
].
deleted
=
true
;
}
else
{
}
else
{
newUsers
[
realIdx
].
status
=
user
.
status
;
showError
(
message
);
newUsers
[
realIdx
].
role
=
user
.
role
;
}
}
setUsers
(
newUsers
);
setLoading
(
false
);
}
else
{
};
showError
(
message
);
}
})();
};
const
renderStatus
=
(
status
)
=>
{
const
onPaginationChange
=
(
e
,
{
activePage
})
=>
{
switch
(
status
)
{
(
async
()
=>
{
case
1
:
if
(
activePage
===
Math
.
ceil
(
users
.
length
/
ITEMS_PER_PAGE
)
+
1
)
{
return
<
Label
basic
>
已激活
<
/Label>
;
// In this case we have to load more data and then append them.
case
2
:
await
loadUsers
(
activePage
-
1
);
return
(
}
<
Label
basic
color
=
'red'
>
setActivePage
(
activePage
);
已封禁
})();
<
/Label
>
};
);
default
:
return
(
<
Label
basic
color
=
'grey'
>
未知状态
<
/Label
>
);
}
};
const
searchUsers
=
async
()
=>
{
useEffect
(()
=>
{
if
(
searchKeyword
===
''
)
{
loadUsers
(
0
)
// if keyword is blank, load files instead.
.
then
()
await
loadUsers
(
0
);
.
catch
((
reason
)
=>
{
setActivePage
(
1
);
showError
(
reason
);
return
;
});
}
},
[]);
setSearching
(
true
);
const
res
=
await
API
.
get
(
`/api/user/search?keyword=
${
searchKeyword
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
setUsers
(
data
);
setActivePage
(
1
);
}
else
{
showError
(
message
);
}
setSearching
(
false
);
};
const
handleKeywordChange
=
async
(
e
,
{
value
})
=>
{
const
manageUser
=
async
(
username
,
action
,
record
)
=>
{
setSearchKeyword
(
value
.
trim
());
const
res
=
await
API
.
post
(
'/api/user/manage'
,
{
};
username
,
action
});
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
showSuccess
(
'操作成功完成!'
);
let
user
=
res
.
data
.
data
;
let
newUsers
=
[...
users
];
if
(
action
===
'delete'
)
{
const
sortUser
=
(
key
)
=>
{
}
else
{
if
(
users
.
length
===
0
)
return
;
record
.
status
=
user
.
status
;
setLoading
(
true
);
record
.
role
=
user
.
role
;
let
sortedUsers
=
[...
users
];
}
sortedUsers
.
sort
((
a
,
b
)
=>
{
setUsers
(
newUsers
);
return
(
''
+
a
[
key
]).
localeCompare
(
b
[
key
]);
}
else
{
});
showError
(
message
);
if
(
sortedUsers
[
0
].
id
===
users
[
0
].
id
)
{
}
sortedUsers
.
reverse
();
};
const
renderStatus
=
(
status
)
=>
{
switch
(
status
)
{
case
1
:
return
<
Tag
size
=
'large'
>
已激活
<
/Tag>
;
case
2
:
return
(
<
Tag
size
=
'large'
color
=
'red'
>
已封禁
<
/Tag>
)
;
default
:
return
(
<
Tag
size
=
'large'
color
=
'grey'
>
未知状态
<
/Tag>
)
;
}
};
const
searchUsers
=
async
()
=>
{
if
(
searchKeyword
===
''
)
{
// if keyword is blank, load files instead.
await
loadUsers
(
0
);
setActivePage
(
1
);
return
;
}
setSearching
(
true
);
const
res
=
await
API
.
get
(
`/api/user/search?keyword=
${
searchKeyword
}
`
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
setUsers
(
data
);
setActivePage
(
1
);
}
else
{
showError
(
message
);
}
setSearching
(
false
);
};
const
handleKeywordChange
=
async
(
value
)
=>
{
setSearchKeyword
(
value
.
trim
());
};
const
sortUser
=
(
key
)
=>
{
if
(
users
.
length
===
0
)
return
;
setLoading
(
true
);
let
sortedUsers
=
[...
users
];
sortedUsers
.
sort
((
a
,
b
)
=>
{
return
(
''
+
a
[
key
]).
localeCompare
(
b
[
key
]);
});
if
(
sortedUsers
[
0
].
id
===
users
[
0
].
id
)
{
sortedUsers
.
reverse
();
}
setUsers
(
sortedUsers
);
setLoading
(
false
);
};
const
pageData
=
users
.
slice
((
activePage
-
1
)
*
ITEMS_PER_PAGE
,
activePage
*
ITEMS_PER_PAGE
);
const
closeAddUser
=
()
=>
{
setShowAddUser
(
false
);
}
}
setUsers
(
sortedUsers
);
setLoading
(
false
);
};
return
(
const
closeEditUser
=
()
=>
{
<>
setShowEditUser
(
false
);
<
Form
onSubmit
=
{
searchUsers
}
>
setEditingUser
({
<
Form
.
Input
id
:
undefined
,
icon
=
'search'
});
fluid
}
iconPosition
=
'left'
placeholder
=
'搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...'
value
=
{
searchKeyword
}
loading
=
{
searching
}
onChange
=
{
handleKeywordChange
}
/
>
<
/Form
>
<
Table
basic
compact
size
=
'small'
>
const
refresh
=
async
()
=>
{
<
Table
.
Header
>
await
loadUsers
(
activePage
-
1
);
<
Table
.
Row
>
};
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'id'
);
}}
>
ID
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'username'
);
}}
>
用户名
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'group'
);
}}
>
分组
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'quota'
);
}}
>
统计信息
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'role'
);
}}
>
用户角色
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
style
=
{{
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
sortUser
(
'status'
);
}}
>
状态
<
/Table.HeaderCell
>
<
Table
.
HeaderCell
>
操作
<
/Table.HeaderCell
>
<
/Table.Row
>
<
/Table.Header
>
<
Table
.
Body
>
return
(
{
users
<>
.
slice
(
<
AddUser
refresh
=
{
refresh
}
visible
=
{
showAddUser
}
handleClose
=
{
closeAddUser
}
><
/AddUser
>
(
activePage
-
1
)
*
ITEMS_PER_PAGE
,
<
EditUser
refresh
=
{
refresh
}
visible
=
{
showEditUser
}
handleClose
=
{
closeEditUser
}
editingUser
=
{
editingUser
}
><
/EditUser
>
activePage
*
ITEMS_PER_PAGE
<
Form
onSubmit
=
{
searchUsers
}
>
)
<
Form
.
Input
.
map
((
user
,
idx
)
=>
{
label
=
'搜索关键字'
if
(
user
.
deleted
)
return
<><
/>
;
icon
=
'search'
return
(
field
=
'keyword'
<
Table
.
Row
key
=
{
user
.
id
}
>
iconPosition
=
'left'
<
Table
.
Cell
>
{
user
.
id
}
<
/Table.Cell
>
placeholder
=
'搜索用户的 ID,用户名,显示名称,以及邮箱地址 ...'
<
Table
.
Cell
>
value
=
{
searchKeyword
}
<
Popup
loading
=
{
searching
}
content
=
{
user
.
email
?
user
.
email
:
'未绑定邮箱地址'
}
onChange
=
{
value
=>
handleKeywordChange
(
value
)}
key
=
{
user
.
username
}
/
>
header
=
{
user
.
display_name
?
user
.
display_name
:
user
.
username
}
<
/Form
>
trigger
=
{
<
span
>
{
renderText
(
user
.
username
,
15
)}
<
/span>
}
hoverable
/>
<
/Table.Cell
>
<
Table
.
Cell
>
{
renderGroup
(
user
.
group
)}
<
/Table.Cell
>
{
/*<Table.Cell>*/
}
{
/* {user.email ? <Popup hoverable content={user.email} trigger={<span>{renderText(user.email, 24)}</span>} /> : '无'}*/
}
{
/*</Table.Cell>*/
}
<
Table
.
Cell
>
<
Popup
content
=
'剩余额度'
trigger
=
{
<
Label
basic
>
{
renderQuota
(
user
.
quota
)}
<
/Label>} /
>
<
Popup
content
=
'已用额度'
trigger
=
{
<
Label
basic
>
{
renderQuota
(
user
.
used_quota
)}
<
/Label>} /
>
<
Popup
content
=
'请求次数'
trigger
=
{
<
Label
basic
>
{
renderNumber
(
user
.
request_count
)}
<
/Label>} /
>
<
/Table.Cell
>
<
Table
.
Cell
>
{
renderRole
(
user
.
role
)}
<
/Table.Cell
>
<
Table
.
Cell
>
{
renderStatus
(
user
.
status
)}
<
/Table.Cell
>
<
Table
.
Cell
>
<
div
>
<
Button
size
=
{
'small'
}
positive
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'promote'
,
idx
);
}}
disabled
=
{
user
.
role
===
100
}
>
提升
<
/Button
>
<
Button
size
=
{
'small'
}
color
=
{
'yellow'
}
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'demote'
,
idx
);
}}
disabled
=
{
user
.
role
===
100
}
>
降级
<
/Button
>
<
Popup
trigger
=
{
<
Button
size
=
'small'
negative
disabled
=
{
user
.
role
===
100
}
>
删除
<
/Button
>
}
on
=
'click'
flowing
hoverable
>
<
Button
negative
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
'delete'
,
idx
);
}}
>
删除用户
{
user
.
username
}
<
/Button
>
<
/Popup
>
<
Button
size
=
{
'small'
}
onClick
=
{()
=>
{
manageUser
(
user
.
username
,
user
.
status
===
1
?
'disable'
:
'enable'
,
idx
);
}}
disabled
=
{
user
.
role
===
100
}
>
{
user
.
status
===
1
?
'禁用'
:
'启用'
}
<
/Button
>
<
Button
size
=
{
'small'
}
as
=
{
Link
}
to
=
{
'/user/edit/'
+
user
.
id
}
>
编辑
<
/Button
>
<
/div
>
<
/Table.Cell
>
<
/Table.Row
>
);
})}
<
/Table.Body
>
<
Table
.
Footer
>
<
Table
columns
=
{
columns
}
dataSource
=
{
pageData
}
pagination
=
{{
<
Table
.
Row
>
currentPage
:
activePage
,
<
Table
.
HeaderCell
colSpan
=
'7'
>
pageSize
:
ITEMS_PER_PAGE
,
<
Button
size
=
'small'
as
=
{
Link
}
to
=
'/user/add'
loading
=
{
loading
}
>
total
:
userCount
,
添加新的用户
pageSizeOpts
:
[
10
,
20
,
50
,
100
],
// onPageChange: handlePageChange,
<
/Button
>
}}
loading
=
{
loading
}
/
>
<
Pagination
<
Button
theme
=
'light'
type
=
'primary'
style
=
{{
marginRight
:
8
}}
onClick
=
{
floated
=
'right'
()
=>
{
activePage
=
{
activePage
}
setShowAddUser
(
true
);
onPageChange
=
{
onPaginationChange
}
size
=
'small'
siblingRange
=
{
1
}
totalPages
=
{
Math
.
ceil
(
users
.
length
/
ITEMS_PER_PAGE
)
+
(
users
.
length
%
ITEMS_PER_PAGE
===
0
?
1
:
0
)
}
}
/
>
}
>
添加用户
<
/Button
>
<
/Table.HeaderCell
>
<
/
>
<
/Table.Row
>
);
<
/Table.Footer
>
<
/Table
>
<
/
>
);
};
};
export
default
UsersTable
;
export
default
UsersTable
;
web/src/helpers/render.js
View file @
b7ac0b18
import
{
Label
}
from
'semantic-ui-react'
;
import
{
Label
}
from
'semantic-ui-react'
;
import
{
Tag
}
from
"@douyinfe/semi-ui"
;
export
function
renderText
(
text
,
limit
)
{
export
function
renderText
(
text
,
limit
)
{
if
(
text
.
length
>
limit
)
{
if
(
text
.
length
>
limit
)
{
...
@@ -9,18 +10,22 @@ export function renderText(text, limit) {
...
@@ -9,18 +10,22 @@ export function renderText(text, limit) {
export
function
renderGroup
(
group
)
{
export
function
renderGroup
(
group
)
{
if
(
group
===
''
)
{
if
(
group
===
''
)
{
return
<
Label
>
default
<
/Label
>
;
return
<
Tag
size
=
'large'
>
default
<
/Tag
>
;
}
}
let
groups
=
group
.
split
(
','
);
let
groups
=
group
.
split
(
','
);
groups
.
sort
();
groups
.
sort
();
return
<>
return
<>
{
groups
.
map
((
group
)
=>
{
{
groups
.
map
((
group
)
=>
{
if
(
group
===
'vip'
||
group
===
'pro'
)
{
if
(
group
===
'vip'
||
group
===
'pro'
)
{
return
<
Label
color
=
'yellow'
>
{
group
}
<
/Label
>
;
return
<
Tag
size
=
'large'
color
=
'yellow'
>
{
group
}
<
/Tag
>
;
}
else
if
(
group
===
'svip'
||
group
===
'premium'
)
{
}
else
if
(
group
===
'svip'
||
group
===
'premium'
)
{
return
<
Label
color
=
'red'
>
{
group
}
<
/Label>
;
return
<
Tag
size
=
'large'
color
=
'red'
>
{
group
}
<
/Tag>
;
}
if
(
group
===
'default'
)
{
return
<
Tag
size
=
'large'
>
{
group
}
<
/Tag>
;
}
else
{
return
<
Tag
size
=
'large'
color
=
{
stringToColor
(
group
)}
>
{
group
}
<
/Tag>
;
}
}
return
<
Label
>
{
group
}
<
/Label>
;
})}
})}
<
/>
;
<
/>
;
}
}
...
...
web/src/pages/Token/EditToken.js
View file @
b7ac0b18
import
React
,
{
useEffect
,
useRef
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useRef
,
useState
}
from
'react'
;
// import {Button, Form, Header, Message, Segment} from 'semantic-ui-react';
import
{
useParams
,
useNavigate
}
from
'react-router-dom'
;
import
{
useParams
,
useNavigate
}
from
'react-router-dom'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
,
timestamp2string
}
from
'../../helpers'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
,
timestamp2string
}
from
'../../helpers'
;
import
{
renderQuota
,
renderQuotaWithPrompt
}
from
'../../helpers/render'
;
import
{
renderQuota
,
renderQuotaWithPrompt
}
from
'../../helpers/render'
;
...
@@ -247,7 +246,7 @@ const EditToken = (props) => {
...
@@ -247,7 +246,7 @@ const EditToken = (props) => {
disabled
=
{
unlimited_quota
}
disabled
=
{
unlimited_quota
}
/
>
/
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Typography
.
Text
>
{
`新建数量`
}
<
/Typography.Text
>
<
Typography
.
Text
>
新建数量
<
/Typography.Text
>
<
/div
>
<
/div
>
{
!
isEdit
&&
(
{
!
isEdit
&&
(
<
AutoComplete
<
AutoComplete
...
...
web/src/pages/User/AddUser.js
View file @
b7ac0b18
import
React
,
{
useState
}
from
'react'
;
import
React
,
{
useState
}
from
'react'
;
import
{
Button
,
Form
,
Header
,
Segment
}
from
'semantic-ui-react'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
API
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
Title
from
"@douyinfe/semi-ui/lib/es/typography/title"
;
import
{
Button
,
SideSheet
,
Space
,
Input
,
Spin
}
from
"@douyinfe/semi-ui"
;
const
AddUser
=
()
=>
{
const
AddUser
=
(
props
)
=>
{
const
originInputs
=
{
const
originInputs
=
{
username
:
''
,
username
:
''
,
display_name
:
''
,
display_name
:
''
,
password
:
''
,
password
:
''
,
};
};
const
[
inputs
,
setInputs
]
=
useState
(
originInputs
);
const
[
inputs
,
setInputs
]
=
useState
(
originInputs
);
const
{
username
,
display_name
,
password
}
=
inputs
;
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
{
username
,
display_name
,
password
}
=
inputs
;
const
handleInputChange
=
(
e
,
{
name
,
value
}
)
=>
{
const
handleInputChange
=
(
name
,
value
)
=>
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
setInputs
((
inputs
)
=>
({...
inputs
,
[
name
]:
value
}));
};
};
const
submit
=
async
()
=>
{
const
submit
=
async
()
=>
{
if
(
inputs
.
username
===
''
||
inputs
.
password
===
''
)
return
;
setLoading
(
true
);
const
res
=
await
API
.
post
(
`/api/user/`
,
inputs
);
if
(
inputs
.
username
===
''
||
inputs
.
password
===
''
)
return
;
const
{
success
,
message
}
=
res
.
data
;
const
res
=
await
API
.
post
(
`/api/user/`
,
inputs
);
if
(
success
)
{
const
{
success
,
message
}
=
res
.
data
;
showSuccess
(
'用户账户创建成功!'
);
if
(
success
)
{
setInputs
(
originInputs
);
showSuccess
(
'用户账户创建成功!'
);
}
else
{
setInputs
(
originInputs
);
showError
(
message
);
props
.
refresh
();
props
.
handleClose
();
}
else
{
showError
(
message
);
}
setLoading
(
false
);
};
const
handleCancel
=
()
=>
{
props
.
handleClose
();
}
}
};
return
(
return
(
<>
<>
<
Segment
>
<
SideSheet
<
Header
as
=
"h3"
>
创建新用户账户
<
/Header
>
placement
=
{
'left'
}
<
Form
autoComplete
=
"off"
>
title
=
{
<
Title
level
=
{
3
}
>
{
'添加用户'
}
<
/Title>
}
<
Form
.
Field
>
headerStyle
=
{{
borderBottom
:
'1px solid var(--semi-color-border)'
}}
<
Form
.
Input
bodyStyle
=
{{
borderBottom
:
'1px solid var(--semi-color-border)'
}}
label
=
"用户名"
visible
=
{
props
.
visible
}
name
=
"username"
footer
=
{
placeholder
=
{
'请输入用户名'
}
<
div
style
=
{{
display
:
'flex'
,
justifyContent
:
'flex-end'
}}
>
onChange
=
{
handleInputChange
}
<
Space
>
value
=
{
username
}
<
Button
theme
=
'solid'
size
=
{
'large'
}
onClick
=
{
submit
}
>
提交
<
/Button
>
autoComplete
=
"off"
<
Button
theme
=
'solid'
size
=
{
'large'
}
type
=
{
'tertiary'
}
onClick
=
{
handleCancel
}
>
取消
<
/Button
>
required
<
/Space
>
/>
<
/div
>
<
/Form.Field
>
}
<
Form
.
Field
>
closeIcon
=
{
null
}
<
Form
.
Input
onCancel
=
{()
=>
handleCancel
()}
label
=
"显示名称"
width
=
{
isMobile
()
?
'100%'
:
600
}
name
=
"display_name"
>
placeholder
=
{
'请输入显示名称'
}
<
Spin
spinning
=
{
loading
}
>
onChange
=
{
handleInputChange
}
<
Input
value
=
{
display_name
}
style
=
{{
marginTop
:
20
}}
autoComplete
=
"off"
label
=
"用户名"
/>
name
=
"username"
<
/Form.Field
>
addonBefore
=
{
'用户名'
}
<
Form
.
Field
>
placeholder
=
{
'请输入用户名'
}
<
Form
.
Input
onChange
=
{
value
=>
handleInputChange
(
'username'
,
value
)}
label
=
"密码"
value
=
{
username
}
name
=
"password"
autoComplete
=
"off"
type
=
{
'password'
}
/>
placeholder
=
{
'请输入密码'
}
<
Input
onChange
=
{
handleInputChange
}
style
=
{{
marginTop
:
20
}}
value
=
{
password
}
addonBefore
=
{
'显示名'
}
autoComplete
=
"off"
label
=
"显示名称"
required
name
=
"display_name"
/>
autoComplete
=
"off"
<
/Form.Field
>
placeholder
=
{
'请输入显示名称'
}
<
Button
positive
type
=
{
'submit'
}
onClick
=
{
submit
}
>
onChange
=
{
value
=>
handleInputChange
(
'display_name'
,
value
)}
提交
value
=
{
display_name
}
<
/Button
>
/
>
<
/Form
>
<
Input
<
/Segment
>
style
=
{{
marginTop
:
20
}}
<
/
>
label
=
"密 码"
);
name
=
"password"
type
=
{
'password'
}
addonBefore
=
{
'密码'
}
placeholder
=
{
'请输入密码'
}
onChange
=
{
value
=>
handleInputChange
(
'password'
,
value
)}
value
=
{
password
}
autoComplete
=
"off"
/>
<
/Spin
>
<
/SideSheet
>
<
/
>
);
};
};
export
default
AddUser
;
export
default
AddUser
;
web/src/pages/User/EditUser.js
View file @
b7ac0b18
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Button
,
Form
,
Header
,
Segment
}
from
'semantic-ui-react'
;
import
{
useParams
,
useNavigate
}
from
'react-router-dom'
;
import
{
useParams
,
useNavigate
}
from
'react-router-dom'
;
import
{
API
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
renderQuota
,
renderQuotaWithPrompt
}
from
'../../helpers/render'
;
import
{
renderQuota
,
renderQuotaWithPrompt
}
from
'../../helpers/render'
;
import
Title
from
"@douyinfe/semi-ui/lib/es/typography/title"
;
import
{
SideSheet
,
Space
,
Button
,
Spin
,
Input
,
Typography
,
Select
,
Divider
}
from
"@douyinfe/semi-ui"
;
const
EditUser
=
()
=>
{
const
EditUser
=
(
props
)
=>
{
const
params
=
useParams
();
const
userId
=
props
.
editingUser
.
id
;
const
userId
=
params
.
id
;
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
inputs
,
setInputs
]
=
useState
({
const
[
inputs
,
setInputs
]
=
useState
({
username
:
''
,
username
:
''
,
...
@@ -21,15 +21,14 @@ const EditUser = () => {
...
@@ -21,15 +21,14 @@ const EditUser = () => {
const
[
groupOptions
,
setGroupOptions
]
=
useState
([]);
const
[
groupOptions
,
setGroupOptions
]
=
useState
([]);
const
{
username
,
display_name
,
password
,
github_id
,
wechat_id
,
email
,
quota
,
group
}
=
const
{
username
,
display_name
,
password
,
github_id
,
wechat_id
,
email
,
quota
,
group
}
=
inputs
;
inputs
;
const
handleInputChange
=
(
e
,
{
name
,
value
}
)
=>
{
const
handleInputChange
=
(
name
,
value
)
=>
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
};
};
const
fetchGroups
=
async
()
=>
{
const
fetchGroups
=
async
()
=>
{
try
{
try
{
let
res
=
await
API
.
get
(
`/api/group/`
);
let
res
=
await
API
.
get
(
`/api/group/`
);
setGroupOptions
(
res
.
data
.
data
.
map
((
group
)
=>
({
setGroupOptions
(
res
.
data
.
data
.
map
((
group
)
=>
({
key
:
group
,
label
:
group
,
text
:
group
,
value
:
group
,
value
:
group
,
})));
})));
}
catch
(
error
)
{
}
catch
(
error
)
{
...
@@ -38,9 +37,10 @@ const EditUser = () => {
...
@@ -38,9 +37,10 @@ const EditUser = () => {
};
};
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
handleCancel
=
()
=>
{
const
handleCancel
=
()
=>
{
navigate
(
"/setting"
);
props
.
handleClose
(
);
}
}
const
loadUser
=
async
()
=>
{
const
loadUser
=
async
()
=>
{
setLoading
(
true
);
let
res
=
undefined
;
let
res
=
undefined
;
if
(
userId
)
{
if
(
userId
)
{
res
=
await
API
.
get
(
`/api/user/
${
userId
}
`
);
res
=
await
API
.
get
(
`/api/user/
${
userId
}
`
);
...
@@ -56,14 +56,16 @@ const EditUser = () => {
...
@@ -56,14 +56,16 @@ const EditUser = () => {
}
}
setLoading
(
false
);
setLoading
(
false
);
};
};
useEffect
(()
=>
{
useEffect
(()
=>
{
loadUser
().
then
();
loadUser
().
then
();
if
(
userId
)
{
if
(
userId
)
{
fetchGroups
().
then
();
fetchGroups
().
then
();
}
}
},
[]);
},
[
props
.
editingUser
.
id
]);
const
submit
=
async
()
=>
{
const
submit
=
async
()
=>
{
setLoading
(
true
);
let
res
=
undefined
;
let
res
=
undefined
;
if
(
userId
)
{
if
(
userId
)
{
let
data
=
{
...
inputs
,
id
:
parseInt
(
userId
)
};
let
data
=
{
...
inputs
,
id
:
parseInt
(
userId
)
};
...
@@ -77,112 +79,134 @@ const EditUser = () => {
...
@@ -77,112 +79,134 @@ const EditUser = () => {
const
{
success
,
message
}
=
res
.
data
;
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
if
(
success
)
{
showSuccess
(
'用户信息更新成功!'
);
showSuccess
(
'用户信息更新成功!'
);
props
.
refresh
();
props
.
handleClose
();
}
else
{
}
else
{
showError
(
message
);
showError
(
message
);
}
}
setLoading
(
false
);
};
};
return
(
return
(
<>
<>
<
Segment
loading
=
{
loading
}
>
<
SideSheet
<
Header
as
=
'h3'
>
更新用户信息
<
/Header
>
placement
=
{
'right'
}
<
Form
autoComplete
=
'new-password'
>
title
=
{
<
Title
level
=
{
3
}
>
{
'编辑用户'
}
<
/Title>
}
<
Form
.
Field
>
headerStyle
=
{{
borderBottom
:
'1px solid var(--semi-color-border)'
}}
<
Form
.
Input
bodyStyle
=
{{
borderBottom
:
'1px solid var(--semi-color-border)'
}}
label
=
'用户名'
visible
=
{
props
.
visible
}
name
=
'username'
footer
=
{
placeholder
=
{
'请输入新的用户名'
}
<
div
style
=
{{
display
:
'flex'
,
justifyContent
:
'flex-end'
}}
>
onChange
=
{
handleInputChange
}
<
Space
>
value
=
{
username
}
<
Button
theme
=
'solid'
size
=
{
'large'
}
onClick
=
{
submit
}
>
提交
<
/Button
>
autoComplete
=
'new-password'
<
Button
theme
=
'solid'
size
=
{
'large'
}
type
=
{
'tertiary'
}
onClick
=
{
handleCancel
}
>
取消
<
/Button
>
/>
<
/Space
>
<
/Form.Field
>
<
/div
>
<
Form
.
Field
>
}
<
Form
.
Input
closeIcon
=
{
null
}
label
=
'密码'
onCancel
=
{()
=>
handleCancel
()}
name
=
'password'
width
=
{
isMobile
()
?
'100%'
:
600
}
type
=
{
'password'
}
>
placeholder
=
{
'请输入新的密码,最短 8 位'
}
<
Spin
spinning
=
{
loading
}
>
onChange
=
{
handleInputChange
}
<
div
style
=
{{
marginTop
:
20
}}
>
value
=
{
password
}
<
Typography
.
Text
>
用户名
<
/Typography.Text
>
autoComplete
=
'new-password'
<
/div
>
/>
<
Input
<
/Form.Field
>
label
=
'用户名'
<
Form
.
Field
>
name
=
'username'
<
Form
.
Input
placeholder
=
{
'请输入新的用户名'
}
label
=
'显示名称'
onChange
=
{
value
=>
handleInputChange
(
'username'
,
value
)}
name
=
'display_name'
value
=
{
username
}
placeholder
=
{
'请输入新的显示名称'
}
autoComplete
=
'new-password'
onChange
=
{
handleInputChange
}
/>
value
=
{
display_name
}
<
div
style
=
{{
marginTop
:
20
}}
>
autoComplete
=
'new-password'
<
Typography
.
Text
>
密码
<
/Typography.Text
>
/>
<
/div
>
<
/Form.Field
>
<
Input
label
=
'密码'
name
=
'password'
type
=
{
'password'
}
placeholder
=
{
'请输入新的密码,最短 8 位'
}
onChange
=
{
value
=>
handleInputChange
(
'password'
,
value
)}
value
=
{
password
}
autoComplete
=
'new-password'
/>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Typography
.
Text
>
显示名称
<
/Typography.Text
>
<
/div
>
<
Input
label
=
'显示名称'
name
=
'display_name'
placeholder
=
{
'请输入新的显示名称'
}
onChange
=
{
value
=>
handleInputChange
(
'display_name'
,
value
)}
value
=
{
display_name
}
autoComplete
=
'new-password'
/>
{
{
userId
&&
<>
userId
&&
<>
<
Form
.
Field
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Form
.
Dropdown
<
Typography
.
Text
>
分组
<
/Typography.Text
>
label
=
'分组'
<
/div
>
placeholder
=
{
'请选择分组'
}
<
Select
name
=
'group'
placeholder
=
{
'请选择分组'
}
fluid
name
=
'group'
search
fluid
selection
search
allowAdditions
selection
additionLabel
=
{
'请在系统设置页面编辑分组倍率以添加新的分组:'
}
allowAdditions
onChange
=
{
handleInputChange
}
additionLabel
=
{
'请在系统设置页面编辑分组倍率以添加新的分组:'
}
value
=
{
inputs
.
group
}
onChange
=
{
value
=>
handleInputChange
(
'group'
,
value
)
}
autoComplete
=
'new-password'
value
=
{
inputs
.
group
}
options
=
{
groupOptions
}
autoComplete
=
'new-password'
/
>
optionList
=
{
groupOptions
}
<
/Form.Field
>
/
>
<
Form
.
Field
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Form
.
Input
<
Typography
.
Text
>
{
`剩余额度
${
renderQuotaWithPrompt
(
quota
)}
`
}
<
/Typography.Text
>
label
=
{
`剩余额度
${
renderQuotaWithPrompt
(
quota
)}
`
}
<
/div
>
name
=
'quota'
<
Input
placeholder
=
{
'请输入新的剩余额度'
}
name
=
'quota'
onChange
=
{
handleInputChange
}
placeholder
=
{
'请输入新的剩余额度'
}
value
=
{
quota
}
onChange
=
{
value
=>
handleInputChange
(
'quota'
,
value
)
}
type
=
{
'number'
}
value
=
{
quota
}
autoComplete
=
'new-password'
type
=
{
'number'
}
/>
autoComplete
=
'new-password'
<
/Form.Field
>
/
>
<
/
>
<
/
>
}
}
<
Form
.
Field
>
<
Divider
style
=
{{
marginTop
:
20
}}
>
以下信息不可修改
<
/Divider
>
<
Form
.
Input
<
div
style
=
{{
marginTop
:
20
}}
>
label
=
'已绑定的 GitHub 账户'
<
Typography
.
Text
>
已绑定的
GitHub
账户
<
/Typography.Text
>
name
=
'github_id'
<
/div
>
value
=
{
github_id
}
<
Input
autoComplete
=
'new-passwor
d'
name
=
'github_i
d'
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
value
=
{
github_id
}
readOnly
autoComplete
=
'new-password'
/>
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
<
/Form.Field
>
readonly
<
Form
.
Field
>
/
>
<
Form
.
Input
<
div
style
=
{{
marginTop
:
20
}}
>
label
=
'已绑定的微信账户'
<
Typography
.
Text
>
已绑定的微信账户
<
/Typography.Text
>
name
=
'wechat_id'
<
/div
>
value
=
{
wechat_id
}
<
Input
autoComplete
=
'new-passwor
d'
name
=
'wechat_i
d'
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
value
=
{
wechat_id
}
readOnly
autoComplete
=
'new-password'
/>
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
<
/Form.Field
>
readonly
<
Form
.
Field
>
/
>
<
Form
.
Input
<
div
style
=
{{
marginTop
:
20
}}
>
label
=
'已绑定的邮箱账户'
<
Typography
.
Text
>
已绑定的邮箱账户
<
/Typography.Text
>
name
=
'email'
<
/div
>
value
=
{
email
}
<
Input
autoComplete
=
'new-password
'
name
=
'email
'
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
value
=
{
email
}
readOnly
autoComplete
=
'new-password'
/>
placeholder
=
'此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
<
/Form.Field
>
readonly
<
Button
onClick
=
{
handleCancel
}
>
取消
<
/Button
>
/
>
<
Button
positive
onClick
=
{
submit
}
>
提交
<
/Butto
n
>
<
/Spi
n
>
<
/Form
>
<
/S
egmen
t
>
<
/S
ideShee
t
>
<
/
>
<
/
>
);
);
};
};
...
...
web/src/pages/User/index.js
View file @
b7ac0b18
import
React
from
'react'
;
import
React
from
'react'
;
import
{
Segment
,
Header
}
from
'semantic-ui-react'
;
import
{
Segment
,
Header
}
from
'semantic-ui-react'
;
import
UsersTable
from
'../../components/UsersTable'
;
import
UsersTable
from
'../../components/UsersTable'
;
import
{
Layout
}
from
"@douyinfe/semi-ui"
;
const
User
=
()
=>
(
const
User
=
()
=>
(
<>
<>
<
Segment
>
<
Layout
>
<
Header
as
=
'h3'
>
管理用户
<
/Header
>
<
Layout
.
Header
>
<
UsersTable
/>
<
h3
>
管理用户
<
/h3
>
<
/Segment
>
<
/Layout.Header
>
<
Layout
.
Content
>
<
UsersTable
/>
<
/Layout.Content
>
<
/Layout
>
<
/
>
<
/
>
);
);
...
...
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