1、后台积分比例需要对图片增加和列表增加传入图片价格,倍率、基础积分、每张积分

2、后台列表视频倍率、视频基础积分、视频每秒积分放在单独的传入视频列里
3、列表图片的对应放在单独的传入图片列里
4、/credits/credit-ratios接口对应返回图片添加的规则
5、/credits/ratios接口对应返回图片添加的规则
6、同步修改前台AI创作的/conversation的具体最终图片积分计算逻辑
This commit is contained in:
2026-07-09 10:48:06 +08:00
parent c05a52498e
commit 790d546451
9 changed files with 184 additions and 62 deletions
File diff suppressed because one or more lines are too long
+36 -36
View File
@@ -1,37 +1,37 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>后台管理</title>
<script>
(function() {
var cached = localStorage.getItem('siteInfo');
if (cached) {
try {
var info = JSON.parse(cached);
if (info.siteName) {
document.title = info.siteName + ' - 管理后台';
}
if (info.siteLogo) {
var link = document.querySelector('link[rel="icon"]');
if (link) {
link.href = info.siteLogo;
link.type = 'image/png';
}
}
} catch (e) {}
}
})();
</script>
<script type="module" crossorigin src="/assets/index-cvtLwU6k.js"></script>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
<title>后台管理</title>
<script>
(function() {
var cached = localStorage.getItem('siteInfo');
if (cached) {
try {
var info = JSON.parse(cached);
if (info.siteName) {
document.title = info.siteName + ' - 管理后台';
}
if (info.siteLogo) {
var link = document.querySelector('link[rel="icon"]');
if (link) {
link.href = info.siteLogo;
link.type = 'image/png';
}
}
} catch (e) {}
}
})();
</script>
<script type="module" crossorigin src="/assets/index-Bq0Cmo9c.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
</head>
<body>
<div id="root"></div>
</body>
</html>
+90 -18
View File
@@ -25,6 +25,9 @@ interface CreditRatio {
inputVideoRatio: number;
inputVideoBaseCredits: number;
inputVideoPerSecondCredits: number;
inputImageRatio: number;
inputImageBaseCredits: number;
inputImagePerImageCredits: number;
}
interface CreditRatioFormValues {
@@ -37,6 +40,9 @@ interface CreditRatioFormValues {
inputVideoRatio?: number;
inputVideoBaseCredits?: number;
inputVideoPerSecondCredits?: number;
inputImageRatio?: number;
inputImageBaseCredits?: number;
inputImagePerImageCredits?: number;
}
const DEFAULT_IMAGE_SIZES = ['2K', '4K'];
@@ -136,6 +142,9 @@ const AdminCreditRatios: React.FC = () => {
payload.input_video_base_credits = values.inputVideoBaseCredits ?? 0;
payload.input_video_per_second_credits = values.inputVideoPerSecondCredits ?? 0;
}
payload.input_image_ratio = values.inputImageRatio ?? 1.0;
payload.input_image_base_credits = values.inputImageBaseCredits ?? 0;
payload.input_image_per_image_credits = values.inputImagePerImageCredits ?? 0;
if (modal.ratio) {
await saveCreditRatio({ id: modal.ratio.id, ...payload });
message.success('已更新');
@@ -188,6 +197,9 @@ const AdminCreditRatios: React.FC = () => {
inputVideoRatio: ratio.inputVideoRatio,
inputVideoBaseCredits: ratio.inputVideoBaseCredits,
inputVideoPerSecondCredits: ratio.inputVideoPerSecondCredits,
inputImageRatio: ratio.inputImageRatio,
inputImageBaseCredits: ratio.inputImageBaseCredits,
inputImagePerImageCredits: ratio.inputImagePerImageCredits,
});
} else {
form.resetFields();
@@ -199,6 +211,9 @@ const AdminCreditRatios: React.FC = () => {
inputVideoRatio: 1.0,
inputVideoBaseCredits: 0,
inputVideoPerSecondCredits: 0.5,
inputImageRatio: 1.0,
inputImageBaseCredits: 0,
inputImagePerImageCredits: 0.5,
});
}
};
@@ -238,31 +253,60 @@ const AdminCreditRatios: React.FC = () => {
),
},
{
title: '视频倍率', dataIndex: 'inputVideoRatio', width: 100,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : (
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
)}</Typography.Text>
),
title: '传入视频',
children: [
{
title: '倍率', dataIndex: 'inputVideoRatio', width: 90,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : (
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
)}</Typography.Text>
),
},
{
title: '基础积分', dataIndex: 'inputVideoBaseCredits', width: 100,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分`}</Typography.Text>
),
},
{
title: '每秒积分', dataIndex: 'inputVideoPerSecondCredits', width: 100,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分/秒`}</Typography.Text>
),
},
],
},
{
title: '视频基础积分', dataIndex: 'inputVideoBaseCredits', width: 110,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分`}</Typography.Text>
),
title: '传入图片',
children: [
{
title: '倍率', dataIndex: 'inputImageRatio', width: 90,
render: (v: number) => (
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
),
},
{
title: '基础积分', dataIndex: 'inputImageBaseCredits', width: 100,
render: (v: number) => <Typography.Text>{v} </Typography.Text>,
},
{
title: '每张积分', dataIndex: 'inputImagePerImageCredits', width: 100,
render: (v: number) => <Typography.Text>{v} /</Typography.Text>,
},
],
},
{
title: '视频每秒积分', dataIndex: 'inputVideoPerSecondCredits', width: 110,
render: (v: number, r: CreditRatio) => (
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分/秒`}</Typography.Text>
),
},
{
title: '示例计算(视频15秒,上传视频15秒)', key: 'example', width: 140,
title: '示例计算', key: 'example', width: 160,
render: (_: any, r: CreditRatio) => {
let total: number;
if (r.genType === 'image') {
total = Math.round(r.baseCredits * r.ratio);
if (r.inputImageRatio && r.inputImagePerImageCredits) {
total += Math.round(
(r.inputImageBaseCredits + r.inputImagePerImageCredits * 3) * r.inputImageRatio
);
}
} else {
total = Math.round((r.baseCredits + r.perSecondCredits * 15) * r.ratio);
if (r.inputVideoRatio && r.inputVideoPerSecondCredits) {
@@ -270,6 +314,11 @@ const AdminCreditRatios: React.FC = () => {
(r.inputVideoBaseCredits + r.inputVideoPerSecondCredits * 15) * r.inputVideoRatio
);
}
if (r.inputImageRatio && r.inputImagePerImageCredits) {
total += Math.round(
(r.inputImageBaseCredits + r.inputImagePerImageCredits * 3) * r.inputImageRatio
);
}
}
return <Typography.Text strong style={{ color: '#6366f1' }}>{total} </Typography.Text>;
},
@@ -346,7 +395,7 @@ const AdminCreditRatios: React.FC = () => {
rowKey="id"
loading={loading}
pagination={false}
scroll={{ x: 1180 }}
scroll={{ x: 1500 }}
/>
</Card>
@@ -421,6 +470,29 @@ const AdminCreditRatios: React.FC = () => {
</div>
</div>
)}
<div style={{
marginTop: 12,
padding: '12px 16px',
backgroundColor: '#f0fff4',
borderRadius: 8,
border: '1px solid #c6f6d5',
}}>
<Typography.Text strong style={{ display: 'block', marginBottom: 8, color: '#059669' }}>
</Typography.Text>
<div style={{ display: 'flex', gap: 16 }}>
<Form.Item name="inputImageRatio" label="倍率" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片倍率' }]}>
<InputNumber min={0} max={10} step={0.1} style={{ width: '100%' }} size="large" />
</Form.Item>
<Form.Item name="inputImageBaseCredits" label="基础积分" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片基础积分' }]}>
<InputNumber min={0} max={500} style={{ width: '100%' }} size="large" />
</Form.Item>
<Form.Item name="inputImagePerImageCredits" label="每张积分" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片每张积分' }]}>
<InputNumber min={0} max={50} style={{ width: '100%' }} size="large" />
</Form.Item>
</div>
</div>
</Form>
</Modal>
</div>