From d13d81baba40f0a7cfa4d3208dd317e0332bad8f Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Mon, 30 Dec 2024 19:51:00 +0800 Subject: [PATCH] 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. --- controller/group.go | 11 ++-- web/src/helpers/render.js | 81 +++++++++++++++++++++++++- web/src/pages/Playground/Playground.js | 18 +++--- web/src/pages/Token/EditToken.js | 9 ++- 4 files changed, 100 insertions(+), 19 deletions(-) diff --git a/controller/group.go b/controller/group.go index b700fc96..2c725a4d 100644 --- a/controller/group.go +++ b/controller/group.go @@ -20,15 +20,18 @@ func GetGroups(c *gin.Context) { } func GetUserGroups(c *gin.Context) { - usableGroups := make(map[string]string) + usableGroups := make(map[string]map[string]interface{}) userGroup := "" userId := c.GetInt("id") 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 := setting.GetUserUsableGroups(userGroup) - if _, ok := userUsableGroups[groupName]; ok { - usableGroups[groupName] = userUsableGroups[groupName] + if desc, ok := userUsableGroups[groupName]; ok { + usableGroups[groupName] = map[string]interface{}{ + "ratio": ratio, + "desc": desc, + } } } c.JSON(http.StatusOK, gin.H{ diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js index 5db5a9fc..9193f644 100644 --- a/web/src/helpers/render.js +++ b/web/src/helpers/render.js @@ -1,5 +1,5 @@ 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'; 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 {ratio} {i18next.t('倍率')}; +} + +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 ( +
+
+ + {value} + + + {label} + +
+ {item.ratio && renderRatio(item.ratio)} +
+ ); +}; + export function renderNumber(num) { if (num >= 1000000000) { 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-1106': 'rgb(32,178,170)', // 浅海洋绿 'gpt-3.5-turbo-16k': 'rgb(149,252,206)', // 淡橙色 - 'gpt-3.5-turbo-16k-0613': 'rgb(119,255,214)', // 淡桃��� + 'gpt-3.5-turbo-16k-0613': 'rgb(119,255,214)', // 淡桃 'gpt-3.5-turbo-instruct': 'rgb(175,238,238)', // 粉蓝色 'gpt-4': 'rgb(135,206,235)', // 天蓝色 // 'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色 @@ -375,7 +450,7 @@ export const modelColorMap = { 'text-embedding-ada-002': 'rgb(255,182,193)', // 浅粉红 'text-embedding-v1': 'rgb(255,174,185)', // 浅粉红色(略有区别) 'text-moderation-latest': 'rgb(255,130,171)', // 强粉色 - 'text-moderation-stable': 'rgb(255,160,122)', // 浅珊瑚色(���Babbage相同,表示同一类功能) + 'text-moderation-stable': 'rgb(255,160,122)', // 浅珊瑚色(与Babbage相同,表示同一类功能) 'tts-1': 'rgb(255,140,0)', // 深橙色 'tts-1-1106': 'rgb(255,165,0)', // 橙色 'tts-1-hd': 'rgb(255,215,0)', // 金色 diff --git a/web/src/pages/Playground/Playground.js b/web/src/pages/Playground/Playground.js index 935e7b6e..d97e5925 100644 --- a/web/src/pages/Playground/Playground.js +++ b/web/src/pages/Playground/Playground.js @@ -2,11 +2,12 @@ import React, { useCallback, useContext, useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { UserContext } from '../../context/User/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 { IconSetting } from '@douyinfe/semi-icons'; import { StyleContext } from '../../context/Style/index.js'; import { useTranslation } from 'react-i18next'; +import { renderGroupOption } from '../../helpers/render.js'; const roleInfo = { user: { @@ -97,15 +98,17 @@ const Playground = () => { let res = await API.get(`/api/user/self/groups`); const { success, message, data } = res.data; if (success) { - let localGroupOptions = Object.keys(data).map((group) => ({ - label: data[group], + let localGroupOptions = Object.entries(data).map(([group, info]) => ({ + label: info.desc, value: group, + ratio: info.ratio })); if (localGroupOptions.length === 0) { localGroupOptions = [{ label: t('用户分组'), value: '', + ratio: 1 }]; } else { const localUser = JSON.parse(localStorage.getItem('user')); @@ -326,12 +329,9 @@ const Playground = () => { }} value={inputs.group} autoComplete='new-password' - optionList={groups.map((group) => ({ - ...group, - label: styleState.isMobile && group.label.length > 16 - ? group.label.substring(0, 16) + '...' - : group.label, - }))} + optionList={groups} + renderOptionItem={renderGroupOption} + style={{ width: '100%' }} />
{t('模型')}: diff --git a/web/src/pages/Token/EditToken.js b/web/src/pages/Token/EditToken.js index 323301b5..627caabd 100644 --- a/web/src/pages/Token/EditToken.js +++ b/web/src/pages/Token/EditToken.js @@ -7,7 +7,7 @@ import { showSuccess, timestamp2string, } from '../../helpers'; -import { renderQuotaWithPrompt } from '../../helpers/render'; +import { renderGroupOption, renderQuotaWithPrompt } from '../../helpers/render'; import { AutoComplete, Banner, @@ -97,9 +97,10 @@ const EditToken = (props) => { let res = await API.get(`/api/user/self/groups`); const { success, message, data } = res.data; if (success) { - let localGroupOptions = Object.keys(data).map((group) => ({ - label: data[group], + let localGroupOptions = Object.entries(data).map(([group, info]) => ({ + label: info.desc, value: group, + ratio: info.ratio })); setGroups(localGroupOptions); } else { @@ -449,6 +450,8 @@ const EditToken = (props) => { onChange={(value) => { handleInputChange('group', value); }} + position={'topLeft'} + renderOptionItem={renderGroupOption} value={inputs.group} autoComplete='new-password' optionList={groups}