From 369ecf365ab531f1b093a3c56298f914459ab047 Mon Sep 17 00:00:00 2001
From: HynoR <20227709+HynoR@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:10:38 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E8=AE=BE=E7=BD=AE=E5=8F=AF=E8=A7=86=E5=8C=96=E7=BC=96?=
=?UTF-8?q?=E8=BE=91=E5=99=A8=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/components/OperationSetting.js | 5 +
.../Operation/ModelSettingsVisualEditor.js | 251 ++++++++++++++++++
2 files changed, 256 insertions(+)
create mode 100644 web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js
index 1c50c971..d7d69f71 100644
--- a/web/src/components/OperationSetting.js
+++ b/web/src/components/OperationSetting.js
@@ -8,6 +8,7 @@ import SettingsDataDashboard from '../pages/Setting/Operation/SettingsDataDashbo
import SettingsMonitoring from '../pages/Setting/Operation/SettingsMonitoring.js';
import SettingsCreditLimit from '../pages/Setting/Operation/SettingsCreditLimit.js';
import SettingsMagnification from '../pages/Setting/Operation/SettingsMagnification.js';
+import ModelSettingsVisualEditor from '../pages/Setting/Operation/ModelSettingsVisualEditor.js';
import { API, showError, showSuccess } from '../helpers';
import SettingsChats from '../pages/Setting/Operation/SettingsChats.js';
@@ -141,6 +142,10 @@ const OperationSetting = () => {
+ {/*可视化倍率设置*/}
+
+
+
>
);
diff --git a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
new file mode 100644
index 00000000..b3ebb9e9
--- /dev/null
+++ b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
@@ -0,0 +1,251 @@
+// ModelSettingsVisualEditor.js
+import React, { useEffect, useState } from 'react';
+import { Table, Button, Input, Modal, Form, Space } from '@douyinfe/semi-ui';
+import { IconDelete, IconPlus, IconSearch } from '@douyinfe/semi-icons';
+import { showError, showSuccess } from '../../../helpers';
+import { API } from '../../../helpers';
+export default function ModelSettingsVisualEditor(props) {
+ const [models, setModels] = useState([]);
+ const [visible, setVisible] = useState(false);
+ const [currentModel, setCurrentModel] = useState(null);
+ const [searchText, setSearchText] = useState('');
+ const [currentPage, setCurrentPage] = useState(1);
+ const pageSize = 10;
+
+ useEffect(() => {
+ try {
+ const modelPrice = JSON.parse(props.options.ModelPrice || '{}');
+ const modelRatio = JSON.parse(props.options.ModelRatio || '{}');
+ const completionRatio = JSON.parse(props.options.CompletionRatio || '{}');
+
+ // 合并所有模型名称
+ const modelNames = new Set([
+ ...Object.keys(modelPrice),
+ ...Object.keys(modelRatio),
+ ...Object.keys(completionRatio)
+ ]);
+
+ const modelData = Array.from(modelNames).map(name => ({
+ name,
+ price: modelPrice[name] === undefined ? '' : modelPrice[name],
+ ratio: modelRatio[name] === undefined ? '' : modelRatio[name],
+ completionRatio: completionRatio[name] === undefined ? '' : completionRatio[name]
+ }));
+
+ setModels(modelData);
+ } catch (error) {
+ console.error('JSON解析错误:', error);
+ }
+ }, [props.options]);
+
+ // 首先声明分页相关的工具函数
+ const getPagedData = (data, currentPage, pageSize) => {
+ const start = (currentPage - 1) * pageSize;
+ const end = start + pageSize;
+ return data.slice(start, end);
+ };
+
+ // 在 return 语句之前,先处理过滤和分页逻辑
+ const filteredModels = models.filter(model =>
+ searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) : true
+ );
+
+ // 然后基于过滤后的数据计算分页数据
+ const pagedData = getPagedData(filteredModels, currentPage, pageSize);
+
+ // 转换回JSON格式
+ const generateJSONOutput = async () => {
+ const output = {
+ ModelPrice: {},
+ ModelRatio: {},
+ CompletionRatio: {}
+ };
+ let currentConvertModelName = '';
+ try {
+ models.forEach(model => {
+ currentConvertModelName = model.name;
+ if (model.price !== '') output.ModelPrice[model.name] = parseFloat(model.price);
+ if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
+ if (model.completionRatio != '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
+ });
+ } catch (error) {
+ console.error('JSON转换错误:', error);
+ showError('JSON转换错误, 请检查输入+模型名称: ' + currentConvertModelName);
+ return;
+ }
+
+ const finalOutput = {
+ ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
+ ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
+ CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
+ }
+
+ forEach(finalOutput, (value, key) => {
+ API.put('/api/option/', {
+ key: key,
+ value
+ }).then(res => {
+ if (res.data.success) {
+ showSuccess('保存成功');
+ } else {
+ showError(res.data.message);
+ }
+ })
+ })
+
+
+ showSuccess('转换成功');
+ props.refresh();
+ };
+
+ const columns = [
+ {
+ title: '模型名称',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: '固定价格',
+ dataIndex: 'price',
+ key: 'price',
+ render: (text, record) => (
+ updateModel(record.name, 'price', value)}
+ />
+ )
+ },
+ {
+ title: '模型倍率',
+ dataIndex: 'ratio',
+ key: 'ratio',
+ render: (text, record) => (
+ updateModel(record.name, 'ratio', value)}
+ />
+ )
+ },
+ {
+ title: '补全倍率',
+ dataIndex: 'completionRatio',
+ key: 'completionRatio',
+ render: (text, record) => (
+ updateModel(record.name, 'completionRatio', value)}
+ />
+ )
+ },
+ {
+ title: '操作',
+ key: 'action',
+ render: (_, record) => (
+ }
+ type="danger"
+ onClick={() => deleteModel(record.name)}
+ />
+ )
+ }
+ ];
+
+ const updateModel = (name, field, value) => {
+ setModels(prev =>
+ prev.map(model =>
+ model.name === name
+ ? { ...model, [field]: value }
+ : model
+ )
+ );
+ };
+
+ const deleteModel = (name) => {
+ setModels(prev => prev.filter(model => model.name !== name));
+ };
+
+ const addModel = (values) => {
+ setModels(prev => [...prev, {
+ name: values.name,
+ price: values.price || '',
+ ratio: values.ratio || '',
+ completionRatio: values.completionRatio || ''
+ }]);
+ setVisible(false);
+ };
+
+
+ return (
+ <>
+
+
+ } onClick={() => setVisible(true)}>
+ 添加模型
+
+
+ }
+ placeholder="搜索模型名称"
+ value={searchText}
+ onChange={value => {
+ setSearchText(value)
+ // 搜索时重置页码
+ setCurrentPage(1);
+ }}
+ style={{ width: 200 }}
+ />
+
+ setCurrentPage(page),
+ showTotal: true,
+ showSizeChanger: false
+ }}
+ />
+
+
+ setVisible(false)}
+ onOk={() => {
+ currentModel && addModel(currentModel);
+ }}
+ >
+ setCurrentModel(prev => ({ ...prev, name: value }))}
+ />
+ setCurrentModel(prev => ({ ...prev, price: value }))}
+ />
+ setCurrentModel(prev => ({ ...prev, ratio: value }))}
+ />
+ setCurrentModel(prev => ({ ...prev, completionRatio: value }))}
+ />
+
+
+ >
+ );
+}
From 8504e0724542fce45ae44a6e6bf592ccea12ccd1 Mon Sep 17 00:00:00 2001
From: HynoR <20227709+HynoR@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:29:43 +0800
Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BF=9D?=
=?UTF-8?q?=E5=AD=98=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E6=95=B0=E6=8D=AE=E6=8F=90=E4=BA=A4=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Operation/ModelSettingsVisualEditor.js | 96 ++++++++++++-------
1 file changed, 60 insertions(+), 36 deletions(-)
diff --git a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
index b3ebb9e9..86ec2cd8 100644
--- a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
+++ b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
@@ -1,7 +1,7 @@
// ModelSettingsVisualEditor.js
import React, { useEffect, useState } from 'react';
import { Table, Button, Input, Modal, Form, Space } from '@douyinfe/semi-ui';
-import { IconDelete, IconPlus, IconSearch } from '@douyinfe/semi-icons';
+import { IconDelete, IconPlus, IconSearch,IconSave } from '@douyinfe/semi-icons';
import { showError, showSuccess } from '../../../helpers';
import { API } from '../../../helpers';
export default function ModelSettingsVisualEditor(props) {
@@ -10,6 +10,7 @@ export default function ModelSettingsVisualEditor(props) {
const [currentModel, setCurrentModel] = useState(null);
const [searchText, setSearchText] = useState('');
const [currentPage, setCurrentPage] = useState(1);
+ const [loading, setLoading] = useState(false);
const pageSize = 10;
useEffect(() => {
@@ -53,49 +54,66 @@ export default function ModelSettingsVisualEditor(props) {
// 然后基于过滤后的数据计算分页数据
const pagedData = getPagedData(filteredModels, currentPage, pageSize);
- // 转换回JSON格式
- const generateJSONOutput = async () => {
+ const SubmitData = async () => {
+ setLoading(true);
const output = {
ModelPrice: {},
ModelRatio: {},
CompletionRatio: {}
};
let currentConvertModelName = '';
+
try {
+ // 数据转换
models.forEach(model => {
currentConvertModelName = model.name;
if (model.price !== '') output.ModelPrice[model.name] = parseFloat(model.price);
if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
if (model.completionRatio != '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
});
- } catch (error) {
- console.error('JSON转换错误:', error);
- showError('JSON转换错误, 请检查输入+模型名称: ' + currentConvertModelName);
- return;
- }
-
- const finalOutput = {
- ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
- ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
- CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
- }
-
- forEach(finalOutput, (value, key) => {
- API.put('/api/option/', {
- key: key,
- value
- }).then(res => {
- if (res.data.success) {
- showSuccess('保存成功');
- } else {
- showError(res.data.message);
+
+ // 准备API请求数组
+ const finalOutput = {
+ ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
+ ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
+ CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
+ };
+
+ const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
+ return API.put('/api/option/', {
+ key,
+ value
+ });
+ });
+
+ // 批量处理请求
+ const results = await Promise.all(requestQueue);
+
+ // 验证结果
+ if (requestQueue.length === 1) {
+ if (results.includes(undefined)) return;
+ } else if (requestQueue.length > 1) {
+ if (results.includes(undefined)) {
+ return showError('部分保存失败,请重试');
}
- })
- })
-
-
- showSuccess('转换成功');
- props.refresh();
+ }
+
+ // 检查每个请求的结果
+ for (const res of results) {
+ if (!res.data.success) {
+ return showError(res.data.message);
+ }
+ }
+
+ showSuccess('保存成功');
+ props.refresh();
+
+ } catch (error) {
+ console.error('保存失败:', error);
+ showError('保存失败,请重试');
+ } finally {
+ setLoading(false);
+ }
};
const columns = [
@@ -111,7 +129,7 @@ export default function ModelSettingsVisualEditor(props) {
render: (text, record) => (
updateModel(record.name, 'price', value)}
/>
)
@@ -166,27 +184,33 @@ export default function ModelSettingsVisualEditor(props) {
const deleteModel = (name) => {
setModels(prev => prev.filter(model => model.name !== name));
};
-
const addModel = (values) => {
- setModels(prev => [...prev, {
+ // 检查模型名称是否存在, 如果存在则拒绝添加
+ if (models.some(model => model.name === values.name)) {
+ showError('模型名称已存在');
+ return;
+ }
+ setModels(prev => [{
name: values.name,
price: values.price || '',
ratio: values.ratio || '',
completionRatio: values.completionRatio || ''
- }]);
+ }, ...prev]);
setVisible(false);
+ showSuccess('添加成功');
};
return (
<>
+ 模型价格
} onClick={() => setVisible(true)}>
添加模型
-
}
From 498590d9fde5c0f5afc37fcb92427ece34e4b535 Mon Sep 17 00:00:00 2001
From: HynoR <20227709+HynoR@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:42:02 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E8=AE=BE=E7=BD=AE=E5=8F=AF=E8=A7=86=E5=8C=96=E7=BC=96?=
=?UTF-8?q?=E8=BE=91=E5=99=A8=EF=BC=8C=E5=A2=9E=E5=BC=BA=E8=BE=93=E5=85=A5?=
=?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=92=8C=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Operation/ModelSettingsVisualEditor.js | 52 ++++++++++++-------
1 file changed, 34 insertions(+), 18 deletions(-)
diff --git a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
index 86ec2cd8..31541f52 100644
--- a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
+++ b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
@@ -1,7 +1,7 @@
// ModelSettingsVisualEditor.js
import React, { useEffect, useState } from 'react';
import { Table, Button, Input, Modal, Form, Space } from '@douyinfe/semi-ui';
-import { IconDelete, IconPlus, IconSearch,IconSave } from '@douyinfe/semi-icons';
+import { IconDelete, IconPlus, IconSearch, IconSave } from '@douyinfe/semi-icons';
import { showError, showSuccess } from '../../../helpers';
import { API } from '../../../helpers';
export default function ModelSettingsVisualEditor(props) {
@@ -55,40 +55,44 @@ export default function ModelSettingsVisualEditor(props) {
const pagedData = getPagedData(filteredModels, currentPage, pageSize);
const SubmitData = async () => {
- setLoading(true);
+ setLoading(true);
const output = {
ModelPrice: {},
ModelRatio: {},
CompletionRatio: {}
};
let currentConvertModelName = '';
-
+
try {
// 数据转换
models.forEach(model => {
currentConvertModelName = model.name;
- if (model.price !== '') output.ModelPrice[model.name] = parseFloat(model.price);
- if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
- if (model.completionRatio != '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
+ if (model.price !== '') {
+ // 如果价格不为空,则转换为浮点数,忽略倍率参数
+ output.ModelPrice[model.name] = parseFloat(model.price)
+ } else {
+ if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
+ if (model.completionRatio != '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
+ }
});
-
+
// 准备API请求数组
const finalOutput = {
ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
- ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
+ ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
};
-
+
const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
return API.put('/api/option/', {
key,
value
});
});
-
+
// 批量处理请求
const results = await Promise.all(requestQueue);
-
+
// 验证结果
if (requestQueue.length === 1) {
if (results.includes(undefined)) return;
@@ -97,17 +101,17 @@ export default function ModelSettingsVisualEditor(props) {
return showError('部分保存失败,请重试');
}
}
-
+
// 检查每个请求的结果
for (const res of results) {
if (!res.data.success) {
return showError(res.data.message);
}
}
-
+
showSuccess('保存成功');
props.refresh();
-
+
} catch (error) {
console.error('保存失败:', error);
showError('保存失败,请重试');
@@ -130,6 +134,7 @@ export default function ModelSettingsVisualEditor(props) {
updateModel(record.name, 'price', value)}
/>
)
@@ -141,7 +146,9 @@ export default function ModelSettingsVisualEditor(props) {
render: (text, record) => (
updateModel(record.name, 'ratio', value)}
/>
)
@@ -153,7 +160,8 @@ export default function ModelSettingsVisualEditor(props) {
render: (text, record) => (
updateModel(record.name, 'completionRatio', value)}
/>
)
@@ -172,6 +180,10 @@ export default function ModelSettingsVisualEditor(props) {
];
const updateModel = (name, field, value) => {
+ if (isNaN(value)) {
+ showError('请输入数字');
+ return;
+ }
setModels(prev =>
prev.map(model =>
model.name === name
@@ -203,7 +215,7 @@ export default function ModelSettingsVisualEditor(props) {
return (
<>
- 模型价格
+ 模型价格
} onClick={() => setVisible(true)}>
@@ -247,6 +259,7 @@ export default function ModelSettingsVisualEditor(props) {
}}
>
setCurrentModel(prev => ({ ...prev, price: value }))}
/>
setCurrentModel(prev => ({ ...prev, ratio: value }))}
/>
setCurrentModel(prev => ({ ...prev, completionRatio: value }))}
/>
From 3587f2c6e9760c5c136f33b00320b9af3052cf3c Mon Sep 17 00:00:00 2001
From: HynoR <20227709+HynoR@users.noreply.github.com>
Date: Fri, 13 Dec 2024 14:45:49 +0800
Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BB=B7?=
=?UTF-8?q?=E6=A0=BC=E5=92=8C=E5=80=8D=E7=8E=87=E7=9A=84=E4=BA=92=E6=96=A5?=
=?UTF-8?q?=E9=AA=8C=E8=AF=81=EF=BC=8C=E4=BC=98=E5=8C=96=E6=A8=A1=E5=9E=8B?=
=?UTF-8?q?=E5=90=8D=E7=A7=B0=E8=BE=93=E5=85=A5=E6=8F=90=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../pages/Setting/Operation/ModelSettingsVisualEditor.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
index 31541f52..6158b17e 100644
--- a/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
+++ b/web/src/pages/Setting/Operation/ModelSettingsVisualEditor.js
@@ -134,7 +134,6 @@ export default function ModelSettingsVisualEditor(props) {
updateModel(record.name, 'price', value)}
/>
)
@@ -202,6 +201,11 @@ export default function ModelSettingsVisualEditor(props) {
showError('模型名称已存在');
return;
}
+ // 不允许同时添加固定价格和倍率
+ if (values.price !== '' && (values.ratio !== '' || values.completionRatio !== '')) {
+ showError('固定价格和倍率不能同时存在');
+ return;
+ }
setModels(prev => [{
name: values.name,
price: values.price || '',
@@ -263,6 +267,7 @@ export default function ModelSettingsVisualEditor(props) {
setCurrentModel(prev => ({ ...prev, name: value }))}
/>