|
|
|
@@ -0,0 +1,932 @@ |
|
|
|
# 扩展可视化倍率设置实施计划 |
|
|
|
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
|
|
|
|
|
|
|
**目标:** 在 ModelSettingsVisualEditor 组件中添加 5 个高级倍率字段支持(缓存、图片、音频倍率) |
|
|
|
|
|
|
|
**架构:** 扩展现有 React 组件的状态管理和表单逻辑,增加高级倍率字段的数据初始化、编辑、保存和验证功能 |
|
|
|
|
|
|
|
**技术栈:** React, Semi UI, i18n, JavaScript |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 文件结构 |
|
|
|
|
|
|
|
### 修改文件 |
|
|
|
- `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx` - 主要功能实现(500+ 行) |
|
|
|
- `web/src/i18n/locales/zh-CN.json` - 中文翻译 |
|
|
|
- `web/src/i18n/locales/en.json` - 英文翻译 |
|
|
|
|
|
|
|
### 参考文件(只读,用于理解实现模式) |
|
|
|
- `feat/channel-pricing-extended:web/src/pages/Setting/Ratio/ChannelPricingView.jsx` - 渠道价格设置的参考实现 |
|
|
|
|
|
|
|
### 数据流向 |
|
|
|
``` |
|
|
|
props.options (API 数据) |
|
|
|
↓ |
|
|
|
useEffect 解析 JSON → models 状态(包含 5 个新字段) |
|
|
|
↓ |
|
|
|
用户编辑表单 → currentModel 状态 |
|
|
|
↓ |
|
|
|
addOrUpdateModel → 更新 models 状态 |
|
|
|
↓ |
|
|
|
SubmitData → 序列化 JSON → API.put('/api/option/') |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 1: 添加国际化文本 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/i18n/locales/zh-CN.json` |
|
|
|
- Modify: `web/src/i18n/locales/en.json` |
|
|
|
|
|
|
|
### 步骤 1.1: 添加中文翻译 |
|
|
|
|
|
|
|
在 `web/src/i18n/locales/zh-CN.json` 中添加以下键值对。找到文件末尾,在最后一个键值对后添加逗号,然后添加: |
|
|
|
|
|
|
|
```json |
|
|
|
"高级比例(留空使用默认值)": "高级比例(留空使用默认值)", |
|
|
|
"缓存读取倍率": "缓存读取倍率", |
|
|
|
"缓存创建倍率(5分钟)": "缓存创建倍率(5分钟)", |
|
|
|
"图片倍率": "图片倍率", |
|
|
|
"音频输入倍率": "音频输入倍率", |
|
|
|
"音频输出倍率": "音频输出倍率", |
|
|
|
"默认值:1.0": "默认值:1.0", |
|
|
|
"默认值:1.25": "默认值:1.25" |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 1.2: 添加英文翻译 |
|
|
|
|
|
|
|
在 `web/src/i18n/locales/en.json` 中添加对应的英文翻译: |
|
|
|
|
|
|
|
```json |
|
|
|
"高级比例(留空使用默认值)": "Advanced Ratios (Leave empty for defaults)", |
|
|
|
"缓存读取倍率": "Cache Read Ratio", |
|
|
|
"缓存创建倍率(5分钟)": "Cache Creation Ratio (5min)", |
|
|
|
"图片倍率": "Image Ratio", |
|
|
|
"音频输入倍率": "Audio Input Ratio", |
|
|
|
"音频输出倍率": "Audio Output Ratio", |
|
|
|
"默认值:1.0": "Default: 1.0", |
|
|
|
"默认值:1.25": "Default: 1.25" |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 1.3: 验证 JSON 格式 |
|
|
|
|
|
|
|
确保添加的 JSON 格式正确,没有语法错误: |
|
|
|
|
|
|
|
```bash |
|
|
|
cd web/src/i18n/locales |
|
|
|
node -e "console.log('zh-CN:', JSON.parse(require('fs').readFileSync('zh-CN.json', 'utf8'))); console.log('en:', JSON.parse(require('fs').readFileSync('en.json', 'utf8')))" |
|
|
|
``` |
|
|
|
|
|
|
|
预期输出:无错误,输出两个对象的内容 |
|
|
|
|
|
|
|
### 步骤 1.4: 提交国际化更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/i18n/locales/zh-CN.json web/src/i18n/locales/en.json |
|
|
|
git commit -m "feat(i18n): add advanced ratio translations |
|
|
|
|
|
|
|
- Add Chinese translations for 5 advanced ratio fields |
|
|
|
- Add English translations for 5 advanced ratio fields |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 2: 扩展数据初始化逻辑 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:59-91` |
|
|
|
|
|
|
|
### 步骤 2.1: 在 useEffect 中解析高级倍率 JSON |
|
|
|
|
|
|
|
找到 `useEffect` 开始(约第 59 行),在 `try` 块中,在现有的 `const completionRatio = JSON.parse(props.options.CompletionRatio || '{}');` 之后添加: |
|
|
|
|
|
|
|
```javascript |
|
|
|
// 新增:解析高级倍率字段 |
|
|
|
const cacheRatio = JSON.parse(props.options.CacheRatio || '{}'); |
|
|
|
const createCacheRatio = JSON.parse(props.options.CreateCacheRatio || '{}'); |
|
|
|
const imageRatio = JSON.parse(props.options.ImageRatio || '{}'); |
|
|
|
const audioRatio = JSON.parse(props.options.AudioRatio || '{}'); |
|
|
|
const audioCompletionRatio = JSON.parse(props.options.AudioCompletionRatio || '{}'); |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 2.2: 扩展模型名称合并逻辑 |
|
|
|
|
|
|
|
在同一个 `useEffect` 中,找到 `const modelNames = new Set([...])`(约第 66 行),将数组扩展为: |
|
|
|
|
|
|
|
```javascript |
|
|
|
const modelNames = new Set([ |
|
|
|
...Object.keys(modelPrice), |
|
|
|
...Object.keys(modelRatio), |
|
|
|
...Object.keys(completionRatio), |
|
|
|
...Object.keys(cacheRatio), |
|
|
|
...Object.keys(createCacheRatio), |
|
|
|
...Object.keys(imageRatio), |
|
|
|
...Object.keys(audioRatio), |
|
|
|
...Object.keys(audioCompletionRatio), |
|
|
|
]); |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 2.3: 扩展模型数据对象 |
|
|
|
|
|
|
|
在同一个 `useEffect` 中,找到 `const modelData = Array.from(modelNames).map((name) => {`(约第 72 行),在返回对象中添加新字段。找到现有的: |
|
|
|
|
|
|
|
```javascript |
|
|
|
const comp = completionRatio[name] === undefined ? '' : completionRatio[name]; |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加: |
|
|
|
|
|
|
|
```javascript |
|
|
|
const cacheR = cacheRatio[name] === undefined ? '' : cacheRatio[name]; |
|
|
|
const cacheCreationR = createCacheRatio[name] === undefined ? '' : createCacheRatio[name]; |
|
|
|
const imageR = imageRatio[name] === undefined ? '' : imageRatio[name]; |
|
|
|
const audioR = audioRatio[name] === undefined ? '' : audioRatio[name]; |
|
|
|
const audioCompR = audioCompletionRatio[name] === undefined ? '' : audioCompletionRatio[name]; |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 2.4: 更新返回对象结构 |
|
|
|
|
|
|
|
继续在同一个 `map` 函数中,找到返回对象(约第 78 行),更新为: |
|
|
|
|
|
|
|
```javascript |
|
|
|
return { |
|
|
|
name, |
|
|
|
price, |
|
|
|
ratio, |
|
|
|
completionRatio: comp, |
|
|
|
cacheRatio: cacheR, |
|
|
|
cacheCreationRatio: cacheCreationR, |
|
|
|
imageRatio: imageR, |
|
|
|
audioRatio: audioR, |
|
|
|
audioCompletionRatio: audioCompR, |
|
|
|
hasConflict: price !== '' && (ratio !== '' || comp !== ''), |
|
|
|
}; |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 2.5: 启动开发服务器验证数据加载 |
|
|
|
|
|
|
|
```bash |
|
|
|
cd web |
|
|
|
bun run dev |
|
|
|
``` |
|
|
|
|
|
|
|
访问:http://localhost:5173/setting/ratio → 切换到"可视化倍率设置"标签 |
|
|
|
|
|
|
|
打开浏览器控制台,输入: |
|
|
|
|
|
|
|
```javascript |
|
|
|
// 检查 models 状态是否包含新字段 |
|
|
|
const tableRows = document.querySelectorAll('.semi-table-row'); |
|
|
|
console.log('Table rows count:', tableRows.length); |
|
|
|
``` |
|
|
|
|
|
|
|
预期:表格正常显示,无 JavaScript 错误 |
|
|
|
|
|
|
|
### 步骤 2.6: 提交数据初始化更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(ratio): parse advanced ratio fields in data initialization |
|
|
|
|
|
|
|
- Parse 5 new JSON fields from props.options |
|
|
|
- Extend modelNames Set to include keys from all ratio fields |
|
|
|
- Add 5 new fields to model data objects |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 3: 添加高级比例表单区域 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:616-737` |
|
|
|
|
|
|
|
### 步骤 3.1: 找到插入位置 |
|
|
|
|
|
|
|
在文件中找到 `{pricingSubMode === 'token-price' && (` 块的闭合位置(约第 736 行),应该在 `)}$ 之前,`{pricingMode === 'per-request' && (` 之前。 |
|
|
|
|
|
|
|
### 步骤 3.2: 插入高级比例表单区域 |
|
|
|
|
|
|
|
在 `{pricingSubMode === 'token-price' && (` 块的闭合 `)}` 之后,`{pricingMode === 'per-request' && (` 之前,添加: |
|
|
|
|
|
|
|
```jsx |
|
|
|
<Form.Section text={t('高级比例(留空使用默认值)')}> |
|
|
|
<Form.InputNumber |
|
|
|
field='cacheRatio' |
|
|
|
label={t('缓存读取倍率')} |
|
|
|
min={0} |
|
|
|
step={0.01} |
|
|
|
placeholder={t('默认值:1.0')} |
|
|
|
/> |
|
|
|
<Form.InputNumber |
|
|
|
field='cacheCreationRatio' |
|
|
|
label={t('缓存创建倍率(5分钟)')} |
|
|
|
min={0} |
|
|
|
step={0.01} |
|
|
|
placeholder={t('默认值:1.25')} |
|
|
|
/> |
|
|
|
<Form.InputNumber |
|
|
|
field='imageRatio' |
|
|
|
label={t('图片倍率')} |
|
|
|
min={0} |
|
|
|
step={0.01} |
|
|
|
placeholder={t('默认值:1.0')} |
|
|
|
/> |
|
|
|
<Form.InputNumber |
|
|
|
field='audioRatio' |
|
|
|
label={t('音频输入倍率')} |
|
|
|
min={0} |
|
|
|
step={0.01} |
|
|
|
placeholder={t('默认值:1.0')} |
|
|
|
/> |
|
|
|
<Form.InputNumber |
|
|
|
field='audioCompletionRatio' |
|
|
|
label={t('音频输出倍率')} |
|
|
|
min={0} |
|
|
|
step={0.01} |
|
|
|
placeholder={t('默认值:1.0')} |
|
|
|
/> |
|
|
|
</Form.Section> |
|
|
|
``` |
|
|
|
|
|
|
|
**注意缩进**:保持与周围代码一致的缩进(约 14-16 个空格) |
|
|
|
|
|
|
|
### 步骤 3.2: 验证表单渲染 |
|
|
|
|
|
|
|
确保开发服务器仍在运行(`bun run dev`),访问:http://localhost:5173/setting/ratio |
|
|
|
|
|
|
|
1. 点击任意模型的"编辑"按钮 |
|
|
|
2. 确认定价模式为"按量计费" |
|
|
|
3. 向下滚动,应该能看到"高级比例(留空使用默认值)"区域 |
|
|
|
4. 确认 5 个输入框都有正确的标签和 placeholder |
|
|
|
|
|
|
|
预期: |
|
|
|
- 新区域显示在"价格设置方式"和"固定价格"之间 |
|
|
|
- 所有 5 个字段可见且可交互 |
|
|
|
- placeholder 显示正确的默认值 |
|
|
|
|
|
|
|
### 步骤 3.3: 测试输入验证 |
|
|
|
|
|
|
|
在高级比例区域的任意输入框中: |
|
|
|
1. 输入负数(如 `-1`),预期:无法输入或显示验证错误 |
|
|
|
2. 输入文字(如 `abc`),预期:无法输入(InputNumber 组件自动限制) |
|
|
|
3. 输入小数(如 `1.23`),预期:正常接受 |
|
|
|
4. 输入多位小数(如 `1.23456`),预期:step=0.01 限制 |
|
|
|
|
|
|
|
### 步骤 3.4: 提交表单 UI 更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(ui): add advanced ratios form section |
|
|
|
|
|
|
|
- Add Form.Section with 5 advanced ratio input fields |
|
|
|
- Configure min={0}, step={0.01} for all fields |
|
|
|
- Add placeholders showing default values |
|
|
|
- Only show when pricingMode === 'per-token' |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 4: 更新编辑模型逻辑 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:395-454` |
|
|
|
|
|
|
|
### 步骤 4.1: 在 editModel 中设置表单值 |
|
|
|
|
|
|
|
找到 `editModel` 函数中的 `setTimeout` 块(约第 435 行),在 `formRef.current.setValues({` 调用中添加新字段。 |
|
|
|
|
|
|
|
找到现有的: |
|
|
|
```javascript |
|
|
|
completionTokenPrice: modelCopy.completionTokenPrice |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加(注意在 `})` 之前添加逗号): |
|
|
|
```javascript |
|
|
|
, |
|
|
|
// 新增:高级倍率字段 |
|
|
|
cacheRatio: modelCopy.cacheRatio, |
|
|
|
cacheCreationRatio: modelCopy.cacheCreationRatio, |
|
|
|
imageRatio: modelCopy.imageRatio, |
|
|
|
audioRatio: modelCopy.audioRatio, |
|
|
|
audioCompletionRatio: modelCopy.audioCompletionRatio |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 4.2: 验证编辑回显 |
|
|
|
|
|
|
|
1. 点击已有数据的模型(如 gpt-4)的"编辑"按钮 |
|
|
|
2. 在浏览器控制台中执行: |
|
|
|
|
|
|
|
```javascript |
|
|
|
// 检查表单值是否正确设置 |
|
|
|
const formApi = document.querySelector('[data-form-id]')?.__semi_form__; |
|
|
|
if (formApi) { |
|
|
|
console.log('Form values:', formApi.getValues()); |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
或者更简单:直接观察高级比例区域的输入框,如果有数据应该显示对应的值 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 如果模型有高级倍率数据,输入框应显示对应值 |
|
|
|
- 如果没有数据,输入框应为空 |
|
|
|
|
|
|
|
### 步骤 4.3: 提交编辑逻辑更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(edit): populate advanced ratios in edit modal |
|
|
|
|
|
|
|
- Add 5 advanced ratio fields to formRef.current.setValues |
|
|
|
- Ensure existing ratio data displays correctly in edit modal |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 5: 更新添加/更新模型逻辑 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:334-382` |
|
|
|
|
|
|
|
### 步骤 5.1: 在 addOrUpdateModel 中处理高级倍率字段 |
|
|
|
|
|
|
|
找到 `addOrUpdateModel` 函数(约第 334 行),在 `const updatedModel = {` 对象中添加新字段。 |
|
|
|
|
|
|
|
找到现有的: |
|
|
|
```javascript |
|
|
|
completionRatio: values.completionRatio || '', |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加: |
|
|
|
```javascript |
|
|
|
// 新增:高级倍率字段 |
|
|
|
cacheRatio: values.cacheRatio || '', |
|
|
|
cacheCreationRatio: values.cacheCreationRatio || '', |
|
|
|
imageRatio: values.imageRatio || '', |
|
|
|
audioRatio: values.audioRatio || '', |
|
|
|
audioCompletionRatio: values.audioCompletionRatio || '', |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 5.2: 测试添加新模型 |
|
|
|
|
|
|
|
1. 点击"添加模型"按钮 |
|
|
|
2. 输入模型名称:`test-model-advanced` |
|
|
|
3. 在高级比例区域输入测试值: |
|
|
|
- 缓存读取倍率:`0.5` |
|
|
|
- 缓存创建倍率:`1.5` |
|
|
|
- 图片倍率:`2.0` |
|
|
|
- 音频输入倍率:`1.2` |
|
|
|
- 音频输出倍率:`0.8` |
|
|
|
4. 点击"确定" |
|
|
|
|
|
|
|
预期: |
|
|
|
- 模型成功添加到表格 |
|
|
|
- 浏览器控制台无错误 |
|
|
|
|
|
|
|
### 步骤 5.3: 验证数据保存到状态 |
|
|
|
|
|
|
|
在浏览器控制台中检查: |
|
|
|
|
|
|
|
```javascript |
|
|
|
// 检查是否能在表格中看到新模型 |
|
|
|
const rows = document.querySelectorAll('.semi-table-row'); |
|
|
|
console.log('Total rows:', rows.length); |
|
|
|
// 应该比之前多 1 行 |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 5.4: 提交添加/更新逻辑更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(add): handle advanced ratios in addOrUpdateModel |
|
|
|
|
|
|
|
- Add 5 advanced ratio fields to updatedModel object |
|
|
|
- Use empty string fallback for undefined values |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 6: 更新保存逻辑 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:110-177` |
|
|
|
|
|
|
|
### 步骤 6.1: 扩展 output 对象 |
|
|
|
|
|
|
|
找到 `SubmitData` 函数中的 `const output = {`(约第 112 行),在对象中添加新字段。 |
|
|
|
|
|
|
|
找到现有的: |
|
|
|
```javascript |
|
|
|
CompletionRatio: {}, |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加: |
|
|
|
```javascript |
|
|
|
// 新增:高级倍率字段 |
|
|
|
CacheRatio: {}, |
|
|
|
CreateCacheRatio: {}, |
|
|
|
ImageRatio: {}, |
|
|
|
AudioRatio: {}, |
|
|
|
AudioCompletionRatio: {} |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 6.2: 添加数据转换逻辑 |
|
|
|
|
|
|
|
在同一个 `SubmitData` 函数中,找到 `models.forEach((model) => {` 块(约第 119 行),在现有的转换逻辑后添加。 |
|
|
|
|
|
|
|
找到: |
|
|
|
```javascript |
|
|
|
if (model.completionRatio !== '') |
|
|
|
output.CompletionRatio[model.name] = parseFloat(model.completionRatio); |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加: |
|
|
|
```javascript |
|
|
|
|
|
|
|
// 新增:处理高级倍率字段 |
|
|
|
if (model.cacheRatio !== '') |
|
|
|
output.CacheRatio[model.name] = parseFloat(model.cacheRatio); |
|
|
|
if (model.cacheCreationRatio !== '') |
|
|
|
output.CreateCacheRatio[model.name] = parseFloat(model.cacheCreationRatio); |
|
|
|
if (model.imageRatio !== '') |
|
|
|
output.ImageRatio[model.name] = parseFloat(model.imageRatio); |
|
|
|
if (model.audioRatio !== '') |
|
|
|
output.AudioRatio[model.name] = parseFloat(model.audioRatio); |
|
|
|
if (model.audioCompletionRatio !== '') |
|
|
|
output.AudioCompletionRatio[model.name] = parseFloat(model.audioCompletionRatio); |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 6.3: 扩展 finalOutput 对象 |
|
|
|
|
|
|
|
找到 `const finalOutput = {`(约第 137 行),在对象中添加新字段。 |
|
|
|
|
|
|
|
找到现有的: |
|
|
|
```javascript |
|
|
|
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2), |
|
|
|
``` |
|
|
|
|
|
|
|
在这一行后添加: |
|
|
|
```javascript |
|
|
|
CacheRatio: JSON.stringify(output.CacheRatio, null, 2), |
|
|
|
CreateCacheRatio: JSON.stringify(output.CreateCacheRatio, null, 2), |
|
|
|
ImageRatio: JSON.stringify(output.ImageRatio, null, 2), |
|
|
|
AudioRatio: JSON.stringify(output.AudioRatio, null, 2), |
|
|
|
AudioCompletionRatio: JSON.stringify(output.AudioCompletionRatio, null, 2), |
|
|
|
``` |
|
|
|
|
|
|
|
### 步骤 6.4: 测试保存功能 |
|
|
|
|
|
|
|
1. 确保 Task 5.2 中添加的 `test-model-advanced` 还在表格中 |
|
|
|
2. 修改它的一些高级倍率值(如将缓存读取倍率改为 `0.8`) |
|
|
|
3. 点击"应用更改"按钮 |
|
|
|
4. 打开浏览器网络标签,筛选 `/api/option/` 请求 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 看到多个 `PUT /api/option/` 请求 |
|
|
|
- 其中包含 `CacheRatio`、`CreateCacheRatio` 等新增字段 |
|
|
|
- 请求体包含 JSON 数据,键为 `test-model-advanced` |
|
|
|
|
|
|
|
### 步骤 6.5: 验证 API 请求体 |
|
|
|
|
|
|
|
在网络标签中点击任意一个 `/api/option/` 请求,查看"Payload"或"请求体": |
|
|
|
|
|
|
|
```json |
|
|
|
{ |
|
|
|
"key": "CacheRatio", |
|
|
|
"value": "{\n \"test-model-advanced\": 0.8\n}" |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
预期: |
|
|
|
- `value` 是格式化的 JSON 字符串(有缩进) |
|
|
|
- 包含正确的模型名和倍率值 |
|
|
|
|
|
|
|
### 步骤 6.6: 提交保存逻辑更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(save): serialize and save advanced ratio fields |
|
|
|
|
|
|
|
- Add 5 new fields to output object |
|
|
|
- Convert model data to float and populate output fields |
|
|
|
- Serialize all fields to JSON in finalOutput |
|
|
|
- Submit 7 total option fields (2 existing + 5 new) |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 7: 更新输入验证逻辑 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx:255-270` |
|
|
|
|
|
|
|
### 步骤 7.1: 确认 updateModel 函数 |
|
|
|
|
|
|
|
找到 `updateModel` 函数(约第 255 行),确认现有的验证逻辑: |
|
|
|
|
|
|
|
```javascript |
|
|
|
const updateModel = (name, field, value) => { |
|
|
|
if (isNaN(value) && value !== '') { |
|
|
|
showError(t('请输入数字')); |
|
|
|
return; |
|
|
|
} |
|
|
|
// ... 更新逻辑 |
|
|
|
}; |
|
|
|
``` |
|
|
|
|
|
|
|
**说明**:由于我们使用 `Form.InputNumber` 组件,它已经内置了数字验证,所以 `updateModel` 函数不需要修改。`isNaN` 检查已经足够处理高级倍率字段。 |
|
|
|
|
|
|
|
### 步骤 7.2: 测试验证逻辑 |
|
|
|
|
|
|
|
在开发服务器运行的情况下: |
|
|
|
|
|
|
|
1. 尝试在表格中直接点击模型倍率或补全倍率输入框 |
|
|
|
2. 输入文字(如 `abc`) |
|
|
|
3. 观察是否显示错误提示"请输入数字" |
|
|
|
|
|
|
|
**注意**:由于我们现在使用 `Form.InputNumber` 而非 `Input`,高级比例字段在弹窗中,`updateModel` 函数不会被它们调用。`Form.InputNumber` 会自动限制只能输入数字。 |
|
|
|
|
|
|
|
### 步骤 7.3: 验证空值处理 |
|
|
|
|
|
|
|
测试空值是否正确处理: |
|
|
|
|
|
|
|
1. 编辑一个模型 |
|
|
|
2. 在高级比例区域,将所有输入框留空 |
|
|
|
3. 点击"确定" |
|
|
|
4. 点击"应用更改" |
|
|
|
|
|
|
|
预期: |
|
|
|
- 保存成功,无错误 |
|
|
|
- 空值保存为空字符串,后端使用默认值 |
|
|
|
|
|
|
|
### 步骤 7.4: 提交验证相关更改 |
|
|
|
|
|
|
|
如果本任务没有代码修改,跳过提交。否则: |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "feat(validation): enhance input validation for advanced ratios |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 8: 完整功能测试 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Test: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx` |
|
|
|
|
|
|
|
### 步骤 8.1: 测试数据初始化 |
|
|
|
|
|
|
|
1. 清空浏览器缓存或使用隐私模式 |
|
|
|
2. 访问:http://localhost:5173/setting/ratio |
|
|
|
3. 切换到"可视化倍率设置"标签 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 表格正常显示 |
|
|
|
- 无 JavaScript 错误 |
|
|
|
- 表格包含所有现有模型 |
|
|
|
|
|
|
|
### 步骤 8.2: 测试添加带高级倍率的模型 |
|
|
|
|
|
|
|
1. 点击"添加模型" |
|
|
|
2. 输入: |
|
|
|
- 模型名称:`claude-3-5-sonnet-20241022` |
|
|
|
- 定价模式:按量计费 |
|
|
|
- 价格设置方式:按倍率设置 |
|
|
|
- 模型倍率:`1.5` |
|
|
|
- 补全倍率:`1.2` |
|
|
|
- 缓存读取倍率:`0.1` |
|
|
|
- 缓存创建倍率:`1.25` |
|
|
|
- 图片倍率:留空 |
|
|
|
- 音频输入倍率:留空 |
|
|
|
- 音频输出倍率:留空 |
|
|
|
3. 点击"确定" |
|
|
|
|
|
|
|
预期: |
|
|
|
- 模型成功添加 |
|
|
|
- 表格中显示新模型 |
|
|
|
- 无错误提示 |
|
|
|
|
|
|
|
### 步骤 8.3: 测试编辑高级倍率 |
|
|
|
|
|
|
|
1. 点击刚才添加的模型的"编辑"按钮 |
|
|
|
2. 修改高级倍率值: |
|
|
|
- 缓存读取倍率:`0.15` |
|
|
|
- 缓存创建倍率:`1.3` |
|
|
|
- 图片倍率:`2.5` |
|
|
|
- 音频输入倍率:`10.0` |
|
|
|
- 音频输出倍率:`20.0` |
|
|
|
3. 点击"确定" |
|
|
|
4. 点击"应用更改" |
|
|
|
|
|
|
|
预期: |
|
|
|
- 编辑成功 |
|
|
|
- 显示"保存成功"提示 |
|
|
|
|
|
|
|
### 步骤 8.4: 验证数据持久化 |
|
|
|
|
|
|
|
刷新浏览器页面,重新访问可视化倍率设置: |
|
|
|
|
|
|
|
1. 点击刚才编辑的模型的"编辑"按钮 |
|
|
|
2. 检查高级比例区域的值是否与步骤 8.3 中设置的一致 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 缓存读取倍率:显示 `0.15` |
|
|
|
- 缓存创建倍率:显示 `1.3` |
|
|
|
- 图片倍率:显示 `2.5` |
|
|
|
- 音频输入倍率:显示 `10.0` |
|
|
|
- 音频输出倍率:显示 `20.0` |
|
|
|
|
|
|
|
### 步骤 8.5: 测试删除模型 |
|
|
|
|
|
|
|
1. 选择测试模型 |
|
|
|
2. 点击"删除"按钮 |
|
|
|
3. 确认删除 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 模型从表格中移除 |
|
|
|
- 删除的高级倍率数据在保存时被清除(后续保存不会包含该模型) |
|
|
|
|
|
|
|
### 步骤 8.6: 测试边界情况 |
|
|
|
|
|
|
|
**测试负数输入**: |
|
|
|
1. 尝试在高级比例输入框输入 `-1` |
|
|
|
2. 预期:`min={0}` 限制,无法输入负数 |
|
|
|
|
|
|
|
**测试小数精度**: |
|
|
|
1. 输入 `0.123456` |
|
|
|
2. 预期:`step={0.01}` 限制,可以输入但会保留多位小数(Semi UI InputNumber 行为) |
|
|
|
|
|
|
|
**测试空值保存**: |
|
|
|
1. 编辑模型,清空所有高级比例输入框 |
|
|
|
2. 点击"确定",然后"应用更改" |
|
|
|
3. 预期:保存成功,后端使用默认值 |
|
|
|
|
|
|
|
### 步骤 8.7: 测试国际化切换 |
|
|
|
|
|
|
|
1. 在系统设置中切换到英文(如果支持) |
|
|
|
2. 重新访问可视化倍率设置 |
|
|
|
3. 检查高级比例区域的标签是否显示为英文 |
|
|
|
|
|
|
|
预期: |
|
|
|
- "高级比例(留空使用默认值)" → "Advanced Ratios (Leave empty for defaults)" |
|
|
|
- "缓存读取倍率" → "Cache Read Ratio" |
|
|
|
- 其他字段类似 |
|
|
|
|
|
|
|
### 步骤 8.8: 创建测试提交 |
|
|
|
|
|
|
|
如果所有测试通过,创建一个测试提交标记: |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "test: verify advanced ratios functionality |
|
|
|
|
|
|
|
- Test data initialization with empty options |
|
|
|
- Test adding model with advanced ratios |
|
|
|
- Test editing advanced ratios |
|
|
|
- Test data persistence after page refresh |
|
|
|
- Test model deletion |
|
|
|
- Test edge cases (negative numbers, decimal precision, empty values) |
|
|
|
- Test internationalization (English/Chinese) |
|
|
|
|
|
|
|
All tests passed successfully. |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Task 9: 代码清理和优化 |
|
|
|
|
|
|
|
**文件:** |
|
|
|
- Modify: `web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx` |
|
|
|
|
|
|
|
### 步骤 9.1: 检查代码格式化 |
|
|
|
|
|
|
|
运行代码格式化工具(如果项目配置了): |
|
|
|
|
|
|
|
```bash |
|
|
|
cd web |
|
|
|
bun run lint --fix |
|
|
|
``` |
|
|
|
|
|
|
|
预期: |
|
|
|
- 无 lint 错误 |
|
|
|
- 代码格式符合项目规范 |
|
|
|
|
|
|
|
### 步骤 9.2: 检查 console.log |
|
|
|
|
|
|
|
搜索并移除任何调试用的 `console.log`: |
|
|
|
|
|
|
|
```bash |
|
|
|
cd web/src/pages/Setting/Ratio |
|
|
|
grep -n "console.log" ModelSettingsVisualEditor.jsx |
|
|
|
``` |
|
|
|
|
|
|
|
如果找到任何 `console.log`,删除它们。 |
|
|
|
|
|
|
|
### 步骤 9.3: 验证组件性能 |
|
|
|
|
|
|
|
检查是否有不必要的重渲染: |
|
|
|
|
|
|
|
1. 打开浏览器 React DevTools(如果可用) |
|
|
|
2. 选择 "Profiler" 标签 |
|
|
|
3. 记录编辑模型时的性能 |
|
|
|
4. 检查是否有异常的重渲染 |
|
|
|
|
|
|
|
预期: |
|
|
|
- 组件重渲染次数合理 |
|
|
|
- 无明显的性能问题 |
|
|
|
|
|
|
|
### 步骤 9.4: 提交清理更改 |
|
|
|
|
|
|
|
如果有任何清理更改: |
|
|
|
|
|
|
|
```bash |
|
|
|
git add web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.jsx |
|
|
|
git commit -m "refactor: code cleanup and optimization |
|
|
|
|
|
|
|
- Remove debug console.log statements |
|
|
|
- Format code with linter |
|
|
|
- Verify component performance |
|
|
|
|
|
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>" |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 验证清单 |
|
|
|
|
|
|
|
在实施完成后,验证以下所有项目: |
|
|
|
|
|
|
|
### 功能验证 |
|
|
|
- [ ] 可以添加带有高级倍率的模型 |
|
|
|
- [ ] 可以编辑模型的高级倍率字段 |
|
|
|
- [ ] 可以删除模型 |
|
|
|
- [ ] 数据正确保存到后端 |
|
|
|
- [ ] 刷新页面后数据正确加载 |
|
|
|
- [ ] 空值字段使用默认值 |
|
|
|
|
|
|
|
### UI/UX 验证 |
|
|
|
- [ ] 高级比例区域正确显示 |
|
|
|
- [ ] 所有 5 个字段都有正确的标签 |
|
|
|
- [ ] Placeholder 显示正确的默认值 |
|
|
|
- [ ] 输入验证正常工作(负数、非数字) |
|
|
|
- [ ] 中英文切换正常 |
|
|
|
|
|
|
|
### 代码质量 |
|
|
|
- [ ] 无 TypeScript/JavaScript 错误 |
|
|
|
- [ ] 无 lint 错误 |
|
|
|
- [ ] 无调试 console.log |
|
|
|
- [ ] 代码格式符合项目规范 |
|
|
|
- [ ] 所有提交都有清晰的 commit message |
|
|
|
|
|
|
|
### 数据完整性 |
|
|
|
- [ ] 前端字段名与后端 Option Key 一致 |
|
|
|
- [ ] JSON 序列化/反序列化正确 |
|
|
|
- [ ] 空值处理正确(空字符串,非 null) |
|
|
|
- [ ] 数值转换正确(parseFloat) |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 实施完成后的最终检查 |
|
|
|
|
|
|
|
### 步骤 1: 查看所有提交 |
|
|
|
|
|
|
|
```bash |
|
|
|
git log --oneline --no-merges | head -20 |
|
|
|
``` |
|
|
|
|
|
|
|
预期:看到一系列清晰的、原子性的提交,每个提交对应一个任务或功能点 |
|
|
|
|
|
|
|
### 步骤 2: 验证文件更改 |
|
|
|
|
|
|
|
```bash |
|
|
|
git diff HEAD~10 HEAD --stat |
|
|
|
``` |
|
|
|
|
|
|
|
预期: |
|
|
|
- `ModelSettingsVisualEditor.jsx` 有显著更改 |
|
|
|
- `zh-CN.json` 和 `en.json` 有新增翻译 |
|
|
|
- 无其他意外更改 |
|
|
|
|
|
|
|
### 步骤 3: 最终功能测试 |
|
|
|
|
|
|
|
重复 Task 8 中的所有测试步骤,确保一切正常。 |
|
|
|
|
|
|
|
### 步骤 4: 合并到主分支(如果需要) |
|
|
|
|
|
|
|
```bash |
|
|
|
git checkout master |
|
|
|
git merge <branch-name> |
|
|
|
git push |
|
|
|
``` |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 故障排查 |
|
|
|
|
|
|
|
### 问题:表格不显示 |
|
|
|
|
|
|
|
**症状**:访问可视化倍率设置页面,表格为空 |
|
|
|
|
|
|
|
**排查**: |
|
|
|
1. 打开浏览器控制台,检查是否有 JavaScript 错误 |
|
|
|
2. 检查 `useEffect` 中的 JSON 解析是否成功 |
|
|
|
3. 验证 `props.options` 是否包含所有必需的字段 |
|
|
|
|
|
|
|
**解决**: |
|
|
|
- 确保后端 API 返回所有 7 个字段(ModelPrice, ModelRatio, CompletionRatio, CacheRatio, CreateCacheRatio, ImageRatio, AudioRatio, AudioCompletionRatio) |
|
|
|
|
|
|
|
### 问题:高级比例区域不显示 |
|
|
|
|
|
|
|
**症状**:编辑模型时看不到高级比例输入框 |
|
|
|
|
|
|
|
**排查**: |
|
|
|
1. 检查定价模式是否为"按量计费"(`pricingMode === 'per-token'`) |
|
|
|
2. 检查 Form.Section 的条件渲染 |
|
|
|
3. 验证国际化文本是否加载 |
|
|
|
|
|
|
|
**解决**: |
|
|
|
- 确保在"按量计费"模式下编辑 |
|
|
|
- 检查 `t()` 函数是否返回正确的翻译文本 |
|
|
|
|
|
|
|
### 问题:数据保存失败 |
|
|
|
|
|
|
|
**症状**:点击"应用更改"后显示错误 |
|
|
|
|
|
|
|
**排查**: |
|
|
|
1. 打开浏览器网络标签,检查 `/api/option/` 请求 |
|
|
|
2. 查看请求响应状态码和消息 |
|
|
|
3. 验证请求数据格式是否正确 |
|
|
|
|
|
|
|
**解决**: |
|
|
|
- 确保所有 7 个字段都包含在 `finalOutput` 中 |
|
|
|
- 检查 JSON 序列化是否正确 |
|
|
|
- 验证后端 API 是否支持所有 7 个 Option Key |
|
|
|
|
|
|
|
### 问题:翻译文本显示为键名 |
|
|
|
|
|
|
|
**症状**:界面显示 `"高级比例(留空使用默认值)"` 而非翻译后的文本 |
|
|
|
|
|
|
|
**排查**: |
|
|
|
1. 检查 i18n 配置文件是否正确加载 |
|
|
|
2. 验证 JSON 格式是否正确 |
|
|
|
3. 检查是否有语法错误导致整个文件无法解析 |
|
|
|
|
|
|
|
**解决**: |
|
|
|
- 修复 JSON 语法错误 |
|
|
|
- 重新加载页面 |
|
|
|
- 清除浏览器缓存 |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 预计时间 |
|
|
|
|
|
|
|
- Task 1 (国际化): 15 分钟 |
|
|
|
- Task 2 (数据初始化): 20 分钟 |
|
|
|
- Task 3 (表单 UI): 15 分钟 |
|
|
|
- Task 4 (编辑逻辑): 10 分钟 |
|
|
|
- Task 5 (添加/更新): 10 分钟 |
|
|
|
- Task 6 (保存逻辑): 15 分钟 |
|
|
|
- Task 7 (验证逻辑): 5 分钟 |
|
|
|
- Task 8 (完整测试): 30 分钟 |
|
|
|
- Task 9 (代码清理): 10 分钟 |
|
|
|
|
|
|
|
**总计**: 约 2.5 小时 |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 参考资源 |
|
|
|
|
|
|
|
### 内部文档 |
|
|
|
- 设计规范: `docs/superpowers/specs/2026-06-04-extended-visual-ratio-settings-design.md` |
|
|
|
- 渠道价格设置实现: `feat/channel-pricing-extended:web/src/pages/Setting/Ratio/ChannelPricingView.jsx` |
|
|
|
|
|
|
|
### API 文档 |
|
|
|
- Semi UI Form: https://douyinfe.github.io/semi-ui/form/form-inputnumber |
|
|
|
- React hooks: https://react.dev/reference/react |
|
|
|
|
|
|
|
### 后端接口 |
|
|
|
- GET `/api/option/` - 获取所有选项 |
|
|
|
- PUT `/api/option/` - 更新单个选项(请求体: `{ key, value }`) |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
**实施计划完成** |