工作台空占位补齐 —— 有内容渲染内容,没内容才落空态
任务面原来缺两处占位:中栏是一行裸文字,对话区在没选中任务时**整栏不渲染**。 补齐后三栏恒在:中栏落设计空态「手上没有任务」,对话区给全壳(题头 + 空态 + 禁用输入条),空着也看得出这是干什么的地方。 但真起一单之后暴露了更重的问题:占位在**说谎**。待处理角标写着 1,正文却 说「手上没有待办」—— 因为 ChatStream(643 行)与 WorkOrderReport(449 行) 早就写好、也从 @features/desk-task 导出了,却没有一处在用。占位不是缺内容, 是内容没接上。三处接回真渲染: 待处理 → WorkOrderReport(工单正文、编号、派发时间、数据与分析) 工作日志 → .jc-log 行,按归侧四类各一色(机器/人类/裁决/等世界) 工作成果 → .jc-file 行,点开进 FileViewer;打开时右槽换「返回」 对话区 → ChatStream,三个回传(说一句 / 答卡 / 答工单)在这里收口 顺带治了窄对话区的抗压:工作区优先保「五按钮 + 翻页」,对话区在 1280 下被 压到 212px,卡头标题 `flex:1` 没设 `min-width:0`,一挤就逐字折断成竖排单字。 降级顺序改成「状态芯片换行下沉、标题始终整词可读」—— 截断能让布局不崩, 但读的人不知道这是什么卡。不改任何一栏尺寸。 五闸:tsc ✓ eslint ✓ depcruise 373 模块 ✓ api:check 公开契约无变化 ✓ 481 测试 ✓ 走查后清了运行数据(仓库自带 reset-runs,配置原样留着),换业务化主题重起一单。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -8,12 +8,13 @@
|
||||
*/
|
||||
import { useState } from 'react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type { Session, TaskCard, TaskDetail } from '@contracts';
|
||||
import type { Session, TaskCard, TaskDetail, WorkOrderReply } from '@contracts';
|
||||
import {
|
||||
listApprovalsForMeRoute,
|
||||
listMessagesRoute,
|
||||
listTasksRoute,
|
||||
logoutRoute,
|
||||
replyWorkOrderRoute,
|
||||
sessionRoute,
|
||||
taskDetailRoute,
|
||||
} from '@contracts';
|
||||
@@ -25,6 +26,7 @@ import { ReportDesk } from '@features/desk-report';
|
||||
import { Ico } from '@web-shared/Ico.tsx';
|
||||
import { call } from '@web-shared/typed-fetch.ts';
|
||||
import { useChatWidth } from '@web-shared/useChatWidth.ts';
|
||||
import { EmptyState } from '@web-shared/desk/module-kit.tsx';
|
||||
import { SURFACE_META, canSeeSurface } from '@web-shared/gov/surfaces.ts';
|
||||
import { queryKeys } from './query.ts';
|
||||
|
||||
@@ -91,6 +93,17 @@ export function AppShell() {
|
||||
|
||||
const me = session.data?.staff.name ?? '—';
|
||||
|
||||
/**
|
||||
* 工单回传 —— 视图区与对话区同一条口。
|
||||
* 回传后按键失效、画面由服务端重投影:前端不自行推演卡与工单的状态(§9.2)。
|
||||
*/
|
||||
const replyOrder = useMutation({
|
||||
mutationFn: (reply: WorkOrderReply) => call(replyWorkOrderRoute, { body: { reply } }),
|
||||
onSuccess: () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ['desk'] });
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<div className="app-body">
|
||||
@@ -186,29 +199,72 @@ export function AppShell() {
|
||||
onPick={setTaskId}
|
||||
onCollapse={() => setCollapsed(true)}
|
||||
/>
|
||||
{/*
|
||||
手上没任务时,三栏也得**站得住**:中栏与对话区各给自己的空态。
|
||||
少一栏会让整个工位看着像坏了 —— 空态是常态,不是缺页。
|
||||
*/}
|
||||
{detail.data !== undefined ? (
|
||||
<ViewRegion key={current} detail={detail.data} />
|
||||
<ViewRegion
|
||||
key={current}
|
||||
detail={detail.data}
|
||||
onReplyOrder={(reply) => replyOrder.mutate(reply)}
|
||||
/>
|
||||
) : (
|
||||
<main className="execution-panel">
|
||||
<div className="jc-list-empty">
|
||||
<Ico name="list-checks" s={18} />
|
||||
{tasks.isLoading ? '载入中…' : '还没有落到你工位的任务'}
|
||||
</div>
|
||||
{tasks.isLoading ? (
|
||||
<div className="jc-list-empty">
|
||||
<Ico name="list-checks" s={18} />
|
||||
载入中…
|
||||
</div>
|
||||
) : (
|
||||
<EmptyState
|
||||
ico="list-checks"
|
||||
tone="brand"
|
||||
title="手上没有任务"
|
||||
desc="流程走到你负责的活动时,任务会连同办这件事要用的全部上下文一起送到这里 —— 待处理的事、往来记录、产出的文件都在一屏之内。"
|
||||
hint="有新任务时左侧列表会亮角标"
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
)}
|
||||
{detail.data !== undefined && (
|
||||
<i
|
||||
className="jc-vsplit"
|
||||
title="拖拽调整对话区宽度 · 双击复位"
|
||||
onPointerDown={split.onSplitDown}
|
||||
onDoubleClick={split.onSplitReset}
|
||||
/>
|
||||
)}
|
||||
{detail.data !== undefined && (
|
||||
<section className="session-chat">
|
||||
<TaskChat key={current} />
|
||||
</section>
|
||||
)}
|
||||
<i
|
||||
className="jc-vsplit"
|
||||
title="拖拽调整对话区宽度 · 双击复位"
|
||||
onPointerDown={split.onSplitDown}
|
||||
onDoubleClick={split.onSplitReset}
|
||||
/>
|
||||
<section className="session-chat">
|
||||
{detail.data !== undefined ? (
|
||||
<TaskChat key={current} detail={detail.data} onLocate={() => undefined} />
|
||||
) : (
|
||||
<div className="jc-tch">
|
||||
<header className="jc-tch-head">
|
||||
<span className="jc-tch-mark">
|
||||
<Ico name="bot" s={15} />
|
||||
</span>
|
||||
<b className="jc-tch-name">任务助理</b>
|
||||
<em className="jc-tch-tag">Agent</em>
|
||||
<span className="jc-tch-desc">开一单即可对话</span>
|
||||
</header>
|
||||
<div className="jc-tch-scroll">
|
||||
<EmptyState
|
||||
ico="messages-square"
|
||||
tone="brand"
|
||||
title="还没有可对话的任务"
|
||||
desc="每一单任务都有自己的对话:AI 有结论、要派工、或者要你拿个主意,都在那一单里说 —— 所以得先有一单在手。"
|
||||
/>
|
||||
</div>
|
||||
<div className="copilot-composer session-composer">
|
||||
<div className="composer-row">
|
||||
<textarea rows={2} disabled placeholder="选中一单任务后就能在这里交代" />
|
||||
<button className="copilot-send" disabled>
|
||||
<Ico name="send" s={14} /> 发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
) : module === 'approvals' ? (
|
||||
<ApprovalDesk />
|
||||
|
||||
@@ -2922,3 +2922,31 @@
|
||||
.proto-root .jc-tnav-btn.back svg {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* ── 工作日志(补充,非原型)—— 一行一步,标明谁的产出 ────────────────── */
|
||||
.proto-root .jc-log { display: flex; flex-direction: column; }
|
||||
.proto-root .jc-log-row { display: flex; gap: 10px; align-items: baseline; padding: 8px 2px; }
|
||||
.proto-root .jc-log-row + .jc-log-row { border-top: 1px solid var(--line-soft); }
|
||||
.proto-root .jc-log-at { flex: none; font-size: 10.5px; color: var(--ink-4); }
|
||||
.proto-root .jc-log-side { flex: none; font-size: 10.5px; font-weight: 700; padding: 1px 7px; border-radius: 999px; background: var(--soft); color: var(--ink-2); }
|
||||
/* 归侧四类各一色:机器 / 人 / 人裁 / 世界等待 —— 一眼看出这一步是谁推的 */
|
||||
.proto-root .jc-log-row.t-machine .jc-log-side { background: var(--brand-wash); color: var(--brand-deep); }
|
||||
.proto-root .jc-log-row.t-human .jc-log-side { background: #e6f7f4; color: #0a6a64; }
|
||||
.proto-root .jc-log-row.t-human-verdict .jc-log-side { background: #fff4e5; color: #de7802; }
|
||||
.proto-root .jc-log-row.t-world-wait .jc-log-side { background: var(--soft); color: var(--ink-3); }
|
||||
.proto-root .jc-log-t { flex: 1; min-width: 0; }
|
||||
.proto-root .jc-log-t b { display: block; font-size: 12.5px; font-weight: 600; }
|
||||
.proto-root .jc-log-t em { display: block; margin-top: 2px; font-style: normal; font-size: 11px; line-height: 1.6; color: var(--ink-3); }
|
||||
/* 成果二级界面的返回键 —— 顶部页签不变,右槽换成它 */
|
||||
.proto-root .jc-tnav-back { width: auto; padding: 0 9px; gap: 5px; font-size: 11.5px; font-weight: 600; }
|
||||
|
||||
/* ── 窄对话区的抗压(补充,非原型)────────────────────────────────────
|
||||
对话区在小屏上会被压到 ~210px(工作区优先保「五按钮 + 翻页」,任务列表可收起让位)。
|
||||
卡片原样搬过来是不抗压的:卡头标题 `flex: 1` 没设 `min-width: 0`,一挤就**逐字折断**
|
||||
成竖排单字;按钮同理。这里只教卡片怎么变窄,不改任何一栏的尺寸。
|
||||
|
||||
降级的顺序是:先让状态芯片换行下沉(标题拿回整行),标题始终整词可读 —— 而不是把
|
||||
「工单 · 执行」截成一个「工」字。截断能让布局不崩,但读的人不知道这是什么卡。 */
|
||||
.proto-root .jc-fc-h { flex-wrap: wrap; row-gap: 3px; }
|
||||
.proto-root .jc-fc-h b { min-width: 0; flex: 1 1 auto; }
|
||||
.proto-root .jc-fc-open { white-space: nowrap; }
|
||||
|
||||
Reference in New Issue
Block a user