feat: 分站货币符号切换功能(¥/$ 可配置,默认人民币)
后端: - Distributor 模型新增 CurrencySymbol 字段 (default: ¥) - /api/dist/site/info 响应中返回 currency_symbol - PUT /api/distributor/self 支持更新 currency_symbol 分站管理后台 (web): - DistributorSettings 新增货币符号切换 (¥ CNY / $ USD) - 添加 i18n 翻译 (en/zh/ja/fr/ru/vi) 分站前端 (dist-site): - Dashboard: 余额、已用额度使用动态符号 - Packages: 套餐价格、原价、订阅余额使用动态符号 - Pricing: 模型输入/输出价格使用动态符号 - Topup: 预设金额、支付金额、历史记录使用动态符号 - 7 个主题 Home 页: 套餐价格展示使用动态符号 - 所有页面通过 useSite().currency_symbol 读取,统一 fallback ¥ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
3ed0f67be0
commit
7a4cbb60d3
11 files changed
+46
-29
No files matched your search
@@ -16,6 +16,7 @@ export default function ClaudeHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -230,9 +231,9 @@ export default function ClaudeHome() {
|
||||
{pkg.description && <p className="text-sm text-[#6B5D4F] mb-4">{pkg.description}</p>}
|
||||
<div className="mt-auto pt-4">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-3xl font-bold text-[#3D3024]">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-[#3D3024]">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-[#8B7D6E] line-through">${pkg.original_price}</span>
|
||||
<span className="text-sm text-[#8B7D6E] line-through">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
</div>
|
||||
{pkg.duration > 0 && <p className="text-xs text-[#8B7D6E] mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
|
||||
@@ -15,6 +15,7 @@ export default function CleanHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -207,9 +208,9 @@ export default function CleanHome() {
|
||||
{pkg.description && <p className="text-sm text-gray-500 mb-4">{pkg.description}</p>}
|
||||
<div className="mt-auto pt-4">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-3xl font-bold text-gray-900">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-gray-900">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-gray-500 line-through">${pkg.original_price}</span>
|
||||
<span className="text-sm text-gray-500 line-through">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
</div>
|
||||
{pkg.duration > 0 && <p className="text-xs text-gray-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
|
||||
@@ -14,6 +14,7 @@ export default function CorporateHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -217,9 +218,9 @@ export default function CorporateHome() {
|
||||
{pkg.description && <p className="text-sm text-slate-500 mb-6">{pkg.description}</p>}
|
||||
<div className="mt-auto">
|
||||
<div className="flex items-baseline gap-1.5">
|
||||
<span className="text-4xl font-bold text-slate-900">${pkg.price}</span>
|
||||
<span className="text-4xl font-bold text-slate-900">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-slate-500 line-through">${pkg.original_price}</span>
|
||||
<span className="text-sm text-slate-500 line-through">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
</div>
|
||||
{pkg.duration > 0 && <p className="text-sm text-slate-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
|
||||
@@ -12,6 +12,7 @@ export default function DarkHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -178,9 +179,9 @@ export default function DarkHome() {
|
||||
<h3 className="text-lg font-semibold text-white mb-1">{pkg.name}</h3>
|
||||
{pkg.description && <p className="text-sm text-neutral-400 mb-4">{pkg.description}</p>}
|
||||
<div className="mt-auto pt-4">
|
||||
<span className="text-3xl font-bold text-emerald-400 font-mono">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-emerald-400 font-mono">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span>
|
||||
<span className="text-sm text-neutral-600 line-through ml-2">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ export default function DefaultHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -181,9 +182,9 @@ export default function DefaultHome() {
|
||||
{pkg.description && <p className="text-sm text-neutral-400 mb-4">{pkg.description}</p>}
|
||||
<div className="mt-auto pt-4">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-3xl font-bold text-white">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-white">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-neutral-500 line-through">${pkg.original_price}</span>
|
||||
<span className="text-sm text-neutral-500 line-through">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
</div>
|
||||
{pkg.duration > 0 && <p className="text-xs text-neutral-500 mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
|
||||
@@ -11,6 +11,7 @@ export default function MinimalHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -142,9 +143,9 @@ export default function MinimalHome() {
|
||||
<h3 className="text-base font-semibold text-white">{pkg.name}</h3>
|
||||
{pkg.description && <p className="text-sm text-neutral-500 mt-1">{pkg.description}</p>}
|
||||
<div className="mt-auto pt-6">
|
||||
<span className="text-3xl font-bold text-white">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-white">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-neutral-600 line-through ml-2">${pkg.original_price}</span>
|
||||
<span className="text-sm text-neutral-600 line-through ml-2">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
{pkg.duration > 0 && <p className="text-xs text-neutral-600 mt-1">{t('home.days', { count: pkg.duration })}</p>}
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@ export default function StarterHome() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { site } = useSite();
|
||||
const cs = site?.currency_symbol || '¥';
|
||||
const [models, setModels] = useState([]);
|
||||
const [packages, setPackages] = useState([]);
|
||||
|
||||
@@ -186,9 +187,9 @@ export default function StarterHome() {
|
||||
)}
|
||||
<div className="mt-auto pt-4">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-3xl font-bold text-white">${pkg.price}</span>
|
||||
<span className="text-3xl font-bold text-white">{cs}{pkg.price}</span>
|
||||
{pkg.original_price > pkg.price && (
|
||||
<span className="text-sm text-neutral-500 line-through">${pkg.original_price}</span>
|
||||
<span className="text-sm text-neutral-500 line-through">{cs}{pkg.original_price}</span>
|
||||
)}
|
||||
</div>
|
||||
{pkg.duration > 0 && (
|
||||
|
||||
Reference in new issue
Block a user