refactor: update group handling and rendering logic
- Changed the structure of usableGroups in GetUserGroups to store additional information (ratio and description) for each group. - Introduced a new renderRatio function to visually represent group ratios with color coding. - Updated the Playground and EditToken components to utilize the new group structure and rendering options. - Enhanced the renderGroupOption function for better UI representation of group options. - Fixed minor comments and improved code readability.
This commit is contained in:
@@ -20,15 +20,18 @@ func GetGroups(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUserGroups(c *gin.Context) {
|
func GetUserGroups(c *gin.Context) {
|
||||||
usableGroups := make(map[string]string)
|
usableGroups := make(map[string]map[string]interface{})
|
||||||
userGroup := ""
|
userGroup := ""
|
||||||
userId := c.GetInt("id")
|
userId := c.GetInt("id")
|
||||||
userGroup, _ = model.GetUserGroup(userId, false)
|
userGroup, _ = model.GetUserGroup(userId, false)
|
||||||
for groupName, _ := range setting.GetGroupRatioCopy() {
|
for groupName, ratio := range setting.GetGroupRatioCopy() {
|
||||||
// UserUsableGroups contains the groups that the user can use
|
// UserUsableGroups contains the groups that the user can use
|
||||||
userUsableGroups := setting.GetUserUsableGroups(userGroup)
|
userUsableGroups := setting.GetUserUsableGroups(userGroup)
|
||||||
if _, ok := userUsableGroups[groupName]; ok {
|
if desc, ok := userUsableGroups[groupName]; ok {
|
||||||
usableGroups[groupName] = userUsableGroups[groupName]
|
usableGroups[groupName] = map[string]interface{}{
|
||||||
|
"ratio": ratio,
|
||||||
|
"desc": desc,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { Modal, Tag } from '@douyinfe/semi-ui';
|
import { Modal, Tag, Typography } from '@douyinfe/semi-ui';
|
||||||
import { copy, showSuccess } from './utils.js';
|
import { copy, showSuccess } from './utils.js';
|
||||||
|
|
||||||
export function renderText(text, limit) {
|
export function renderText(text, limit) {
|
||||||
@@ -55,6 +55,81 @@ export function renderGroup(group) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderRatio(ratio) {
|
||||||
|
let color = 'green';
|
||||||
|
if (ratio > 5) {
|
||||||
|
color = 'red';
|
||||||
|
} else if (ratio > 3) {
|
||||||
|
color = 'orange';
|
||||||
|
} else if (ratio > 1) {
|
||||||
|
color = 'blue';
|
||||||
|
}
|
||||||
|
return <Tag color={color}>{ratio} {i18next.t('倍率')}</Tag>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const renderGroupOption = (item) => {
|
||||||
|
const {
|
||||||
|
disabled,
|
||||||
|
selected,
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
focused,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
onMouseEnter,
|
||||||
|
onClick,
|
||||||
|
empty,
|
||||||
|
emptyContent,
|
||||||
|
...rest
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
const baseStyle = {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '8px 16px',
|
||||||
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||||
|
backgroundColor: focused ? 'var(--semi-color-fill-0)' : 'transparent',
|
||||||
|
opacity: disabled ? 0.5 : 1,
|
||||||
|
...(selected && {
|
||||||
|
backgroundColor: 'var(--semi-color-primary-light-default)',
|
||||||
|
}),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: !disabled && 'var(--semi-color-fill-1)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (!disabled && onClick) {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseEnter = (e) => {
|
||||||
|
if (!disabled && onMouseEnter) {
|
||||||
|
onMouseEnter(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={baseStyle}
|
||||||
|
onClick={handleClick}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||||
|
<Typography.Text strong type={disabled ? 'tertiary' : undefined}>
|
||||||
|
{value}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography.Text type="secondary" size="small">
|
||||||
|
{label}
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
{item.ratio && renderRatio(item.ratio)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export function renderNumber(num) {
|
export function renderNumber(num) {
|
||||||
if (num >= 1000000000) {
|
if (num >= 1000000000) {
|
||||||
return (num / 1000000000).toFixed(1) + 'B';
|
return (num / 1000000000).toFixed(1) + 'B';
|
||||||
@@ -352,7 +427,7 @@ export const modelColorMap = {
|
|||||||
'gpt-3.5-turbo-0613': 'rgb(60,179,113)', // 海洋绿
|
'gpt-3.5-turbo-0613': 'rgb(60,179,113)', // 海洋绿
|
||||||
'gpt-3.5-turbo-1106': 'rgb(32,178,170)', // 浅海洋绿
|
'gpt-3.5-turbo-1106': 'rgb(32,178,170)', // 浅海洋绿
|
||||||
'gpt-3.5-turbo-16k': 'rgb(149,252,206)', // 淡橙色
|
'gpt-3.5-turbo-16k': 'rgb(149,252,206)', // 淡橙色
|
||||||
'gpt-3.5-turbo-16k-0613': 'rgb(119,255,214)', // 淡桃<EFBFBD><EFBFBD><EFBFBD>
|
'gpt-3.5-turbo-16k-0613': 'rgb(119,255,214)', // 淡桃
|
||||||
'gpt-3.5-turbo-instruct': 'rgb(175,238,238)', // 粉蓝色
|
'gpt-3.5-turbo-instruct': 'rgb(175,238,238)', // 粉蓝色
|
||||||
'gpt-4': 'rgb(135,206,235)', // 天蓝色
|
'gpt-4': 'rgb(135,206,235)', // 天蓝色
|
||||||
// 'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色
|
// 'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色
|
||||||
@@ -375,7 +450,7 @@ export const modelColorMap = {
|
|||||||
'text-embedding-ada-002': 'rgb(255,182,193)', // 浅粉红
|
'text-embedding-ada-002': 'rgb(255,182,193)', // 浅粉红
|
||||||
'text-embedding-v1': 'rgb(255,174,185)', // 浅粉红色(略有区别)
|
'text-embedding-v1': 'rgb(255,174,185)', // 浅粉红色(略有区别)
|
||||||
'text-moderation-latest': 'rgb(255,130,171)', // 强粉色
|
'text-moderation-latest': 'rgb(255,130,171)', // 强粉色
|
||||||
'text-moderation-stable': 'rgb(255,160,122)', // 浅珊瑚色(<EFBFBD><EFBFBD><EFBFBD>Babbage相同,表示同一类功能)
|
'text-moderation-stable': 'rgb(255,160,122)', // 浅珊瑚色(与Babbage相同,表示同一类功能)
|
||||||
'tts-1': 'rgb(255,140,0)', // 深橙色
|
'tts-1': 'rgb(255,140,0)', // 深橙色
|
||||||
'tts-1-1106': 'rgb(255,165,0)', // 橙色
|
'tts-1-1106': 'rgb(255,165,0)', // 橙色
|
||||||
'tts-1-hd': 'rgb(255,215,0)', // 金色
|
'tts-1-hd': 'rgb(255,215,0)', // 金色
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import { UserContext } from '../../context/User/index.js';
|
import { UserContext } from '../../context/User/index.js';
|
||||||
import { API, getUserIdFromLocalStorage, showError } from '../../helpers/index.js';
|
import { API, getUserIdFromLocalStorage, showError } from '../../helpers/index.js';
|
||||||
import { Card, Chat, Input, Layout, Select, Slider, TextArea, Typography, Button } from '@douyinfe/semi-ui';
|
import { Card, Chat, Input, Layout, Select, Slider, TextArea, Typography, Button, Highlight } from '@douyinfe/semi-ui';
|
||||||
import { SSE } from 'sse';
|
import { SSE } from 'sse';
|
||||||
import { IconSetting } from '@douyinfe/semi-icons';
|
import { IconSetting } from '@douyinfe/semi-icons';
|
||||||
import { StyleContext } from '../../context/Style/index.js';
|
import { StyleContext } from '../../context/Style/index.js';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { renderGroupOption } from '../../helpers/render.js';
|
||||||
|
|
||||||
const roleInfo = {
|
const roleInfo = {
|
||||||
user: {
|
user: {
|
||||||
@@ -97,15 +98,17 @@ const Playground = () => {
|
|||||||
let res = await API.get(`/api/user/self/groups`);
|
let res = await API.get(`/api/user/self/groups`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
let localGroupOptions = Object.keys(data).map((group) => ({
|
let localGroupOptions = Object.entries(data).map(([group, info]) => ({
|
||||||
label: data[group],
|
label: info.desc,
|
||||||
value: group,
|
value: group,
|
||||||
|
ratio: info.ratio
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (localGroupOptions.length === 0) {
|
if (localGroupOptions.length === 0) {
|
||||||
localGroupOptions = [{
|
localGroupOptions = [{
|
||||||
label: t('用户分组'),
|
label: t('用户分组'),
|
||||||
value: '',
|
value: '',
|
||||||
|
ratio: 1
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
const localUser = JSON.parse(localStorage.getItem('user'));
|
const localUser = JSON.parse(localStorage.getItem('user'));
|
||||||
@@ -326,12 +329,9 @@ const Playground = () => {
|
|||||||
}}
|
}}
|
||||||
value={inputs.group}
|
value={inputs.group}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
optionList={groups.map((group) => ({
|
optionList={groups}
|
||||||
...group,
|
renderOptionItem={renderGroupOption}
|
||||||
label: styleState.isMobile && group.label.length > 16
|
style={{ width: '100%' }}
|
||||||
? group.label.substring(0, 16) + '...'
|
|
||||||
: group.label,
|
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
<div style={{ marginTop: 10 }}>
|
<div style={{ marginTop: 10 }}>
|
||||||
<Typography.Text strong>{t('模型')}:</Typography.Text>
|
<Typography.Text strong>{t('模型')}:</Typography.Text>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
showSuccess,
|
showSuccess,
|
||||||
timestamp2string,
|
timestamp2string,
|
||||||
} from '../../helpers';
|
} from '../../helpers';
|
||||||
import { renderQuotaWithPrompt } from '../../helpers/render';
|
import { renderGroupOption, renderQuotaWithPrompt } from '../../helpers/render';
|
||||||
import {
|
import {
|
||||||
AutoComplete,
|
AutoComplete,
|
||||||
Banner,
|
Banner,
|
||||||
@@ -97,9 +97,10 @@ const EditToken = (props) => {
|
|||||||
let res = await API.get(`/api/user/self/groups`);
|
let res = await API.get(`/api/user/self/groups`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
let localGroupOptions = Object.keys(data).map((group) => ({
|
let localGroupOptions = Object.entries(data).map(([group, info]) => ({
|
||||||
label: data[group],
|
label: info.desc,
|
||||||
value: group,
|
value: group,
|
||||||
|
ratio: info.ratio
|
||||||
}));
|
}));
|
||||||
setGroups(localGroupOptions);
|
setGroups(localGroupOptions);
|
||||||
} else {
|
} else {
|
||||||
@@ -449,6 +450,8 @@ const EditToken = (props) => {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleInputChange('group', value);
|
handleInputChange('group', value);
|
||||||
}}
|
}}
|
||||||
|
position={'topLeft'}
|
||||||
|
renderOptionItem={renderGroupOption}
|
||||||
value={inputs.group}
|
value={inputs.group}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
optionList={groups}
|
optionList={groups}
|
||||||
|
|||||||
Reference in New Issue
Block a user