From c123ea31790ed8415e641981817d618929669107 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sun, 17 Aug 2025 11:45:55 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20style(Account=20UX):=20resilient=20?= =?UTF-8?q?binding=20layout,=20copyable=20popovers,=20pastel=20header,=20a?= =?UTF-8?q?nd=20custom=20pay=20colors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AccountManagement.js - Prevent action button from shifting when account IDs are long by adding gap, min-w-0, and flex-shrink-0; keep buttons in a fixed position. - Add copyable Popover for account identifiers (email/GitHub/OIDC/Telegram/LinuxDO) using Typography.Paragraph with copyable; reveal full text on hover. - Ensure ellipsis works by rendering the popover trigger as `block max-w-full truncate`. - Import Popover and wire up `renderAccountInfo` across all binding rows. - UserInfoHeader.js - Apply unified `with-pastel-balls` background to match PricingVendorIntro. - Remove legacy absolute-positioned circles and top gradient bar to avoid visual overlap. - RechargeCard.jsx - Colorize non-Alipay/WeChat/Stripe payment icons using backend `pay_methods[].color`; fallback to `var(--semi-color-text-2)`. - Add `showClear` to the redemption code input for quicker clearing. Notes: - No linter errors introduced. - i18n strings and behavior remain unchanged except for improved UX and visual consistency. --- .../personal/cards/AccountManagement.js | 235 ++++++++++-------- .../personal/cards/NotificationSettings.js | 12 +- .../personal/components/UserInfoHeader.js | 35 +-- web/src/components/topup/InvitationCard.jsx | 7 +- web/src/components/topup/RechargeCard.jsx | 3 +- .../topup/modals/PaymentConfirmModal.jsx | 16 +- web/src/i18n/locales/en.json | 4 +- web/src/index.css | 53 ++++ 8 files changed, 208 insertions(+), 157 deletions(-) diff --git a/web/src/components/settings/personal/cards/AccountManagement.js b/web/src/components/settings/personal/cards/AccountManagement.js index b2c9017e..2e30b25f 100644 --- a/web/src/components/settings/personal/cards/AccountManagement.js +++ b/web/src/components/settings/personal/cards/AccountManagement.js @@ -26,7 +26,8 @@ import { Typography, Avatar, Tabs, - TabPane + TabPane, + Popover } from '@douyinfe/semi-ui'; import { IconMail, @@ -58,6 +59,30 @@ const AccountManagement = ({ setShowChangePasswordModal, setShowAccountDeleteModal }) => { + const renderAccountInfo = (accountId, label) => { + if (!accountId || accountId === '') { + return {t('未绑定')}; + } + + const popContent = ( +
+ + {accountId} + + {label ? ( +
{label}
+ ) : null} +
+ ); + + return ( + + + {accountId} + + + ); + }; return ( {/* 卡片头部 */} @@ -71,7 +96,7 @@ const AccountManagement = ({ - + {/* 账户绑定 Tab */} {/* 邮箱绑定 */} -
-
-
+
+
+
{t('邮箱')}
- {userState.user && userState.user.email !== '' - ? userState.user.email - : t('未绑定')} + {renderAccountInfo(userState.user?.email, t('邮箱地址'))}
- +
+ +
{/* 微信绑定 */} -
-
-
+
+
+
@@ -130,110 +154,107 @@ const AccountManagement = ({
- +
+ +
{/* GitHub绑定 */} -
-
-
+
+
+
{t('GitHub')}
- {userState.user && userState.user.github_id !== '' - ? userState.user.github_id - : t('未绑定')} + {renderAccountInfo(userState.user?.github_id, t('GitHub ID'))}
- +
+ +
{/* OIDC绑定 */} -
-
-
+
+
+
{t('OIDC')}
- {userState.user && userState.user.oidc_id !== '' - ? userState.user.oidc_id - : t('未绑定')} + {renderAccountInfo(userState.user?.oidc_id, t('OIDC ID'))}
- +
+ +
{/* Telegram绑定 */} -
-
-
+
+
+
{t('Telegram')}
- {userState.user && userState.user.telegram_id !== '' - ? userState.user.telegram_id - : t('未绑定')} + {renderAccountInfo(userState.user?.telegram_id, t('Telegram ID'))}
{status.telegram_oauth ? ( userState.user.telegram_id !== '' ? ( - ) : ( @@ -245,7 +266,7 @@ const AccountManagement = ({
) ) : ( - )} @@ -255,33 +276,32 @@ const AccountManagement = ({ {/* LinuxDO绑定 */} -
-
-
+
+
+
{t('LinuxDO')}
- {userState.user && userState.user.linux_do_id !== '' - ? userState.user.linux_do_id - : t('未绑定')} + {renderAccountInfo(userState.user?.linux_do_id, t('LinuxDO ID'))}
- +
+ +
@@ -322,7 +342,6 @@ const AccountManagement = ({ value={systemToken} onClick={handleSystemTokenClick} size="large" - className="!rounded-lg" prefix={} />
@@ -333,7 +352,7 @@ const AccountManagement = ({ type="primary" theme="solid" onClick={generateAccessToken} - className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto" + className="!bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto" icon={} > {systemToken ? t('重新生成') : t('生成令牌')} @@ -361,7 +380,7 @@ const AccountManagement = ({ type="primary" theme="solid" onClick={() => setShowChangePasswordModal(true)} - className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto" + className="!bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto" icon={} > {t('修改密码')} @@ -392,7 +411,7 @@ const AccountManagement = ({ type="danger" theme="solid" onClick={() => setShowAccountDeleteModal(true)} - className="!rounded-lg w-full sm:w-auto !bg-slate-500 hover:!bg-slate-600" + className="w-full sm:w-auto !bg-slate-500 hover:!bg-slate-600" icon={} > {t('删除账户')} diff --git a/web/src/components/settings/personal/cards/NotificationSettings.js b/web/src/components/settings/personal/cards/NotificationSettings.js index dfa37e1b..64a41039 100644 --- a/web/src/components/settings/personal/cards/NotificationSettings.js +++ b/web/src/components/settings/personal/cards/NotificationSettings.js @@ -106,7 +106,7 @@ const NotificationSettings = ({ onSubmit={handleSubmit} > {() => ( - + {/* 通知配置 Tab */}
-
type: 通知类型 (quota_exceed: 额度预警)
-
title: 通知标题
-
content: 通知内容,支持 {`{{value}}`} 变量占位符
-
values: 按顺序替换content中的变量占位符
-
timestamp: Unix时间戳
+
type: {t('通知类型 (quota_exceed: 额度预警)')}
+
title: {t('通知标题')}
+
content: {t('通知内容,支持 {{value}} 变量占位符')}
+
values: {t('按顺序替换content中的变量占位符')}
+
timestamp: {t('Unix时间戳')}
diff --git a/web/src/components/settings/personal/components/UserInfoHeader.js b/web/src/components/settings/personal/components/UserInfoHeader.js index d4f232f1..39707dee 100644 --- a/web/src/components/settings/personal/components/UserInfoHeader.js +++ b/web/src/components/settings/personal/components/UserInfoHeader.js @@ -19,12 +19,10 @@ For commercial licensing, please contact support@quantumnous.com import React from 'react'; import { Avatar, Card, Tag, Divider, Typography } from '@douyinfe/semi-ui'; -import { isRoot, isAdmin, renderQuota } from '../../../../helpers'; -import { useTheme } from '../../../../context/Theme'; +import { isRoot, isAdmin, renderQuota, stringToColor } from '../../../../helpers'; import { Coins, BarChart2, Users } from 'lucide-react'; const UserInfoHeader = ({ t, userState }) => { - const theme = useTheme(); const getUsername = () => { if (userState.user) { @@ -44,34 +42,19 @@ const UserInfoHeader = ({ t, userState }) => { }; return ( - - {/* 装饰性背景元素 */} -
-
-
-
-
- -
+ +
{getAvatarText()}
-
+
{getUsername()}
@@ -113,7 +96,7 @@ const UserInfoHeader = ({ t, userState }) => { {/* 右上角统计信息(Semi UI 卡片) */}
- +
@@ -182,11 +165,9 @@ const UserInfoHeader = ({ t, userState }) => {
- -
); }; -export default UserInfoHeader; +export default UserInfoHeader; \ No newline at end of file diff --git a/web/src/components/topup/InvitationCard.jsx b/web/src/components/topup/InvitationCard.jsx index 65d46952..85b18cc4 100644 --- a/web/src/components/topup/InvitationCard.jsx +++ b/web/src/components/topup/InvitationCard.jsx @@ -54,7 +54,7 @@ const InvitationCard = ({ {/* 收益展示区域 */}
{/* 主要收益卡片 - 待使用收益 */} - +
@@ -75,7 +75,6 @@ const InvitationCard = ({
{renderQuota(userState?.user?.aff_quota || 0)}
-
{t('可随时划转到账户余额')}
{/* 统计数据网格 */} @@ -88,7 +87,6 @@ const InvitationCard = ({
{renderQuota(userState?.user?.aff_history_quota || 0)}
-
{t('累计获得')}
@@ -99,7 +97,6 @@ const InvitationCard = ({
{userState?.user?.aff_count || 0} {t('人')}
-
{t('成功邀请')}
@@ -126,7 +123,7 @@ const InvitationCard = ({ {/* 奖励说明 */} {t('奖励说明')} diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx index 9491d705..a16f5cdb 100644 --- a/web/src/components/topup/RechargeCard.jsx +++ b/web/src/components/topup/RechargeCard.jsx @@ -224,7 +224,7 @@ const RechargeCard = ({ ) : payMethod.type === 'stripe' ? ( ) : ( - + )}
{payMethod.name}
@@ -269,6 +269,7 @@ const RechargeCard = ({ onChange={(value) => setRedemptionCode(value)} className='!rounded-lg' prefix={} + showClear />
diff --git a/web/src/components/topup/modals/PaymentConfirmModal.jsx b/web/src/components/topup/modals/PaymentConfirmModal.jsx index e81199ca..12fcac95 100644 --- a/web/src/components/topup/modals/PaymentConfirmModal.jsx +++ b/web/src/components/topup/modals/PaymentConfirmModal.jsx @@ -24,7 +24,7 @@ import { Card, Skeleton, } from '@douyinfe/semi-ui'; -import { SiAlipay, SiWechat } from 'react-icons/si'; +import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si'; import { CreditCard } from 'lucide-react'; const { Text } = Typography; @@ -86,11 +86,13 @@ const PaymentConfirmModal = ({ return ( <> {payMethod.type === 'zfb' ? ( - + ) : payMethod.type === 'wx' ? ( - + + ) : payMethod.type === 'stripe' ? ( + ) : ( - + )} {payMethod.name} @@ -100,21 +102,21 @@ const PaymentConfirmModal = ({ if (payWay === 'zfb') { return ( <> - + {t('支付宝')} ); } else if (payWay === 'stripe') { return ( <> - + Stripe ); } else { return ( <> - + {t('微信')} ); diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 78d0ed35..9b0e73e0 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -853,13 +853,11 @@ "支付宝": "Alipay", "待使用收益": "Proceeds to be used", "邀请人数": "Number of people invited", - "可随时划转到账户余额": "Can be transferred to the account balance at any time", - "成功邀请": "Successfully invited", - "累计获得": "Accumulated", "兑换码充值": "Redemption code recharge", "奖励说明": "Reward description", "人": "", "选择支付方式": "Select payment method", + "处理中": "Processing", "账户充值": "Account recharge", "多种充值方式,安全便捷": "Multiple recharge methods, safe and convenient", "支付方式": "Payment method", diff --git a/web/src/index.css b/web/src/index.css index ab6673b9..53c79511 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -668,6 +668,59 @@ html.dark .with-pastel-balls::before { mix-blend-mode: screen; } +/* ==================== 卡片马卡龙模糊球(温暖色系) ==================== */ +.with-pastel-balls-warm { + position: relative; + overflow: hidden; + /* 温暖色系变量(明亮模式) */ + --pb1: #ffe4b5; + /* 桃杏 */ + --pb2: #d4ffdd; + /* 薄荷绿 */ + --pb3: #ffecb3; + /* 浅金 */ + --pb4: #e8f5e8; + /* 淡绿 */ + --pb-opacity: 0.55; +} + +.with-pastel-balls-warm::before { + content: ''; + position: absolute; + inset: 0; + pointer-events: none; + z-index: 0; + background: + radial-gradient(circle at -5% -10%, var(--pb1) 0%, transparent 60%), + radial-gradient(circle at 105% -10%, var(--pb2) 0%, transparent 55%), + radial-gradient(circle at 5% 110%, var(--pb3) 0%, transparent 55%), + radial-gradient(circle at 105% 110%, var(--pb4) 0%, transparent 50%); + opacity: var(--pb-opacity); +} + +.with-pastel-balls-warm>* { + position: relative; + z-index: 1; +} + +/* 暗黑模式下更柔和的色彩和透明度 */ +html.dark .with-pastel-balls-warm { + --pb1: #ffe4b5; + /* 桃杏 */ + --pb2: #d4ffdd; + /* 薄荷绿 */ + --pb3: #ffecb3; + /* 浅金 */ + --pb4: #e8f5e8; + /* 淡绿 */ + --pb-opacity: 0.36; +} + +/* 暗黑模式下用更柔和的混合模式 */ +html.dark .with-pastel-balls-warm::before { + mix-blend-mode: screen; +} + /* ==================== 表格卡片滚动设置 ==================== */ .table-scroll-card { display: flex;