一些按钮改为图标
This commit is contained in:
parent
0f51100890
commit
05ee4d5977
@ -442,7 +442,7 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
logContent = fmt.Sprintf("单价: $%.6g/1k tokens", inputPrice)
|
logContent = fmt.Sprintf("单价: $%.6g/1k tokens", inputPrice)
|
||||||
} else {
|
} else {
|
||||||
outputPrice := inputPrice * completionRatio
|
outputPrice := inputPrice * completionRatio
|
||||||
logContent = fmt.Sprintf("输入: $%.6g/1k tokens, 输出: $%.6g/1k tokens", inputPrice, outputPrice)
|
logContent = fmt.Sprintf("输入:$%.6g/1k tokens, 输出:$%.6g/1k tokens", inputPrice, outputPrice)
|
||||||
}
|
}
|
||||||
model.RecordConsumeLog(ctx, userId, channelId, promptTokens, completionTokens, textRequest.Model, tokenName, quota, logContent)
|
model.RecordConsumeLog(ctx, userId, channelId, promptTokens, completionTokens, textRequest.Model, tokenName, quota, logContent)
|
||||||
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(userId, quota)
|
||||||
|
@ -442,20 +442,18 @@ const ChannelsTable = () => {
|
|||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
|
icon='play' // 示例图标
|
||||||
size={'small'}
|
size={'small'}
|
||||||
positive
|
positive
|
||||||
style={{ backgroundColor: 'var(--czl-success-color)' }}
|
style={{ backgroundColor: 'var(--czl-success-color)' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
testChannel(channel.id, channel.name, idx);
|
testChannel(channel.id, channel.name, idx);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
测试
|
|
||||||
</Button>
|
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' negative style={{ backgroundColor: 'var(--czl-error-color)' }}>
|
<Button icon='trash' size='small' negative style={{ backgroundColor: 'var(--czl-error-color)' }} />
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
flowing
|
flowing
|
||||||
@ -471,9 +469,11 @@ const ChannelsTable = () => {
|
|||||||
删除渠道 {channel.name}
|
删除渠道 {channel.name}
|
||||||
</Button>
|
</Button>
|
||||||
</Popup>
|
</Popup>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
negative
|
icon={channel.status === 1 ? 'ban' : 'check'} // 示例图标
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
negative
|
||||||
style={{ backgroundColor: 'var(--czl-warning-color)' }}
|
style={{ backgroundColor: 'var(--czl-warning-color)' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
manageChannel(
|
manageChannel(
|
||||||
@ -482,20 +482,18 @@ const ChannelsTable = () => {
|
|||||||
idx
|
idx
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{channel.status === 1 ? '禁用' : '启用'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
negative
|
icon='edit' // 示例图标
|
||||||
size={'small'}
|
size={'small'}
|
||||||
as={Link}
|
as={Link}
|
||||||
to={'/channel/edit/' + channel.id}
|
to={'/channel/edit/' + channel.id}
|
||||||
style={{ backgroundColor: 'var(--czl-primary-color)' }}
|
style={{ backgroundColor: 'var(--czl-primary-color)' }}
|
||||||
>
|
/>
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -43,7 +43,7 @@ let headerButtons = [
|
|||||||
{
|
{
|
||||||
name: '用户',
|
name: '用户',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
icon: 'user',
|
icon: 'users',
|
||||||
color: 'var(--czl-primary-color)',
|
color: 'var(--czl-primary-color)',
|
||||||
admin: true
|
admin: true
|
||||||
},
|
},
|
||||||
@ -54,9 +54,9 @@ let headerButtons = [
|
|||||||
color: 'var(--czl-primary-color)'
|
color: 'var(--czl-primary-color)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '设置',
|
name: '个人',
|
||||||
to: '/setting',
|
to: '/setting',
|
||||||
icon: 'setting',
|
icon: 'user',
|
||||||
color: 'var(--czl-primary-color)'
|
color: 'var(--czl-primary-color)'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -476,6 +476,8 @@ const LogsTable = () => {
|
|||||||
Math.ceil(logs.length / ITEMS_PER_PAGE) +
|
Math.ceil(logs.length / ITEMS_PER_PAGE) +
|
||||||
(logs.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
(logs.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
firstItem={null} // 不显示第一页按钮
|
||||||
|
lastItem={null} // 不显示最后一页按钮
|
||||||
/>
|
/>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
@ -32,6 +32,12 @@ const PersonalSetting = () => {
|
|||||||
const [usedQuota, setUsedQuota] = useState(null);
|
const [usedQuota, setUsedQuota] = useState(null);
|
||||||
const [requestCount, setRequestCount] = useState(null);
|
const [requestCount, setRequestCount] = useState(null);
|
||||||
const [githubID, setGithubID] = useState(null);
|
const [githubID, setGithubID] = useState(null);
|
||||||
|
const [username, setUsername] = useState(null);
|
||||||
|
const [display_name, setDisplay_name] = useState(null);
|
||||||
|
const [email, setEmail] = useState(null);
|
||||||
|
|
||||||
|
const [modelsByOwner, setModelsByOwner] = useState({});
|
||||||
|
const [key, setKey] = useState("");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -52,7 +58,10 @@ const PersonalSetting = () => {
|
|||||||
(async () => {
|
(async () => {
|
||||||
const res = await API.get(`/api/user/self`);
|
const res = await API.get(`/api/user/self`);
|
||||||
if (res.data.success) {
|
if (res.data.success) {
|
||||||
|
setDisplay_name(res.data.data.display_name);
|
||||||
|
setUsername(res.data.data.username);
|
||||||
setUserGroup(res.data.data.group);
|
setUserGroup(res.data.data.group);
|
||||||
|
setEmail(res.data.data.email);
|
||||||
setQuota(res.data.data.quota);
|
setQuota(res.data.data.quota);
|
||||||
setUsedQuota(res.data.data.used_quota);
|
setUsedQuota(res.data.data.used_quota);
|
||||||
setRequestCount(res.data.data.request_count);
|
setRequestCount(res.data.data.request_count);
|
||||||
@ -64,6 +73,66 @@ const PersonalSetting = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
const quotaPerUnit = parseInt(localStorage.getItem("quota_per_unit"));
|
const quotaPerUnit = parseInt(localStorage.getItem("quota_per_unit"));
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// // 获取用户的第一个key
|
||||||
|
// const fetchFirstKey = async () => {
|
||||||
|
// try {
|
||||||
|
// const tokenRes = await API.get('/api/token/?p=0');
|
||||||
|
// if (tokenRes.data.success && tokenRes.data.data.length > 0) {
|
||||||
|
// const firstKey = tokenRes.data.data[0].key;
|
||||||
|
// setKey(firstKey);
|
||||||
|
// fetchModels(firstKey);
|
||||||
|
// } else {
|
||||||
|
// // 如果没有获取到key,显示提示消息
|
||||||
|
// showError('请先创建一个key');
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// showError('获取key失败');
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // 获取模型信息
|
||||||
|
// const fetchModels = async (key) => {
|
||||||
|
// try {
|
||||||
|
// const modelsRes = await API.get('/v1/models', {
|
||||||
|
// headers: { Authorization: `Bearer sk-${key}` },
|
||||||
|
// });
|
||||||
|
// if (modelsRes.data && modelsRes.data.data) {
|
||||||
|
// const models = modelsRes.data.data;
|
||||||
|
// const groupedByOwner = models.reduce((acc, model) => {
|
||||||
|
// const owner = model.owned_by.toUpperCase();
|
||||||
|
// if (!acc[owner]) {
|
||||||
|
// acc[owner] = [];
|
||||||
|
// }
|
||||||
|
// acc[owner].push(model.id);
|
||||||
|
// return acc;
|
||||||
|
// }, {});
|
||||||
|
|
||||||
|
// // 对owners进行排序
|
||||||
|
// const sortedOwners = Object.keys(groupedByOwner).sort();
|
||||||
|
// const sortedGroupedByOwner = {};
|
||||||
|
// sortedOwners.forEach(owner => {
|
||||||
|
// // 对每个owner的models进行排序
|
||||||
|
// sortedGroupedByOwner[owner] = groupedByOwner[owner].sort();
|
||||||
|
// });
|
||||||
|
// setModelsByOwner(sortedGroupedByOwner);
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// showError('获取模型失败');
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// fetchFirstKey();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// // 定义一个固定宽度的label样式
|
||||||
|
// const fixedWidthLabelStyle = {
|
||||||
|
// display: 'inline-block',
|
||||||
|
// minWidth: '150px', // 根据需要调整宽度
|
||||||
|
// textAlign: 'center',
|
||||||
|
// margin: '5px',
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
const transformUserGroup = (group) => {
|
const transformUserGroup = (group) => {
|
||||||
switch (group) {
|
switch (group) {
|
||||||
@ -207,25 +276,69 @@ const PersonalSetting = () => {
|
|||||||
return (
|
return (
|
||||||
<div style={{ lineHeight: '40px' }}>
|
<div style={{ lineHeight: '40px' }}>
|
||||||
<Header as='h3'>使用信息</Header>
|
<Header as='h3'>使用信息</Header>
|
||||||
{userGroup && (
|
<div style={{ marginBottom: '1em' }}>
|
||||||
<Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
{display_name && (
|
||||||
用户组:{transformUserGroup(userGroup)}
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
</Label>
|
昵称:{transformUserGroup(display_name)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
{username && (
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
用户名:{transformUserGroup(username)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
邮箱:{email ? email : "未绑定"}
|
||||||
|
</Label>
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
GitHub 账号:{githubID ? githubID : "未绑定"}
|
||||||
|
</Label>
|
||||||
|
<br></br>
|
||||||
|
{userGroup && (
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
用户组:{transformUserGroup(userGroup)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
{quota !== null && quotaPerUnit && (
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
额度:${(quota / quotaPerUnit).toFixed(2)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
{usedQuota !== null && quotaPerUnit && (
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
已用额度:${(usedQuota / quotaPerUnit).toFixed(2)}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
{requestCount !== null && (
|
||||||
|
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
||||||
|
调用次数:{requestCount}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
{/* <Header as='h3'>模型支持度</Header>
|
||||||
|
{Object.keys(modelsByOwner).length > 0 ? (
|
||||||
|
Object.entries(modelsByOwner).map(([owner, models], index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<Header as='h4'>{owner}</Header>
|
||||||
|
<div>
|
||||||
|
{models.map((modelId, index) => (
|
||||||
|
<Label key={index} style={fixedWidthLabelStyle}>
|
||||||
|
{modelId}
|
||||||
|
</Label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Message info>
|
||||||
|
<Message.Header>尚未绑定模型</Message.Header>
|
||||||
|
<p>请先创建一个key</p>
|
||||||
|
</Message>
|
||||||
)}
|
)}
|
||||||
{quota !== null && quotaPerUnit && (
|
<Divider /> */}
|
||||||
<Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
|
||||||
额度:${(quota / quotaPerUnit).toFixed(2)}</Label>
|
|
||||||
)}
|
|
||||||
{usedQuota !== null && quotaPerUnit && (
|
|
||||||
<Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
|
||||||
已用额度:${(usedQuota / quotaPerUnit).toFixed(2)}</Label>
|
|
||||||
)}
|
|
||||||
{requestCount !== null && <Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
|
||||||
调用次数:{requestCount}</Label>}
|
|
||||||
<Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
|
|
||||||
GitHub 账号:{githubID ? githubID : "未绑定"}
|
|
||||||
</Label>
|
|
||||||
<Divider />
|
|
||||||
<Header as='h3'>通用设置</Header>
|
<Header as='h3'>通用设置</Header>
|
||||||
{/* <Message>
|
{/* <Message>
|
||||||
注意,此处生成的Key用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
|
注意,此处生成的Key用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
|
||||||
|
@ -17,13 +17,13 @@ function renderTimestamp(timestamp) {
|
|||||||
function renderStatus(status) {
|
function renderStatus(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 1:
|
case 1:
|
||||||
return <Label basic style={{color: 'var(--czl-primary-color)'}}>未使用</Label>;
|
return <Label basic style={{ color: 'var(--czl-primary-color)' }}>未使用</Label>;
|
||||||
case 2:
|
case 2:
|
||||||
return <Label basic style={{color: 'var(--czl-error-color)'}}> 已禁用 </Label>;
|
return <Label basic style={{ color: 'var(--czl-error-color)' }}> 已禁用 </Label>;
|
||||||
case 3:
|
case 3:
|
||||||
return <Label basic style={{color: 'var(--czl-success-color)'}}> 已使用 </Label>;
|
return <Label basic style={{ color: 'var(--czl-success-color)' }}> 已使用 </Label>;
|
||||||
default:
|
default:
|
||||||
return <Label basic style={{color: 'var(--czl-grayD)'}}> 未知状态 </Label>;
|
return <Label basic style={{ color: 'var(--czl-grayD)' }}> 未知状态 </Label>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,66 +232,67 @@ const RedemptionsTable = () => {
|
|||||||
<Table.Cell>{redemption.redeemed_time ? renderTimestamp(redemption.redeemed_time) : "尚未兑换"} </Table.Cell>
|
<Table.Cell>{redemption.redeemed_time ? renderTimestamp(redemption.redeemed_time) : "尚未兑换"} </Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
positive
|
icon='copy' // 选择了一个代表复制的图标
|
||||||
onClick={async () => {
|
positive
|
||||||
if (await copy(redemption.key)) {
|
onClick={async () => {
|
||||||
showSuccess('已复制到剪贴板!');
|
if (await copy(redemption.key)) {
|
||||||
} else {
|
showSuccess('已复制到剪贴板!');
|
||||||
showWarning('无法复制到剪贴板,请手动复制,已将兑换码填入搜索框。')
|
} else {
|
||||||
setSearchKeyword(redemption.key);
|
showWarning('无法复制到剪贴板,请手动复制,已将兑换码填入搜索框。')
|
||||||
}
|
setSearchKeyword(redemption.key);
|
||||||
}}
|
}
|
||||||
style={{ backgroundColor: "var(--czl-success-color)", color: "white"}}
|
}}
|
||||||
>
|
style={{ backgroundColor: "var(--czl-success-color)", color: "white" }}
|
||||||
复制
|
/>
|
||||||
</Button>
|
<Popup
|
||||||
<Popup
|
trigger={
|
||||||
trigger={
|
<Button
|
||||||
<Button size='small' negative style={{ backgroundColor: "var(--czl-error-color)", color: "white"}}>
|
size='small'
|
||||||
删除
|
icon='delete'
|
||||||
</Button>
|
negative
|
||||||
}
|
style={{ backgroundColor: "var(--czl-error-color)", color: "white" }}
|
||||||
on='click'
|
/>
|
||||||
flowing
|
}
|
||||||
hoverable
|
on='click'
|
||||||
>
|
flowing
|
||||||
<Button
|
hoverable
|
||||||
negative
|
>
|
||||||
onClick={() => {
|
<Button
|
||||||
manageRedemption(redemption.id, 'delete', idx);
|
negative
|
||||||
}}
|
icon='delete'
|
||||||
style={{ backgroundColor: "var(--czl-error-color)", color: "white"}}
|
onClick={() => {
|
||||||
>
|
manageRedemption(redemption.id, 'delete', idx);
|
||||||
确认删除
|
}}
|
||||||
</Button>
|
style={{ backgroundColor: "var(--czl-error-color)", color: "white" }}
|
||||||
</Popup>
|
>
|
||||||
<Button
|
确认删除
|
||||||
size={'small'}
|
</Button>
|
||||||
disabled={redemption.status === 3} // used
|
</Popup>
|
||||||
onClick={() => {
|
<Button
|
||||||
manageRedemption(
|
size={'small'}
|
||||||
redemption.id,
|
icon={redemption.status === 1 ? 'ban' : 'check'} // 根据状态选择对应的图标
|
||||||
redemption.status === 1 ? 'disable' : 'enable',
|
disabled={redemption.status === 3} // used
|
||||||
idx
|
onClick={() => {
|
||||||
);
|
manageRedemption(
|
||||||
}}
|
redemption.id,
|
||||||
style={{ backgroundColor: "var(--czl-link-color)", color: "white"}}
|
redemption.status === 1 ? 'disable' : 'enable',
|
||||||
>
|
idx
|
||||||
{redemption.status === 1 ? '禁用' : '启用'}
|
);
|
||||||
</Button>
|
}}
|
||||||
<Button
|
style={{ backgroundColor: "var(--czl-link-color)", color: "white" }}
|
||||||
size={'small'}
|
/>
|
||||||
as={Link}
|
<Button
|
||||||
to={'/redemption/edit/' + redemption.id}
|
size={'small'}
|
||||||
style={{ backgroundColor: "var(--czl-primary-color)", color: "white"}}
|
icon='edit' // 选择了一个代表编辑的图标
|
||||||
>
|
as={Link}
|
||||||
编辑
|
to={'/redemption/edit/' + redemption.id}
|
||||||
</Button>
|
style={{ backgroundColor: "var(--czl-primary-color)", color: "white" }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -313,6 +314,8 @@ const RedemptionsTable = () => {
|
|||||||
Math.ceil(redemptions.length / ITEMS_PER_PAGE) +
|
Math.ceil(redemptions.length / ITEMS_PER_PAGE) +
|
||||||
(redemptions.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
(redemptions.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
firstItem={null} // 不显示第一页按钮
|
||||||
|
lastItem={null} // 不显示最后一页按钮
|
||||||
/>
|
/>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Button, Dropdown, Form, Label, Pagination, Popup, Table } from 'semantic-ui-react';
|
import { Button, Dropdown, Form, Label, Pagination, Popup, Table } from 'semantic-ui-react';
|
||||||
|
// import { Icon } from 'semantic-ui-react';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { API, copy, showError, showSuccess, showWarning, timestamp2string } from '../helpers';
|
import { API, copy, showError, showSuccess, showWarning, timestamp2string } from '../helpers';
|
||||||
|
|
||||||
@ -7,6 +9,7 @@ import { ITEMS_PER_PAGE } from '../constants';
|
|||||||
import { renderQuota } from '../helpers/render';
|
import { renderQuota } from '../helpers/render';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -234,6 +237,14 @@ const TokensTable = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 对key脱敏
|
||||||
|
function renderKey(key) {
|
||||||
|
// 使用固定数量的星号(例如8个)
|
||||||
|
const fixedNumberOfAsterisks = '********';
|
||||||
|
return `sk-${key.substring(0, 4)}${fixedNumberOfAsterisks}${key.substring(key.length - 4)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form onSubmit={searchTokens}>
|
<Form onSubmit={searchTokens}>
|
||||||
@ -259,6 +270,14 @@ const TokensTable = () => {
|
|||||||
>
|
>
|
||||||
名称
|
名称
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
sortToken('key');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Key
|
||||||
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -314,6 +333,7 @@ const TokensTable = () => {
|
|||||||
return (
|
return (
|
||||||
<Table.Row key={token.id}>
|
<Table.Row key={token.id}>
|
||||||
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
|
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
|
||||||
|
<Table.Cell>{renderKey(token.key)}</Table.Cell>
|
||||||
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
|
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
|
||||||
<Table.Cell>{renderQuota(token.used_quota)}</Table.Cell>
|
<Table.Cell>{renderQuota(token.used_quota)}</Table.Cell>
|
||||||
<Table.Cell>{token.unlimited_quota ? '无限制' : renderQuota(token.remain_quota, 2)}</Table.Cell>
|
<Table.Cell>{token.unlimited_quota ? '无限制' : renderQuota(token.remain_quota, 2)}</Table.Cell>
|
||||||
@ -323,20 +343,16 @@ const TokensTable = () => {
|
|||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
icon="copy"
|
||||||
positive
|
positive
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await onCopy('', token.key);
|
await onCopy('', token.key);
|
||||||
}}
|
}}
|
||||||
style={{ backgroundColor: 'var(--czl-success-color)', borderColor: 'var(--czl-success-color)' }}
|
style={{ backgroundColor: 'var(--czl-success-color)', borderColor: 'var(--czl-success-color)' }}
|
||||||
>
|
/>
|
||||||
复制
|
|
||||||
</Button>
|
|
||||||
{' '}
|
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' negative style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }}>
|
<Button size='small' icon="delete" negative style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }} />
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
flowing
|
flowing
|
||||||
@ -344,16 +360,20 @@ const TokensTable = () => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
negative
|
negative
|
||||||
|
icon="delete"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
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'}
|
||||||
|
negative
|
||||||
|
icon={token.status === 1 ? 'ban' : 'check'}
|
||||||
|
style={{
|
||||||
|
backgroundColor: token.status === 1 ? 'var(--czl-warning-color)' : 'var(--czl-success-color)',
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
manageToken(
|
manageToken(
|
||||||
token.id,
|
token.id,
|
||||||
@ -361,20 +381,19 @@ const TokensTable = () => {
|
|||||||
idx
|
idx
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{token.status === 1 ? '禁用' : '启用'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
negative
|
negative
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
icon="edit"
|
||||||
as={Link}
|
as={Link}
|
||||||
to={'/token/edit/' + token.id}
|
to={'/token/edit/' + token.id}
|
||||||
style={{ backgroundColor: 'var(--czl-primary-color)', borderColor: 'var(--czl-primary-color)' }}
|
style={{ backgroundColor: 'var(--czl-primary-color)', borderColor: 'var(--czl-primary-color)' }}
|
||||||
>
|
/>
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -382,7 +401,7 @@ const TokensTable = () => {
|
|||||||
|
|
||||||
<Table.Footer>
|
<Table.Footer>
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.HeaderCell colSpan='7'>
|
<Table.HeaderCell colSpan='8'>
|
||||||
<Button
|
<Button
|
||||||
size='small'
|
size='small'
|
||||||
as={Link}
|
as={Link}
|
||||||
@ -390,7 +409,7 @@ const TokensTable = () => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
style={{ color: "var(--czl-main)", backgroundColor: "var(--czl-link-color)" }}
|
style={{ color: "var(--czl-main)", backgroundColor: "var(--czl-link-color)" }}
|
||||||
>
|
>
|
||||||
添加新的Key
|
创建Key
|
||||||
</Button>
|
</Button>
|
||||||
<Button size='small' onClick={refresh} loading={loading}>刷新</Button>
|
<Button size='small' onClick={refresh} loading={loading}>刷新</Button>
|
||||||
|
|
||||||
@ -399,12 +418,16 @@ const TokensTable = () => {
|
|||||||
activePage={activePage}
|
activePage={activePage}
|
||||||
onPageChange={onPaginationChange}
|
onPageChange={onPaginationChange}
|
||||||
size='small'
|
size='small'
|
||||||
siblingRange={1}
|
siblingRange={0} // 不显示邻近页码
|
||||||
totalPages={
|
totalPages={
|
||||||
Math.ceil(tokens.length / ITEMS_PER_PAGE) +
|
Math.ceil(tokens.length / ITEMS_PER_PAGE) +
|
||||||
(tokens.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
(tokens.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
ellipsisItem={null} // 不显示省略号
|
||||||
|
firstItem={null} // 不显示第一页按钮
|
||||||
|
lastItem={null} // 不显示最后一页按钮
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Footer>
|
</Table.Footer>
|
||||||
|
@ -61,20 +61,41 @@ const UsersTable = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const manageUser = (username, idx, newGroup) => {
|
const manageUser = (username, action, idx, value = null) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await API.post('/api/user/manage', {
|
let dataToSend = {
|
||||||
username,
|
username,
|
||||||
newGroup
|
action
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// 如果是修改用户组,需要在请求体中包含新的组别信息
|
||||||
|
if (action === 'changeGroup' && value !== null) {
|
||||||
|
dataToSend.newGroup = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await API.post('/api/user/manage', dataToSend);
|
||||||
|
const { success, message, data } = res.data;
|
||||||
|
|
||||||
const { success, message } = res.data;
|
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess('操作成功完成!');
|
showSuccess('操作成功完成!');
|
||||||
let user = res.data.data;
|
|
||||||
let newUsers = [...users];
|
let newUsers = [...users];
|
||||||
let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
|
let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
|
||||||
newUsers[realIdx].group = user.group; // 用新的 user.group 更新用户分组
|
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
newUsers[realIdx].deleted = true;
|
||||||
|
break;
|
||||||
|
case 'disable':
|
||||||
|
case 'enable':
|
||||||
|
newUsers[realIdx].status = data.status; // 假设API返回了新的状态
|
||||||
|
break;
|
||||||
|
case 'changeGroup':
|
||||||
|
newUsers[realIdx].group = data.group; // 假设API返回了新的用户组信息
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error('未知的操作类型');
|
||||||
|
}
|
||||||
|
|
||||||
setUsers(newUsers);
|
setUsers(newUsers);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -83,6 +104,7 @@ const UsersTable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const groupOptions = [
|
const groupOptions = [
|
||||||
{ key: 'default', value: 'default', text: '默认', color: 'var(--czl-grayA)' },
|
{ key: 'default', value: 'default', text: '默认', color: 'var(--czl-grayA)' },
|
||||||
{ key: 'vip', value: 'vip', text: 'VIP', color: 'var(--czl-success-color)' },
|
{ key: 'vip', value: 'vip', text: 'VIP', color: 'var(--czl-success-color)' },
|
||||||
@ -257,13 +279,13 @@ const UsersTable = () => {
|
|||||||
<Table.Cell>{renderStatus(user.status)}</Table.Cell>
|
<Table.Cell>{renderStatus(user.status)}</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div>
|
<div>
|
||||||
<Button.Group size={'small'} style={{marginRight: '10px'}}>
|
<Button.Group size={'small'} style={{ marginRight: '10px' }}>
|
||||||
<Button
|
<Button
|
||||||
positive
|
positive
|
||||||
size={'small'}
|
size={'small'}
|
||||||
className={`group-button ${user.group}`}
|
className={`group-button ${user.group}`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: groupColor(user.group), // 设置透明背景颜色
|
backgroundColor: groupColor(user.group),
|
||||||
width: '100px',
|
width: '100px',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
@ -274,18 +296,18 @@ const UsersTable = () => {
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
className="button icon"
|
className="button icon"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: groupColor(user.group),}}
|
backgroundColor: groupColor(user.group),
|
||||||
|
}}
|
||||||
floating
|
floating
|
||||||
options={groupOptions}
|
options={groupOptions}
|
||||||
trigger={<></>}
|
trigger={<></>}
|
||||||
onChange={(e, { value }) => manageUser(user.username, idx, value)}
|
onChange={(e, { value }) => manageUser(user.username, 'changeGroup', idx, value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Button.Group>
|
</Button.Group>
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' negative disabled={user.role === 100}>
|
<Button size='small' negative icon='delete' disabled={user.role === 100} />
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
flowing
|
flowing
|
||||||
@ -293,15 +315,19 @@ const UsersTable = () => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
negative
|
negative
|
||||||
|
icon='delete'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
manageUser(user.username, 'delete', idx);
|
manageUser(user.username, 'delete', idx);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
删除用户 {user.username}
|
|
||||||
</Button>
|
|
||||||
</Popup>
|
</Popup>
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
negative
|
||||||
|
icon={user.status === 1 ? 'ban' : 'check'}
|
||||||
|
style={{
|
||||||
|
backgroundColor: user.status === 1 ? 'var(--czl-warning-color)' : 'var(--czl-success-color)',
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
manageUser(
|
manageUser(
|
||||||
user.username,
|
user.username,
|
||||||
@ -310,18 +336,18 @@ const UsersTable = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
disabled={user.role === 100}
|
disabled={user.role === 100}
|
||||||
>
|
/>
|
||||||
{user.status === 1 ? '禁用' : '启用'}
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
|
icon="edit"
|
||||||
|
style={{ backgroundColor: 'var(--czl-primary-color)' }}
|
||||||
as={Link}
|
as={Link}
|
||||||
to={'/user/edit/' + user.id}
|
to={'/user/edit/' + user.id}
|
||||||
>
|
/>
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -343,6 +369,8 @@ const UsersTable = () => {
|
|||||||
Math.ceil(users.length / ITEMS_PER_PAGE) +
|
Math.ceil(users.length / ITEMS_PER_PAGE) +
|
||||||
(users.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
(users.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
firstItem={null} // 不显示第一页按钮
|
||||||
|
lastItem={null} // 不显示最后一页按钮
|
||||||
/>
|
/>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
|
@ -217,4 +217,6 @@ code {
|
|||||||
/* padding: 0 !important; */
|
/* padding: 0 !important; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ const TopUp = () => {
|
|||||||
const res = await API.post('/api/user/topup', {
|
const res = await API.post('/api/user/topup', {
|
||||||
key: redemptionCode
|
key: redemptionCode
|
||||||
});
|
});
|
||||||
const { success, message, data ,upgradedToVIP } = res.data;
|
const { success, message, data, upgradedToVIP } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
if (upgradedToVIP) { // 如果用户成功升级为 VIP
|
if (upgradedToVIP) { // 如果用户成功升级为 VIP
|
||||||
showSuccess('充值成功,升级为 VIP 会员');
|
showSuccess('充值成功,升级为 VIP 会员');
|
||||||
@ -85,13 +85,23 @@ const TopUp = () => {
|
|||||||
setRedemptionCode(e.target.value);
|
setRedemptionCode(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button negative style={{ backgroundColor: 'var(--czl-primary-color)' }} onClick={openTopUpLink}>
|
<Button
|
||||||
获取兑换码
|
negative
|
||||||
</Button>
|
icon='shop'
|
||||||
<Button negative style={{ backgroundColor: 'var(--czl-success-color)' }} onClick={topUp} disabled={isSubmitting}>
|
labelPosition='left'
|
||||||
{isSubmitting ? '兑换中...' : '兑换'}
|
content='获取兑换码'
|
||||||
</Button>
|
style={{ backgroundColor: 'var(--czl-primary-color)' }}
|
||||||
|
onClick={openTopUpLink}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
negative
|
||||||
|
icon='exchange'
|
||||||
|
labelPosition='left'
|
||||||
|
content={isSubmitting ? '兑换中...' : '兑换'}
|
||||||
|
style={{ backgroundColor: 'var(--czl-success-color)' }}
|
||||||
|
onClick={topUp}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
|
||||||
</Form>
|
</Form>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
Loading…
Reference in New Issue
Block a user