feat(settings): 表单分区收窄到 440px,套餐卡收敛成两种互斥状态
任务书 TASK-cend-surfaces-claude-alignment-20260916 §R8(T8.1 / T8.2)。 T8.1(E13):880px 弹窗里「个人资料」「通用设置」的一列表单铺满约 690px, 字段与标签被拉断。表单分区的内容区加 `settings-dialog-content--form`, 子元素上限 440px 并左对齐;「星盘资料」「账户与点数」两个列表分区保持铺满。 上限只挂在滚动容器的子元素上,不挂 `.settings-modal` / `.account-settings-shell`, 因此传不到固定尺寸的弹窗(BUG-554 / BUG-698 的现象无法复现)。 T8.2(D13):套餐卡的三个修饰符可同时命中,叠出没有设计定义的边框与底色。 收敛成两种互斥状态 `is-current`(当前套餐)与 `is-recommended`(推荐), 「推荐且当前」判给当前套餐(该卡的动作是续费)。 `highlighted` 不再进 className,删掉 `--highlighted` 那条描边规则, 定位交给 use-billing-panel 里已有的 scrollIntoView + focus。 手机端「月卡置顶」改挂 `[data-plan-alias="monthly"]`——版位不是状态, 月卡成为当前套餐后排序不变。 测试:3350 → 3354(+4),fail 31 → 31 且失败清单与基线逐条一致。 6 处既有断言改动均就地写了「原值/新值/原因」,另有 3 次破坏性验证。 tsc 0 错;lint 0 error / 118 warning(持平);干净 next build 后 `/` 仍 `○ Static`, 产物 CSS gzip 39,034 → 38,882 B(−0.39%),三条新规则在产物里逐条取证。 环境缺口(未记为通过):四个分区切换不跳尺寸、支付下单流程、`?plan=` 滚动定位的 视觉确认——无 Chrome、无受控账号,见 PROGRESS 的环境缺口段。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
co-authored by
Claude Opus 5
parent
50ce02c837
commit
34628ecb67
@@ -31,6 +31,15 @@ const settingsItems: ReadonlyArray<{
|
||||
{ dialog: "general", label: "通用设置", icon: Settings },
|
||||
];
|
||||
|
||||
/**
|
||||
* Panes whose body is a single column of form fields. They get a reading-width cap
|
||||
* inside the content box; the list panes (星盘资料 / 账户与点数) stay full-bleed.
|
||||
* The cap is purely inside `.settings-dialog-content`: every pane still renders under
|
||||
* the same `.settings-modal` box, so the dialog cannot change size between panes
|
||||
* (BUG-554 / BUG-698).
|
||||
*/
|
||||
const formPanes: ReadonlyArray<AccountSettingsDialog> = ["profile", "general"];
|
||||
|
||||
export const AccountDialogOverlay = memo(function AccountDialogOverlay({
|
||||
open,
|
||||
dialog,
|
||||
@@ -43,6 +52,7 @@ export const AccountDialogOverlay = memo(function AccountDialogOverlay({
|
||||
if (!open || dialog === null || model === null) return null;
|
||||
|
||||
const isSettingsDialog = dialog !== "logout";
|
||||
const isFormPane = dialog !== "logout" && formPanes.includes(dialog);
|
||||
const renderSettingsContent = () => {
|
||||
if (dialog === "profile") return model.renderProfile();
|
||||
if (dialog === "chart-library") return model.renderChartLibrary();
|
||||
@@ -90,7 +100,11 @@ export const AccountDialogOverlay = memo(function AccountDialogOverlay({
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<div className="settings-dialog-content">{renderSettingsContent()}</div>
|
||||
<div
|
||||
className={`settings-dialog-content${isFormPane ? " settings-dialog-content--form" : ""}`}
|
||||
>
|
||||
{renderSettingsContent()}
|
||||
</div>
|
||||
</div>
|
||||
) : model.renderLogout()}
|
||||
</section>
|
||||
|
||||
@@ -126,13 +126,18 @@ export function BillingPanel({ account: seedAccount, highlightedPlan, initialTab
|
||||
const recommended = alias === "monthly";
|
||||
const isCurrent = currentProductCode === product.code;
|
||||
const highlighted = alias === highlightedPlan;
|
||||
// D13: two mutually exclusive states. 当前套餐 wins over 推荐 because the
|
||||
// card's own action is 续费, not 立即购买. `highlighted` carries no
|
||||
// appearance any more — it only picks the scroll/focus target.
|
||||
const stateClass = isCurrent ? " is-current" : recommended ? " is-recommended" : "";
|
||||
return (
|
||||
<article
|
||||
key={product.id}
|
||||
ref={highlighted ? highlightedCardRef : undefined}
|
||||
tabIndex={highlighted ? -1 : undefined}
|
||||
aria-current={highlighted ? "true" : undefined}
|
||||
className={`membership-plan-card${recommended ? " membership-plan-card--recommended" : ""}${isCurrent ? " membership-plan-card--current" : ""}${highlighted ? " membership-plan-card--highlighted" : ""}`}
|
||||
data-plan-alias={alias ?? undefined}
|
||||
className={`membership-plan-card${stateClass}`}
|
||||
>
|
||||
<header className="membership-card-header">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user