Commit a8eb0ff1 by luoqi

feat(web): 助手隐藏语音输入 + 右上角加最大化

- 语音输入(麦克风)暂时隐藏:VOICE_INPUT_ENABLED=false 门控按钮,听写链路代码保留待恢复。
- 助手小窗头部加「最大化/还原」按钮(Maximize2Minimize2):点击在 400×620 吸附窗 
  1000×634 居中大窗间切换(inset-0 m-auto 定宽高居中);收起时自动还原为小窗。
  本地实测:麦克风不渲染;最大化后面板 1000×634 居中、图标切还原,链路通。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent 0ce83421
Pipeline #3384 failed in 0 seconds
......@@ -11,6 +11,7 @@ import {
LayoutTemplate,
Loader2,
Maximize2,
Minimize2,
Mic,
Send,
Sparkles,
......@@ -39,6 +40,9 @@ const MODELS = [
{ value: 'qwen', label: '通义千问' },
];
// 语音输入(听写)暂时隐藏 —— 置 true 即恢复麦克风按钮(听写链路代码保留)。
const VOICE_INPUT_ENABLED = false;
const EXAMPLES = [
'今日推荐:挑几个该优先跟进的患者,并说明理由',
'查一下患者孙柯的信息、就诊事实和画像',
......@@ -419,11 +423,17 @@ function ModelSelect({
export function AssistantChat({
variant = 'page',
onClose,
onToggleMaximize,
maximized = false,
examples,
}: {
/** page = /assistant 整页;widget = 详情页右下角吸附小窗(紧凑头部 + 可收起) */
variant?: 'page' | 'widget';
onClose?: () => void;
/** widget:最大化/还原切换(不传则不显示最大化按钮) */
onToggleMaximize?: () => void;
/** widget:当前是否最大化(控制图标 Maximize2↔Minimize2) */
maximized?: boolean;
/** 空态开场建议(规则生成,调用方按场景定制);不传用页面版默认 */
examples?: string[];
} = {}) {
......@@ -578,6 +588,16 @@ export function AssistantChat({
</h1>
<div className="ml-auto flex items-center gap-1.5">
<ModelSelect model={model} setModel={setModel} disabled={status === 'streaming'} />
{onToggleMaximize && (
<button
type="button"
onClick={onToggleMaximize}
title={maximized ? '还原' : '最大化'}
className="inline-flex h-6 w-6 items-center justify-center rounded-md text-slate-400 hover:bg-slate-100 hover:text-slate-600"
>
{maximized ? <Minimize2 className="h-3.5 w-3.5" /> : <Maximize2 className="h-3.5 w-3.5" />}
</button>
)}
{onClose && (
<button
type="button"
......@@ -650,6 +670,7 @@ export function AssistantChat({
placeholder="问关于患者画像 / 事实 / 召回计划的问题… (Enter 发送,Shift+Enter 换行)"
className="max-h-[140px] flex-1 resize-none bg-transparent py-1 text-[13.5px] leading-relaxed text-slate-800 placeholder:text-slate-400 focus:outline-none"
/>
{VOICE_INPUT_ENABLED && (
<button
type="button"
onClick={toggleVoice}
......@@ -663,6 +684,7 @@ export function AssistantChat({
>
<Mic className="h-4 w-4" />
</button>
)}
{status === 'streaming' ? (
<button
type="button"
......
......@@ -19,6 +19,7 @@ import { AssistantChat } from './assistant-chat';
export function AssistantWidget() {
const allowed = useHasPermission(Permission.AGENT_INVOKE);
const [open, setOpen] = useState(false);
const [maximized, setMaximized] = useState(false); // 最大化:居中放大窗
// 窗口落点 + 尺寸:打开瞬间按"钮的当前位置"计算(拖钮的意义所在),并夹紧在视窗内
const [winRect, setWinRect] = useState<{ x: number; y: number; w: number; h: number } | null>(null);
......@@ -52,16 +53,26 @@ export function AssistantWidget() {
<>
{/* 吸附窗:常驻挂载,关着时 invisible(状态不丢) */}
<div
style={winRect ? { left: winRect.x, top: winRect.y } : undefined}
style={maximized ? undefined : winRect ? { left: winRect.x, top: winRect.y } : undefined}
className={cn(
'fixed z-[60] flex flex-col overflow-hidden rounded-xl border border-slate-100 shadow-2xl',
!winRect && 'bottom-4 right-4',
'h-[620px] max-h-[78vh] w-[400px] max-w-[calc(100vw-2rem)]',
'transition-[opacity,transform] duration-150',
maximized
? 'inset-0 m-auto h-[min(88vh,900px)] w-[min(1000px,94vw)]' // 居中放大(margin:auto 定宽高居中)
: cn(!winRect && 'bottom-4 right-4', 'h-[620px] max-h-[78vh] w-[400px] max-w-[calc(100vw-2rem)]'),
open ? 'translate-y-0 opacity-100' : 'pointer-events-none invisible translate-y-2 opacity-0',
)}
>
<AssistantChat variant="widget" onClose={() => setOpen(false)} examples={examples} />
<AssistantChat
variant="widget"
onClose={() => {
setOpen(false);
setMaximized(false); // 收起时还原尺寸,下次打开仍是小窗
}}
onToggleMaximize={() => setMaximized((m) => !m)}
maximized={maximized}
examples={examples}
/>
</div>
{/* 助手入口(静态版):固定右下角,点击开/关助手窗;只在 AI 思考/说话、成约、切患者时原地反馈。
完整桌宠(漫游/物理/狩猎)保留在 /pet-lab 演示页,不进工作区(老板反馈"做得太过")。 */}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment