Files
ftb-project-management/apps/web/app/wenfan-xiaobao/page.tsx
Script Generator 846bf9e8d1 feat(问翻小宝): 增加静态入口页面
关键改动:

- 新增问翻小宝页面,包含历史记录、对话和语音输入区域

- 在侧边栏加入独立入口并放在小宝预警之前

- 增加页面结构和导航顺序测试

Co-Authored-By: Codex GPT-5 <codex@openai.com>
2026-06-30 19:10:41 +08:00

188 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import {
BotMessageSquare,
MessageCircleQuestionMark,
Mic,
Plus,
Search,
SendHorizontal,
SquarePen,
} from 'lucide-react';
const HISTORY_ITEMS = [
{ title: '如何从产品创建到版本发布?', time: '今天' },
{ title: '需求池的状态分别代表什么?', time: '今天' },
{ title: '成员离职后怎么转交任务?', time: '昨天' },
{ title: 'AI 配置和模型在哪里维护?', time: '昨天' },
{ title: '测试用例失败后怎么提 Bug', time: '周一' },
];
const QUICK_QUESTIONS = [
'怎么创建一个版本?',
'怎么把需求关联到项目?',
'小宝预警的红点是什么意思?',
];
const MESSAGES = [
{
role: 'assistant',
title: '问翻小宝',
content: '我是 FTB 的系统帮助助手。你可以直接问产品、项目、版本、需求、任务、测试、Bug、权限和 AI 配置相关的使用问题。',
},
{
role: 'user',
title: '我',
content: '怎么把需求放进某个版本里?',
},
{
role: 'assistant',
title: '问翻小宝',
content: '进入版本详情后,在需求区域选择当前项目下已采纳的需求;保存后,版本会基于这些需求继续承载开发任务、测试用例和发布风险分析。',
},
];
export default function WenfanXiaobaoPage() {
return (
<div className="h-full bg-white text-[#171717]">
<main className="grid h-full min-h-0 grid-cols-1 overflow-hidden lg:grid-cols-[300px_minmax(0,1fr)]">
<aside className="hidden min-h-0 flex-col border-r border-[#e5e5e5] bg-white lg:flex">
<div className="space-y-3 p-3">
<button
type="button"
className="flex h-10 w-full items-center gap-2 rounded-lg px-3 text-left text-[14px] font-medium text-[#171717] hover:bg-white"
>
<SquarePen className="h-4 w-4" strokeWidth={1.9} />
</button>
<label className="flex h-9 items-center gap-2 rounded-lg bg-white px-3 text-[#8a8a8a] ring-1 ring-[#e5e5e5]">
<Search className="h-4 w-4 shrink-0" strokeWidth={1.8} />
<input
className="min-w-0 flex-1 bg-transparent text-[13px] text-[#171717] outline-none placeholder:text-[#8a8a8a]"
placeholder="搜索历史记录"
/>
</label>
</div>
<div className="min-h-0 flex-1 overflow-y-auto px-2 pb-3">
<p className="px-3 py-2 text-[12px] font-medium text-[#6b6b6b]"></p>
<div className="space-y-1">
{HISTORY_ITEMS.map((item, index) => (
<button
key={item.title}
type="button"
className={`w-full rounded-lg p-3 text-left transition-colors ${
index === 0
? 'border border-[var(--accent)] bg-[var(--accent-soft)]'
: 'bg-white hover:bg-[var(--bg-subtle)]'
}`}
>
<p className="truncate text-[13px] leading-5 text-[#202123]">{item.title}</p>
<p className="mt-0.5 text-[11px] text-[#8a8a8a]">{item.time}</p>
</button>
))}
</div>
</div>
</aside>
<section className="flex min-h-0 flex-col bg-white">
<header className="flex h-14 shrink-0 items-center justify-between border-b border-[#ececec] px-5">
<div className="flex min-w-0 items-center gap-2.5">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#f2f2f2] text-[#171717]">
<MessageCircleQuestionMark className="h-4 w-4" strokeWidth={2} />
</div>
<div className="min-w-0">
<h1 className="truncate text-[15px] font-semibold text-[#171717]"></h1>
</div>
</div>
<span className="rounded-full bg-[#f4f4f4] px-3 py-1 text-[12px] text-[#6b6b6b]"></span>
</header>
<div className="min-h-0 flex-1 overflow-y-auto">
<div className="mx-auto flex w-full max-w-3xl flex-col gap-7 px-6 py-8">
{MESSAGES.map((message) => (
<MessageRow key={`${message.title}-${message.content}`} message={message} />
))}
<div className="flex flex-wrap gap-2 pt-2">
{QUICK_QUESTIONS.map((question) => (
<button
key={question}
type="button"
className="rounded-full border border-[#dedede] px-3 py-2 text-[13px] text-[#3f3f46] transition-colors hover:bg-[#f7f7f8]"
>
{question}
</button>
))}
</div>
</div>
</div>
<footer className="shrink-0 bg-white px-6 pb-6 pt-2">
<div className="mx-auto w-full max-w-3xl">
<div className="rounded-[28px] border border-[#d9d9e3] bg-white shadow-[0_8px_28px_rgba(0,0,0,0.08)] focus-within:border-[#b9b9c6]">
<textarea
rows={2}
className="max-h-32 min-h-14 w-full resize-none bg-transparent px-5 pt-4 text-[14px] leading-6 text-[#171717] outline-none placeholder:text-[#8a8a8a]"
placeholder="输入系统使用问题,或点击语音按钮"
/>
<div className="flex items-center justify-between px-3 pb-3">
<div className="flex items-center gap-1">
<button
type="button"
className="flex h-8 w-8 items-center justify-center rounded-full text-[#6b6b6b] hover:bg-[#f4f4f4] hover:text-[#171717]"
title="语音输入"
>
<Mic className="h-4 w-4" strokeWidth={1.9} />
</button>
<button
type="button"
className="flex h-8 w-8 items-center justify-center rounded-full text-[#6b6b6b] hover:bg-[#f4f4f4] hover:text-[#171717]"
title="帮助中心"
>
<BotMessageSquare className="h-4 w-4" strokeWidth={1.9} />
</button>
</div>
<button
type="button"
className="flex h-8 w-8 items-center justify-center rounded-full bg-[#171717] text-white transition-colors hover:bg-[#303030]"
title="发送"
>
<SendHorizontal className="h-4 w-4" strokeWidth={2} />
</button>
</div>
</div>
</div>
</footer>
</section>
</main>
</div>
);
}
function MessageRow({ message }: { message: { role: string; title: string; content: string } }) {
const isUser = message.role === 'user';
if (isUser) {
return (
<div className="flex justify-end">
<div className="max-w-[76%] rounded-3xl bg-[#f4f4f4] px-4 py-2.5 text-[14px] leading-6 text-[#171717]">
{message.content}
</div>
</div>
);
}
return (
<div className="flex gap-3">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#171717] text-white">
<BotMessageSquare className="h-4 w-4" strokeWidth={1.9} />
</div>
<div className="min-w-0 flex-1 pt-0.5">
<p className="mb-1 text-[13px] font-semibold text-[#171717]">{message.title}</p>
<p className="text-[14px] leading-7 text-[#202123]">{message.content}</p>
</div>
</div>
);
}