Archived
M6(4/n): 工作台五要素齐活 —— 审批 / 消息 / 纪要 / 汇报
四个 desk-* feature 自 workbench 原型 ModuleWorkspace.tsx 平移(jc-* 类名原样),
共用件落 shared/desk/module-kit;mock 数据换成服务端投影:
· desk-approval:审批点卡到审批人工位;依据快照、驳回走向(声明式)、驳回必附意见
· desk-message:通知 + 跨任务征答**就地作答**;卡面表单由 card.ts schema 驱动,
与后端回传校验同 schema;跨人卡不占任务清单,不开放任务对话流
· desk-minute:纪要维护 + 决议登记起单;正文可续、决议只增
· desk-report:四周期草案 + 度量八指标 + 确认即提交
左轨角标常驻(待批 / 未读),责任面要一眼看见什么在等我。
修一个真 bug:process.start 从不校验流程包的 callInterface,
先插工作流再由 task.start 撞活动契约 —— 失败就留下一条永远推不动的在途单。
改为**先校验后建单**(CallInterfaceViolationError),一条工作流都不留;
路由回 400 并带上缺哪个字段。已清掉这次触发出的两条孤儿单。
判据 ⑩ 纪要一环跑通:决议 → 登记触发 → 工作流 → A1 落到 BD 冯兵,
登记人即来源已记在单上;同一决议重复登记被 412 挡下。
gates: tsc / eslint / depcruise(316 模块)/ api-report / 380 测试 全绿
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -9,8 +9,12 @@
|
||||
import { useState } from 'react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type { Session, TaskCard, TaskDetail } from '@contracts';
|
||||
import { listMessagesRoute, listTasksRoute, replyWorkOrderRoute, sayRoute, sessionRoute, taskDetailRoute } from '@contracts';
|
||||
import { listApprovalsForMeRoute, listMessagesRoute, listTasksRoute, replyWorkOrderRoute, sayRoute, sessionRoute, taskDetailRoute } from '@contracts';
|
||||
import { TaskChat, TaskList, ViewRegion } from '@features/desk-task';
|
||||
import { ApprovalDesk } from '@features/desk-approval';
|
||||
import { MessageDesk } from '@features/desk-message';
|
||||
import { MinuteDesk } from '@features/desk-minute';
|
||||
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';
|
||||
@@ -43,12 +47,14 @@ export function AppShell() {
|
||||
queryFn: () => call<{ tasks: TaskCard[] }>(listTasksRoute),
|
||||
});
|
||||
|
||||
// 角标常驻:责任面要一眼看见「什么在等我」,不能等切过去才知道
|
||||
const messages = useQuery({
|
||||
queryKey: queryKeys.messages,
|
||||
queryFn: () => call<{ messages: Array<{ id: string; title: string; body: string; at: string; read: boolean }> }>(
|
||||
listMessagesRoute,
|
||||
),
|
||||
enabled: module === 'messages',
|
||||
queryFn: () => call<{ messages: Array<{ id: string; read: boolean }> }>(listMessagesRoute),
|
||||
});
|
||||
const approvals = useQuery({
|
||||
queryKey: ['approvals'],
|
||||
queryFn: () => call<{ approvals: Array<{ state: string }> }>(listApprovalsForMeRoute),
|
||||
});
|
||||
|
||||
const list = tasks.data?.tasks ?? [];
|
||||
@@ -89,7 +95,14 @@ export function AppShell() {
|
||||
</div>
|
||||
<span className="jc-rail-brand">JCAI</span>
|
||||
{NAV_MODULES.map((m) => {
|
||||
const badge = m.key === 'tasks' ? active.length : undefined;
|
||||
const badge =
|
||||
m.key === 'tasks'
|
||||
? active.length
|
||||
: m.key === 'approvals'
|
||||
? (approvals.data?.approvals ?? []).filter((a) => a.state === 'pending').length
|
||||
: m.key === 'messages'
|
||||
? (messages.data?.messages ?? []).filter((x) => !x.read).length
|
||||
: undefined;
|
||||
return (
|
||||
<button
|
||||
key={m.key}
|
||||
@@ -161,34 +174,14 @@ export function AppShell() {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : module === 'approvals' ? (
|
||||
<ApprovalDesk />
|
||||
) : module === 'messages' ? (
|
||||
<MessageDesk />
|
||||
) : module === 'minutes' ? (
|
||||
<MinuteDesk />
|
||||
) : (
|
||||
<main className="execution-panel jc-panel-card">
|
||||
<header className="jc-vr-head">
|
||||
<div className="jc-vr-title">
|
||||
<h1>{NAV_MODULES.find((m) => m.key === module)?.label}</h1>
|
||||
<span className="jc-vr-sub">工作台五要素 —— 同一份过程事实的五种投影</span>
|
||||
</div>
|
||||
</header>
|
||||
{module === 'messages' ? (
|
||||
<ul className="jc-msg-list">
|
||||
{(messages.data?.messages ?? []).map((m) => (
|
||||
<li key={m.id} className={m.read ? 'read' : ''}>
|
||||
<b>{m.title}</b>
|
||||
<p>{m.body}</p>
|
||||
<time className="jc-mono">{m.at.slice(0, 19).replace('T', ' ')}</time>
|
||||
</li>
|
||||
))}
|
||||
{(messages.data?.messages ?? []).length === 0 && (
|
||||
<li className="jc-list-empty">暂无消息</li>
|
||||
)}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="jc-list-empty">
|
||||
<Ico name="bar-chart" s={18} />
|
||||
该要素随 M6 后续接入
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
<ReportDesk />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user