20 KiB
我的页签反馈入口与反馈页 Implementation Plan
For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.
Goal: 在平台“我的”页签中新增“反馈”入口,点击后进入独立反馈路由,并按用户提供的参考图落地反馈页面 UI。
Architecture: 复用现有前端单页路由体系:SelectionStage 负责页面阶段,appPageRoutes.ts 负责 URL 映射,PlatformEntryFlowShellImpl 负责按阶段渲染视图。“我的”页签只增加一个入口回调,不在当前面板下方展开内容;反馈页作为独立页面组件挂到新阶段。首版先做前端静态表单与本地提交成功态,不新增后端表结构或 SpacetimeDB 写入,除非产品补充明确要求持久化反馈。
Tech Stack: React 19、TypeScript、Tailwind utility class、lucide-react、现有 Genarrative 平台入口组件体系。
Current context / assumptions
Reference image
参考图是一张移动端“帮助与反馈”页面,视觉和信息结构如下:
- 页面整体:浅灰背景,白色圆角卡片,黑/深灰标题文字,浅灰 placeholder,蓝色主按钮与蓝色文本链接。
- 顶部栏:白色导航/header,左侧为小 home 图标,中间标题为“帮助与反馈”,右侧为胶囊形更多/控制区。项目实现时可按现有平台导航规范简化为返回按钮 + 居中标题;若需要完全贴近图片,可使用 home 图标作为返回到“我的”页签的按钮。
- 内容区 section label:左上灰色文字“反馈问题”。
- 第一张表单卡:标题“问题描述”,大文本输入区域,placeholder 为“请填写10个字以上的问题描述以便我们提供更好的帮助,温馨提醒您请勿填写身份证号等个人隐私信息。”,右下角字数统计“0/200”。
- 第二张表单卡:标题“上传凭证(提供问题截图)”,左侧虚线边框上传方块,内含图片/上传 + 加号图标,文字“上传凭证”“(最多四张)”。
- 第三张表单卡:标题“联系电话”,placeholder 为“选填,如您填写则将会同步开发者与您联系”。
- 底部操作:大号蓝色圆角按钮“提交”,下方居中蓝色链接“查看反馈与投诉记录”。
实现约束:
- 反馈页面应命名为“帮助与反馈”,但“我的”页签入口可显示为“反馈”或“帮助与反馈”,优先以清爽短入口为准。
- 问题描述最少 10 个字、最多 200 个字,并实时显示
当前字数/200。 - 上传凭证首版如不接后端,可先支持前端选择/预览最多 4 张图片,提交时仅进入成功态;如无法快速安全实现预览,可先保留上传占位并在文档中标注待接入。
- 联系电话为选填。
- “查看反馈与投诉记录”首版无后端记录时可以先禁用、隐藏,或点击后给出轻量提示;若保留可见,应在计划/PRD 标明记录页不在首版范围。
- 当前工作区是
/home/dsk/workspace/Genarrative/.worktrees/hermes-19e77eb0,不要额外拼接Genarrative/。 - 平台首页复用
src/components/rpg-entry/RpgEntryHomeView.tsx;src/components/platform-entry/PlatformEntryHomeView.tsx只是 re-export。 - “我的”页签的常用功能区域位于
src/components/rpg-entry/RpgEntryHomeView.tsx:3958-4000,现有入口包括“每日任务 / 邀请好友 / 填邀请码 / 玩家社区”。 - 当前页面阶段类型位于
src/components/platform-entry/platformEntryTypes.ts:16-38;路由映射位于src/routing/appPageRoutes.ts:7-27。 src/App.tsx:60-63调用pushAppHistoryPath(resolvePathForSelectionStage(stage)),所以新增阶段必须同步APP_STAGE_ROUTES。PlatformEntryFlowShellImpl在src/components/platform-entry/PlatformEntryFlowShellImpl.tsx:5081+根据selectionStage渲染不同页面,平台首页在selectionStage === 'platform'分支。- 参考图片已保存到
.hermes/plans/assets/profile-feedback-reference-2026-05-08.png,计划与实现均以该图片内容为主要 UI 依据。 - 按项目约束,工程修改需同步文档;若没有更具体 PRD,需要先补一份简洁落地文档到
docs/。
Proposed approach
新增一个轻量前端反馈页面阶段:
- 路由:
/profile/feedback - 阶段:
profile-feedback - 组件:
src/components/platform-entry/PlatformFeedbackView.tsx - “我的”页签入口:在常用功能区增加“反馈”按钮,点击调用新 prop
onOpenFeedback。 - 页面行为:
- 顶部返回按钮返回
platform阶段,并切回profile页签。 - 未登录用户点击入口时,优先弹登录;如果产品允许匿名反馈,可改为允许进入。
- 表单字段首版只在前端维护:问题描述、上传凭证图片、联系电话。
- 提交后显示成功态,不做 API 请求;后续如要持久化,再补
shared-contracts + api-server + SpacetimeDB方案。
- 顶部返回按钮返回
Step-by-step plan
Task 1: 补充反馈页落地文档
Objective: 先把反馈入口和页面边界写清楚,避免编码时需求漂移。
Files:
- Create:
docs/prd/PROFILE_FEEDBACK_ENTRY_PRD_2026-05-08.md
Step 1: 新建 PRD 文档
写入内容建议包含:
# 我的页签反馈入口 PRD
## 目标
- 在“我的”页签提供反馈入口。
- 点击入口进入独立反馈路由 `/profile/feedback`。
- 反馈页移动端优先,桌面端居中卡片展示。
## 首版范围
- 前端表单:问题描述、上传凭证占位/前端图片预览、联系电话。
- 问题描述 10-200 字,显示实时字数统计。
- 提交后显示成功态。
- 不新增后端存储,不修改 SpacetimeDB 表结构。
## 交互
- 已登录用户:点击“反馈”进入反馈页。
- 未登录用户:点击入口触发登录弹窗。
- 返回:回到平台首页并定位“我的”页签。
## UI
- 以 `.hermes/plans/assets/profile-feedback-reference-2026-05-08.png` 为准,落地“帮助与反馈”移动端表单。
- 不在 UI 中堆叠说明性长文案。
- 入口是独立页面导航,不在“我的”面板下方展开。
## 验收
- `/profile/feedback` 可被浏览器前进/后退访问。
- “我的”页签反馈入口可进入该路由。
- 移动端和桌面端均不溢出。
- `npm run check:encoding`、`npm run typecheck` 通过。
Step 2: 验证文档编码
Run: npm run check:encoding
Expected: PASS,无中文编码错误。
Step 3: Commit
git add docs/prd/PROFILE_FEEDBACK_ENTRY_PRD_2026-05-08.md
git commit -m "docs: add profile feedback entry prd"
Task 2: 扩展页面阶段与路由映射
Objective: 让 /profile/feedback 成为主应用可识别的独立路由。
Files:
- Modify:
src/components/platform-entry/platformEntryTypes.ts - Modify:
src/routing/appPageRoutes.ts
Step 1: 修改 SelectionStage 类型
在 SelectionStage union 中追加:
| 'profile-feedback'
推荐放在 'platform' 附近或末尾,保持字面量清晰。
Step 2: 修改 STAGE_ROUTE_ENTRIES
在 src/routing/appPageRoutes.ts 的 STAGE_ROUTE_ENTRIES 中追加:
['profile-feedback', '/profile/feedback'],
建议放在 ['platform', '/'] 后面,表示平台个人页子路由。
Step 3: 验证类型推导
Run: npm run typecheck
Expected: 若还未创建渲染组件,可能只通过路由类型;若出现 exhaustive 相关错误,留到后续任务处理。
Step 4: Commit
git add src/components/platform-entry/platformEntryTypes.ts src/routing/appPageRoutes.ts
git commit -m "feat: add profile feedback route stage"
Task 3: 新建反馈页面组件
Objective: 创建移动端优先的独立反馈页面。
Files:
- Create:
src/components/platform-entry/PlatformFeedbackView.tsx
Step 1: 创建组件 props
组件接口建议:
export type PlatformFeedbackViewProps = {
onBack: () => void;
onSubmit?: (payload: PlatformFeedbackPayload) => void | Promise<void>;
};
export type PlatformFeedbackPayload = {
description: string;
contactPhone: string;
evidenceFiles: File[];
};
Step 2: 实现 UI 状态
使用 useState 管理:
descriptioncontactPhoneevidenceFilesevidencePreviewUrlserrorisSubmittingsubmitted
Step 3: 实现页面结构
建议结构:
import { ArrowLeft, CheckCircle2, Home, ImagePlus, Send } from 'lucide-react';
import { useEffect, useState } from 'react';
const MAX_FEEDBACK_DESCRIPTION_LENGTH = 200;
const MIN_FEEDBACK_DESCRIPTION_LENGTH = 10;
const MAX_FEEDBACK_EVIDENCE_COUNT = 4;
页面外壳建议复用现有视觉变量:
<div className="platform-page-stage platform-remap-surface min-h-0 min-w-0 overflow-y-auto px-4 py-4 sm:px-6 lg:px-8">
<div className="mx-auto flex w-full max-w-2xl flex-col gap-4">
<header className="platform-surface platform-surface--soft rounded-[1.6rem] px-4 py-4">
<button type="button" onClick={onBack} ...>
<ArrowLeft ... /> 返回
</button>
<h1>反馈</h1>
</header>
...
</div>
</div>
注意:不要写大段“功能说明类文案”;字段 label 简短即可。
Step 4: 表单校验
提交时:
description.trim().length < 10:提示“请填写10个字以上的问题描述”description.trim().length > 200:提示“问题描述不能超过 200 字”contactPhone.trim().length > 40:提示“联系电话不能超过 40 字”- 上传凭证最多 4 张;超出时提示“最多上传四张凭证”
Step 5: 提交行为
首版无后端时:
await onSubmit?.({
description: description.trim(),
contactPhone: contactPhone.trim(),
evidenceFiles,
});
setSubmitted(true);
如果没有传 onSubmit,也显示成功态。代码注释说明:
// 中文注释:首版反馈页只完成前端收集与成功态;接入后端时在 onSubmit 中替换为 API 调用。
Step 6: Commit
git add src/components/platform-entry/PlatformFeedbackView.tsx
git commit -m "feat: add platform feedback view"
Task 4: 在“我的”页签增加反馈入口 prop
Objective: 让 Profile 页面能触发反馈路由,同时保持组件职责清晰。
Files:
- Modify:
src/components/rpg-entry/RpgEntryHomeView.tsx - Modify:
src/components/platform-entry/PlatformEntryHomeView.tsx(通常无需改,re-export 类型会自动带出)
Step 1: 扩展 Props
在 RpgEntryHomeViewProps 中新增:
onOpenFeedback?: () => void;
Step 2: 从 props 解构
在 RpgEntryHomeView 函数参数解构区新增:
onOpenFeedback,
Step 3: 增加入口按钮
在 profileContent 的常用功能 grid 中,建议在“玩家社区”后追加:
<ProfileShortcutButton
label="反馈"
subLabel="问题与建议"
icon={MessageCircle}
onClick={onOpenFeedback}
/>
如果参考图中入口位置不同,按参考图调整;但仍必须进入独立路由。
Step 4: 未提供回调时行为
ProfileShortcutButton 已允许 onClick 为空;此处传 onOpenFeedback 即可。若希望按钮始终可点,应在父组件必传。
Step 5: 验证类型
Run: npm run typecheck
Expected: PASS 或只剩父组件未传 prop 的问题。
Step 6: Commit
git add src/components/rpg-entry/RpgEntryHomeView.tsx src/components/platform-entry/PlatformEntryHomeView.tsx
git commit -m "feat: add feedback shortcut to profile tab"
Task 5: 接入 PlatformEntryFlowShellImpl 渲染与导航
Objective: 点击“反馈”进入 /profile/feedback,返回后回到“我的”页签。
Files:
- Modify:
src/components/platform-entry/PlatformEntryFlowShellImpl.tsx
Step 1: 导入组件
在 imports 中新增:
import { PlatformFeedbackView } from './PlatformFeedbackView';
Step 2: 创建打开反馈页函数
在 const { setPlatformTab } = platformBootstrap; 附近新增:
const openProfileFeedback = useCallback(() => {
if (!authUi?.user) {
authUi?.openLoginModal();
return;
}
setPlatformTab('profile');
setSelectionStage('profile-feedback');
}, [authUi, setPlatformTab, setSelectionStage]);
如产品允许匿名反馈,则移除登录判断。
Step 3: 给首页传入入口回调
在 PlatformEntryHomeView props 中加入:
onOpenFeedback={openProfileFeedback}
Step 4: 增加渲染分支
在 selectionStage === 'platform' 分支后、详情页分支前新增:
{selectionStage === 'profile-feedback' && (
<motion.div
key="platform-profile-feedback"
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
className="flex h-full min-h-0 flex-col"
>
<PlatformFeedbackView
onBack={() => {
setPlatformTab('profile');
setSelectionStage('platform');
}}
/>
</motion.div>
)}
Step 5: 直接访问路由的 tab 同步
为处理用户直接访问 /profile/feedback 后返回,返回逻辑已 setPlatformTab('profile')。如需要进入反馈页时也设置 tab,可加 effect:
useEffect(() => {
if (selectionStage === 'profile-feedback') {
setPlatformTab('profile');
}
}, [selectionStage, setPlatformTab]);
Step 6: Commit
git add src/components/platform-entry/PlatformEntryFlowShellImpl.tsx
git commit -m "feat: wire profile feedback navigation"
Task 6: 增加路由与反馈页基础测试
Objective: 用自动化测试覆盖新路由映射和反馈页核心交互。
Files:
- Create or Modify:
src/routing/appPageRoutes.test.ts - Create:
src/components/platform-entry/PlatformFeedbackView.test.tsx
Step 1: 路由测试
如果已有 appPageRoutes.test.ts,追加;否则创建:
import { describe, expect, it } from 'vitest';
import {
resolvePathForSelectionStage,
resolveSelectionStageFromPath,
} from './appPageRoutes';
describe('appPageRoutes', () => {
it('resolves profile feedback route', () => {
expect(resolveSelectionStageFromPath('/profile/feedback')).toBe('profile-feedback');
expect(resolvePathForSelectionStage('profile-feedback')).toBe('/profile/feedback');
});
});
Step 2: 反馈页测试
测试重点:
- 渲染“帮助与反馈”标题。
- 问题描述过短时提交显示错误。
- 输入有效问题描述后提交显示成功态。
- 字数统计随输入更新。
- 上传凭证入口最多接受 4 张图片。
- 点击返回调用
onBack。
示例:
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { PlatformFeedbackView } from './PlatformFeedbackView';
describe('PlatformFeedbackView', () => {
it('validates content before submit', () => {
render(<PlatformFeedbackView onBack={vi.fn()} />);
fireEvent.click(screen.getByRole('button', { name: '提交' }));
expect(screen.getByText('请填写10个字以上的问题描述')).toBeInTheDocument();
});
});
注意检查项目当前 test setup 是否已引入 jest-dom matcher;若没有,使用 truthy DOM 节点断言:
expect(screen.getByText('请补充反馈内容')).toBeTruthy();
Step 3: 运行定向测试
Run:
npm run test -- src/routing/appPageRoutes.test.ts src/components/platform-entry/PlatformFeedbackView.test.tsx
Expected: PASS。
Step 4: Commit
git add src/routing/appPageRoutes.test.ts src/components/platform-entry/PlatformFeedbackView.test.tsx
git commit -m "test: cover profile feedback route and form"
Task 7: 全量前端验证与移动端 smoke
Objective: 确认新增页面不破坏编码、类型和基础交互。
Files:
- No code changes unless validation finds issues.
Step 1: 编码检查
Run: npm run check:encoding
Expected: PASS。
Step 2: ESLint
Run: npm run lint:eslint
Expected: PASS。
Step 3: TypeScript
Run: npm run typecheck
Expected: PASS。
Step 4: 测试
Run: npm run test -- src/routing/appPageRoutes.test.ts src/components/platform-entry/PlatformFeedbackView.test.tsx
Expected: PASS。
Step 5: 本地页面 smoke
Run: npm run dev:web
手动验证:
- 打开
http://127.0.0.1:3000/。 - 登录后进入“我的”页签。
- 点击“反馈”。
- 地址变为
/profile/feedback。 - 页面显示反馈表单。
- 提交空内容出现错误。
- 输入有效内容后显示成功态。
- 点击返回后回到首页“我的”页签。
- 直接打开
http://127.0.0.1:3000/profile/feedback能显示反馈页。 - 使用移动端视口(如 390×844)确认按钮和表单不溢出。
Step 6: Commit validation fixes if any
git add <fixed-files>
git commit -m "fix: polish profile feedback validation"
Files likely to change
docs/prd/PROFILE_FEEDBACK_ENTRY_PRD_2026-05-08.md:新增反馈入口落地文档。src/components/platform-entry/platformEntryTypes.ts:新增profile-feedback阶段。src/routing/appPageRoutes.ts:新增/profile/feedback路由映射。.hermes/plans/assets/profile-feedback-reference-2026-05-08.png:反馈页参考图。src/components/platform-entry/PlatformFeedbackView.tsx:新增反馈页面。src/components/rpg-entry/RpgEntryHomeView.tsx:新增“我的”页签反馈入口和onOpenFeedbackprop。src/components/platform-entry/PlatformEntryFlowShellImpl.tsx:接入反馈页打开与返回导航。src/routing/appPageRoutes.test.ts:新增路由映射测试。src/components/platform-entry/PlatformFeedbackView.test.tsx:新增反馈页交互测试。
Tests / validation
Minimum required:
npm run check:encoding
npm run typecheck
npm run test -- src/routing/appPageRoutes.test.ts src/components/platform-entry/PlatformFeedbackView.test.tsx
Recommended before merge:
npm run lint:eslint
npm run test
npm run build
Manual smoke:
- 登录后“我的”页签显示“反馈”入口。
- 点击入口进入
/profile/feedback。 - 浏览器后退和页面返回按钮行为符合预期。
- 移动端视口无横向溢出。
- 页面没有把反馈表单展开在“我的”页签下方。
Risks, tradeoffs, and open questions
- 参考图落地风险: 参考图是浅色移动端表单,而项目现有平台 UI 可能偏游戏化/深色变量;实现时需要优先复刻信息结构与交互,不要为了完全一致而破坏现有主题适配。
- 反馈是否需要后端存储: 本计划首版不新增后端,只做前端收集和成功态。若产品要求真实提交,需要新增后端方案:
shared-contractsDTO、api-server路由、SpacetimeDB 表/迁移、后台查看入口,并按 SpacetimeDB skills 执行。 - 登录要求: 计划默认未登录用户点击入口弹登录。若希望匿名反馈,应取消该限制,并在 payload 中允许无用户身份。
- 入口位置: 当前建议放在“我的”页签常用功能 grid 中。若参考图明确是列表项或设置区入口,应按图调整,但仍进入独立路由。
- 图标复用: 可先用
MessageCircle或MessageSquareText,避免引入新依赖。 - 现有大文件风险:
RpgEntryHomeView.tsx很大,实施时必须局部补丁,避免整文件重写导致中文编码或格式大范围变化。
Implementation notes
- 所有中文注释和文案保持 UTF-8。
- 不要新增
.env.local到.gitignore。 - 不要把反馈页做成“我的”页签内部展开面板。
- 不要新增后端或数据库,除非用户确认反馈必须持久化。
- 若后续接入后端,必须先补技术文档,再按 DDD 与 SpacetimeDB 约束落地。
