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}