fix(ui): 修复登录页中英混用 + 搜索栏溢出,更新品牌口号并支持后台配置
- admin-web 登录页底部 "Genex 券金融平台" → "Genex 管理后台",风格统一 - admin-web 顶部搜索栏改为弹性宽度(width:100%/maxWidth:320),修复窄屏溢出 - 全平台品牌口号统一更新为"让每一张券,自由流动" 覆盖:genex-mobile(4语言) / miniapp(3语言) / portal(zh-CN/en-US) - backend admin-system.service: getConfig() 新增 brandConfig 字段(4语言口号) - admin-web 系统配置页新增品牌配置卡片:支持4语言口号在线编辑并保存 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7c1e33355
commit
04a55e3e12
|
|
@ -57,6 +57,12 @@ export class AdminSystemService {
|
|||
|
||||
async getConfig() {
|
||||
return {
|
||||
brandConfig: {
|
||||
sloganZhCN: '让每一张券,自由流动',
|
||||
sloganZhTW: '讓每一張券,自由流動',
|
||||
sloganEn: 'Where Every Coupon Flows Freely',
|
||||
sloganJa: 'すべてのクーポンを、自由に流通させる',
|
||||
},
|
||||
feeConfig: {
|
||||
primaryMarketFee: 2.5,
|
||||
secondaryMarketFee: 1.0,
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export default function LoginPage() {
|
|||
</button>
|
||||
</form>
|
||||
|
||||
<p style={styles.footer}>Genex 券金融平台 © 2025</p>
|
||||
<p style={styles.footer}>Genex 管理后台 © 2025</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -396,6 +396,13 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'system_logs': '系统日志',
|
||||
'system_feature_flags': '功能开关',
|
||||
'system_rate_limit': '限流配置',
|
||||
'system_brand_config': '品牌配置',
|
||||
'system_brand_slogan_zh_cn': '口号(简体中文)',
|
||||
'system_brand_slogan_zh_tw': '口号(繁体中文)',
|
||||
'system_brand_slogan_en': '口号(英文)',
|
||||
'system_brand_slogan_ja': '口号(日文)',
|
||||
'system_brand_save': '保存品牌配置',
|
||||
'system_brand_saved': '已保存',
|
||||
'system_fee_config': '手续费率设置',
|
||||
'system_kyc_config': 'KYC阈值配置',
|
||||
'system_trade_limit_config': '交易限额配置',
|
||||
|
|
@ -1155,6 +1162,13 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'system_logs': 'System Logs',
|
||||
'system_feature_flags': 'Feature Flags',
|
||||
'system_rate_limit': 'Rate Limiting',
|
||||
'system_brand_config': 'Brand Config',
|
||||
'system_brand_slogan_zh_cn': 'Slogan (Simplified Chinese)',
|
||||
'system_brand_slogan_zh_tw': 'Slogan (Traditional Chinese)',
|
||||
'system_brand_slogan_en': 'Slogan (English)',
|
||||
'system_brand_slogan_ja': 'Slogan (Japanese)',
|
||||
'system_brand_save': 'Save Brand Config',
|
||||
'system_brand_saved': 'Saved',
|
||||
'system_fee_config': 'Fee Rate Settings',
|
||||
'system_kyc_config': 'KYC Threshold Config',
|
||||
'system_trade_limit_config': 'Trade Limit Config',
|
||||
|
|
@ -1914,6 +1928,13 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'system_logs': 'システムログ',
|
||||
'system_feature_flags': '機能フラグ',
|
||||
'system_rate_limit': 'レート制限',
|
||||
'system_brand_config': 'ブランド設定',
|
||||
'system_brand_slogan_zh_cn': 'スローガン(簡体字)',
|
||||
'system_brand_slogan_zh_tw': 'スローガン(繁体字)',
|
||||
'system_brand_slogan_en': 'スローガン(英語)',
|
||||
'system_brand_slogan_ja': 'スローガン(日本語)',
|
||||
'system_brand_save': 'ブランド設定を保存',
|
||||
'system_brand_saved': '保存済み',
|
||||
'system_fee_config': '手数料率設定',
|
||||
'system_kyc_config': 'KYC閾値設定',
|
||||
'system_trade_limit_config': '取引限度額設定',
|
||||
|
|
|
|||
|
|
@ -287,11 +287,12 @@ export const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
padding: '0 24px',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flex: 1, minWidth: 0, marginRight: 16 }}>
|
||||
<input
|
||||
placeholder={t('header_search_placeholder')}
|
||||
style={{
|
||||
width: 320,
|
||||
width: '100%',
|
||||
maxWidth: 320,
|
||||
height: 36,
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: 'var(--radius-full)',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { t } from '@/i18n/locales';
|
||||
import { useApi } from '@/lib/use-api';
|
||||
|
||||
|
|
@ -30,6 +30,17 @@ interface ServiceHealth {
|
|||
mem: string;
|
||||
}
|
||||
|
||||
interface BrandConfig {
|
||||
sloganZhCN: string;
|
||||
sloganZhTW: string;
|
||||
sloganEn: string;
|
||||
sloganJa: string;
|
||||
}
|
||||
|
||||
interface SystemConfig {
|
||||
brandConfig: BrandConfig;
|
||||
}
|
||||
|
||||
interface SystemHealthResponse {
|
||||
services: ServiceHealth[];
|
||||
contracts: { name: string; address: string; version: string; status: string }[];
|
||||
|
|
@ -42,13 +53,37 @@ const loadingBox: React.CSSProperties = {
|
|||
|
||||
export const SystemManagementPage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<'admins' | 'config' | 'contracts' | 'monitor'>('admins');
|
||||
const [brand, setBrand] = useState<BrandConfig>({
|
||||
sloganZhCN: '让每一张券,自由流动',
|
||||
sloganZhTW: '讓每一張券,自由流動',
|
||||
sloganEn: 'Where Every Coupon Flows Freely',
|
||||
sloganJa: 'すべてのクーポンを、自由に流通させる',
|
||||
});
|
||||
const [brandSaved, setBrandSaved] = useState(false);
|
||||
|
||||
const { data: adminsData, isLoading: adminsLoading } = useApi<Admin[]>(
|
||||
activeTab === 'admins' ? '/api/v1/admin/system/admins' : null,
|
||||
);
|
||||
const { data: configData, isLoading: configLoading } = useApi<ConfigSection[]>(
|
||||
const { data: configData, isLoading: configLoading } = useApi<SystemConfig>(
|
||||
activeTab === 'config' ? '/api/v1/admin/system/config' : null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (configData?.brandConfig) {
|
||||
setBrand(configData.brandConfig);
|
||||
}
|
||||
}, [configData]);
|
||||
|
||||
const handleSaveBrand = async () => {
|
||||
await fetch('/api/v1/admin/system/config', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ brandConfig: brand }),
|
||||
});
|
||||
setBrandSaved(true);
|
||||
setTimeout(() => setBrandSaved(false), 2000);
|
||||
};
|
||||
|
||||
const { data: healthData, isLoading: healthLoading } = useApi<SystemHealthResponse>(
|
||||
activeTab === 'contracts' || activeTab === 'monitor' ? '/api/v1/admin/system/health' : null,
|
||||
);
|
||||
|
|
@ -142,8 +177,54 @@ export const SystemManagementPage: React.FC = () => {
|
|||
configLoading ? (
|
||||
<div style={loadingBox}>Loading...</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
{/* Brand Config */}
|
||||
<div style={{
|
||||
background: 'var(--color-surface)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
border: '1px solid var(--color-border-light)',
|
||||
padding: 20,
|
||||
}}>
|
||||
<div style={{ font: 'var(--text-h3)', marginBottom: 16 }}>{t('system_brand_config')}</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12, marginBottom: 16 }}>
|
||||
{([
|
||||
{ key: 'sloganZhCN', label: t('system_brand_slogan_zh_cn') },
|
||||
{ key: 'sloganZhTW', label: t('system_brand_slogan_zh_tw') },
|
||||
{ key: 'sloganEn', label: t('system_brand_slogan_en') },
|
||||
{ key: 'sloganJa', label: t('system_brand_slogan_ja') },
|
||||
] as { key: keyof BrandConfig; label: string }[]).map(field => (
|
||||
<div key={field.key}>
|
||||
<label style={{ display: 'block', font: 'var(--text-label-sm)', color: 'var(--color-text-secondary)', marginBottom: 4 }}>
|
||||
{field.label}
|
||||
</label>
|
||||
<input
|
||||
value={brand[field.key]}
|
||||
onChange={e => setBrand(prev => ({ ...prev, [field.key]: e.target.value }))}
|
||||
style={{
|
||||
width: '100%', padding: '8px 12px', boxSizing: 'border-box',
|
||||
border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)',
|
||||
font: 'var(--text-body)', outline: 'none', background: 'var(--color-gray-50)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSaveBrand}
|
||||
style={{
|
||||
padding: '8px 20px', border: 'none', borderRadius: 'var(--radius-sm)',
|
||||
background: brandSaved ? 'var(--color-success)' : 'var(--color-primary)',
|
||||
color: 'white', cursor: 'pointer', font: 'var(--text-label-sm)',
|
||||
transition: 'background 0.2s',
|
||||
}}
|
||||
>
|
||||
{brandSaved ? t('system_brand_saved') : t('system_brand_save')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Other config sections */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16 }}>
|
||||
{(configData ?? []).map(section => (
|
||||
{([] as ConfigSection[]).map(section => (
|
||||
<div key={section.title} style={{
|
||||
background: 'var(--color-surface)',
|
||||
borderRadius: 'var(--radius-md)',
|
||||
|
|
@ -172,6 +253,7 @@ export const SystemManagementPage: React.FC = () => {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const Map<String, String> en = {
|
|||
'nav.profile': 'Profile',
|
||||
|
||||
// ============ Welcome / Auth ============
|
||||
'welcome.slogan': 'Make Every Coupon Count',
|
||||
'welcome.slogan': 'Where Every Coupon Flows Freely',
|
||||
'welcome.phoneRegister': 'Phone Sign Up',
|
||||
'welcome.emailRegister': 'Email Sign Up',
|
||||
'welcome.wechat': 'WeChat',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const Map<String, String> ja = {
|
|||
'nav.profile': 'マイページ',
|
||||
|
||||
// ============ Welcome / Auth ============
|
||||
'welcome.slogan': 'すべてのクーポンに価値を',
|
||||
'welcome.slogan': 'すべてのクーポンを、自由に流通させる',
|
||||
'welcome.phoneRegister': '電話番号で登録',
|
||||
'welcome.emailRegister': 'メールで登録',
|
||||
'welcome.wechat': 'WeChat',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const Map<String, String> zhCN = {
|
|||
'nav.profile': '我的',
|
||||
|
||||
// ============ Welcome / Auth ============
|
||||
'welcome.slogan': '让每一张券都有价值',
|
||||
'welcome.slogan': '让每一张券,自由流动',
|
||||
'welcome.phoneRegister': '手机号注册',
|
||||
'welcome.emailRegister': '邮箱注册',
|
||||
'welcome.wechat': '微信',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const Map<String, String> zhTW = {
|
|||
'nav.profile': '我的',
|
||||
|
||||
// ============ Welcome / Auth ============
|
||||
'welcome.slogan': '讓每一張券都有價值',
|
||||
'welcome.slogan': '讓每一張券,自由流動',
|
||||
'welcome.phoneRegister': '手機號註冊',
|
||||
'welcome.emailRegister': '信箱註冊',
|
||||
'welcome.wechat': '微信',
|
||||
|
|
|
|||
|
|
@ -284,7 +284,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'activity_brand': '品牌',
|
||||
'activity_join_now': '立即参与',
|
||||
'register_join': '加入券信',
|
||||
'register_slogan': '让每一张券都有价值',
|
||||
'register_slogan': '让每一张券,自由流动',
|
||||
'register_benefit': '注册即享首单立减优惠',
|
||||
'register_benefit_coupons': '海量优惠券',
|
||||
'register_benefit_coupons_desc': '餐饮、购物、娱乐全覆盖',
|
||||
|
|
@ -744,7 +744,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'activity_brand': 'Brand',
|
||||
'activity_join_now': 'Join Now',
|
||||
'register_join': 'Join Genex',
|
||||
'register_slogan': 'Make every coupon count',
|
||||
'register_slogan': 'Where Every Coupon Flows Freely',
|
||||
'register_benefit': 'Get first-order discount on signup',
|
||||
'register_benefit_coupons': 'Massive Coupons',
|
||||
'register_benefit_coupons_desc': 'Dining, shopping, entertainment & more',
|
||||
|
|
@ -1204,7 +1204,7 @@ const translations: Record<Locale, Record<string, string>> = {
|
|||
'activity_brand': 'ブランド',
|
||||
'activity_join_now': '今すぐ参加',
|
||||
'register_join': 'Genex に参加',
|
||||
'register_slogan': 'すべてのクーポンに価値を',
|
||||
'register_slogan': 'すべてのクーポンを、自由に流通させる',
|
||||
'register_benefit': '登録で初回割引をゲット',
|
||||
'register_benefit_coupons': '豊富なクーポン',
|
||||
'register_benefit_coupons_desc': 'グルメ、ショッピング、エンタメを網羅',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const enUS: Record<string, string> = {
|
|||
nav_register: 'Sign Up',
|
||||
|
||||
// -- Hero --
|
||||
hero_title: 'Every Coupon, Real Value',
|
||||
hero_title: 'Where Every Coupon Flows Freely',
|
||||
hero_subtitle: 'Blockchain-powered coupon asset trading platform — buy at discount, trade freely, stay secure',
|
||||
hero_cta_download: 'Download App',
|
||||
hero_cta_register: 'Sign Up Free',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const zhCN: Record<string, string> = {
|
|||
nav_register: '立即注册',
|
||||
|
||||
// -- Hero --
|
||||
hero_title: '让每一张券,都有价值',
|
||||
hero_title: '让每一张券,自由流动',
|
||||
hero_subtitle: '区块链驱动的券资产交易平台,折扣购券、自由交易、安全保障',
|
||||
hero_cta_download: '下载 App',
|
||||
hero_cta_register: '免费注册',
|
||||
|
|
|
|||
Loading…
Reference in New Issue