收口认证表单输入组件
迁移登录、重置密码、绑定手机号、邀请码和账号安全表单到 PlatformTextField 与 PlatformFieldLabel 补充认证表单公共输入断言和绑定手机号独立测试 更新 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
@@ -61,6 +61,7 @@
|
|||||||
- 2026-06-10 追加:`PlatformTextField` / `PlatformSelectField` 支持 `surface="editorDark"` 和 `tone="sky"`,承接 RPG 暗色弹窗 / 运行面板里的普通输入框、文本域、下拉框、禁用态、密度、字号和焦点色;自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿已迁移,业务 JSX 不再手写暗色 `border-white/10 bg-black/30 px-4 py-3` 或 `focus:border-*` 输入 chrome。验证命令:`npm run test -- src/components/common/PlatformTextField.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CharacterChatModal.test.tsx`。
|
- 2026-06-10 追加:`PlatformTextField` / `PlatformSelectField` 支持 `surface="editorDark"` 和 `tone="sky"`,承接 RPG 暗色弹窗 / 运行面板里的普通输入框、文本域、下拉框、禁用态、密度、字号和焦点色;自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿已迁移,业务 JSX 不再手写暗色 `border-white/10 bg-black/30 px-4 py-3` 或 `focus:border-*` 输入 chrome。验证命令:`npm run test -- src/components/common/PlatformTextField.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CharacterChatModal.test.tsx`。
|
||||||
- 2026-06-10 追加:`PlatformTagEditor` 内部新增标签输入框也使用 `PlatformTextField density="compact" size="xs"`;标签编辑器只保留新增状态、解析、Enter / Escape 行为和按钮组合,不再手写白底 input chrome。
|
- 2026-06-10 追加:`PlatformTagEditor` 内部新增标签输入框也使用 `PlatformTextField density="compact" size="xs"`;标签编辑器只保留新增状态、解析、Enter / Escape 行为和按钮组合,不再手写白底 input chrome。
|
||||||
- 2026-06-10 追加:认证图形验证码答案输入使用 `PlatformTextField density="compact"`;验证码组件只保留 challenge 展示、答案受控值和变更回调,不再手写 `platform-input` 输入框 chrome。
|
- 2026-06-10 追加:认证图形验证码答案输入使用 `PlatformTextField density="compact"`;验证码组件只保留 challenge 展示、答案受控值和变更回调,不再手写 `platform-input` 输入框 chrome。
|
||||||
|
- 2026-06-10 追加:认证入口的短信 / 密码登录、重置密码、绑定手机号、邀请码和账号安全表单字段使用 `PlatformTextField surface="platform"` 与 `PlatformFieldLabel variant="form"`;认证业务组件只保留受控值、登录 / 绑定流程、原生 input 属性和校验提示,字段可访问名称继续由外层原生 `label` 承接,不再手写 `platform-input` 或表单标题 class。
|
||||||
- 2026-06-09 追加:平台字段标签统一使用 `src/components/common/PlatformFieldLabel.tsx` 承载 `field`、`section`、`form`、`pill` 与 `accentPill` 五类字段标题视觉;视觉小说结果页、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移,业务页只保留字段文案和必要局部布局 class,不再重复拼普通字段名、分区标题、表单标题、普通胶囊和强调胶囊 class。
|
- 2026-06-09 追加:平台字段标签统一使用 `src/components/common/PlatformFieldLabel.tsx` 承载 `field`、`section`、`form`、`pill` 与 `accentPill` 五类字段标题视觉;视觉小说结果页、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移,业务页只保留字段文案和必要局部布局 class,不再重复拼普通字段名、分区标题、表单标题、普通胶囊和强调胶囊 class。
|
||||||
- 2026-06-09 追加:抓大鹅结果页作品信息、发布封面和物品素材详情中的 section 字段标题迁移到 `PlatformFieldLabel variant="section"`;业务页不再重复拼 `text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`。
|
- 2026-06-09 追加:抓大鹅结果页作品信息、发布封面和物品素材详情中的 section 字段标题迁移到 `PlatformFieldLabel variant="section"`;业务页不再重复拼 `text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`。
|
||||||
- 2026-06-09 追加:方洞结果页主信息、形状选项、洞口选项和历史生成标题迁移到 `PlatformFieldLabel variant="section"`;业务页只保留字段文案、图标和按钮布局,不再重复拼 section 标题 class。
|
- 2026-06-09 追加:方洞结果页主信息、形状选项、洞口选项和历史生成标题迁移到 `PlatformFieldLabel variant="section"`;业务页只保留字段文案、图标和按钮布局,不再重复拼 section 标题 class。
|
||||||
@@ -244,6 +245,7 @@
|
|||||||
- 2026-06-10 验证补充:标签编辑新增输入行 soft 子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
|
- 2026-06-10 验证补充:标签编辑新增输入行 soft 子面板收口补跑 `npm run test -- src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
|
||||||
- 2026-06-10 验证补充:标签编辑新增输入框收口到 `PlatformTextField` 后,补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
|
- 2026-06-10 验证补充:标签编辑新增输入框收口到 `PlatformTextField` 后,补跑 `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/common/PlatformTagEditor.test.tsx`。
|
||||||
- 2026-06-10 验证补充:认证图形验证码图片和答案输入分别收口到 `PlatformMediaFrame` 与 `PlatformTextField` 后,补跑 `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
|
- 2026-06-10 验证补充:认证图形验证码图片和答案输入分别收口到 `PlatformMediaFrame` 与 `PlatformTextField` 后,补跑 `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`。
|
||||||
|
- 2026-06-10 验证补充:认证登录、重置密码、绑定手机号、邀请码和账号安全表单字段收口到 `PlatformTextField` 与 `PlatformFieldLabel` 后,补跑 `npm run test -- src/components/auth/AuthGate.test.tsx src/components/auth/AccountModal.test.tsx src/components/auth/BindPhoneScreen.test.tsx src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformFieldLabel.test.tsx`。
|
||||||
- 2026-06-10 验证补充:背包文书按钮收口到暗色 `PlatformSubpanel`、故事档案 QA 提示收口到 `PlatformStatusMessage surface="editorDark"` 后,补跑 `npm run test -- src/components/InventoryPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
- 2026-06-10 验证补充:背包文书按钮收口到暗色 `PlatformSubpanel`、故事档案 QA 提示收口到 `PlatformStatusMessage surface="editorDark"` 后,补跑 `npm run test -- src/components/InventoryPanel.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
||||||
- 2026-06-10 验证补充:NPC 叙事提示和交易详情属性格收口后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
- 2026-06-10 验证补充:NPC 叙事提示和交易详情属性格收口后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformSubpanel.test.tsx src/components/common/PlatformStatusMessage.test.tsx`。
|
||||||
- 2026-06-10 验证补充:NPC 暗色可选项按钮卡收口到 `PlatformDarkOptionCard` 后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
|
- 2026-06-10 验证补充:NPC 暗色可选项按钮卡收口到 `PlatformDarkOptionCard` 后,补跑 `npm run test -- src/components/NpcModals.test.tsx src/components/common/PlatformDarkOptionCard.test.tsx`。
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/compon
|
|||||||
|
|
||||||
平台入口错误 / 完成弹窗的文案归一、来源格式、候选择一、dismiss key 与任务完成文案收口到 `src/components/platform-entry/platformDialogStateModel.ts`,规则见 [【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md](./technical/【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md)。
|
平台入口错误 / 完成弹窗的文案归一、来源格式、候选择一、dismiss key 与任务完成文案收口到 `src/components/platform-entry/platformDialogStateModel.ts`,规则见 [【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md](./technical/【前端架构】PlatformDialogStateModel收口计划-2026-06-03.md)。
|
||||||
|
|
||||||
平台 UI Kit 的提示 / 确认弹窗收口到 `src/components/common/UnifiedConfirmDialog.tsx`,复制反馈收口到 `src/components/common/useCopyFeedback.ts`、`src/components/common/CopyFeedbackButton.tsx`、`src/components/common/CopyCodeButton.tsx` 与 `src/components/common/CopyFeedbackMessage.tsx`,基础状态提示收口到 `src/components/common/PlatformStatusMessage.tsx`,运行态短错误 / 成功 / 反馈 toast 收口到 `src/components/common/PlatformRuntimeStatusToast.tsx`,平台空态 / 轻量加载态收口到 `src/components/common/PlatformEmptyState.tsx`,平台动作按钮收口到 `src/components/common/PlatformActionButton.tsx`,平台白底子面板 / 小型列表卡片收口到 `src/components/common/PlatformSubpanel.tsx`,平台媒体预览框收口到 `src/components/common/PlatformMediaFrame.tsx`,平台胶囊状态标签收口到 `src/components/common/PlatformPillBadge.tsx`,平台 / 个人中心弹窗关闭按钮收口到 `src/components/common/PlatformModalCloseButton.tsx`,底层继续复用 `UnifiedModal`;普通提示、确认 / 取消、危险确认、复制状态机、短代码复制 chip、复制按钮表现、白底 / 个人中心 / 认证入口 token 状态条、运行态状态 toast、无操作空态、主动作按钮、白底子面板、白底交互列表卡片、图片源 / fallback / 固定比例媒体预览、单个状态 / 标签 chip 和圆形关闭按钮优先使用公共 Module,规则见 [【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md](./technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md)。
|
平台 UI Kit 的提示 / 确认弹窗收口到 `src/components/common/UnifiedConfirmDialog.tsx`,复制反馈收口到 `src/components/common/useCopyFeedback.ts`、`src/components/common/CopyFeedbackButton.tsx`、`src/components/common/CopyCodeButton.tsx` 与 `src/components/common/CopyFeedbackMessage.tsx`,基础状态提示收口到 `src/components/common/PlatformStatusMessage.tsx`,运行态短错误 / 成功 / 反馈 toast 收口到 `src/components/common/PlatformRuntimeStatusToast.tsx`,平台空态 / 轻量加载态收口到 `src/components/common/PlatformEmptyState.tsx`,平台动作按钮收口到 `src/components/common/PlatformActionButton.tsx`,平台白底子面板 / 小型列表卡片收口到 `src/components/common/PlatformSubpanel.tsx`,平台输入框 / 文本域收口到 `src/components/common/PlatformTextField.tsx`,平台字段标题收口到 `src/components/common/PlatformFieldLabel.tsx`,平台媒体预览框收口到 `src/components/common/PlatformMediaFrame.tsx`,平台胶囊状态标签收口到 `src/components/common/PlatformPillBadge.tsx`,平台 / 个人中心弹窗关闭按钮收口到 `src/components/common/PlatformModalCloseButton.tsx`,底层继续复用 `UnifiedModal`;普通提示、确认 / 取消、危险确认、复制状态机、短代码复制 chip、复制按钮表现、白底 / 个人中心 / 认证入口 token 状态条、运行态状态 toast、无操作空态、主动作按钮、白底子面板、白底交互列表卡片、普通输入字段、字段标题、图片源 / fallback / 固定比例媒体预览、单个状态 / 标签 chip 和圆形关闭按钮优先使用公共 Module,规则见 [【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md](./technical/【前端架构】PlatformUiKit弹窗组件收口计划-2026-06-08.md)。
|
||||||
|
|
||||||
平台入口受保护数据失效后的 stage 去留判定,以及缺失草稿 / 作品 / run 时的阶段回退,收口到 `src/components/platform-entry/platformSelectionStageModel.ts`,壳层只执行缓存清空、布尔事实汇总和必要跳转,规则见 [【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md)。
|
平台入口受保护数据失效后的 stage 去留判定,以及缺失草稿 / 作品 / run 时的阶段回退,收口到 `src/components/platform-entry/platformSelectionStageModel.ts`,壳层只执行缓存清空、布尔事实汇总和必要跳转,规则见 [【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformSelectionStageModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
- 新增 `src/components/common/PlatformUploadPreviewCard.tsx` 作为平台上传预览 Module,统一承载上传后的缩略图壳、预览图片、右上角移除按钮和禁用态。
|
- 新增 `src/components/common/PlatformUploadPreviewCard.tsx` 作为平台上传预览 Module,统一承载上传后的缩略图壳、预览图片、右上角移除按钮和禁用态。
|
||||||
- 新增 `src/components/common/PlatformPillSwitch.tsx` 作为平台胶囊开关 Module,统一承载图片面板中类似 AI 重绘的 label + switch 语义、轨道、圆点、禁用态和白底浮层 chrome。
|
- 新增 `src/components/common/PlatformPillSwitch.tsx` 作为平台胶囊开关 Module,统一承载图片面板中类似 AI 重绘的 label + switch 语义、轨道、圆点、禁用态和白底浮层 chrome。
|
||||||
- 新增 `src/components/common/PlatformToggleRow.tsx` 作为平台整行开关 Module,统一承载设置面板和结果页配置里的白底 label + checkbox / 状态行。
|
- 新增 `src/components/common/PlatformToggleRow.tsx` 作为平台整行开关 Module,统一承载设置面板和结果页配置里的白底 label + checkbox / 状态行。
|
||||||
|
- 新增 `src/components/common/PlatformTextField.tsx` 作为平台输入字段 Module,统一承载白底 / 暗色 input、textarea 和下拉框共用 chrome,认证表单也只保留受控值、原生属性和业务校验。
|
||||||
- 新增 `src/components/common/PlatformFieldLabel.tsx` 作为平台字段标签 Module,统一承载结果页、编辑弹窗和创作工作台中的普通字段名、分区标题、表单字段标题、胶囊字段标题和强调胶囊字段标题。
|
- 新增 `src/components/common/PlatformFieldLabel.tsx` 作为平台字段标签 Module,统一承载结果页、编辑弹窗和创作工作台中的普通字段名、分区标题、表单字段标题、胶囊字段标题和强调胶囊字段标题。
|
||||||
- 新增 `src/components/common/PlatformSegmentedTabs.tsx` 作为平台分段选择 Module,统一承载白底结果页 Tab、编辑弹窗二选一和轻量配置 Tab 的容器、按钮、选中态、禁用态、列数、尺寸和截断标签。
|
- 新增 `src/components/common/PlatformSegmentedTabs.tsx` 作为平台分段选择 Module,统一承载白底结果页 Tab、编辑弹窗二选一和轻量配置 Tab 的容器、按钮、选中态、禁用态、列数、尺寸和截断标签。
|
||||||
- 新增 `src/components/common/PlatformStatGrid.tsx` 作为平台统计小卡 Module,统一承载结果页里的数值 / 标签摘要、轻量状态 chip、响应式列数、密度、surface 和 label/value 顺序。
|
- 新增 `src/components/common/PlatformStatGrid.tsx` 作为平台统计小卡 Module,统一承载结果页里的数值 / 标签摘要、轻量状态 chip、响应式列数、密度、surface 和 label/value 顺序。
|
||||||
@@ -65,9 +66,9 @@
|
|||||||
- `PlatformRuntimeStatusToast`:接收 `tone="error" | "success" | "info" | "warning" | "neutral"`、`surface="light" | "dark" | "solid"`、`size="xs" | "sm" | "md"`、`shape="pill" | "rounded"`、`children` 和 `className`;根节点固定带 `platform-runtime-status-toast` 稳定类名,默认按 `tone` 写入 `role="alert/status"` 与 `aria-live`。它只承接运行态 HUD 中短错误、成功和反馈 chip 的圆角、字号、阴影、色值和可访问语义,具体浮层位置、玩法资产按钮、计分牌、蓄力提示、强品牌 primary 按钮仍由玩法 runtime 控制。跳一跳、拼图、敲木鱼、方洞和宝贝爱画运行态的短错误 / 成功 / 投放反馈已先迁移;后续同类短 toast 不再手写 `rounded-full bg-white/* text-*`、暗色 `border-rose/emerald bg-*/text-*` 或单玩法 `*-runtime-error-chip`。
|
- `PlatformRuntimeStatusToast`:接收 `tone="error" | "success" | "info" | "warning" | "neutral"`、`surface="light" | "dark" | "solid"`、`size="xs" | "sm" | "md"`、`shape="pill" | "rounded"`、`children` 和 `className`;根节点固定带 `platform-runtime-status-toast` 稳定类名,默认按 `tone` 写入 `role="alert/status"` 与 `aria-live`。它只承接运行态 HUD 中短错误、成功和反馈 chip 的圆角、字号、阴影、色值和可访问语义,具体浮层位置、玩法资产按钮、计分牌、蓄力提示、强品牌 primary 按钮仍由玩法 runtime 控制。跳一跳、拼图、敲木鱼、方洞和宝贝爱画运行态的短错误 / 成功 / 投放反馈已先迁移;后续同类短 toast 不再手写 `rounded-full bg-white/* text-*`、暗色 `border-rose/emerald bg-*/text-*` 或单玩法 `*-runtime-error-chip`。
|
||||||
- `PlatformDarkOptionCard`:接收 `selected`、`tone="emerald" | "sky" | "rose" | "amber"`、`radius="sm" | "md" | "lg"`、`padding="sm" | "md" | "lg"`、`children`、`className` 和原生 button props;根节点固定带 `platform-dark-option-card` 稳定类名,统一承接 RPG 暗色弹窗 / 面板中的 selected / idle / hover / disabled 可选项卡按钮外观。NPC 交易模式、交易物品行、赠礼候选、招募替换候选、角色素材工作室动作预览格、营地编组替换位按钮和角色聊天建议按钮已先迁移;业务页只保留选中判断、点击回调和内容布局,不再重复手写 `rounded-* border px-3 py-*`、`border-*-400/* bg-*-500/10` 或 `border-white/* bg-black/20 hover:border-white/15`。
|
- `PlatformDarkOptionCard`:接收 `selected`、`tone="emerald" | "sky" | "rose" | "amber"`、`radius="sm" | "md" | "lg"`、`padding="sm" | "md" | "lg"`、`children`、`className` 和原生 button props;根节点固定带 `platform-dark-option-card` 稳定类名,统一承接 RPG 暗色弹窗 / 面板中的 selected / idle / hover / disabled 可选项卡按钮外观。NPC 交易模式、交易物品行、赠礼候选、招募替换候选、角色素材工作室动作预览格、营地编组替换位按钮和角色聊天建议按钮已先迁移;业务页只保留选中判断、点击回调和内容布局,不再重复手写 `rounded-* border px-3 py-*`、`border-*-400/* bg-*-500/10` 或 `border-white/* bg-black/20 hover:border-white/15`。
|
||||||
- `PlatformEmptyState`:接收 `surface="soft" | "dashed" | "subpanel" | "editorDark"`、`size="compact" | "panel" | "inline"`、`tone="base" | "soft"`、`children` 和 `className`;根节点固定带 `platform-empty-state` 稳定类名,业务测试可断言公共空态接入。`soft + compact` 用于公开广场、排行和作品架内的轻量空态,`soft + panel` 用于创作中心作品架整块空态,`dashed + panel` 用于素材选择、历史资源等弹窗的大面积空态或读取态,`subpanel + inline` 用于视觉小说 runtime 等白底子面板内的无操作空态,`editorDark + compact/inline` 用于 RPG 大编辑器、实体详情弹窗、营地编组、角色聊天和运行态设置弹窗等暗色面板里的纯展示空态 / 禁用提示。组件只承接外观,不内置业务文案。
|
- `PlatformEmptyState`:接收 `surface="soft" | "dashed" | "subpanel" | "editorDark"`、`size="compact" | "panel" | "inline"`、`tone="base" | "soft"`、`children` 和 `className`;根节点固定带 `platform-empty-state` 稳定类名,业务测试可断言公共空态接入。`soft + compact` 用于公开广场、排行和作品架内的轻量空态,`soft + panel` 用于创作中心作品架整块空态,`dashed + panel` 用于素材选择、历史资源等弹窗的大面积空态或读取态,`subpanel + inline` 用于视觉小说 runtime 等白底子面板内的无操作空态,`editorDark + compact/inline` 用于 RPG 大编辑器、实体详情弹窗、营地编组、角色聊天和运行态设置弹窗等暗色面板里的纯展示空态 / 禁用提示。组件只承接外观,不内置业务文案。
|
||||||
- `PlatformTextField`:接收 `variant="input" | "textarea"`、`surface="platform" | "editorDark"`、`size="xs" | "sm" | "md" | "lg"`、`density="default" | "compact" | "roomy"`、`tone="warm" | "rose" | "emerald" | "sky"`、`className` 和原生 input / textarea props;统一承接平台白底与 RPG 暗色弹窗里的圆角输入框、文本域、禁用态、密度、字号 / 行高和焦点色,暗色 surface 根节点固定带 `platform-text-field--editor-dark` 稳定类名。`PlatformSelectField` 复用同一套输入 chrome 承接下拉框。业务页继续持有 `value`、`onChange`、`aria-label`、`rows`、`placeholder`、`option` 等语义,不再重复拼 `rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/86 px-3 py-3`、`rounded-[0.85rem] bg-white/90 px-3 py-2`、`bg-white/90 px-4 py-3`、暗色 `border-white/10 bg-black/30 px-4 py-3` 或 `focus:border-* focus:ring-*`。抓大鹅结果页作品信息、封面描述、素材名称和批量物品名称,方洞结果页主信息表单和形状 / 洞口选项字段,拼图 / 敲木鱼结果页作品信息字段,视觉小说结果页的音乐生成、作品信息、开场、运行配置、角色、场景、阶段和世界观普通文本 / 下拉字段,以及视觉小说 / 抓大鹅 / 汪汪声浪 / 宝贝识物 / 拼消消 / 跳一跳创作工作台普通输入字段已先迁移;自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿等暗色字段使用 `surface="editorDark"`。通用创作图片输入面板的提示词文本域也使用该 Module,只通过局部 class 保留高度和底部浮动上传按钮避让。
|
- `PlatformTextField`:接收 `variant="input" | "textarea"`、`surface="platform" | "editorDark"`、`size="xs" | "sm" | "md" | "lg"`、`density="default" | "compact" | "roomy"`、`tone="warm" | "rose" | "emerald" | "sky"`、`className` 和原生 input / textarea props;统一承接平台白底与 RPG 暗色弹窗里的圆角输入框、文本域、禁用态、密度、字号 / 行高和焦点色,暗色 surface 根节点固定带 `platform-text-field--editor-dark` 稳定类名。`PlatformSelectField` 复用同一套输入 chrome 承接下拉框。业务页继续持有 `value`、`onChange`、`aria-label`、`rows`、`placeholder`、`option` 等语义,不再重复拼 `rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/86 px-3 py-3`、`rounded-[0.85rem] bg-white/90 px-3 py-2`、`bg-white/90 px-4 py-3`、暗色 `border-white/10 bg-black/30 px-4 py-3` 或 `focus:border-* focus:ring-*`。抓大鹅结果页作品信息、封面描述、素材名称和批量物品名称,方洞结果页主信息表单和形状 / 洞口选项字段,拼图 / 敲木鱼结果页作品信息字段,视觉小说结果页的音乐生成、作品信息、开场、运行配置、角色、场景、阶段和世界观普通文本 / 下拉字段,以及视觉小说 / 抓大鹅 / 汪汪声浪 / 宝贝识物 / 拼消消 / 跳一跳创作工作台普通输入字段已先迁移;自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿等暗色字段使用 `surface="editorDark"`。通用创作图片输入面板的提示词文本域也使用该 Module,只通过局部 class 保留高度和底部浮动上传按钮避让。认证图形验证码答案、短信 / 密码登录、重置密码、绑定手机号、邀请码和账号安全表单字段使用 `surface="platform"`,业务层只保留认证流程、受控值、原生属性和校验提示。
|
||||||
- `PlatformTextField` 补充:`PlatformTagEditor` 内部新增标签输入框使用 `density="compact" size="xs"` 复用同一输入 chrome;标签编辑器只保留新增输入状态、解析、Enter / Escape 行为和按钮组合,不再手写输入框边框、白底、字号、焦点色或禁用态。
|
- `PlatformTextField` 补充:`PlatformTagEditor` 内部新增标签输入框使用 `density="compact" size="xs"` 复用同一输入 chrome;标签编辑器只保留新增输入状态、解析、Enter / Escape 行为和按钮组合,不再手写输入框边框、白底、字号、焦点色或禁用态。
|
||||||
- `PlatformFieldLabel`:接收 `variant="field" | "section" | "form" | "pill" | "accentPill"`、`children` 和 `className`;`field` 用于视觉小说等结果页的普通字段名,`section` 用于平台白底面板内小标题,`form` 用于创作工作台普通表单字段标题,`pill` / `accentPill` 用于汪汪声浪等工作台里的胶囊字段标题。业务页只传字段文案和必要的局部 class,不再重复写 `text-xs font-bold text-[var(--platform-text-soft)]`、`text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`、`mb-2 block text-sm font-black`、普通胶囊或 rose 强调胶囊 class。视觉小说结果页、抓大鹅结果页作品 / 封面 / 素材字段标题、方洞结果页主信息 / 形状 / 洞口 / 历史图片字段标题、拼图结果页关卡详情 / 发布弹窗字段标题、拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题、大鱼素材弹窗 prompt、RPG 发布弹窗发布检查 / 封面设置、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移。
|
- `PlatformFieldLabel`:接收 `variant="field" | "section" | "form" | "pill" | "accentPill"`、`children` 和 `className`;`field` 用于视觉小说等结果页的普通字段名,`section` 用于平台白底面板内小标题,`form` 用于创作工作台和认证表单普通字段标题,`pill` / `accentPill` 用于汪汪声浪等工作台里的胶囊字段标题。业务页只传字段文案和必要的局部 class,不再重复写 `text-xs font-bold text-[var(--platform-text-soft)]`、`text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]`、`mb-2 block text-sm font-black`、普通胶囊或 rose 强调胶囊 class。视觉小说结果页、抓大鹅结果页作品 / 封面 / 素材字段标题、方洞结果页主信息 / 形状 / 洞口 / 历史图片字段标题、拼图结果页关卡详情 / 发布弹窗字段标题、拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题、大鱼素材弹窗 prompt、RPG 发布弹窗发布检查 / 封面设置、汪汪声浪轻配置编辑器、宝贝识物工作台,以及认证表单中的手机号 / 验证码 / 密码 / 邀请码标题已先迁移。认证字段继续保留外层原生 `label` 关联,不把可访问命名交给装饰性标题组件。
|
||||||
- `PlatformSegmentedTabs`:接收 `items`、`activeId`、`onChange`、`columns="one" | "two" | "three" | "four" | "threeToSix"`、`gap="sm" | "md"`、`radius="md" | "lg" | "xl"`、`size="sm" | "md" | "compact" | "choice" | "tab"`、`surface="default" | "soft" | "transparent"`、`tone="neutral" | "warm" | "rose" | "underline"`、`frame="panel" | "bare"`、`semantics="segment" | "tabs"`、`ariaLabel`、`truncateLabels`、`disabled`、`className` 和 `itemClassName`;普通分段统一写入 `aria-pressed`,Tab 语义统一写入 `role="tablist"` / `role="tab"` / `aria-selected`,并承载禁用阻断、白底选中态、空闲 hover、焦点 ring、响应式列数、裸分段外壳、下划线选中态和 label 截断。拼图结果页、抓大鹅结果页、抓大鹅素材配置、抓大鹅创作 / 结果页难度选择、视觉小说结果页、creative-agent 模板确认弹窗和认证入口短信 / 密码登录切换已先迁移。后续白底结果页 Tab、弹窗分段选择、四选一配置项或认证 / 设置类下划线 Tab 只传选项、当前值和变更回调,不再重复 `grid + border + bg-white/62 + button aria-pressed` 或本地 `role="tab"` 下划线按钮。
|
- `PlatformSegmentedTabs`:接收 `items`、`activeId`、`onChange`、`columns="one" | "two" | "three" | "four" | "threeToSix"`、`gap="sm" | "md"`、`radius="md" | "lg" | "xl"`、`size="sm" | "md" | "compact" | "choice" | "tab"`、`surface="default" | "soft" | "transparent"`、`tone="neutral" | "warm" | "rose" | "underline"`、`frame="panel" | "bare"`、`semantics="segment" | "tabs"`、`ariaLabel`、`truncateLabels`、`disabled`、`className` 和 `itemClassName`;普通分段统一写入 `aria-pressed`,Tab 语义统一写入 `role="tablist"` / `role="tab"` / `aria-selected`,并承载禁用阻断、白底选中态、空闲 hover、焦点 ring、响应式列数、裸分段外壳、下划线选中态和 label 截断。拼图结果页、抓大鹅结果页、抓大鹅素材配置、抓大鹅创作 / 结果页难度选择、视觉小说结果页、creative-agent 模板确认弹窗和认证入口短信 / 密码登录切换已先迁移。后续白底结果页 Tab、弹窗分段选择、四选一配置项或认证 / 设置类下划线 Tab 只传选项、当前值和变更回调,不再重复 `grid + border + bg-white/62 + button aria-pressed` 或本地 `role="tab"` 下划线按钮。
|
||||||
- `PlatformStatGrid`:接收 `items`、`columns="two" | "three" | "four" | "twoToFour"`、`density="compact" | "default"`、`order="valueFirst" | "labelFirst"`、`surface="soft" | "plain"`、`textAlign="left" | "center"`、`className` 和 `itemClassName`;统一承载平台结果页里的统计小卡、状态 chip、白底摘要卡、label/value 排版和响应式列数。拼消消结果页素材摘要、方洞结果页封面状态 chip、抓大鹅结果页难度摘要、creative-agent 模板确认摘要和自定义世界实体目录世界页档案规模已先迁移,业务页只传统计项数组和少量布局参数,不再重复写 `grid + rounded + bg-white/* + text-xl/text-xs`。
|
- `PlatformStatGrid`:接收 `items`、`columns="two" | "three" | "four" | "twoToFour"`、`density="compact" | "default"`、`order="valueFirst" | "labelFirst"`、`surface="soft" | "plain"`、`textAlign="left" | "center"`、`className` 和 `itemClassName`;统一承载平台结果页里的统计小卡、状态 chip、白底摘要卡、label/value 排版和响应式列数。拼消消结果页素材摘要、方洞结果页封面状态 chip、抓大鹅结果页难度摘要、creative-agent 模板确认摘要和自定义世界实体目录世界页档案规模已先迁移,业务页只传统计项数组和少量布局参数,不再重复写 `grid + rounded + bg-white/* + text-xl/text-xs`。
|
||||||
- `PlatformPillBadge`:接收 `tone="muted" | "neutral" | "neutralSolid" | "lightOverlay" | "success" | "warning" | "danger" | "cool" | "profile" | "profileAccent" | "darkSoft" | "darkNeutral" | "darkSky" | "darkEmerald" | "darkAmber" | "darkRose"`、`size="xxs" | "xs" | "sm"`、可选 `icon`、`children`、`className` 和原生 span props;统一承接单个状态 / 标签 chip 的圆角、边框、底色、字号和图标间距,并通过 `platformPillBadgeModel.ts` 的 `getPlatformPillBadgeClassName` 给复制类交互按钮复用同一视觉 chrome。`xxs` 用于实体目录卡片等密集元信息 chip,`muted` 用于平台白底柔和选择态和地图节点当前状态,`lightOverlay` 用于主动作按钮内部的泥点消耗等浅色叠层小胶囊,`danger` 用于删除 / 选中危险态,`profile` / `profileAccent` 用于个人中心玫瑰色信息 / 分类 chip,`dark*` 用于 RPG 暗色弹窗和角色详情里的纯展示 chip。宝贝识物结果页发布状态、主题标签与占位资源 overlay,宝贝识物 / 拼图 / 抓大鹅 / 视觉小说工作台 BETA chip、汪汪声浪轻配置 chip、汪汪声浪结果页草稿 chip、汪汪声浪预览 VS chip、敲木鱼结果页飘字 chip、creative-agent 顶部阶段 / 过程计数 / 条目 meta chip、通用音频输入面板限制标签、自定义世界实体目录批量选择 / 生成中 / 开局 CG / 可扮演角色元信息 badge、RPG 首页公开作品卡 / 搜索结果 / 充值商品 / 移动端创建入口 / 桌面发现区 chip、RPG 世界详情静态元信息 chip、RPG 角色身份 / 等级 / 技能出手方式 / 技能详情与状态标签 / 背景故事解锁状态 / 好感等级 / 角色资产工作室动作状态 / 角色编辑技能动作状态 / 角色资源应用状态 / 场景角色选择状态 / 地标当前连接状态 / 地图节点方向标签 / 地图场景切换方向标签 / 营地编组状态数值 / 作品封面来源状态 / 开局物品标签、NPC 交易数量 / 赠礼好感和背包工坊材料需求等暗色展示 chip、抓大鹅批量新增 / 批量重生成物品名称预览 chip、抓大鹅 / RPG / 拼图 / 方洞结果页自动保存状态、抓大鹅结果页当前难度 badge、拼图结果页关卡生成中 overlay / 列表 badge、大鱼吃小鱼结果页终局 / 发布校验成功 badge、拼图图库详情页题材标签、自定义世界作品卡二级 badge、生成失败 chip,以及个人中心泥点账单余额、玩过总时长和玩过作品类型 chip 已先迁移。后续作品卡状态、结果页标签、个人中心轻量信息、按钮内消耗标签和轻量配置 chip 优先使用该 Module;多项数值 / 标签摘要仍使用 `PlatformStatGrid`,可交互标签编辑仍使用 `PlatformTagEditor`,可点击复制 / 分享 chip 使用 `CopyCodeButton` / `CopyFeedbackButton actionAppearance="pill"`。
|
- `PlatformPillBadge`:接收 `tone="muted" | "neutral" | "neutralSolid" | "lightOverlay" | "success" | "warning" | "danger" | "cool" | "profile" | "profileAccent" | "darkSoft" | "darkNeutral" | "darkSky" | "darkEmerald" | "darkAmber" | "darkRose"`、`size="xxs" | "xs" | "sm"`、可选 `icon`、`children`、`className` 和原生 span props;统一承接单个状态 / 标签 chip 的圆角、边框、底色、字号和图标间距,并通过 `platformPillBadgeModel.ts` 的 `getPlatformPillBadgeClassName` 给复制类交互按钮复用同一视觉 chrome。`xxs` 用于实体目录卡片等密集元信息 chip,`muted` 用于平台白底柔和选择态和地图节点当前状态,`lightOverlay` 用于主动作按钮内部的泥点消耗等浅色叠层小胶囊,`danger` 用于删除 / 选中危险态,`profile` / `profileAccent` 用于个人中心玫瑰色信息 / 分类 chip,`dark*` 用于 RPG 暗色弹窗和角色详情里的纯展示 chip。宝贝识物结果页发布状态、主题标签与占位资源 overlay,宝贝识物 / 拼图 / 抓大鹅 / 视觉小说工作台 BETA chip、汪汪声浪轻配置 chip、汪汪声浪结果页草稿 chip、汪汪声浪预览 VS chip、敲木鱼结果页飘字 chip、creative-agent 顶部阶段 / 过程计数 / 条目 meta chip、通用音频输入面板限制标签、自定义世界实体目录批量选择 / 生成中 / 开局 CG / 可扮演角色元信息 badge、RPG 首页公开作品卡 / 搜索结果 / 充值商品 / 移动端创建入口 / 桌面发现区 chip、RPG 世界详情静态元信息 chip、RPG 角色身份 / 等级 / 技能出手方式 / 技能详情与状态标签 / 背景故事解锁状态 / 好感等级 / 角色资产工作室动作状态 / 角色编辑技能动作状态 / 角色资源应用状态 / 场景角色选择状态 / 地标当前连接状态 / 地图节点方向标签 / 地图场景切换方向标签 / 营地编组状态数值 / 作品封面来源状态 / 开局物品标签、NPC 交易数量 / 赠礼好感和背包工坊材料需求等暗色展示 chip、抓大鹅批量新增 / 批量重生成物品名称预览 chip、抓大鹅 / RPG / 拼图 / 方洞结果页自动保存状态、抓大鹅结果页当前难度 badge、拼图结果页关卡生成中 overlay / 列表 badge、大鱼吃小鱼结果页终局 / 发布校验成功 badge、拼图图库详情页题材标签、自定义世界作品卡二级 badge、生成失败 chip,以及个人中心泥点账单余额、玩过总时长和玩过作品类型 chip 已先迁移。后续作品卡状态、结果页标签、个人中心轻量信息、按钮内消耗标签和轻量配置 chip 优先使用该 Module;多项数值 / 标签摘要仍使用 `PlatformStatGrid`,可交互标签编辑仍使用 `PlatformTagEditor`,可点击复制 / 分享 chip 使用 `CopyCodeButton` / `CopyFeedbackButton actionAppearance="pill"`。
|
||||||
@@ -129,8 +130,8 @@
|
|||||||
17.3. RPG 大编辑器主壳层和紧凑对话壳层的右上角关闭入口迁移到 `PlatformModalCloseButton variant="platformIcon"`;暗色编辑器仍保留原 `platform-icon-button` 视觉 token,但业务 JSX 不再手写 `button`、`aria-label` 和默认关闭图标。
|
17.3. RPG 大编辑器主壳层和紧凑对话壳层的右上角关闭入口迁移到 `PlatformModalCloseButton variant="platformIcon"`;暗色编辑器仍保留原 `platform-icon-button` 视觉 token,但业务 JSX 不再手写 `button`、`aria-label` 和默认关闭图标。
|
||||||
18. RPG 首页、公开广场、排行、作品架、视觉小说 runtime 普通白底面板、历史素材选择弹窗、视觉小说上传资产弹窗本地上传占位、自定义世界实体目录搜索无结果、RPG 大编辑器纯展示暗色列表、背景故事空档案和 RPG 运行态设置保存禁用提示中的无操作空态 / 轻量读取态迁移到 `PlatformEmptyState`;后续空态如果包含 CTA、插画、复杂列表恢复动作或玩法 HUD,再保留专用布局。
|
18. RPG 首页、公开广场、排行、作品架、视觉小说 runtime 普通白底面板、历史素材选择弹窗、视觉小说上传资产弹窗本地上传占位、自定义世界实体目录搜索无结果、RPG 大编辑器纯展示暗色列表、背景故事空档案和 RPG 运行态设置保存禁用提示中的无操作空态 / 轻量读取态迁移到 `PlatformEmptyState`;后续空态如果包含 CTA、插画、复杂列表恢复动作或玩法 HUD,再保留专用布局。
|
||||||
18.1. 历史图片 / 历史素材 / 可引用素材选择迁移到 `PlatformAssetPickerCard` 与 `PlatformAssetPickerGrid`;拼图历史图片弹窗、方洞历史生成、视觉小说历史素材选择器、RPG 大编辑器历史素材弹窗和抓大鹅封面编辑可引用素材网格已先迁移。后续素材选择只传素材数组、`imageSrc`、主副文案、可访问名称、surface、选中判断和选择回调,不再在业务页重复缩略图、边框、选中 ring、禁用态、`ResolvedAssetImage` 壳层、虚线读取 / 空态和网格 JSX。
|
18.1. 历史图片 / 历史素材 / 可引用素材选择迁移到 `PlatformAssetPickerCard` 与 `PlatformAssetPickerGrid`;拼图历史图片弹窗、方洞历史生成、视觉小说历史素材选择器、RPG 大编辑器历史素材弹窗和抓大鹅封面编辑可引用素材网格已先迁移。后续素材选择只传素材数组、`imageSrc`、主副文案、可访问名称、surface、选中判断和选择回调,不再在业务页重复缩略图、边框、选中 ring、禁用态、`ResolvedAssetImage` 壳层、虚线读取 / 空态和网格 JSX。
|
||||||
18.2. 平台白底圆角输入框和文本域迁移到 `PlatformTextField surface="platform"`;RPG 暗色弹窗 / 运行面板里的普通输入框、文本域和下拉框迁移到 `PlatformTextField surface="editorDark"` / `PlatformSelectField surface="editorDark"`;抓大鹅结果页作品名称 / 描述、封面描述、素材名称、批量新增 / 批量重生成物品名称,方洞结果页游戏名称、标签、简介、题材主题、反差规则、背景提示、形状数量、形状 / 洞口名称、形状目标洞口和图片提示词,拼图结果页作品名称 / 描述、关卡名称和智能修订输入,敲木鱼结果页作品标题 / 简介,视觉小说结果页的音乐生成、作品信息、开场、运行配置、角色、场景、阶段和世界观普通文本 / 下拉字段,以及视觉小说 / 抓大鹅 / 汪汪声浪 / 宝贝识物 / 拼消消 / 跳一跳创作工作台普通输入字段、敲木鱼创作工作台功德词条输入、creative-agent 模板确认调整弹层关卡数输入已先迁移。通用输入 Composer、通用创作图片输入面板的提示词文本域、自定义世界实体目录搜索框、认证验证码答案输入、自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿也使用 `PlatformTextField` / `PlatformSelectField`;浮动胶囊 Composer 可继续由 `.creative-agent-composer--floating textarea` 覆盖尺寸和背景,图片输入面板可通过局部 class 保留高度与浮动上传按钮避让,实体目录搜索框可通过局部 class 保留紧凑圆角和底色,验证码答案输入可通过局部 class 保留认证表单高度,暗色聊天草稿可通过局部 class 保留 `bg-black/25`,不在业务 JSX 中手写 textarea / input / select chrome。默认密度用于结果页主表单,`density="compact"` 用于选项卡片、工具条或认证提示内的紧凑字段,`density="roomy"` 用于宽内边距文本域、关卡详情字段或暗色弹窗主文本域;默认 `tone="warm"`,玩法需要保留调性焦点色时使用 `tone="rose"`、`tone="emerald"` 或 `tone="sky"`,不要在业务 JSX 中重复写 `focus:border-* focus:ring-*`。后续结果页、工作台、目录工具条、认证提示或 RPG 暗色弹窗内的普通文本输入 / 下拉框只传受控值、事件、可访问名称、占位符、选项和局部布局 class,不再重复基础边框、背景、内边距、字号、禁用态和焦点色。
|
18.2. 平台白底圆角输入框和文本域迁移到 `PlatformTextField surface="platform"`;RPG 暗色弹窗 / 运行面板里的普通输入框、文本域和下拉框迁移到 `PlatformTextField surface="editorDark"` / `PlatformSelectField surface="editorDark"`;抓大鹅结果页作品名称 / 描述、封面描述、素材名称、批量新增 / 批量重生成物品名称,方洞结果页游戏名称、标签、简介、题材主题、反差规则、背景提示、形状数量、形状 / 洞口名称、形状目标洞口和图片提示词,拼图结果页作品名称 / 描述、关卡名称和智能修订输入,敲木鱼结果页作品标题 / 简介,视觉小说结果页的音乐生成、作品信息、开场、运行配置、角色、场景、阶段和世界观普通文本 / 下拉字段,以及视觉小说 / 抓大鹅 / 汪汪声浪 / 宝贝识物 / 拼消消 / 跳一跳创作工作台普通输入字段、敲木鱼创作工作台功德词条输入、creative-agent 模板确认调整弹层关卡数输入已先迁移。通用输入 Composer、通用创作图片输入面板的提示词文本域、自定义世界实体目录搜索框、认证验证码答案输入、短信 / 密码登录、重置密码、绑定手机号、邀请码、账号安全表单字段、自定义选择弹窗角色名字 / 背景补充 / 生成模式 / 世界描述和角色聊天草稿也使用 `PlatformTextField` / `PlatformSelectField`;浮动胶囊 Composer 可继续由 `.creative-agent-composer--floating textarea` 覆盖尺寸和背景,图片输入面板可通过局部 class 保留高度与浮动上传按钮避让,实体目录搜索框可通过局部 class 保留紧凑圆角和底色,验证码答案输入和认证表单字段可通过局部 class 保留表单高度、横向验证码按钮布局和原生 `label` 关联,暗色聊天草稿可通过局部 class 保留 `bg-black/25`,不在业务 JSX 中手写 textarea / input / select chrome。默认密度用于结果页主表单,`density="compact"` 用于选项卡片、工具条或认证提示内的紧凑字段,`density="roomy"` 用于宽内边距文本域、关卡详情字段或暗色弹窗主文本域;默认 `tone="warm"`,玩法需要保留调性焦点色时使用 `tone="rose"`、`tone="emerald"` 或 `tone="sky"`,不要在业务 JSX 中重复写 `focus:border-* focus:ring-*`。后续结果页、工作台、目录工具条、认证提示、认证表单或 RPG 暗色弹窗内的普通文本输入 / 下拉框只传受控值、事件、可访问名称、占位符、选项和局部布局 class,不再重复基础边框、背景、内边距、字号、禁用态和焦点色。
|
||||||
18.3. 平台字段标签迁移到 `PlatformFieldLabel`;视觉小说结果页、抓大鹅结果页作品 / 封面 / 素材字段标题、方洞结果页主信息 / 形状 / 洞口 / 历史图片字段标题、拼图结果页关卡详情 / 发布弹窗字段标题、拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题、大鱼素材弹窗 prompt、RPG 发布弹窗发布检查 / 封面设置、汪汪声浪轻配置编辑器和宝贝识物工作台已先迁移。后续结果页、编辑弹窗或工作台中只表达字段名称的小标题,优先选择 `field` / `section` / `form` / `pill` / `accentPill`,不要在业务 JSX 中重复拼字段标题 class;带品牌化插画、运行态 HUD 或复杂步骤标题时可暂保留专用标题。
|
18.3. 平台字段标签迁移到 `PlatformFieldLabel`;视觉小说结果页、抓大鹅结果页作品 / 封面 / 素材字段标题、方洞结果页主信息 / 形状 / 洞口 / 历史图片字段标题、拼图结果页关卡详情 / 发布弹窗字段标题、拼消消创作工作台作品标题 / 简介 / 主题词、跳一跳创作工作台主题、大鱼素材弹窗 prompt、RPG 发布弹窗发布检查 / 封面设置、汪汪声浪轻配置编辑器、宝贝识物工作台,以及认证登录 / 绑定 / 邀请码 / 账号安全表单标题已先迁移。后续结果页、编辑弹窗、工作台或认证表单中只表达字段名称的小标题,优先选择 `field` / `section` / `form` / `pill` / `accentPill`,不要在业务 JSX 中重复拼字段标题 class;认证表单字段保留外层原生 `label`,带品牌化插画、运行态 HUD 或复杂步骤标题时可暂保留专用标题。
|
||||||
18.4. 平台白底分段 Tab / 二选一 / 四选一配置项迁移到 `PlatformSegmentedTabs`;拼图结果页、抓大鹅结果页、抓大鹅素材配置、抓大鹅创作 / 结果页难度选择、视觉小说结果页和 creative-agent 模板确认弹窗已先迁移。后续同类控件只传选项、当前 id、变更回调、列数、尺寸、调性和外壳形态,不再在业务 JSX 中重复容器边框、`bg-white/62`、选中态和 `aria-pressed`。
|
18.4. 平台白底分段 Tab / 二选一 / 四选一配置项迁移到 `PlatformSegmentedTabs`;拼图结果页、抓大鹅结果页、抓大鹅素材配置、抓大鹅创作 / 结果页难度选择、视觉小说结果页和 creative-agent 模板确认弹窗已先迁移。后续同类控件只传选项、当前 id、变更回调、列数、尺寸、调性和外壳形态,不再在业务 JSX 中重复容器边框、`bg-white/62`、选中态和 `aria-pressed`。
|
||||||
18.4.1. `PlatformSegmentedTabs` 支持 `semantics="tabs"`、`tone="underline"`、`size="tab"` 和 `columns="one"`,用于承接认证入口短信 / 密码登录切换这类真实 Tab 语义;业务页不再维护本地 `LoginTabButton`、`role="tab"`、`aria-selected` 和下划线选中态。
|
18.4.1. `PlatformSegmentedTabs` 支持 `semantics="tabs"`、`tone="underline"`、`size="tab"` 和 `columns="one"`,用于承接认证入口短信 / 密码登录切换这类真实 Tab 语义;业务页不再维护本地 `LoginTabButton`、`role="tab"`、`aria-selected` 和下划线选中态。
|
||||||
18.5. 平台只读信息块迁移到 `PlatformInfoBlock`;错误弹窗和生成完成弹窗的来源、错误和状态展示、分享弹窗正文,以及汪汪声浪预览卡场景 / 形象 / 难度 / 声浪信息行已先迁移。后续弹窗、详情页或预览卡里只是展示短标签 + 只读正文,或无标签纯只读正文时,优先使用该 Module;横向信息行通过 `labelClassName` / `valueClassName` 保留标签和值排版,不在业务 JSX 中重复白底信息块 chrome。
|
18.5. 平台只读信息块迁移到 `PlatformInfoBlock`;错误弹窗和生成完成弹窗的来源、错误和状态展示、分享弹窗正文,以及汪汪声浪预览卡场景 / 形象 / 难度 / 声浪信息行已先迁移。后续弹窗、详情页或预览卡里只是展示短标签 + 只读正文,或无标签纯只读正文时,优先使用该 Module;横向信息行通过 `labelClassName` / `valueClassName` 保留标签和值排版,不在业务 JSX 中重复白底信息块 chrome。
|
||||||
@@ -251,6 +252,7 @@
|
|||||||
- `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CharacterChatModal.test.tsx`
|
- `npm run test -- src/components/common/PlatformTextField.test.tsx src/components/SelectionCustomizationModals.test.tsx src/components/CharacterChatModal.test.tsx`
|
||||||
- `npm run test -- src/components/SelectionCustomizationModals.test.tsx src/components/common/PlatformSubpanel.test.tsx`
|
- `npm run test -- src/components/SelectionCustomizationModals.test.tsx src/components/common/PlatformSubpanel.test.tsx`
|
||||||
- `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`
|
- `npm run test -- src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformMediaFrame.test.tsx`
|
||||||
|
- `npm run test -- src/components/auth/AuthGate.test.tsx src/components/auth/AccountModal.test.tsx src/components/auth/BindPhoneScreen.test.tsx src/components/auth/CaptchaChallengeField.test.tsx src/components/common/PlatformTextField.test.tsx src/components/common/PlatformFieldLabel.test.tsx`
|
||||||
- `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`
|
- `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/puzzle-result/PuzzleResultView.test.tsx src/components/visual-novel-result/VisualNovelResultView.test.tsx src/components/match3d-result/Match3DResultView.test.tsx src/components/creative-agent/CreativeAgentTemplateConfirmPanel.test.tsx`
|
||||||
- `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/auth/AuthGate.test.tsx`
|
- `npm run test -- src/components/common/PlatformSegmentedTabs.test.tsx src/components/auth/AuthGate.test.tsx`
|
||||||
- `npm run test -- src/components/common/PlatformInfoBlock.test.tsx src/components/platform-entry/PlatformErrorDialog.test.tsx`
|
- `npm run test -- src/components/common/PlatformInfoBlock.test.tsx src/components/platform-entry/PlatformErrorDialog.test.tsx`
|
||||||
|
|||||||
@@ -259,8 +259,14 @@ test('account actions open in independent panels instead of inline expansion', a
|
|||||||
const changePhoneDialog = screen.getByRole('dialog', {
|
const changePhoneDialog = screen.getByRole('dialog', {
|
||||||
name: '绑定新手机号',
|
name: '绑定新手机号',
|
||||||
});
|
});
|
||||||
expect(within(changePhoneDialog).getByLabelText('新手机号')).toBeTruthy();
|
const phoneInput = within(changePhoneDialog).getByLabelText(
|
||||||
expect(within(changePhoneDialog).getByLabelText('验证码')).toBeTruthy();
|
'新手机号',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const codeInput = within(changePhoneDialog).getByLabelText(
|
||||||
|
'验证码',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
expect(phoneInput.className).toContain('platform-text-field');
|
||||||
|
expect(codeInput.className).toContain('platform-text-field');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('nested settings panels keep back navigation without an extra close action', async () => {
|
test('nested settings panels keep back navigation without an extra close action', async () => {
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ import type {
|
|||||||
AuthUser,
|
AuthUser,
|
||||||
} from '../../services/authService';
|
} from '../../services/authService';
|
||||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||||
|
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||||
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
||||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||||
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||||||
|
import { PlatformTextField } from '../common/PlatformTextField';
|
||||||
import type { PlatformSettingsSection } from './AuthUiContext';
|
import type { PlatformSettingsSection } from './AuthUiContext';
|
||||||
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
||||||
|
|
||||||
@@ -1010,10 +1012,12 @@ export function AccountModal({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>新手机号</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
新手机号
|
||||||
className="platform-input h-11"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
|
className="h-11"
|
||||||
value={phone}
|
value={phone}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
placeholder="13800000000"
|
placeholder="13800000000"
|
||||||
@@ -1021,11 +1025,13 @@ export function AccountModal({
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>验证码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
|
验证码
|
||||||
|
</PlatformFieldLabel>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<PlatformTextField
|
||||||
className="platform-input h-11 min-w-0 flex-1"
|
className="h-11 min-w-0 flex-1"
|
||||||
value={code}
|
value={code}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
placeholder="输入验证码"
|
placeholder="输入验证码"
|
||||||
@@ -1135,10 +1141,12 @@ export function AccountModal({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>当前密码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
当前密码
|
||||||
className="platform-input h-11"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
|
className="h-11"
|
||||||
value={currentPassword}
|
value={currentPassword}
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
@@ -1148,10 +1156,12 @@ export function AccountModal({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>新密码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
新密码
|
||||||
className="platform-input h-11"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
|
className="h-11"
|
||||||
value={newPassword}
|
value={newPassword}
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
|
|||||||
@@ -34,12 +34,20 @@ const authMocks = vi.hoisted(() => ({
|
|||||||
consumeAuthCallbackResult: vi.fn(),
|
consumeAuthCallbackResult: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../../services/apiClient', () => ({
|
vi.mock('../../services/apiClient', async () => {
|
||||||
|
const actual =
|
||||||
|
await vi.importActual<typeof import('../../services/apiClient')>(
|
||||||
|
'../../services/apiClient',
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
AUTH_STATE_EVENT: 'genarrative-auth-state-changed',
|
AUTH_STATE_EVENT: 'genarrative-auth-state-changed',
|
||||||
ensureStoredAccessToken: authMocks.ensureStoredAccessToken,
|
ensureStoredAccessToken: authMocks.ensureStoredAccessToken,
|
||||||
getStoredAccessToken: authMocks.getStoredAccessToken,
|
getStoredAccessToken: authMocks.getStoredAccessToken,
|
||||||
refreshStoredAccessToken: authMocks.refreshStoredAccessToken,
|
refreshStoredAccessToken: authMocks.refreshStoredAccessToken,
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
|
|
||||||
vi.mock('../../services/authService', () => ({
|
vi.mock('../../services/authService', () => ({
|
||||||
authEntry: authMocks.authEntry,
|
authEntry: authMocks.authEntry,
|
||||||
@@ -408,8 +416,17 @@ test('auth gate opens a login modal for protected actions and resumes after logi
|
|||||||
expect(dialog).toBeTruthy();
|
expect(dialog).toBeTruthy();
|
||||||
expect(screen.queryByText('先登录账号,再同步你的冒险进度。')).toBeNull();
|
expect(screen.queryByText('先登录账号,再同步你的冒险进度。')).toBeNull();
|
||||||
|
|
||||||
await user.type(within(dialog).getByLabelText('手机号'), '13800000000');
|
const phoneInput = within(dialog).getByLabelText(
|
||||||
await user.type(within(dialog).getByLabelText('验证码'), '123456');
|
'手机号',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const codeInput = within(dialog).getByLabelText(
|
||||||
|
'验证码',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
expect(phoneInput.className).toContain('platform-text-field');
|
||||||
|
expect(codeInput.className).toContain('platform-text-field');
|
||||||
|
|
||||||
|
await user.type(phoneInput, '13800000000');
|
||||||
|
await user.type(codeInput, '123456');
|
||||||
await acceptLegalConsent(user, dialog);
|
await acceptLegalConsent(user, dialog);
|
||||||
await user.click(within(dialog).getByRole('button', { name: '登录' }));
|
await user.click(within(dialog).getByRole('button', { name: '登录' }));
|
||||||
|
|
||||||
@@ -592,9 +609,11 @@ test('auth gate hides register entry and opens invite modal for new sms account'
|
|||||||
const inviteDialog = await screen.findByRole('dialog', {
|
const inviteDialog = await screen.findByRole('dialog', {
|
||||||
name: '请填写邀请码',
|
name: '请填写邀请码',
|
||||||
});
|
});
|
||||||
expect(
|
const inviteCodeInput = within(inviteDialog).getByLabelText(
|
||||||
(within(inviteDialog).getByLabelText('邀请码') as HTMLInputElement).value,
|
'邀请码',
|
||||||
).toBe('SPRING2026');
|
) as HTMLInputElement;
|
||||||
|
expect(inviteCodeInput.value).toBe('SPRING2026');
|
||||||
|
expect(inviteCodeInput.className).toContain('platform-text-field');
|
||||||
expect(
|
expect(
|
||||||
within(inviteDialog).getByRole('button', { name: '提交' }),
|
within(inviteDialog).getByRole('button', { name: '提交' }),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
@@ -792,7 +811,11 @@ test('login modal resets draft state every time it is reopened', async () => {
|
|||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
await user.type(within(firstDialog).getByLabelText('验证码'), '123456');
|
await user.type(within(firstDialog).getByLabelText('验证码'), '123456');
|
||||||
await user.click(within(firstDialog).getByRole('tab', { name: '密码登录' }));
|
await user.click(within(firstDialog).getByRole('tab', { name: '密码登录' }));
|
||||||
await user.type(within(firstDialog).getByLabelText('密码'), 'passw0rd');
|
const passwordInput = within(firstDialog).getByLabelText(
|
||||||
|
'密码',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
expect(passwordInput.className).toContain('platform-text-field');
|
||||||
|
await user.type(passwordInput, 'passw0rd');
|
||||||
await user.click(
|
await user.click(
|
||||||
within(firstDialog).getByRole('button', { name: '忘记密码' }),
|
within(firstDialog).getByRole('button', { name: '忘记密码' }),
|
||||||
);
|
);
|
||||||
|
|||||||
56
src/components/auth/BindPhoneScreen.test.tsx
Normal file
56
src/components/auth/BindPhoneScreen.test.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/* @vitest-environment jsdom */
|
||||||
|
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
|
import type { AuthUser } from '../../services/authService';
|
||||||
|
import { BindPhoneScreen } from './BindPhoneScreen';
|
||||||
|
|
||||||
|
const baseUser: AuthUser = {
|
||||||
|
id: 'user-1',
|
||||||
|
displayName: '微信旅人',
|
||||||
|
avatarUrl: null,
|
||||||
|
publicUserCode: 'user-bind-phone',
|
||||||
|
phoneNumberMasked: null,
|
||||||
|
loginMethod: 'wechat',
|
||||||
|
bindingStatus: 'pending_bind_phone',
|
||||||
|
wechatBound: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
test('绑定手机号表单复用平台输入和字段标题', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<BindPhoneScreen
|
||||||
|
user={baseUser}
|
||||||
|
platformTheme="light"
|
||||||
|
sendingCode={false}
|
||||||
|
binding={false}
|
||||||
|
error=""
|
||||||
|
captchaChallenge={null}
|
||||||
|
onSendCode={vi.fn().mockResolvedValue({
|
||||||
|
cooldownSeconds: 60,
|
||||||
|
expiresInSeconds: 300,
|
||||||
|
})}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
onLogout={vi.fn().mockResolvedValue(undefined)}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const phoneInput = screen.getByLabelText('手机号') as HTMLInputElement;
|
||||||
|
const codeInput = screen.getByLabelText('验证码') as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(phoneInput.className).toContain('platform-text-field');
|
||||||
|
expect(codeInput.className).toContain('platform-text-field');
|
||||||
|
expect(screen.getByText('手机号').className).toContain(
|
||||||
|
'text-[var(--platform-text-strong)]',
|
||||||
|
);
|
||||||
|
|
||||||
|
await user.type(phoneInput, '13800000000');
|
||||||
|
await user.type(codeInput, '123456');
|
||||||
|
await user.click(screen.getByRole('button', { name: '绑定手机号并进入游戏' }));
|
||||||
|
|
||||||
|
expect(onSubmit).toHaveBeenCalledWith('13800000000', '123456');
|
||||||
|
});
|
||||||
@@ -3,7 +3,9 @@ import { useEffect, useState } from 'react';
|
|||||||
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
|
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
|
||||||
import type { AuthCaptchaChallenge, AuthUser } from '../../services/authService';
|
import type { AuthCaptchaChallenge, AuthUser } from '../../services/authService';
|
||||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||||
|
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||||
|
import { PlatformTextField } from '../common/PlatformTextField';
|
||||||
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
||||||
|
|
||||||
type BindPhoneScreenProps = {
|
type BindPhoneScreenProps = {
|
||||||
@@ -88,10 +90,11 @@ export function BindPhoneScreen({
|
|||||||
void onSubmit(phone, code);
|
void onSubmit(phone, code);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>手机号</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
手机号
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="tel"
|
autoComplete="tel"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={phone}
|
value={phone}
|
||||||
@@ -100,11 +103,13 @@ export function BindPhoneScreen({
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>验证码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
|
验证码
|
||||||
|
</PlatformFieldLabel>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<PlatformTextField
|
||||||
className="platform-input min-w-0 flex-1"
|
className="min-w-0 flex-1"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={code}
|
value={code}
|
||||||
onChange={(event) => setCode(event.target.value)}
|
onChange={(event) => setCode(event.target.value)}
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ import {
|
|||||||
readStoredLegalConsent,
|
readStoredLegalConsent,
|
||||||
} from '../common/legalDocuments';
|
} from '../common/legalDocuments';
|
||||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||||
|
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||||
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
||||||
import { PlatformSegmentedTabs } from '../common/PlatformSegmentedTabs';
|
import { PlatformSegmentedTabs } from '../common/PlatformSegmentedTabs';
|
||||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||||
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||||||
|
import { PlatformTextField } from '../common/PlatformTextField';
|
||||||
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
import { CaptchaChallengeField } from './CaptchaChallengeField';
|
||||||
|
|
||||||
type SmsScene = 'login' | 'reset_password';
|
type SmsScene = 'login' | 'reset_password';
|
||||||
@@ -273,10 +275,11 @@ export function LoginScreen({
|
|||||||
void onPasswordSubmit(phone, password);
|
void onPasswordSubmit(phone, password);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>手机号</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
手机号
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="tel"
|
autoComplete="tel"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={phone}
|
value={phone}
|
||||||
@@ -284,10 +287,11 @@ export function LoginScreen({
|
|||||||
placeholder="13800000000"
|
placeholder="13800000000"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>密码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
密码
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
@@ -516,10 +520,11 @@ function PhoneCodeForm({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{showPhoneField ? (
|
{showPhoneField ? (
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>手机号</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
手机号
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="tel"
|
autoComplete="tel"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={phone}
|
value={phone}
|
||||||
@@ -529,11 +534,13 @@ function PhoneCodeForm({
|
|||||||
</label>
|
</label>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>验证码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
|
验证码
|
||||||
|
</PlatformFieldLabel>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<PlatformTextField
|
||||||
className="platform-input min-w-0 flex-1"
|
className="min-w-0 flex-1"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={code}
|
value={code}
|
||||||
onChange={(event) => onCodeChange(event.target.value)}
|
onChange={(event) => onCodeChange(event.target.value)}
|
||||||
@@ -609,10 +616,11 @@ function PasswordResetPanel({
|
|||||||
void onSubmit();
|
void onSubmit();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>手机号</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
手机号
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="tel"
|
autoComplete="tel"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={phone}
|
value={phone}
|
||||||
@@ -620,11 +628,13 @@ function PasswordResetPanel({
|
|||||||
placeholder="13800000000"
|
placeholder="13800000000"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>验证码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
|
验证码
|
||||||
|
</PlatformFieldLabel>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<input
|
<PlatformTextField
|
||||||
className="platform-input min-w-0 flex-1"
|
className="min-w-0 flex-1"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
value={code}
|
value={code}
|
||||||
onChange={(event) => onCodeChange(event.target.value)}
|
onChange={(event) => onCodeChange(event.target.value)}
|
||||||
@@ -645,10 +655,11 @@ function PasswordResetPanel({
|
|||||||
</PlatformActionButton>
|
</PlatformActionButton>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>新密码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
新密码
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
|
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
|
||||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||||
|
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||||
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
|
||||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||||
|
import { PlatformTextField } from '../common/PlatformTextField';
|
||||||
|
|
||||||
type RegistrationInviteModalProps = {
|
type RegistrationInviteModalProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -84,10 +86,11 @@ export function RegistrationInviteModal({
|
|||||||
void onSubmit(normalizedInviteCode);
|
void onSubmit(normalizedInviteCode);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label className="grid gap-2 text-sm text-[var(--platform-text-base)]">
|
<label className="grid gap-2">
|
||||||
<span>邀请码</span>
|
<PlatformFieldLabel variant="form" className="mb-0">
|
||||||
<input
|
邀请码
|
||||||
className="platform-input"
|
</PlatformFieldLabel>
|
||||||
|
<PlatformTextField
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
value={inviteCode}
|
value={inviteCode}
|
||||||
onChange={(event) => setInviteCode(event.target.value)}
|
onChange={(event) => setInviteCode(event.target.value)}
|
||||||
|
|||||||
Reference in New Issue
Block a user