🎨 refactor(EditTagModal): tidy imports & enhance state-sync on open

Motivation
• Remove unused UI components to keep the bundle lean and silence linter warnings.
• Ensure every time the side-sheet opens it reflects the latest tag data, avoiding stale form values (e.g., model / group mismatches).

Key Changes
1. UI Imports
   – Dropped `Input`, `Select`, `TextArea` from `@douyinfe/semi-ui` (unused in Form-based version).
2. State Reset & Form Sync
   – On `visible` or `tag` change:
     • Refresh model & group options.
     • Reset `inputs` to clean defaults (`originInputs`) carrying the current `tag`.
     • Pre-fill Form through `formApiRef` to keep controlled fields aligned.
3. Minor Cleanup
   – Added inline comment clarifying local state reset purpose.

Result
Opening the “Edit Tag” side-sheet now always displays accurate data without residual selections, and build output is cleaner due to removed dead imports.
This commit is contained in:
t0ng7u
2025-07-04 06:14:15 +08:00
parent 7d691f362d
commit c0a23ffa62

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { import {
API, API,
showError, showError,
@@ -11,15 +11,13 @@ import {
SideSheet, SideSheet,
Space, Space,
Button, Button,
Input,
Typography, Typography,
Spin, Spin,
Select,
Banner, Banner,
TextArea,
Card, Card,
Tag, Tag,
Avatar, Avatar,
Form,
} from '@douyinfe/semi-ui'; } from '@douyinfe/semi-ui';
import { import {
IconSave, IconSave,
@@ -53,9 +51,14 @@ const EditTagModal = (props) => {
models: [], models: [],
}; };
const [inputs, setInputs] = useState(originInputs); const [inputs, setInputs] = useState(originInputs);
const formApiRef = useRef(null);
const getInitValues = () => ({ ...originInputs });
const handleInputChange = (name, value) => { const handleInputChange = (name, value) => {
setInputs((inputs) => ({ ...inputs, [name]: value })); setInputs((inputs) => ({ ...inputs, [name]: value }));
if (formApiRef.current) {
formApiRef.current.setValue(name, value);
}
if (name === 'type') { if (name === 'type') {
let localModels = []; let localModels = [];
switch (value) { switch (value) {
@@ -133,27 +136,25 @@ const EditTagModal = (props) => {
} }
}; };
const handleSave = async () => { const handleSave = async (values) => {
setLoading(true); setLoading(true);
let data = { const formVals = values || formApiRef.current?.getValues() || {};
tag: tag, let data = { tag };
}; if (formVals.model_mapping) {
if (inputs.model_mapping !== null && inputs.model_mapping !== '') { if (!verifyJSON(formVals.model_mapping)) {
if (inputs.model_mapping !== '' && !verifyJSON(inputs.model_mapping)) {
showInfo('模型映射必须是合法的 JSON 格式!'); showInfo('模型映射必须是合法的 JSON 格式!');
setLoading(false); setLoading(false);
return; return;
} }
data.model_mapping = inputs.model_mapping; data.model_mapping = formVals.model_mapping;
} }
if (inputs.groups.length > 0) { if (formVals.groups && formVals.groups.length > 0) {
data.groups = inputs.groups.join(','); data.groups = formVals.groups.join(',');
} }
if (inputs.models.length > 0) { if (formVals.models && formVals.models.length > 0) {
data.models = inputs.models.join(','); data.models = formVals.models.join(',');
} }
data.new_tag = inputs.new_tag; data.new_tag = formVals.new_tag;
// check have any change
if ( if (
data.model_mapping === undefined && data.model_mapping === undefined &&
data.groups === undefined && data.groups === undefined &&
@@ -202,7 +203,7 @@ const EditTagModal = (props) => {
const res = await API.get(`/api/channel/tag/models?tag=${tag}`); const res = await API.get(`/api/channel/tag/models?tag=${tag}`);
if (res?.data?.success) { if (res?.data?.success) {
const models = res.data.data ? res.data.data.split(',') : []; const models = res.data.data ? res.data.data.split(',') : [];
setInputs((inputs) => ({ ...inputs, models: models })); handleInputChange('models', models);
} else { } else {
showError(res.data.message); showError(res.data.message);
} }
@@ -213,19 +214,32 @@ const EditTagModal = (props) => {
} }
}; };
fetchModels().then();
fetchGroups().then();
fetchTagModels().then();
if (formApiRef.current) {
formApiRef.current.setValues({
...getInitValues(),
tag: tag,
new_tag: tag,
});
}
setInputs({ setInputs({
...originInputs, ...originInputs,
tag: tag, tag: tag,
new_tag: tag, new_tag: tag,
}); });
fetchModels().then(); }, [visible, tag]);
fetchGroups().then();
fetchTagModels().then(); // Call the new function useEffect(() => {
}, [visible, tag]); // Add tag to dependency array if (formApiRef.current) {
formApiRef.current.setValues(inputs);
}
}, [inputs]);
const addCustomModels = () => { const addCustomModels = () => {
if (customModel.trim() === '') return; if (customModel.trim() === '') return;
// 使用逗号分隔字符串,然后去除每个模型名称前后的空格
const modelArray = customModel.split(',').map((model) => model.trim()); const modelArray = customModel.split(',').map((model) => model.trim());
let localModels = [...inputs.models]; let localModels = [...inputs.models];
@@ -233,11 +247,9 @@ const EditTagModal = (props) => {
const addedModels = []; const addedModels = [];
modelArray.forEach((model) => { modelArray.forEach((model) => {
// 检查模型是否已存在,且模型名称非空
if (model && !localModels.includes(model)) { if (model && !localModels.includes(model)) {
localModels.push(model); // 添加到模型列表 localModels.push(model);
localModelOptions.push({ localModelOptions.push({
// 添加到下拉选项
key: model, key: model,
text: model, text: model,
value: model, value: model,
@@ -246,7 +258,6 @@ const EditTagModal = (props) => {
} }
}); });
// 更新状态值
setModelOptions(localModelOptions); setModelOptions(localModelOptions);
setCustomModel(''); setCustomModel('');
handleInputChange('models', localModels); handleInputChange('models', localModels);
@@ -283,7 +294,7 @@ const EditTagModal = (props) => {
<Space> <Space>
<Button <Button
theme="solid" theme="solid"
onClick={handleSave} onClick={() => formApiRef.current?.submitForm()}
loading={loading} loading={loading}
icon={<IconSave />} icon={<IconSave />}
> >
@@ -302,6 +313,13 @@ const EditTagModal = (props) => {
} }
closeIcon={null} closeIcon={null}
> >
<Form
key={tag || 'edit'}
initValues={getInitValues()}
getFormApi={(api) => (formApiRef.current = api)}
onSubmit={handleSave}
>
{() => (
<Spin spinning={loading}> <Spin spinning={loading}>
<div className="p-2"> <div className="p-2">
<Card className="!rounded-2xl shadow-sm border-0 mb-6"> <Card className="!rounded-2xl shadow-sm border-0 mb-6">
@@ -323,15 +341,13 @@ const EditTagModal = (props) => {
/> />
<div className="space-y-4"> <div className="space-y-4">
<div> <Form.Input
<Text strong className="block mb-2">{t('标签名称')}</Text> field='new_tag'
<Input label={t('标签名称')}
value={inputs.new_tag}
onChange={(value) => setInputs({ ...inputs, new_tag: value })}
placeholder={t('请输入新标签,留空则解散标签')} placeholder={t('请输入新标签,留空则解散标签')}
onChange={(value) => handleInputChange('new_tag', value)}
/> />
</div> </div>
</div>
</Card> </Card>
<Card className="!rounded-2xl shadow-sm border-0 mb-6"> <Card className="!rounded-2xl shadow-sm border-0 mb-6">
@@ -347,68 +363,45 @@ const EditTagModal = (props) => {
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div>
<Text strong className="block mb-2">{t('模型')}</Text>
<Banner <Banner
type="info" type="info"
description={t('当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。')} description={t('当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。')}
className="!rounded-lg mb-4" className="!rounded-lg mb-4"
/> />
<Select <Form.Select
field='models'
label={t('模型')}
placeholder={t('请选择该渠道所支持的模型,留空则不更改')} placeholder={t('请选择该渠道所支持的模型,留空则不更改')}
name='models'
multiple multiple
filter filter
searchPosition='dropdown' searchPosition='dropdown'
onChange={(value) => handleInputChange('models', value)}
value={inputs.models}
optionList={modelOptions} optionList={modelOptions}
style={{ width: '100%' }}
onChange={(value) => handleInputChange('models', value)}
/> />
</div>
<div> <Form.Input
<Input field='custom_model'
addonAfter={ label={t('自定义模型名称')}
<Button type='primary' onClick={addCustomModels} className="!rounded-r-lg">
{t('填入')}
</Button>
}
placeholder={t('输入自定义模型名称')} placeholder={t('输入自定义模型名称')}
value={customModel}
onChange={(value) => setCustomModel(value.trim())} onChange={(value) => setCustomModel(value.trim())}
suffix={<Button size='small' type='primary' onClick={addCustomModels}>{t('填入')}</Button>}
/> />
</div>
<div> <Form.TextArea
<Text strong className="block mb-2">{t('模型重定向')}</Text> field='model_mapping'
<TextArea label={t('模型重定向')}
placeholder={t('此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改')} placeholder={t('此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改')}
name='model_mapping'
onChange={(value) => handleInputChange('model_mapping', value)}
autosize autosize
value={inputs.model_mapping} onChange={(value) => handleInputChange('model_mapping', value)}
/> extraText={(
<Space className="mt-2"> <Space>
<Text <Text className="!text-semi-color-primary cursor-pointer" onClick={() => handleInputChange('model_mapping', JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2))}>{t('填入模板')}</Text>
className="!text-semi-color-primary cursor-pointer" <Text className="!text-semi-color-primary cursor-pointer" onClick={() => handleInputChange('model_mapping', JSON.stringify({}, null, 2))}>{t('清空重定向')}</Text>
onClick={() => handleInputChange('model_mapping', JSON.stringify(MODEL_MAPPING_EXAMPLE, null, 2))} <Text className="!text-semi-color-primary cursor-pointer" onClick={() => handleInputChange('model_mapping', '')}>{t('不更改')}</Text>
>
{t('填入模板')}
</Text>
<Text
className="!text-semi-color-primary cursor-pointer"
onClick={() => handleInputChange('model_mapping', JSON.stringify({}, null, 2))}
>
{t('清空重定向')}
</Text>
<Text
className="!text-semi-color-primary cursor-pointer"
onClick={() => handleInputChange('model_mapping', '')}
>
{t('不更改')}
</Text>
</Space> </Space>
</div> )}
/>
</div> </div>
</Card> </Card>
@@ -425,23 +418,23 @@ const EditTagModal = (props) => {
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<div> <Form.Select
<Text strong className="block mb-2">{t('分组')}</Text> field='groups'
<Select label={t('分组')}
placeholder={t('请选择可以使用该渠道的分组,留空则不更改')} placeholder={t('请选择可以使用该渠道的分组,留空则不更改')}
name='groups'
multiple multiple
allowAdditions allowAdditions
additionLabel={t('请在系统设置页面编辑分组倍率以添加新的分组:')} additionLabel={t('请在系统设置页面编辑分组倍率以添加新的分组:')}
onChange={(value) => handleInputChange('groups', value)}
value={inputs.groups}
optionList={groupOptions} optionList={groupOptions}
style={{ width: '100%' }}
onChange={(value) => handleInputChange('groups', value)}
/> />
</div> </div>
</div>
</Card> </Card>
</div> </div>
</Spin> </Spin>
)}
</Form>
</SideSheet> </SideSheet>
); );
}; };