Restore consistent RMB display and stable image-studio option state after save
Constraint: Fix the visible regressions without changing the broader pricing model or image-studio backend contract. Rejected: Ad hoc page-specific symbol patches | would leave model-pricing views inconsistent and fragile. Confidence: high Scope-risk: narrow Directive: Keep model-pricing summaries on shared currency formatting and keep dynamic image-studio model controls fully controlled after save. Tested: cd web && NODE_OPTIONS='--max-old-space-size=4096' npm run build Not-tested: Browser-click verification on the live model plaza and image-studio settings form.
This commit is contained in:
@@ -20,7 +20,11 @@ For commercial licensing, please contact support@quantumnous.com
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card, Avatar, Typography, Table, Tag } from '@douyinfe/semi-ui';
|
import { Card, Avatar, Typography, Table, Tag } from '@douyinfe/semi-ui';
|
||||||
import { IconCoinMoneyStroked } from '@douyinfe/semi-icons';
|
import { IconCoinMoneyStroked } from '@douyinfe/semi-icons';
|
||||||
import { calculateModelPrice, getModelPriceItems } from '../../../../../helpers';
|
import {
|
||||||
|
calculateModelPrice,
|
||||||
|
formatPriceItemValue,
|
||||||
|
getModelPriceItems,
|
||||||
|
} from '../../../../../helpers';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ const ModelPricingTable = ({
|
|||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<div key={item.key}>
|
<div key={item.key}>
|
||||||
<div className='font-semibold text-orange-600'>
|
<div className='font-semibold text-orange-600'>
|
||||||
{item.label} {item.value}
|
{item.label} {formatPriceItemValue(item, siteDisplayType)}
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs text-gray-500'>{item.suffix}</div>
|
<div className='text-xs text-gray-500'>{item.suffix}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
46
web/src/helpers/utils.jsx
vendored
46
web/src/helpers/utils.jsx
vendored
@@ -880,7 +880,7 @@ export const formatPriceInfo = (priceData, t, quotaDisplayType = 'USD') => {
|
|||||||
<>
|
<>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<span key={item.key} style={{ color: 'var(--semi-color-text-1)' }}>
|
<span key={item.key} style={{ color: 'var(--semi-color-text-1)' }}>
|
||||||
{item.label} {item.value}
|
{item.label} {formatPriceItemValue(item, quotaDisplayType)}
|
||||||
{item.suffix}
|
{item.suffix}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
@@ -888,6 +888,50 @@ export const formatPriceInfo = (priceData, t, quotaDisplayType = 'USD') => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatPriceItemValue = (item, quotaDisplayType = 'USD') => {
|
||||||
|
if (!item) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawValue = item.value;
|
||||||
|
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quotaDisplayType === 'TOKENS' || String(item.suffix || '').includes('x')) {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusStr = localStorage.getItem('status');
|
||||||
|
let symbol = '¥';
|
||||||
|
let rate = 1;
|
||||||
|
|
||||||
|
if (quotaDisplayType === 'CNY' || quotaDisplayType === 'USD') {
|
||||||
|
symbol = '¥';
|
||||||
|
try {
|
||||||
|
if (statusStr) {
|
||||||
|
const status = JSON.parse(statusStr);
|
||||||
|
rate = status?.usd_exchange_rate || 7;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
} else if (quotaDisplayType === 'CUSTOM') {
|
||||||
|
try {
|
||||||
|
if (statusStr) {
|
||||||
|
const status = JSON.parse(statusStr);
|
||||||
|
symbol = status?.custom_currency_symbol || '¤';
|
||||||
|
rate = status?.custom_currency_exchange_rate || 1;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericValue = Number(rawValue);
|
||||||
|
if (!Number.isFinite(numericValue)) {
|
||||||
|
return `${symbol}${rawValue}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${symbol}${Number((numericValue * rate).toFixed(6))}`;
|
||||||
|
};
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// CardPro 分页配置函数
|
// CardPro 分页配置函数
|
||||||
// 用于创建 CardPro 的 paginationArea 配置
|
// 用于创建 CardPro 的 paginationArea 配置
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ const SIZE_OPTIONS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const QUALITY_OPTIONS = ['auto', 'low', 'medium', 'high'];
|
const QUALITY_OPTIONS = ['auto', 'low', 'medium', 'high'];
|
||||||
|
const ALL_SIZE_OPTIONS = [...SIZE_OPTIONS];
|
||||||
|
const ALL_QUALITY_OPTIONS = [...QUALITY_OPTIONS];
|
||||||
|
|
||||||
const normalizeSettings = (value) => {
|
const normalizeSettings = (value) => {
|
||||||
const next = { ...DEFAULT_SETTINGS, ...(value || {}) };
|
const next = { ...DEFAULT_SETTINGS, ...(value || {}) };
|
||||||
@@ -52,10 +54,10 @@ const normalizeSettings = (value) => {
|
|||||||
allow_quality: item?.allow_quality !== false,
|
allow_quality: item?.allow_quality !== false,
|
||||||
allowed_sizes: Array.isArray(item?.allowed_sizes)
|
allowed_sizes: Array.isArray(item?.allowed_sizes)
|
||||||
? item.allowed_sizes.filter(Boolean)
|
? item.allowed_sizes.filter(Boolean)
|
||||||
: ['auto'],
|
: ALL_SIZE_OPTIONS,
|
||||||
allowed_qualities: Array.isArray(item?.allowed_qualities)
|
allowed_qualities: Array.isArray(item?.allowed_qualities)
|
||||||
? item.allowed_qualities.filter(Boolean)
|
? item.allowed_qualities.filter(Boolean)
|
||||||
: ['auto', 'low', 'medium', 'high'],
|
: ALL_QUALITY_OPTIONS,
|
||||||
default_size: item?.default_size || 'auto',
|
default_size: item?.default_size || 'auto',
|
||||||
default_quality: item?.default_quality || 'auto',
|
default_quality: item?.default_quality || 'auto',
|
||||||
}))
|
}))
|
||||||
@@ -102,6 +104,10 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
key: 'ImageStudioRetentionMinutes',
|
key: 'ImageStudioRetentionMinutes',
|
||||||
value: String(inputs.retention_minutes || 30),
|
value: String(inputs.retention_minutes || 30),
|
||||||
});
|
});
|
||||||
|
const snapshot = structuredClone(inputs);
|
||||||
|
setInputs(snapshot);
|
||||||
|
setInputsRow(snapshot);
|
||||||
|
refForm.current?.setValues(snapshot);
|
||||||
showSuccess(t('保存成功'));
|
showSuccess(t('保存成功'));
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -224,8 +230,8 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
allow_ratio: true,
|
allow_ratio: true,
|
||||||
allow_resolution: true,
|
allow_resolution: true,
|
||||||
allow_quality: true,
|
allow_quality: true,
|
||||||
allowed_sizes: ['auto'],
|
allowed_sizes: ALL_SIZE_OPTIONS,
|
||||||
allowed_qualities: ['auto', 'low', 'medium', 'high'],
|
allowed_qualities: ALL_QUALITY_OPTIONS,
|
||||||
default_size: 'auto',
|
default_size: 'auto',
|
||||||
default_quality: 'auto',
|
default_quality: 'auto',
|
||||||
};
|
};
|
||||||
@@ -248,7 +254,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('启用该模型')}
|
label={t('启用该模型')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.enabled}
|
checked={setting.enabled}
|
||||||
onChange={(value) => updateModelSetting(modelName, { enabled: value })}
|
onChange={(value) => updateModelSetting(modelName, { enabled: value })}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -258,7 +264,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许生成')}
|
label={t('允许生成')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.allow_generate}
|
checked={setting.allow_generate}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, { allow_generate: value })
|
updateModelSetting(modelName, { allow_generate: value })
|
||||||
}
|
}
|
||||||
@@ -270,7 +276,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许编辑')}
|
label={t('允许编辑')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.allow_edit}
|
checked={setting.allow_edit}
|
||||||
onChange={(value) => updateModelSetting(modelName, { allow_edit: value })}
|
onChange={(value) => updateModelSetting(modelName, { allow_edit: value })}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -280,7 +286,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许选择质量')}
|
label={t('允许选择质量')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.allow_quality}
|
checked={setting.allow_quality}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, { allow_quality: value })
|
updateModelSetting(modelName, { allow_quality: value })
|
||||||
}
|
}
|
||||||
@@ -294,7 +300,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许切换比例')}
|
label={t('允许切换比例')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.allow_ratio}
|
checked={setting.allow_ratio}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, { allow_ratio: value })
|
updateModelSetting(modelName, { allow_ratio: value })
|
||||||
}
|
}
|
||||||
@@ -306,7 +312,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许切换分辨率')}
|
label={t('允许切换分辨率')}
|
||||||
checkedText='|'
|
checkedText='|'
|
||||||
uncheckedText='〇'
|
uncheckedText='〇'
|
||||||
initValue={setting.allow_resolution}
|
checked={setting.allow_resolution}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, { allow_resolution: value })
|
updateModelSetting(modelName, { allow_resolution: value })
|
||||||
}
|
}
|
||||||
@@ -320,7 +326,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许分辨率')}
|
label={t('允许分辨率')}
|
||||||
multiple
|
multiple
|
||||||
optionList={SIZE_OPTIONS.map((item) => ({ label: item, value: item }))}
|
optionList={SIZE_OPTIONS.map((item) => ({ label: item, value: item }))}
|
||||||
initValue={setting.allowed_sizes}
|
value={setting.allowed_sizes}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, {
|
updateModelSetting(modelName, {
|
||||||
allowed_sizes: Array.isArray(value) ? value : [],
|
allowed_sizes: Array.isArray(value) ? value : [],
|
||||||
@@ -334,7 +340,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
label={t('允许质量')}
|
label={t('允许质量')}
|
||||||
multiple
|
multiple
|
||||||
optionList={QUALITY_OPTIONS.map((item) => ({ label: item, value: item }))}
|
optionList={QUALITY_OPTIONS.map((item) => ({ label: item, value: item }))}
|
||||||
initValue={setting.allowed_qualities}
|
value={setting.allowed_qualities}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, {
|
updateModelSetting(modelName, {
|
||||||
allowed_qualities: Array.isArray(value) ? value : [],
|
allowed_qualities: Array.isArray(value) ? value : [],
|
||||||
@@ -349,7 +355,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
field={`model_settings_${modelName}_default_size`}
|
field={`model_settings_${modelName}_default_size`}
|
||||||
label={t('默认分辨率')}
|
label={t('默认分辨率')}
|
||||||
optionList={SIZE_OPTIONS.map((item) => ({ label: item, value: item }))}
|
optionList={SIZE_OPTIONS.map((item) => ({ label: item, value: item }))}
|
||||||
initValue={setting.default_size}
|
value={setting.default_size}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, { default_size: String(value || 'auto') })
|
updateModelSetting(modelName, { default_size: String(value || 'auto') })
|
||||||
}
|
}
|
||||||
@@ -360,7 +366,7 @@ export default function SettingsImageStudio({ options, refresh }) {
|
|||||||
field={`model_settings_${modelName}_default_quality`}
|
field={`model_settings_${modelName}_default_quality`}
|
||||||
label={t('默认质量')}
|
label={t('默认质量')}
|
||||||
optionList={QUALITY_OPTIONS.map((item) => ({ label: item, value: item }))}
|
optionList={QUALITY_OPTIONS.map((item) => ({ label: item, value: item }))}
|
||||||
initValue={setting.default_quality}
|
value={setting.default_quality}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
updateModelSetting(modelName, {
|
updateModelSetting(modelName, {
|
||||||
default_quality: String(value || 'auto'),
|
default_quality: String(value || 'auto'),
|
||||||
|
|||||||
Reference in New Issue
Block a user