♻️refactor(EditChannel & EditTagModal): enhance UI/UX and optimize code structure

BREAKING CHANGE: None

- UI Improvements:
  - Add gradient backgrounds to card headers
  - Enhance visual hierarchy with decorative elements
  - Improve button styles with rounded corners and icons
  - Standardize input field sizes and styles
  - Add consistent border radius to components

- Code Optimizations:
  - Remove unused imports (IconHelpCircle, IconKey, IconPlus)
  - Remove unused showWarning import
  - Remove unused loadChannelModels import
  - Remove unused useRef and useParams hooks
  - Clean up whitespace and formatting

- Style Enhancements:
  - Convert static colors to gradient backgrounds
  - Add floating circle decorations for visual interest
  - Improve text contrast with white text on gradient backgrounds
  - Add semi-transparent white backgrounds to icons
  - Standardize card header layouts

- Accessibility:
  - Improve text contrast ratios
  - Maintain consistent visual hierarchy
  - Add relative positioning for better overlay handling

This refactor focuses on modernizing the UI while maintaining all existing functionality. The changes are purely presentational and do not affect the component's behavior.
This commit is contained in:
Apple\Apple
2025-05-25 12:53:00 +08:00
parent 452853c1a4
commit dadc2cf329
4 changed files with 973 additions and 871 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -14,26 +14,35 @@ import {
Input,
Typography,
Spin,
Modal,
Select,
Banner,
TextArea,
Card,
Tag,
} from '@douyinfe/semi-ui';
import TextInput from '../../components/custom/TextInput.js';
import {
IconSave,
IconClose,
IconBookmark,
IconUser,
IconCode,
} from '@douyinfe/semi-icons';
import { getChannelModels } from '../../components/utils.js';
import { useTranslation } from 'react-i18next';
const { Text, Title } = Typography;
const MODEL_MAPPING_EXAMPLE = {
'gpt-3.5-turbo': 'gpt-3.5-turbo-0125',
};
const EditTagModal = (props) => {
const { t } = useTranslation();
const { visible, tag, handleClose, refresh } = props;
const [loading, setLoading] = useState(false);
const [originModelOptions, setOriginModelOptions] = useState([]);
const [modelOptions, setModelOptions] = useState([]);
const [groupOptions, setGroupOptions] = useState([]);
const [basicModels, setBasicModels] = useState([]);
const [fullModels, setFullModels] = useState([]);
const [customModel, setCustomModel] = useState('');
const originInputs = {
tag: '',
@@ -90,7 +99,6 @@ const EditTagModal = (props) => {
if (inputs.models.length === 0) {
setInputs((inputs) => ({ ...inputs, models: localModels }));
}
setBasicModels(localModels);
}
};
@@ -102,14 +110,6 @@ const EditTagModal = (props) => {
value: model.id,
}));
setOriginModelOptions(localModelOptions);
setFullModels(res.data.data.map((model) => model.id));
setBasicModels(
res.data.data
.filter((model) => {
return model.id.startsWith('gpt-') || model.id.startsWith('text-');
})
.map((model) => model.id),
);
} catch (error) {
showError(error.message);
}
@@ -238,136 +238,215 @@ const EditTagModal = (props) => {
return (
<SideSheet
title='编辑标签'
placement='right'
title={
<Space>
<Tag color="blue" shape="circle">{t('编辑')}</Tag>
<Title heading={4} className="m-0">
{t('编辑标签')}
</Title>
</Space>
}
headerStyle={{
borderBottom: '1px solid var(--semi-color-border)',
padding: '24px'
}}
bodyStyle={{
backgroundColor: 'var(--semi-color-bg-0)',
padding: '0'
}}
visible={visible}
width={600}
onCancel={handleClose}
footer={
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<div className="flex justify-end bg-white">
<Space>
<Button onClick={handleClose}>取消</Button>
<Button type='primary' onClick={handleSave} loading={loading}>
保存
<Button
theme="solid"
size="large"
className="!rounded-full"
onClick={handleSave}
loading={loading}
icon={<IconSave />}
>
{t('保存')}
</Button>
<Button
theme="light"
size="large"
className="!rounded-full"
type="primary"
onClick={handleClose}
icon={<IconClose />}
>
{t('取消')}
</Button>
</Space>
</div>
}
closeIcon={null}
>
<div style={{ marginTop: 10 }}>
<Banner
type={'warning'}
description={<>所有编辑均为覆盖操作留空则不更改</>}
></Banner>
</div>
<Spin spinning={loading}>
<TextInput
label='标签名,留空则解散标签'
name='newTag'
value={inputs.new_tag}
onChange={(value) => setInputs({ ...inputs, new_tag: value })}
placeholder='请输入新标签'
/>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>模型留空则不更改</Typography.Text>
<div className="p-6">
<Card className="!rounded-2xl shadow-sm border-0 mb-6">
<div className="flex items-center mb-4 p-6 rounded-xl" style={{
background: 'linear-gradient(135deg, #1e3a8a 0%, #2563eb 50%, #3b82f6 100%)',
position: 'relative'
}}>
<div className="absolute inset-0 overflow-hidden">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
<div className="absolute -bottom-8 -left-8 w-24 h-24 bg-white opacity-10 rounded-full"></div>
</div>
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center mr-4 relative">
<IconBookmark size="large" style={{ color: '#ffffff' }} />
</div>
<div className="relative">
<Text style={{ color: '#ffffff' }} className="text-lg font-medium">{t('标签信息')}</Text>
<div style={{ color: '#ffffff' }} className="text-sm opacity-80">{t('标签的基本配置')}</div>
</div>
</div>
<Banner
type="warning"
description={t('所有编辑均为覆盖操作,留空则不更改')}
className="!rounded-lg mb-4"
/>
<div className="space-y-4">
<div>
<Text strong className="block mb-2">{t('标签名称')}</Text>
<Input
value={inputs.new_tag}
onChange={(value) => setInputs({ ...inputs, new_tag: value })}
placeholder={t('请输入新标签,留空则解散标签')}
size="large"
className="!rounded-lg"
/>
</div>
</div>
</Card>
<Card className="!rounded-2xl shadow-sm border-0 mb-6">
<div className="flex items-center mb-4 p-6 rounded-xl" style={{
background: 'linear-gradient(135deg, #4c1d95 0%, #6d28d9 50%, #7c3aed 100%)',
position: 'relative'
}}>
<div className="absolute inset-0 overflow-hidden">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
<div className="absolute -bottom-8 -left-8 w-24 h-24 bg-white opacity-10 rounded-full"></div>
</div>
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center mr-4 relative">
<IconCode size="large" style={{ color: '#ffffff' }} />
</div>
<div className="relative">
<Text style={{ color: '#ffffff' }} className="text-lg font-medium">{t('模型配置')}</Text>
<div style={{ color: '#ffffff' }} className="text-sm opacity-80">{t('模型选择和映射设置')}</div>
</div>
</div>
<div className="space-y-4">
<div>
<Text strong className="block mb-2">{t('模型')}</Text>
<Select
placeholder={t('请选择该渠道所支持的模型,留空则不更改')}
name='models'
multiple
filter
searchPosition='dropdown'
onChange={(value) => handleInputChange('models', value)}
value={inputs.models}
optionList={modelOptions}
size="large"
className="!rounded-lg"
/>
</div>
<div>
<Input
addonAfter={
<Button type='primary' onClick={addCustomModels} className="!rounded-r-lg">
{t('填入')}
</Button>
}
placeholder={t('输入自定义模型名称')}
value={customModel}
onChange={(value) => setCustomModel(value.trim())}
size="large"
className="!rounded-lg"
/>
</div>
<div>
<Text strong className="block mb-2">{t('模型重定向')}</Text>
<TextArea
placeholder={t('此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改')}
name='model_mapping'
onChange={(value) => handleInputChange('model_mapping', value)}
autosize
value={inputs.model_mapping}
className="!rounded-lg font-mono"
/>
<Space className="mt-2">
<Text
className="text-blue-500 cursor-pointer"
onClick={() => handleInputChange('model_mapping', JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2))}
>
{t('填入模板')}
</Text>
<Text
className="text-blue-500 cursor-pointer"
onClick={() => handleInputChange('model_mapping', JSON.stringify({}, null, 2))}
>
{t('清空重定向')}
</Text>
<Text
className="text-blue-500 cursor-pointer"
onClick={() => handleInputChange('model_mapping', '')}
>
{t('不更改')}
</Text>
</Space>
</div>
</div>
</Card>
<Card className="!rounded-2xl shadow-sm border-0">
<div className="flex items-center mb-4 p-6 rounded-xl" style={{
background: 'linear-gradient(135deg, #065f46 0%, #059669 50%, #10b981 100%)',
position: 'relative'
}}>
<div className="absolute inset-0 overflow-hidden">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
<div className="absolute -bottom-8 -left-8 w-24 h-24 bg-white opacity-10 rounded-full"></div>
</div>
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center mr-4 relative">
<IconUser size="large" style={{ color: '#ffffff' }} />
</div>
<div className="relative">
<Text style={{ color: '#ffffff' }} className="text-lg font-medium">{t('分组设置')}</Text>
<div style={{ color: '#ffffff' }} className="text-sm opacity-80">{t('用户分组配置')}</div>
</div>
</div>
<div className="space-y-4">
<div>
<Text strong className="block mb-2">{t('分组')}</Text>
<Select
placeholder={t('请选择可以使用该渠道的分组,留空则不更改')}
name='groups'
multiple
allowAdditions
additionLabel={t('请在系统设置页面编辑分组倍率以添加新的分组:')}
onChange={(value) => handleInputChange('groups', value)}
value={inputs.groups}
optionList={groupOptions}
size="large"
className="!rounded-lg"
/>
</div>
</div>
</Card>
</div>
<Select
placeholder={'请选择该渠道所支持的模型,留空则不更改'}
name='models'
required
multiple
selection
filter
searchPosition='dropdown'
onChange={(value) => {
handleInputChange('models', value);
}}
value={inputs.models}
autoComplete='new-password'
optionList={modelOptions}
/>
<Input
addonAfter={
<Button type='primary' onClick={addCustomModels}>
填入
</Button>
}
placeholder='输入自定义模型名称'
value={customModel}
onChange={(value) => {
setCustomModel(value.trim());
}}
/>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>分组留空则不更改</Typography.Text>
</div>
<Select
placeholder={'请选择可以使用该渠道的分组,留空则不更改'}
name='groups'
required
multiple
selection
allowAdditions
additionLabel={'请在系统设置页面编辑分组倍率以添加新的分组:'}
onChange={(value) => {
handleInputChange('groups', value);
}}
value={inputs.groups}
autoComplete='new-password'
optionList={groupOptions}
/>
<div style={{ marginTop: 10 }}>
<Typography.Text strong>模型重定向</Typography.Text>
</div>
<TextArea
placeholder={`此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改`}
name='model_mapping'
onChange={(value) => {
handleInputChange('model_mapping', value);
}}
autosize
value={inputs.model_mapping}
autoComplete='new-password'
/>
<Space>
<Typography.Text
style={{
color: 'rgba(var(--semi-blue-5), 1)',
userSelect: 'none',
cursor: 'pointer',
}}
onClick={() => {
handleInputChange(
'model_mapping',
JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2),
);
}}
>
填入模板
</Typography.Text>
<Typography.Text
style={{
color: 'rgba(var(--semi-blue-5), 1)',
userSelect: 'none',
cursor: 'pointer',
}}
onClick={() => {
handleInputChange('model_mapping', JSON.stringify({}, null, 2));
}}
>
清空重定向
</Typography.Text>
<Typography.Text
style={{
color: 'rgba(var(--semi-blue-5), 1)',
userSelect: 'none',
cursor: 'pointer',
}}
onClick={() => {
handleInputChange('model_mapping', '');
}}
>
不更改
</Typography.Text>
</Space>
</Spin>
</SideSheet>
);