改点样式

This commit is contained in:
wood 2023-11-16 05:02:34 +08:00
parent 05ee4d5977
commit 5e503d671e
11 changed files with 150 additions and 42 deletions

View File

@ -174,7 +174,7 @@ func UpdateToken(c *gin.Context) {
if len(token.Name) > 30 { if len(token.Name) > 30 {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": false, "success": false,
"message": "Key名称过长", "message": "Key名称过长,不能超过30位",
}) })
return return
} }

View File

@ -553,6 +553,7 @@ func CreateUser(c *gin.Context) {
type ManageRequest struct { type ManageRequest struct {
Username string `json:"username"` Username string `json:"username"`
Action string `json:"action"`
NewGroup string `json:"newGroup"` NewGroup string `json:"newGroup"`
} }
@ -591,8 +592,68 @@ func ManageUser(c *gin.Context) {
return return
} }
// 更新用户分组 switch req.Action {
case "disable":
user.Status = common.UserStatusDisabled
if user.Role == common.RoleRootUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无法禁用超级管理员用户",
})
return
}
case "enable":
user.Status = common.UserStatusEnabled
case "delete":
if user.Role == common.RoleRootUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无法删除超级管理员用户",
})
return
}
if err := user.Delete(); err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
case "promote":
if myRole != common.RoleRootUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "普通管理员用户无法提升其他用户为管理员",
})
return
}
if user.Role >= common.RoleAdminUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "该用户已经是管理员",
})
return
}
user.Role = common.RoleAdminUser
case "demote":
if user.Role == common.RoleRootUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无法降级超级管理员用户",
})
return
}
if user.Role == common.RoleCommonUser {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "该用户已经是普通用户",
})
return
}
user.Role = common.RoleCommonUser
case "changeGroup":
user.Group = req.NewGroup user.Group = req.NewGroup
}
if err := user.Update(false); err != nil { if err := user.Update(false); err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
@ -617,6 +678,7 @@ func ManageUser(c *gin.Context) {
} }
func EmailBind(c *gin.Context) { func EmailBind(c *gin.Context) {
email := c.Query("email") email := c.Query("email")
code := c.Query("code") code := c.Query("code")

View File

@ -86,7 +86,7 @@ func Redeem(key string, userId int) (quota int, upgradedToVIP bool, err error) {
} }
// 检查是否需要升级为 VIP // 检查是否需要升级为 VIP
if user.Group != "vip" && user.Quota >= 5*500000 { if user.Group != "vip" && user.Group != "svip" && user.Quota >= 5*500000 {
// 升级用户到 VIP // 升级用户到 VIP
err = DB.Model(&User{}).Where("id = ?", userId).Update("group", "vip").Error err = DB.Model(&User{}).Where("id = ?", userId).Update("group", "vip").Error
if err != nil { if err != nil {

View File

@ -466,7 +466,7 @@ const ChannelsTable = () => {
manageChannel(channel.id, 'delete', idx); manageChannel(channel.id, 'delete', idx);
}} }}
> >
删除渠道 {channel.name} 确认删除 {channel.name}
</Button> </Button>
</Popup> </Popup>

View File

@ -6,6 +6,8 @@ import { Button, Container, Dropdown, Icon, Menu, Segment } from 'semantic-ui-re
import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../helpers'; import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../helpers';
import '../index.css'; import '../index.css';
// Header Buttons // Header Buttons
let headerButtons = [ let headerButtons = [
{ {
@ -83,6 +85,8 @@ const Header = () => {
const [showSidebar, setShowSidebar] = useState(false); const [showSidebar, setShowSidebar] = useState(false);
const systemName = getSystemName(); const systemName = getSystemName();
const logo = getLogo(); const logo = getLogo();
const [activeItem, setActiveItem] = useState(null);
async function logout() { async function logout() {
setShowSidebar(false); setShowSidebar(false);
@ -100,6 +104,7 @@ const Header = () => {
const renderButtons = (isMobile) => { const renderButtons = (isMobile) => {
return headerButtons.map((button) => { return headerButtons.map((button) => {
if (button.admin && !isAdmin()) return <></>; if (button.admin && !isAdmin()) return <></>;
const isActive = activeItem === button.name;
if (isMobile) { if (isMobile) {
return ( return (
<Menu.Item <Menu.Item
@ -113,14 +118,22 @@ const Header = () => {
); );
} }
return ( return (
<Menu.Item key={button.name} as={Link} to={button.to}> <Menu.Item
<Icon name={button.icon} /> key={button.name}
as={Link}
to={button.to}
onClick={() => setActiveItem(button.name)}
>
<div className={isActive ? 'active-menu-item' : ''}>
{button.icon && <Icon name={button.icon} />}
{button.name} {button.name}
</div>
</Menu.Item> </Menu.Item>
); );
}); });
}; };
if (isMobile()) { if (isMobile()) {
return ( return (
<> <>

View File

@ -158,7 +158,12 @@ const LogsTable = () => {
const getLogStat = async () => { const getLogStat = async () => {
let localStartTimestamp = Date.parse(start_timestamp) / 1000; let localStartTimestamp = Date.parse(start_timestamp) / 1000;
let localEndTimestamp = Date.parse(end_timestamp) / 1000; let localEndTimestamp = Date.parse(end_timestamp) / 1000;
let res = await API.get(`/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`); let url = `/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`;
let res = await API.get(url);
if (!res || !res.data) {
showError('No response or response data');
return;
}
const { success, message, data } = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
setStat(data); setStat(data);
@ -185,6 +190,10 @@ const LogsTable = () => {
url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`; url = `/api/log/self/?p=${startIdx}&type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`;
} }
const res = await API.get(url); const res = await API.get(url);
if (!res || !res.data) {
showError('No response or response data');
return;
}
const { success, message, data } = res.data; const { success, message, data } = res.data;
if (success) { if (success) {
if (startIdx === 0) { if (startIdx === 0) {

View File

@ -365,7 +365,8 @@ const TokensTable = () => {
manageToken(token.id, 'delete', idx); manageToken(token.id, 'delete', idx);
}} }}
style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }} style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }}
/> >
确认删除key: {token.name}</Button>
</Popup> </Popup>
<Button <Button
size={'small'} size={'small'}

View File

@ -319,7 +319,9 @@ const UsersTable = () => {
onClick={() => { onClick={() => {
manageUser(user.username, 'delete', idx); manageUser(user.username, 'delete', idx);
}} }}
/> >
确认删除{user.username}
</Button>
</Popup> </Popup>
<Button <Button
size={'small'} size={'small'}

View File

@ -188,7 +188,7 @@ body::-webkit-scrollbar {
} }
code { code {
font-family: "CZL",source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; font-family: "CZL", source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
} }
.main-content { .main-content {
@ -199,24 +199,43 @@ code {
font-size: 1em !important; font-size: 1em !important;
} }
.custom-footer { /* .custom-footer {
font-size: 1.1em; font-size: 1.1em;
} } */
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
.hide-on-mobile { .hide-on-mobile {
display: none !important; display: none !important;
} }
} }
@media only screen and (min-width: 1600px){
.main-content { @media only screen and (min-width: 1200px) {
.main-content {
width: 1300px !important; width: 1300px !important;
} }
.ui.menu .ui.container {
width: 1608px !important; /* 或者你想要设置的宽度 */ .ui.menu .ui.container {
height: 80px;
font-size: 1em;
width: 90% !important;
/* 或者你想要设置的宽度 */
/* padding: 0 !important; */ /* padding: 0 !important; */
} }
.ui.menu .item>img:not(.ui) {
width: 4.5rem !important;
}
} }
.active-menu-item {
background-color: var(--czl-grayD) !important;
color: white !important;
border-radius: 16px !important;
padding: 10px 16px !important;
display: flex;
align-items: center;
}

View File

@ -10,7 +10,7 @@ const EditToken = () => {
const isEdit = tokenId !== undefined; const isEdit = tokenId !== undefined;
const [loading, setLoading] = useState(isEdit); const [loading, setLoading] = useState(isEdit);
const originInputs = { const originInputs = {
name: '', name: '默认key',
remain_quota: isEdit ? 0 : 100, remain_quota: isEdit ? 0 : 100,
expired_time: -1, expired_time: -1,
unlimited_quota: false unlimited_quota: false
@ -86,11 +86,12 @@ const EditToken = () => {
const { success, message } = res.data; const { success, message } = res.data;
if (success) { if (success) {
if (isEdit) { if (isEdit) {
showSuccess('Key更新成功'); showSuccess('Key更新成功');
} else { } else {
showSuccess('Key创建成功请在列表页面点击复制获取Key'); showSuccess('Key创建成功');
setInputs(originInputs); setInputs(originInputs);
} }
navigate("/token"); // 在这里添加导航到 "/token"
} else { } else {
showError(message); showError(message);
} }

View File

@ -91,6 +91,16 @@ const EditUser = () => {
<Segment loading={loading}> <Segment loading={loading}>
<Header as='h3'>更新用户信息</Header> <Header as='h3'>更新用户信息</Header>
<Form autoComplete='new-password'> <Form autoComplete='new-password'>
<Form.Field>
<Form.Input
label='昵称'
name='display_name'
placeholder={'请输入新的昵称'}
onChange={handleInputChange}
value={display_name}
autoComplete='new-password'
/>
</Form.Field>
<Form.Field> <Form.Field>
<Form.Input <Form.Input
label='用户名' label='用户名'
@ -112,16 +122,7 @@ const EditUser = () => {
autoComplete='new-password' autoComplete='new-password'
/> />
</Form.Field> </Form.Field>
<Form.Field>
<Form.Input
label='显示名称'
name='display_name'
placeholder={'请输入新的显示名称'}
onChange={handleInputChange}
value={display_name}
autoComplete='new-password'
/>
</Form.Field>
{ {
userId && <> userId && <>
<Form.Field> <Form.Field>
@ -153,7 +154,7 @@ const EditUser = () => {
</Form.Field> </Form.Field>
</> </>
} }
<Form.Field> {/* <Form.Field>
<Form.Input <Form.Input
label='已绑定的 GitHub 账户' label='已绑定的 GitHub 账户'
name='github_id' name='github_id'
@ -162,8 +163,8 @@ const EditUser = () => {
placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改' placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
readOnly readOnly
/> />
</Form.Field> </Form.Field> */}
<Form.Field> {/* <Form.Field>
<Form.Input <Form.Input
label='已绑定的微信账户' label='已绑定的微信账户'
name='wechat_id' name='wechat_id'
@ -172,8 +173,8 @@ const EditUser = () => {
placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改' placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
readOnly readOnly
/> />
</Form.Field> </Form.Field> */}
<Form.Field> {/* <Form.Field>
<Form.Input <Form.Input
label='已绑定的邮箱账户' label='已绑定的邮箱账户'
name='email' name='email'
@ -182,7 +183,7 @@ const EditUser = () => {
placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改' placeholder='此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改'
readOnly readOnly
/> />
</Form.Field> </Form.Field> */}
<Button onClick={handleCancel}>取消</Button> <Button onClick={handleCancel}>取消</Button>
<Button positive onClick={submit}>提交</Button> <Button positive onClick={submit}>提交</Button>
</Form> </Form>