收口个人中心标准弹窗壳层
扩展 UnifiedModal 支持关闭按钮变体与局部交互语义覆写 将昵称修改充值任务与兑换码弹窗迁移到 UnifiedModal 更新 PlatformUiKit 收口计划和 Hermes 决策记录
This commit is contained in:
@@ -132,6 +132,7 @@ import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||||
import { PlatformTextField } from '../common/PlatformTextField';
|
||||
import { RUNTIME_RESOURCE_PENDING_SELECTOR } from '../common/RuntimeResourcePendingMarker';
|
||||
import { SquareImageCropModal } from '../common/SquareImageCropModal';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
import {
|
||||
buildCenteredSquareImageCropRect,
|
||||
clampSquareImageCropRect,
|
||||
@@ -2824,6 +2825,13 @@ function ProfileReferralUserAvatar({
|
||||
);
|
||||
}
|
||||
|
||||
const PROFILE_MODAL_OVERLAY_CLASS =
|
||||
'platform-modal-backdrop !items-center !justify-center !px-4 !py-6';
|
||||
const PROFILE_MODAL_HEADER_CLASS = 'border-white/10 px-5 py-4';
|
||||
const PROFILE_MODAL_TITLE_CLASS = 'text-base font-black';
|
||||
const PROFILE_MODAL_DESCRIPTION_CLASS =
|
||||
'mt-1 text-xs font-semibold text-[var(--platform-text-soft)]';
|
||||
|
||||
function ProfileNicknameModal({
|
||||
value,
|
||||
error,
|
||||
@@ -2840,65 +2848,61 @@ function ProfileNicknameModal({
|
||||
onSubmit: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="profile-nickname-title"
|
||||
className="platform-modal-shell platform-remap-surface w-full max-w-sm overflow-hidden rounded-[1.4rem]"
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div id="profile-nickname-title" className="text-base font-black">
|
||||
修改昵称
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭昵称修改"
|
||||
variant="profileCompact"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
</div>
|
||||
<div className="px-5 py-5">
|
||||
<label className="block">
|
||||
<span className="sr-only">新昵称</span>
|
||||
<PlatformTextField
|
||||
autoFocus
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
onSubmit();
|
||||
}
|
||||
}}
|
||||
maxLength={20}
|
||||
surface="editorDark"
|
||||
size="lg"
|
||||
density="roomy"
|
||||
className="rounded-2xl border-white/12 bg-white/10 text-[var(--platform-text-strong)] focus:border-[var(--platform-surface-hover-border)]"
|
||||
placeholder="输入新昵称"
|
||||
/>
|
||||
</label>
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="tinted"
|
||||
className="mt-3 rounded-2xl border-rose-400/25 text-rose-600"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
<div className="mt-5 grid grid-cols-2 gap-3">
|
||||
<PlatformActionButton tone="secondary" onClick={onClose}>
|
||||
取消
|
||||
</PlatformActionButton>
|
||||
<PlatformActionButton onClick={onSubmit} disabled={isSaving}>
|
||||
{isSaving ? '保存中' : '保存'}
|
||||
</PlatformActionButton>
|
||||
</div>
|
||||
</div>
|
||||
<UnifiedModal
|
||||
open
|
||||
title="修改昵称"
|
||||
onClose={onClose}
|
||||
closeLabel="关闭昵称修改"
|
||||
closeVariant="profileCompact"
|
||||
closeOnBackdrop={false}
|
||||
closeOnEscape={false}
|
||||
portal={false}
|
||||
size="sm"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
|
||||
panelClassName="platform-remap-surface !max-w-sm rounded-[1.4rem]"
|
||||
headerClassName={PROFILE_MODAL_HEADER_CLASS}
|
||||
titleClassName={PROFILE_MODAL_TITLE_CLASS}
|
||||
bodyClassName="px-5 py-5"
|
||||
>
|
||||
<label className="block">
|
||||
<span className="sr-only">新昵称</span>
|
||||
<PlatformTextField
|
||||
autoFocus
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
onSubmit();
|
||||
}
|
||||
}}
|
||||
maxLength={20}
|
||||
surface="editorDark"
|
||||
size="lg"
|
||||
density="roomy"
|
||||
className="rounded-2xl border-white/12 bg-white/10 text-[var(--platform-text-strong)] focus:border-[var(--platform-surface-hover-border)]"
|
||||
placeholder="输入新昵称"
|
||||
/>
|
||||
</label>
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="tinted"
|
||||
className="mt-3 rounded-2xl border-rose-400/25 text-rose-600"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
<div className="mt-5 grid grid-cols-2 gap-3">
|
||||
<PlatformActionButton tone="secondary" onClick={onClose}>
|
||||
取消
|
||||
</PlatformActionButton>
|
||||
<PlatformActionButton onClick={onSubmit} disabled={isSaving}>
|
||||
{isSaving ? '保存中' : '保存'}
|
||||
</PlatformActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3189,125 +3193,126 @@ function ProfileRechargeModal({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div className="platform-recharge-modal w-full max-w-[34rem] overflow-hidden rounded-[1.4rem]">
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div>
|
||||
<div className="text-base font-black">账户充值</div>
|
||||
<div className="mt-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||
{center
|
||||
? `${center.walletBalance}泥点 · ${memberLabel}`
|
||||
: '读取中'}
|
||||
</div>
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭账户充值"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-[min(76vh,36rem)] overflow-y-auto px-5 py-5">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onTabChange('points')}
|
||||
className={`platform-category-chip justify-center ${activeTab === 'points' ? 'platform-category-chip--active' : ''}`}
|
||||
>
|
||||
泥点充值
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onTabChange('membership')}
|
||||
className={`platform-category-chip justify-center ${activeTab === 'membership' ? 'platform-category-chip--active' : ''}`}
|
||||
>
|
||||
会员卡
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-4 rounded-2xl font-semibold"
|
||||
>
|
||||
<div>{error}</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-3"
|
||||
onClick={onRetry}
|
||||
>
|
||||
重新加载
|
||||
</PlatformActionButton>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-28 animate-pulse rounded-[1.15rem] bg-white/10"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : products.length > 0 ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{products.map((product) => (
|
||||
<RechargeProductCard
|
||||
key={product.productId}
|
||||
product={product}
|
||||
submittingProductId={submittingProductId}
|
||||
onBuy={onBuy}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformEmptyState
|
||||
surface="subpanel"
|
||||
size="inline"
|
||||
className="mt-4"
|
||||
>
|
||||
暂无可购买套餐
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
|
||||
{nativePayment ? (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
radius="sm"
|
||||
padding="md"
|
||||
className="mt-4 text-center"
|
||||
>
|
||||
<div className="text-sm font-black">微信扫码支付</div>
|
||||
<div className="mx-auto mt-3 flex h-[180px] w-[180px] items-center justify-center rounded-xl bg-white p-2">
|
||||
{nativeQrImageUrl ? (
|
||||
<img
|
||||
src={nativeQrImageUrl}
|
||||
alt="微信 Native 支付二维码"
|
||||
className="h-full w-full"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs font-semibold text-slate-500">
|
||||
生成中
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-4 disabled:cursor-wait"
|
||||
onClick={onConfirmNativePayment}
|
||||
disabled={nativePayment.isConfirming}
|
||||
>
|
||||
{nativePayment.isConfirming ? '确认中' : '我已支付'}
|
||||
</PlatformActionButton>
|
||||
</PlatformSubpanel>
|
||||
) : null}
|
||||
</div>
|
||||
<UnifiedModal
|
||||
open
|
||||
title="账户充值"
|
||||
description={
|
||||
center ? `${center.walletBalance}泥点 · ${memberLabel}` : '读取中'
|
||||
}
|
||||
onClose={onClose}
|
||||
closeLabel="关闭账户充值"
|
||||
closeVariant="profile"
|
||||
closeOnBackdrop={false}
|
||||
closeOnEscape={false}
|
||||
portal={false}
|
||||
size="md"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
|
||||
panelClassName="platform-recharge-modal !max-w-[34rem] rounded-[1.4rem]"
|
||||
headerClassName={PROFILE_MODAL_HEADER_CLASS}
|
||||
titleClassName={PROFILE_MODAL_TITLE_CLASS}
|
||||
descriptionClassName={PROFILE_MODAL_DESCRIPTION_CLASS}
|
||||
bodyClassName="max-h-[min(76vh,36rem)] overflow-y-auto px-5 py-5"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onTabChange('points')}
|
||||
className={`platform-category-chip justify-center ${activeTab === 'points' ? 'platform-category-chip--active' : ''}`}
|
||||
>
|
||||
泥点充值
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onTabChange('membership')}
|
||||
className={`platform-category-chip justify-center ${activeTab === 'membership' ? 'platform-category-chip--active' : ''}`}
|
||||
>
|
||||
会员卡
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-4 rounded-2xl font-semibold"
|
||||
>
|
||||
<div>{error}</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-3"
|
||||
onClick={onRetry}
|
||||
>
|
||||
重新加载
|
||||
</PlatformActionButton>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-28 animate-pulse rounded-[1.15rem] bg-white/10"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : products.length > 0 ? (
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
{products.map((product) => (
|
||||
<RechargeProductCard
|
||||
key={product.productId}
|
||||
product={product}
|
||||
submittingProductId={submittingProductId}
|
||||
onBuy={onBuy}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<PlatformEmptyState
|
||||
surface="subpanel"
|
||||
size="inline"
|
||||
className="mt-4"
|
||||
>
|
||||
暂无可购买套餐
|
||||
</PlatformEmptyState>
|
||||
)}
|
||||
|
||||
{nativePayment ? (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
radius="sm"
|
||||
padding="md"
|
||||
className="mt-4 text-center"
|
||||
>
|
||||
<div className="text-sm font-black">微信扫码支付</div>
|
||||
<div className="mx-auto mt-3 flex h-[180px] w-[180px] items-center justify-center rounded-xl bg-white p-2">
|
||||
{nativeQrImageUrl ? (
|
||||
<img
|
||||
src={nativeQrImageUrl}
|
||||
alt="微信 Native 支付二维码"
|
||||
className="h-full w-full"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs font-semibold text-slate-500">
|
||||
生成中
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-4 disabled:cursor-wait"
|
||||
onClick={onConfirmNativePayment}
|
||||
disabled={nativePayment.isConfirming}
|
||||
>
|
||||
{nativePayment.isConfirming ? '确认中' : '我已支付'}
|
||||
</PlatformActionButton>
|
||||
</PlatformSubpanel>
|
||||
) : null}
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3545,113 +3550,114 @@ function ProfileTaskCenterModal({
|
||||
const walletBalance = center?.walletBalance ?? fallbackBalance;
|
||||
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div className="platform-recharge-modal w-full max-w-md overflow-hidden rounded-[1.4rem]">
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div>
|
||||
<div className="text-base font-black">每日任务</div>
|
||||
<div className="mt-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||
{walletBalance}泥点
|
||||
</div>
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭每日任务"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
<UnifiedModal
|
||||
open
|
||||
title="每日任务"
|
||||
description={`${walletBalance}泥点`}
|
||||
onClose={onClose}
|
||||
closeLabel="关闭每日任务"
|
||||
closeVariant="profile"
|
||||
closeOnBackdrop={false}
|
||||
closeOnEscape={false}
|
||||
portal={false}
|
||||
size="sm"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
|
||||
panelClassName="platform-recharge-modal !max-w-md rounded-[1.4rem]"
|
||||
headerClassName={PROFILE_MODAL_HEADER_CLASS}
|
||||
titleClassName={PROFILE_MODAL_TITLE_CLASS}
|
||||
descriptionClassName={PROFILE_MODAL_DESCRIPTION_CLASS}
|
||||
bodyClassName="space-y-3 px-5 py-5"
|
||||
>
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
<div>{error}</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-3"
|
||||
onClick={onRetry}
|
||||
>
|
||||
重新加载
|
||||
</PlatformActionButton>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{success ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{success}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{isLoading ? (
|
||||
<div className="space-y-3">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-20 animate-pulse rounded-2xl bg-white/10"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="space-y-3 px-5 py-5">
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
<div>{error}</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="mt-3"
|
||||
onClick={onRetry}
|
||||
>
|
||||
重新加载
|
||||
</PlatformActionButton>
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{success ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{success}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{isLoading ? (
|
||||
<div className="space-y-3">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-20 animate-pulse rounded-2xl bg-white/10"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : tasks.length === 0 ? (
|
||||
<PlatformEmptyState surface="subpanel" size="inline">
|
||||
暂无任务
|
||||
</PlatformEmptyState>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{tasks.map((task) => {
|
||||
const isClaimable = task.status === 'claimable';
|
||||
const isClaiming = claimingTaskId === task.taskId;
|
||||
const progressLabel = buildProfileTaskProgressLabel(task);
|
||||
) : tasks.length === 0 ? (
|
||||
<PlatformEmptyState surface="subpanel" size="inline">
|
||||
暂无任务
|
||||
</PlatformEmptyState>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{tasks.map((task) => {
|
||||
const isClaimable = task.status === 'claimable';
|
||||
const isClaiming = claimingTaskId === task.taskId;
|
||||
const progressLabel = buildProfileTaskProgressLabel(task);
|
||||
|
||||
return (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={task.taskId}
|
||||
radius="sm"
|
||||
padding="md"
|
||||
>
|
||||
<div className="flex min-w-0 items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="text-base font-black text-[var(--platform-text-strong)]">
|
||||
{task.title}
|
||||
</div>
|
||||
<div className="mt-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||
{progressLabel}
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 text-right">
|
||||
<div className="text-sm font-black text-[var(--platform-text-strong)]">
|
||||
+{task.rewardPoints}
|
||||
</div>
|
||||
<div className="mt-1 text-[11px] font-semibold text-[var(--platform-text-soft)]">
|
||||
{getProfileTaskStatusLabel(task.status)}
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<PlatformSubpanel
|
||||
as="div"
|
||||
key={task.taskId}
|
||||
radius="sm"
|
||||
padding="md"
|
||||
>
|
||||
<div className="flex min-w-0 items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="text-base font-black text-[var(--platform-text-strong)]">
|
||||
{task.title}
|
||||
</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
fullWidth
|
||||
size="sm"
|
||||
className="mt-3 disabled:opacity-50"
|
||||
disabled={!isClaimable || Boolean(claimingTaskId)}
|
||||
onClick={() => onClaim(task.taskId)}
|
||||
>
|
||||
{getProfileTaskClaimButtonLabel(task, isClaiming)}
|
||||
</PlatformActionButton>
|
||||
</PlatformSubpanel>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||
{progressLabel}
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 text-right">
|
||||
<div className="text-sm font-black text-[var(--platform-text-strong)]">
|
||||
+{task.rewardPoints}
|
||||
</div>
|
||||
<div className="mt-1 text-[11px] font-semibold text-[var(--platform-text-soft)]">
|
||||
{getProfileTaskStatusLabel(task.status)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
fullWidth
|
||||
size="sm"
|
||||
className="mt-3 disabled:opacity-50"
|
||||
disabled={!isClaimable || Boolean(claimingTaskId)}
|
||||
onClick={() => onClaim(task.taskId)}
|
||||
>
|
||||
{getProfileTaskClaimButtonLabel(task, isClaiming)}
|
||||
</PlatformActionButton>
|
||||
</PlatformSubpanel>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3673,65 +3679,69 @@ function RewardCodeRedeemModal({
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6">
|
||||
<div className="platform-recharge-modal w-full max-w-sm overflow-hidden rounded-[1.4rem]">
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div className="text-base font-black">兑换码</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭兑换码"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3 px-5 py-5">
|
||||
<PlatformTextField
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
onSubmit();
|
||||
}
|
||||
}}
|
||||
size="sm"
|
||||
density="roomy"
|
||||
className="uppercase tracking-normal"
|
||||
placeholder="输入兑换码"
|
||||
aria-label="兑换码"
|
||||
autoFocus
|
||||
/>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
fullWidth
|
||||
size="md"
|
||||
className="disabled:opacity-50"
|
||||
onClick={onSubmit}
|
||||
disabled={isSubmitting || !value.trim()}
|
||||
>
|
||||
{isSubmitting ? '兑换中' : '兑换'}
|
||||
</PlatformActionButton>
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{success ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{success}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<UnifiedModal
|
||||
open
|
||||
title="兑换码"
|
||||
onClose={onClose}
|
||||
closeLabel="关闭兑换码"
|
||||
closeVariant="profile"
|
||||
closeOnBackdrop={false}
|
||||
closeOnEscape={false}
|
||||
portal={false}
|
||||
size="sm"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
|
||||
panelClassName="platform-recharge-modal !max-w-sm rounded-[1.4rem]"
|
||||
headerClassName={PROFILE_MODAL_HEADER_CLASS}
|
||||
titleClassName={PROFILE_MODAL_TITLE_CLASS}
|
||||
bodyClassName="space-y-3 px-5 py-5"
|
||||
>
|
||||
<PlatformTextField
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
onSubmit();
|
||||
}
|
||||
}}
|
||||
size="sm"
|
||||
density="roomy"
|
||||
className="uppercase tracking-normal"
|
||||
placeholder="输入兑换码"
|
||||
aria-label="兑换码"
|
||||
autoFocus
|
||||
/>
|
||||
<PlatformActionButton
|
||||
surface="profile"
|
||||
fullWidth
|
||||
size="md"
|
||||
className="disabled:opacity-50"
|
||||
onClick={onSubmit}
|
||||
disabled={isSubmitting || !value.trim()}
|
||||
>
|
||||
{isSubmitting ? '兑换中' : '兑换'}
|
||||
</PlatformActionButton>
|
||||
{error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
{success ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{success}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user