import { useState, useRef, useEffect } from "react"; import { Sparkles, X, Send, Bot, Lock } from "lucide-react"; import { Button } from "./ui/button"; import { cn } from "./ui/utils"; import { useRole } from "../role-context"; interface Msg { role: "bot" | "user"; text: string; } const quickReplies = [ "如何发起招标委托?", "邀请招标和公开招标有什么区别?", "在线评标怎么打分?", "供应商如何入库?", ]; const knowledge: Record = { 委托: "发起招标委托很简单:① 点击工作台或项目管理页的「发起委托」按钮;② 在「填写需求」步骤用自然语言描述需求,点击「AI 智能解析」即可自动生成《招标委托表》;③ 确认预填单后提交审批即可。需要我带您过去吗?", 区别: "公开招标面向所有符合条件的供应商发布寻源公告,参与方需报名审核;邀请招标则由您从长名单中筛选短名单进行定向邀请。两者在本平台均支持全流程线上闭环。", 评标: "进入「在线评标室」后:左栏选择匿名投标人,中栏查看脱敏标书,右栏按技术分/商务分/价格分录入分数并填写评分说明,系统会同步展示 AI 雷同检测与价格合理性分析,最后点击「提交评分」。", 入库: "在「供应商管理」页点击「发起入库流程」,或在短名单管理中对供应商点击「发起入库」,系统将自动同步工商风险信息并进入审批。", 合并: "您好,当系统中存在同类型未完成立项的委托时,系统会自动推送合并建议。您可以在立项阶段的「合并招标建议」卡片中查看并确认合并。需要我为您演示具体步骤吗?", 上传: "进入「投标中心」→ 选择项目 → 在「上传投标文件」区域上传文件,系统会自动加密并上链存证。开标前任何人都无法查看,只有您本人可查看和修改,投标截止前还可撤回重传。", }; const dataKeywords = ["报价", "投标价", "多少钱", "金额", "中标", "得分", "分数", "名单", "谁"]; export function XiaobinAssistant() { const { sensitive } = useRole(); const [open, setOpen] = useState(false); const [input, setInput] = useState(""); const [msgs, setMsgs] = useState([ { role: "bot", text: "您好,我是智能助手「小滨」👋 我可以为您解答系统操作与业务咨询。注意:我不参与任何业务决策哦。请问有什么可以帮您?", }, ]); const endRef = useRef(null); useEffect(() => { endRef.current?.scrollIntoView({ behavior: "smooth" }); }, [msgs, open]); function answer(q: string) { // 流程敏感期:拒答数据类问题 if (sensitive && dataKeywords.some((k) => q.includes(k))) { return "当前为教学模式(流程敏感期),数据类问题暂无法回答。暂未到查看时间,请等待系统通知。我可以继续为您讲解系统操作哦~"; } const key = Object.keys(knowledge).find((k) => q.includes(k)); return key ? knowledge[key] : "这是一个很好的问题。作为操作助手,我建议您查阅对应模块的帮助说明,或描述更具体的操作场景,我会尽力为您讲解系统使用方法。(温馨提示:业务决策需由有权限的人员完成)"; } function send(text: string) { const q = text.trim(); if (!q) return; setMsgs((m) => [...m, { role: "user", text: q }]); setInput(""); setTimeout(() => { setMsgs((m) => [...m, { role: "bot", text: answer(q) }]); }, 500); } return ( <> {!open && ( )} {open && (
智能助手 · 小滨
在线 · 操作教学 & 业务咨询
{sensitive && (
当前为教学模式,数据类问题暂无法回答
)}
{msgs.map((m, i) => (
{m.text}
))} {msgs.length <= 1 && (

猜你想问:

{quickReplies.map((q) => ( ))}
)}
setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send(input)} placeholder="输入您的问题…" className="h-9 flex-1 rounded-lg border bg-input-background px-3 text-sm outline-none focus:border-primary" />
)} ); }