一些按钮改为图标

This commit is contained in:
wood 2023-11-16 03:21:36 +08:00
parent 0f51100890
commit 05ee4d5977
10 changed files with 328 additions and 149 deletions

View File

@ -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)

View File

@ -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>
); );
})} })}

View File

@ -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)'
}, },
{ {

View File

@ -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>

View File

@ -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 相关的服务请知悉

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -217,4 +217,6 @@ code {
/* padding: 0 !important; */ /* padding: 0 !important; */
} }
} }

View File

@ -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>