一些按钮改为图标

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,20 +442,18 @@ const ChannelsTable = () => {
<Table.Cell>
<div>
<Button
icon='play' // 示例图标
size={'small'}
positive
style={{ backgroundColor: 'var(--czl-success-color)' }}
onClick={() => {
testChannel(channel.id, channel.name, idx);
}}
>
测试
</Button>
/>
<Popup
trigger={
<Button size='small' negative style={{ backgroundColor: 'var(--czl-error-color)' }}>
删除
</Button>
<Button icon='trash' size='small' negative style={{ backgroundColor: 'var(--czl-error-color)' }} />
}
on='click'
flowing
@ -471,9 +469,11 @@ const ChannelsTable = () => {
删除渠道 {channel.name}
</Button>
</Popup>
<Button
negative
icon={channel.status === 1 ? 'ban' : 'check'} // 示例图标
size={'small'}
negative
style={{ backgroundColor: 'var(--czl-warning-color)' }}
onClick={() => {
manageChannel(
@ -482,20 +482,18 @@ const ChannelsTable = () => {
idx
);
}}
>
{channel.status === 1 ? '禁用' : '启用'}
</Button>
/>
<Button
negative
icon='edit' // 示例图标
size={'small'}
as={Link}
to={'/channel/edit/' + channel.id}
style={{ backgroundColor: 'var(--czl-primary-color)' }}
>
编辑
</Button>
/>
</div>
</Table.Cell>
</Table.Row>
);
})}

View File

@ -43,7 +43,7 @@ let headerButtons = [
{
name: '用户',
to: '/user',
icon: 'user',
icon: 'users',
color: 'var(--czl-primary-color)',
admin: true
},
@ -54,9 +54,9 @@ let headerButtons = [
color: 'var(--czl-primary-color)'
},
{
name: '设置',
name: '个人',
to: '/setting',
icon: 'setting',
icon: 'user',
color: 'var(--czl-primary-color)'
},
{

View File

@ -476,6 +476,8 @@ const LogsTable = () => {
Math.ceil(logs.length / ITEMS_PER_PAGE) +
(logs.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
}
firstItem={null} // 不显示第一页按钮
lastItem={null} // 不显示最后一页按钮
/>
</Table.HeaderCell>
</Table.Row>

View File

@ -32,6 +32,12 @@ const PersonalSetting = () => {
const [usedQuota, setUsedQuota] = useState(null);
const [requestCount, setRequestCount] = 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 () => {
const res = await API.get(`/api/user/self`);
if (res.data.success) {
setDisplay_name(res.data.data.display_name);
setUsername(res.data.data.username);
setUserGroup(res.data.data.group);
setEmail(res.data.data.email);
setQuota(res.data.data.quota);
setUsedQuota(res.data.data.used_quota);
setRequestCount(res.data.data.request_count);
@ -64,6 +73,66 @@ const PersonalSetting = () => {
}, []);
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) => {
switch (group) {
@ -207,25 +276,69 @@ const PersonalSetting = () => {
return (
<div style={{ lineHeight: '40px' }}>
<Header as='h3'>使用信息</Header>
<div style={{ marginBottom: '1em' }}>
{display_name && (
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
昵称{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={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
<Label basic style={{ margin: '0.5em', color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
用户组{transformUserGroup(userGroup)}
</Label>
)}
{quota !== null && quotaPerUnit && (
<Label basic style={{ color: getUserGroupColor(userGroup), borderColor: getUserGroupColor(userGroup) }}>
额度${(quota / quotaPerUnit).toFixed(2)}</Label>
<Label basic style={{ margin: '0.5em', 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 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>
)}
<Divider /> */}
<Header as='h3'>通用设置</Header>
{/* <Message>
注意此处生成的Key用于系统管理而非用于请求 OpenAI 相关的服务请知悉

View File

@ -234,6 +234,7 @@ const RedemptionsTable = () => {
<div>
<Button
size={'small'}
icon='copy' // 选择了一个代表复制的图标
positive
onClick={async () => {
if (await copy(redemption.key)) {
@ -244,14 +245,15 @@ const RedemptionsTable = () => {
}
}}
style={{ backgroundColor: "var(--czl-success-color)", color: "white" }}
>
复制
</Button>
/>
<Popup
trigger={
<Button size='small' negative style={{ backgroundColor: "var(--czl-error-color)", color: "white"}}>
删除
</Button>
<Button
size='small'
icon='delete'
negative
style={{ backgroundColor: "var(--czl-error-color)", color: "white" }}
/>
}
on='click'
flowing
@ -259,6 +261,7 @@ const RedemptionsTable = () => {
>
<Button
negative
icon='delete'
onClick={() => {
manageRedemption(redemption.id, 'delete', idx);
}}
@ -269,6 +272,7 @@ const RedemptionsTable = () => {
</Popup>
<Button
size={'small'}
icon={redemption.status === 1 ? 'ban' : 'check'} // 根据状态选择对应的图标
disabled={redemption.status === 3} // used
onClick={() => {
manageRedemption(
@ -278,20 +282,17 @@ const RedemptionsTable = () => {
);
}}
style={{ backgroundColor: "var(--czl-link-color)", color: "white" }}
>
{redemption.status === 1 ? '禁用' : '启用'}
</Button>
/>
<Button
size={'small'}
icon='edit' // 选择了一个代表编辑的图标
as={Link}
to={'/redemption/edit/' + redemption.id}
style={{ backgroundColor: "var(--czl-primary-color)", color: "white" }}
>
编辑
</Button>
/>
</div>
</Table.Cell>
</Table.Row>
);
})}
@ -313,6 +314,8 @@ const RedemptionsTable = () => {
Math.ceil(redemptions.length / ITEMS_PER_PAGE) +
(redemptions.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
}
firstItem={null} // 不显示第一页按钮
lastItem={null} // 不显示最后一页按钮
/>
</Table.HeaderCell>
</Table.Row>

View File

@ -1,5 +1,7 @@
import React, { useEffect, useState } from '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 { API, copy, showError, showSuccess, showWarning, timestamp2string } from '../helpers';
@ -7,6 +9,7 @@ import { ITEMS_PER_PAGE } from '../constants';
import { renderQuota } from '../helpers/render';
function renderTimestamp(timestamp) {
return (
<>
@ -234,6 +237,14 @@ const TokensTable = () => {
setLoading(false);
};
// 对key脱敏
function renderKey(key) {
// 使用固定数量的星号例如8个
const fixedNumberOfAsterisks = '********';
return `sk-${key.substring(0, 4)}${fixedNumberOfAsterisks}${key.substring(key.length - 4)}`;
}
return (
<>
<Form onSubmit={searchTokens}>
@ -259,6 +270,14 @@ const TokensTable = () => {
>
名称
</Table.HeaderCell>
<Table.HeaderCell
style={{ cursor: 'pointer' }}
onClick={() => {
sortToken('key');
}}
>
Key
</Table.HeaderCell>
<Table.HeaderCell
style={{ cursor: 'pointer' }}
onClick={() => {
@ -314,6 +333,7 @@ const TokensTable = () => {
return (
<Table.Row key={token.id}>
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
<Table.Cell>{renderKey(token.key)}</Table.Cell>
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
<Table.Cell>{renderQuota(token.used_quota)}</Table.Cell>
<Table.Cell>{token.unlimited_quota ? '无限制' : renderQuota(token.remain_quota, 2)}</Table.Cell>
@ -323,20 +343,16 @@ const TokensTable = () => {
<div>
<Button
size={'small'}
icon="copy"
positive
onClick={async () => {
await onCopy('', token.key);
}}
style={{ backgroundColor: 'var(--czl-success-color)', borderColor: 'var(--czl-success-color)' }}
>
复制
</Button>
{' '}
/>
<Popup
trigger={
<Button size='small' negative style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }}>
删除
</Button>
<Button size='small' icon="delete" negative style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }} />
}
on='click'
flowing
@ -344,16 +360,20 @@ const TokensTable = () => {
>
<Button
negative
icon="delete"
onClick={() => {
manageToken(token.id, 'delete', idx);
}}
style={{ backgroundColor: 'var(--czl-error-color)', borderColor: 'var(--czl-error-color)' }}
>
删除Key {token.name}
</Button>
/>
</Popup>
<Button
size={'small'}
negative
icon={token.status === 1 ? 'ban' : 'check'}
style={{
backgroundColor: token.status === 1 ? 'var(--czl-warning-color)' : 'var(--czl-success-color)',
}}
onClick={() => {
manageToken(
token.id,
@ -361,20 +381,19 @@ const TokensTable = () => {
idx
);
}}
>
{token.status === 1 ? '禁用' : '启用'}
</Button>
/>
<Button
negative
size={'small'}
icon="edit"
as={Link}
to={'/token/edit/' + token.id}
style={{ backgroundColor: 'var(--czl-primary-color)', borderColor: 'var(--czl-primary-color)' }}
>
编辑
</Button>
/>
</div>
</Table.Cell>
</Table.Row>
);
})}
@ -382,7 +401,7 @@ const TokensTable = () => {
<Table.Footer>
<Table.Row>
<Table.HeaderCell colSpan='7'>
<Table.HeaderCell colSpan='8'>
<Button
size='small'
as={Link}
@ -390,7 +409,7 @@ const TokensTable = () => {
loading={loading}
style={{ color: "var(--czl-main)", backgroundColor: "var(--czl-link-color)" }}
>
添加新的Key
创建Key
</Button>
<Button size='small' onClick={refresh} loading={loading}>刷新</Button>
@ -399,12 +418,16 @@ const TokensTable = () => {
activePage={activePage}
onPageChange={onPaginationChange}
size='small'
siblingRange={1}
siblingRange={0} // 不显示邻近页码
totalPages={
Math.ceil(tokens.length / ITEMS_PER_PAGE) +
(tokens.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
}
ellipsisItem={null} // 不显示省略号
firstItem={null} // 不显示第一页按钮
lastItem={null} // 不显示最后一页按钮
/>
</Table.HeaderCell>
</Table.Row>
</Table.Footer>

View File

@ -61,20 +61,41 @@ const UsersTable = () => {
});
}, []);
const manageUser = (username, idx, newGroup) => {
const manageUser = (username, action, idx, value = null) => {
(async () => {
const res = await API.post('/api/user/manage', {
let dataToSend = {
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) {
showSuccess('操作成功完成!');
let user = res.data.data;
let newUsers = [...users];
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);
} else {
showError(message);
@ -83,6 +104,7 @@ const UsersTable = () => {
};
const groupOptions = [
{ key: 'default', value: 'default', text: '默认', color: 'var(--czl-grayA)' },
{ key: 'vip', value: 'vip', text: 'VIP', color: 'var(--czl-success-color)' },
@ -263,7 +285,7 @@ const UsersTable = () => {
size={'small'}
className={`group-button ${user.group}`}
style={{
backgroundColor: groupColor(user.group), // 设置透明背景颜色
backgroundColor: groupColor(user.group),
width: '100px',
overflow: 'hidden',
textOverflow: 'ellipsis',
@ -274,18 +296,18 @@ const UsersTable = () => {
<Dropdown
className="button icon"
style={{
backgroundColor: groupColor(user.group),}}
backgroundColor: groupColor(user.group),
}}
floating
options={groupOptions}
trigger={<></>}
onChange={(e, { value }) => manageUser(user.username, idx, value)}
onChange={(e, { value }) => manageUser(user.username, 'changeGroup', idx, value)}
/>
</Button.Group>
<Popup
trigger={
<Button size='small' negative disabled={user.role === 100}>
删除
</Button>
<Button size='small' negative icon='delete' disabled={user.role === 100} />
}
on='click'
flowing
@ -293,15 +315,19 @@ const UsersTable = () => {
>
<Button
negative
icon='delete'
onClick={() => {
manageUser(user.username, 'delete', idx);
}}
>
删除用户 {user.username}
</Button>
/>
</Popup>
<Button
size={'small'}
negative
icon={user.status === 1 ? 'ban' : 'check'}
style={{
backgroundColor: user.status === 1 ? 'var(--czl-warning-color)' : 'var(--czl-success-color)',
}}
onClick={() => {
manageUser(
user.username,
@ -310,18 +336,18 @@ const UsersTable = () => {
);
}}
disabled={user.role === 100}
>
{user.status === 1 ? '禁用' : '启用'}
</Button>
/>
<Button
size={'small'}
icon="edit"
style={{ backgroundColor: 'var(--czl-primary-color)' }}
as={Link}
to={'/user/edit/' + user.id}
>
编辑
</Button>
/>
</div>
</Table.Cell>
</Table.Row>
);
})}
@ -343,6 +369,8 @@ const UsersTable = () => {
Math.ceil(users.length / ITEMS_PER_PAGE) +
(users.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
}
firstItem={null} // 不显示第一页按钮
lastItem={null} // 不显示最后一页按钮
/>
</Table.HeaderCell>
</Table.Row>

View File

@ -218,3 +218,5 @@ code {
}
}

View File

@ -85,13 +85,23 @@ const TopUp = () => {
setRedemptionCode(e.target.value);
}}
/>
<Button negative style={{ backgroundColor: 'var(--czl-primary-color)' }} onClick={openTopUpLink}>
获取兑换码
</Button>
<Button negative style={{ backgroundColor: 'var(--czl-success-color)' }} onClick={topUp} disabled={isSubmitting}>
{isSubmitting ? '兑换中...' : '兑换'}
</Button>
<Button
negative
icon='shop'
labelPosition='left'
content='获取兑换码'
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>
</Grid.Column>