diff --git a/apps/web/src/app/styles/app.css b/apps/web/src/app/styles/app.css
index df384bc..a167b91 100644
--- a/apps/web/src/app/styles/app.css
+++ b/apps/web/src/app/styles/app.css
@@ -476,16 +476,14 @@
.proto-root .jc-sup:focus { outline: 0; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(51,112,255,.12); }
.proto-root .jc-sup:disabled { background: #f3f4f6; color: #5c626b; }
-/* ===== 左侧一体化导航轨:品牌 → 五大模块(身份已挪去顶栏右侧,见下)===== */
+/* ===== 左侧一体化导航轨:品牌 → 五大模块 → 轨底身份区(2026-08-26 用户定:顶栏撤了,身份收回轨底)===== */
.proto-root .app-nav { padding: 14px 10px 12px; }
.proto-root .jc-rail-brand { text-align: center; font-size: 10px; font-weight: 800; letter-spacing: .14em; color: var(--ink-3); margin: -4px 0 6px; }
.proto-root .app-nav button { min-height: 58px; transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast); }
.proto-root .app-nav button:active { transform: scale(.97); }
-/* 轨底身份区已撤:头像与姓名挪去顶栏右侧身份菜单(2026-08-25 用户定);
- .jc-nav-ava 保留 —— 顶栏身份菜单复用这枚渐变头像 */
.proto-root .jc-nav-ava { width: 32px; height: 32px; border-radius: 999px; display: grid; place-items: center; flex: 0 0 auto; line-height: 1; color: #fff; font-size: 13px; font-weight: 800; background: linear-gradient(140deg, #2bc7d4, #0e9fd8); box-shadow: 0 2px 6px rgba(14, 159, 216, .35); }
-/* ===== 顶栏:44px 通栏,只放右侧身份菜单 —— 与左轨互不重叠,登出只有这一处入口 ===== */
+/* ===== 主区:顶栏已撤(2026-08-26 用户定),内容区直接铺满 ===== */
.proto-root .app-main { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.proto-root .app-main-body { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.proto-root .app-main-body > * { flex: 1 1 auto; min-width: 0; min-height: 0; }
@@ -500,8 +498,8 @@
/* 但右上角装饰光晕定位 right:-60px,裁切一放开它就探出视口右缘 60px,整页出现横向滚动
(2026-08-25 用户报:页面能左右滑)—— 把它收回盒内,光晕照留 */
.proto-root .jc-topbar.jc-topbar-app::after { right: 0; }
-/* 顶栏右侧身份菜单:头像 + 姓名,悬停出下拉(个人中心 / 退出登录)—— 纯 CSS 悬停无 JS;
- 登出入口从独立按钮收进下拉,仍是全站唯一入口(2026-08-25 用户定) */
+/* 身份菜单:头像 + 姓名,悬停出下拉(个人中心 / 设计中心 / 退出登录)—— 纯 CSS 悬停无 JS;
+ 登出入口收进下拉,是全站唯一入口(2026-08-25 用户定);2026-08-26 从顶栏挪进左轨底部(见下方 rail 变体) */
.proto-root .jc-usermenu { position: relative; display: flex; align-items: center; gap: 8px; padding: 4px 8px 4px 4px; border-radius: 10px; cursor: default; color: var(--ink-4); transition: background var(--t-fast); }
.proto-root .jc-usermenu:hover { background: var(--soft); }
.proto-root .jc-usermenu .jc-nav-ava { width: 28px; height: 28px; font-size: 12px; }
@@ -514,6 +512,14 @@
.proto-root .jc-usermenu-pop button.danger:hover { background: var(--danger-soft); color: var(--danger); }
.proto-root .jc-usermenu-pop button:disabled { opacity: .6; cursor: default; }
+/* 轨底变体(2026-08-26 用户定):轨宽 78px,头像上姓名下竖排,与模块钮之间一道细分隔;
+ 下拉改从轨右侧弹出、底缘与触发区对齐,悬停桥跟着转到右侧 */
+.proto-root .jc-usermenu-rail { margin-top: auto; align-self: stretch; flex-direction: column; justify-content: center; gap: 4px; padding: 10px 2px 4px; border-top: 1px solid var(--line-soft); border-radius: 0; }
+.proto-root .jc-usermenu-rail:hover { background: none; }
+.proto-root .jc-usermenu-rail .jc-topbar-name { max-width: 66px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; text-align: center; }
+.proto-root .jc-usermenu-rail::after { top: 0; bottom: 0; left: 100%; right: auto; width: 10px; height: auto; }
+.proto-root .jc-usermenu-rail .jc-usermenu-pop { top: auto; right: auto; bottom: 0; left: calc(100% + 10px); transform: translateX(-4px); }
+
/* 「建设中」提示弹窗:未开放模块(审批/消息/纪要/汇报)与个人中心的占位口径 —— 不跳页只弹提示 */
.proto-root .jc-wip-overlay { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; background: rgba(31, 35, 41, .45); }
.proto-root .jc-wip-modal { width: 360px; max-width: 92vw; padding: 24px 20px 18px; background: #fff; border-radius: 16px; box-shadow: 0 24px 60px rgba(31, 35, 41, .22); text-align: center; animation: rise var(--t-med) both; }
From 023d3157a012b7f49927a9dfa2ec736ac1a19bfe Mon Sep 17 00:00:00 2001
From: shanqin <45168502@qq.com>
Date: Wed, 26 Aug 2026 19:59:46 +0800
Subject: [PATCH 3/8] =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E4=B8=8D=E5=8F=AF?=
=?UTF-8?q?=E5=88=A0=E9=99=A4=E3=80=81=E4=B8=8D=E5=8F=AF=E7=BC=96=E8=BE=91?=
=?UTF-8?q?(2026-08-26=20=E7=94=A8=E6=88=B7=E5=AE=9A):L3=20=E6=92=A4?=
=?UTF-8?q?=E6=8E=89=E7=BC=96=E8=BE=91/=E5=88=A0=E9=99=A4=E5=85=A5?=
=?UTF-8?q?=E5=8F=A3,=E5=8F=AA=E7=95=99=E8=BF=9B=E8=A1=8C=E4=B8=AD?=
=?UTF-8?q?=E7=9A=84=E7=BB=88=E6=AD=A2=E6=B4=BB=E5=8A=A8;=E9=9C=80?=
=?UTF-8?q?=E6=B1=82=E7=A8=BF=E5=90=8C=E6=AD=A5=E5=85=B3=E9=97=AD=E4=B8=A4?=
=?UTF-8?q?=E6=9D=A1=E5=BE=85=E7=A1=AE=E8=AE=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/app/gov-shell.tsx | 2 +-
.../src/features/gov-task/internal/panel.tsx | 24 +++++--------------
docs/活动模块重构需求-2026-08-25.md | 7 +++---
3 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/apps/web/src/app/gov-shell.tsx b/apps/web/src/app/gov-shell.tsx
index c2e46ef..4fa6df7 100644
--- a/apps/web/src/app/gov-shell.tsx
+++ b/apps/web/src/app/gov-shell.tsx
@@ -332,7 +332,7 @@ function Detail({ route, nav }: { route: Route; nav: Nav }) {
case 'actd':
return
;
case 'acti':
- return
;
+ return
;
case 'tool':
return
;
case 'obj':
diff --git a/apps/web/src/features/gov-task/internal/panel.tsx b/apps/web/src/features/gov-task/internal/panel.tsx
index 1d65eda..8b7807e 100644
--- a/apps/web/src/features/gov-task/internal/panel.tsx
+++ b/apps/web/src/features/gov-task/internal/panel.tsx
@@ -724,10 +724,11 @@ const STEP_RES_TONE: Readonly
> = {
/**
* L3 · 实例详情 —— 一次运行的完整视图(2026-08-26 规划,用户确认后落地):
* 主从 2:1(DESIGN-SPEC §9)—— 左「动作过程记录」是主角(追溯与验收的落点),
- * 右栏输入/输出是辅助;编辑与删除收在这一层(功能 5/6),不污染清单。
+ * 右栏输入/输出是辅助。实例不可删除、不可编辑(2026-08-26 用户定)——
+ * 编辑/删除入口撤掉,唯一的动作是进行中的「终止活动」。
* 数据为静态占位,结构按将来接口的形状铺。
*/
-export function ActiPage({ id, nav }: { id: string; nav: Nav }) {
+export function ActiPage({ id }: { id: string }) {
const [aid, iid] = id.split(':');
const a = DEMO_ACTIVITIES.find((x) => x.id === aid);
const inst = a !== undefined ? demoInstances(a.owner).find((x) => x.id === iid) : undefined;
@@ -735,7 +736,7 @@ export function ActiPage({ id, nav }: { id: string; nav: Nav }) {
term 记下终止时刻 —— 终止的过程面与「自己跑挂」不同(当前步记「已终止」,不是「附件缺失」那种失败原因) */
const [term, setTerm] = useState(undefined);
if (a === undefined || inst === undefined) {
- return 实例不存在 —— 它可能已被删除,从面包屑返回活动详情看看。;
+ return 实例不存在 —— 它不在该活动的清单里,从面包屑返回活动详情看看。;
}
const state: InstanceState = term !== undefined ? '失败' : inst.state;
const steps = term !== undefined ? terminatedSteps(inst.startedAt) : demoSteps(state, inst.startedAt);
@@ -771,9 +772,9 @@ export function ActiPage({ id, nav }: { id: string; nav: Nav }) {
{a.llm}
>
}
- desc="一次运行的完整视图:输入 / 过程 / 输出 / 状态。编辑与删除收在这一层,清单页不受打扰。"
+ desc="一次运行的完整视图:输入 / 过程 / 输出 / 状态 —— 实例不可编辑、不可删除,进行中的运行可终止。"
acts={[
- /* 头部动作(2026-08-26 规划):进行中可终止(二次确认);编辑本期只留入口(编什么待定);删除二次确认(用户标注存疑,先按规划做) */
+ /* 唯一动作(2026-08-26 用户定:实例不许删、不许编):进行中可终止,二次确认 */
...(state === '进行中'
? [
{
@@ -790,19 +791,6 @@ export function ActiPage({ id, nav }: { id: string; nav: Nav }) {
} satisfies ActSpec,
]
: []),
- { label: '编辑实例', ico: 'pencil-line', disabled: true },
- {
- label: '删除实例',
- ico: 'trash-2',
- danger: true,
- ask: {
- title: `确认删除实例 ${inst.id}?`,
- note: '删除后,这次运行的输入 / 过程 / 输出记录一并移除,清单里也不再出现。',
- impact: { who: '该实例及其全部运行记录', undo: '不可撤销 —— 删除即清空', when: '确认后立即生效(本期前端演示,接口对接后落库)' },
- okLabel: '确认删除',
- onOk: () => nav.push({ k: 'actd', id: a.id }),
- },
- },
]}
/>
{reason !== undefined && {reason}}
diff --git a/docs/活动模块重构需求-2026-08-25.md b/docs/活动模块重构需求-2026-08-25.md
index c8159aa..666c8f3 100644
--- a/docs/活动模块重构需求-2026-08-25.md
+++ b/docs/活动模块重构需求-2026-08-25.md
@@ -89,7 +89,8 @@ L1(模块首页 · 活动清单)需要一个**标签页**切换,因为活
- 2026-08-25 · **文案口径(用户定)**:创建实例 → **启动活动**;实例清单 → **历史活动**;停止 → **终止活动**。
- 2026-08-26 · **L2 历史活动改列表(用户定)**:卡片式清单撤掉,改表格列(实例 / 负责人 / 输入 / 输出 / 启动时间 / 状态 / 操作,操作栏带标题),隔行浅斑马 + 悬停亮行,行内操作(终止活动 / 查看详情)常驻;分页单页 8 条不变。
- 2026-08-26 · **L2 历史活动加检索(用户定)**:列表上方检索条 —— 条件下拉四选(实例 / 负责人 / 状态 / 启动时间),控件随条件换:实例=按实例号模糊输入、负责人=人员下拉(候选取自现有实例,接接口后换人员接口)、状态=五态下拉、启动时间=日期段(含两端整日);筛中显示「筛出 X / 共 Y」并可一键清空;分页按筛出的集合走。
-- 2026-08-26 · **L3 实例详情已落地(静态占位版,用户确认规划后实施)**:路由 `acti`(id = `活动id:实例号`),L2 行内「查看详情」接入。布局 = 主从 2:1(DESIGN-SPEC §9):左侧「动作过程记录」竖时间线是主角(五步节点流:输入接收 → 要素结构化 → 定价测算 → **负责人验收** → 输出归档 —— 验收永远留给人,对应人机分工),当前步呼吸高亮、未到达空心灰显、连接线成不间断竖轨;右栏输入(键值)/ 输出(文件行,下载随接口开放)。头部 = 状态丸 + 负责人/启动/结束·耗时/专属 LLM;驳回/失败出原因说明块;头部动作三个:终止活动(进行中才有,二次确认)、编辑实例(disabled 占位)、删除实例(二次确认 —— 删除存疑仍悬着,先按规划做进 UI)。终止后的过程面与「自己跑挂」区分:当前步记「已终止」而非失败原因。数据仍是静态假数据。
+- 2026-08-26 · **L3 实例详情已落地(静态占位版,用户确认规划后实施)**:路由 `acti`(id = `活动id:实例号`),L2 行内「查看详情」接入。布局 = 主从 2:1(DESIGN-SPEC §9):左侧「动作过程记录」竖时间线是主角(五步节点流:输入接收 → 要素结构化 → 定价测算 → **负责人验收** → 输出归档 —— 验收永远留给人,对应人机分工),当前步呼吸高亮、未到达空心灰显、连接线成不间断竖轨;右栏输入(键值)/ 输出(文件行,下载随接口开放)。头部 = 状态丸 + 负责人/启动/结束·耗时/专属 LLM;驳回/失败出原因说明块;终止后的过程面与「自己跑挂」区分:当前步记「已终止」而非失败原因。数据仍是静态假数据。
+- 2026-08-26 · **实例不可删除、不可编辑(用户定,推翻 L3 原规划)**:L3 的编辑/删除入口撤掉,唯一动作是进行中的「终止活动」(二次确认);六功能里实例的「编辑/删除」两项就此划掉,待确认问题里的两条(编辑指什么 / 删除是否放开)同时关闭。
## 三、功能表(整理稿)
@@ -100,6 +101,6 @@ L1(模块首页 · 活动清单)需要一个**标签页**切换,因为活
- ~~活动模块里的「设计」与设计中心「活动库」是什么关系?~~ **已答(2026-08-25)**:不再有「设计中心 vs 运营中心」的分工 —— 设计并入运营,活动模块自带设计能力;设计中心各区后续按同思路并入运营中心重做。
- ~~「设计」在活动模块里做到什么程度?~~ **已答(2026-08-25)**:六功能已界定 —— 活动(定义)的创建与展示 + 实例的创建/展示/编辑/删除;定义只许创建不许编删,创建要过审核。
- 活动创建的**审核**:谁来审、在模块内审还是别处、审什么(输入输出契约的合理性?)?(待用户展开)
-- 实例的**编辑**具体指编辑什么:实例的输入数据?进行中的干预(暂停/叫停)?还是产出修正?(待用户展开)
-- 实例删除是否真放开 —— 用户已标注存疑,先规划,后续按需求调。(悬着,不阻塞)
+- 实例的**编辑**~~具体指编辑什么~~:**已答(2026-08-26):实例不可编辑**,入口不做。
+- 实例删除~~是否真放开~~:**已答(2026-08-26):实例不可删除**,入口不做;六功能只剩实例的创建/展示。
- 「运营」侧还要不要监控/验收类功能(此前提过负责人验收规划与统计产出)—— 六功能里未直接出现,是否归在「实例的展示/编辑」里,还是另有入口?(待用户展开)
From 237286e908679481eabd8a9b9fbc25462aaef33d Mon Sep 17 00:00:00 2001
From: shanqin <45168502@qq.com>
Date: Wed, 26 Aug 2026 20:15:13 +0800
Subject: [PATCH 4/8] =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E6=A8=A1=E5=9D=97?=
=?UTF-8?q?=E4=BA=8C=E6=AC=A1=E7=A1=AE=E8=AE=A4=E6=94=B9=E6=B0=94=E6=B3=A1?=
=?UTF-8?q?=E5=BC=B9=E5=B1=82(2026-08-26=20=E7=94=A8=E6=88=B7=E5=AE=9A):?=
=?UTF-8?q?=E9=94=9A=E5=AE=9A=E9=A1=B5=E5=A4=B4=E5=8F=B3=E4=B8=8B+?=
=?UTF-8?q?=E5=B0=8F=E4=B8=89=E8=A7=92=E6=8C=87=E5=9B=9E=E8=A7=A6=E5=8F=91?=
=?UTF-8?q?=E9=92=AE,=E4=B8=89=E5=BF=85=E7=AD=94=E6=94=B6=E7=AB=96?=
=?UTF-8?q?=E6=8E=92;L2=20=E7=A6=81=E7=94=A8/=E6=BF=80=E6=B4=BB=E4=B8=8E?=
=?UTF-8?q?=20L3=20=E7=BB=88=E6=AD=A2=E5=90=8C=E7=94=9F=E6=95=88;=E9=A1=B5?=
=?UTF-8?q?=E5=A4=B4=E5=AE=B9=E5=99=A8=E8=A1=A5=E6=AD=A3=20z-index=20?=
=?UTF-8?q?=E7=A0=B4=E5=8A=A8=E7=94=BB=E5=B1=82=E5=8F=A0=E5=9B=B0=E5=B1=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/app/styles/console.css | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/apps/web/src/app/styles/console.css b/apps/web/src/app/styles/console.css
index 1a7a550..3448ef1 100644
--- a/apps/web/src/app/styles/console.css
+++ b/apps/web/src/app/styles/console.css
@@ -708,6 +708,32 @@
.g-actid .g-tl-row.current .g-tl-dot { animation: none; box-shadow: 0 0 0 3px var(--tone-soft); }
.g-actid .g-dh .chips > * { animation: none; }
}
+
+/* ══ 二次确认改气泡弹层(2026-08-26 用户定:不要瘫在 box 下方的确认带)══
+ 锚在页头右下、小三角指回触发钮;三必答从三栏收成竖排三行;
+ 阴影只给弹层(§10),危险确认沿用非实心 danger 钮(§11),进场 180ms 从触发点展开(§13) */
+/* 注意:`.g-actvd/.g-actid > *` 的入场动画 forwards 填充会让每个区块长期保持独立层叠上下文,
+ 气泡的 z-index 只在页头内部作数 —— 所以页头容器自己要有正 z-index,气泡才盖得住后面的区块 */
+.g-actvd .g-dhwrap, .g-actid .g-dhwrap { position: relative; z-index: 30; }
+.g-actvd .g-ask, .g-actid .g-ask { position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
+ width: 372px; padding: 13px 15px 12px; gap: 10px;
+ border: 1px solid var(--tone-line, var(--cline)); border-radius: 12px;
+ box-shadow: 0 12px 32px rgba(22, 26, 34, .14), 0 2px 8px rgba(22, 26, 34, .08);
+ animation: actvd-ask .18s cubic-bezier(.3, 0, .2, 1) both; transform-origin: top right; }
+@keyframes actvd-ask { from { opacity: 0; transform: translateY(-4px) scale(.97); } }
+/* 小三角:指回右上方的触发钮(左/上两条边旋转 45° 拼出) */
+.g-actvd .g-ask::before, .g-actid .g-ask::before { content: ""; position: absolute; top: -6px; right: 60px;
+ width: 10px; height: 10px; background: #fff; transform: rotate(45deg);
+ border-left: 1px solid var(--tone-line, var(--cline)); border-top: 1px solid var(--tone-line, var(--cline)); }
+/* 三必答竖排:标签定宽左、内容右,气泡窄也读得顺 */
+.g-actvd .g-impact, .g-actid .g-impact { grid-template-columns: 1fr; border-radius: 8px; }
+.g-actvd .g-impact > div, .g-actid .g-impact > div { flex-direction: row; align-items: baseline; gap: 10px; padding: 7px 10px; }
+.g-actvd .g-impact em, .g-actid .g-impact em { flex: none; width: 52px; }
+.g-actvd .g-impact b, .g-actid .g-impact b { font-weight: 500; }
+.g-actvd .g-ask-a .g-btn, .g-actid .g-ask-a .g-btn { height: 28px; }
+@media (prefers-reduced-motion: reduce) {
+ .g-actvd .g-ask, .g-actid .g-ask { animation: none; }
+}
.g-fcard .mbtn { flex: none; width: 26px; height: 26px; display: grid; place-items: center; border: 1px solid transparent; border-radius: 7px; background: transparent; color: var(--ink-4); cursor: pointer; }
.g-fcard .mbtn:hover, .g-fcard .mbtn.on { border-color: var(--cline); background: #f3f6fa; color: var(--ink); }
.g-fcard .mbtn.bad:hover { border-color: #e6c4c4; background: #fdf2f2; color: var(--danger); }
From 8139948c28ba7be2226a5e5ee03f9c7923232758 Mon Sep 17 00:00:00 2001
From: shanqin <45168502@qq.com>
Date: Wed, 26 Aug 2026 20:41:47 +0800
Subject: [PATCH 5/8] =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E6=A8=A1=E5=9D=97?=
=?UTF-8?q?=E4=BA=8C=E6=AC=A1=E7=A1=AE=E8=AE=A4=E7=98=A6=E8=BA=AB=E6=88=90?=
=?UTF-8?q?=E8=BD=BB=E6=B0=94=E6=B3=A1(2026-08-26=20=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E5=AE=9A):=E5=8F=AA=E9=97=AE=E4=B8=80=E5=8F=A5+=E7=A1=AE?=
=?UTF-8?q?=E8=AE=A4/=E5=8F=96=E6=B6=88,=E4=B8=8D=E6=91=86=E5=BD=B1?=
=?UTF-8?q?=E5=93=8D=E4=B8=89=E5=BF=85=E7=AD=94;=E6=9C=AC=E5=9C=B0=20PopAs?=
=?UTF-8?q?k=20=E7=BB=84=E4=BB=B6=E6=9B=BF=E4=BB=A3=E5=85=B1=E4=BA=AB=20As?=
=?UTF-8?q?kBand,L2=20=E7=A6=81=E7=94=A8/=E6=BF=80=E6=B4=BB=E4=B8=8E=20L3?=
=?UTF-8?q?=20=E7=BB=88=E6=AD=A2=E5=90=8C=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/app/styles/console.css | 29 ++-
.../src/features/gov-task/internal/panel.tsx | 173 +++++++++++-------
docs/活动模块重构需求-2026-08-25.md | 1 +
3 files changed, 120 insertions(+), 83 deletions(-)
diff --git a/apps/web/src/app/styles/console.css b/apps/web/src/app/styles/console.css
index 3448ef1..7e98475 100644
--- a/apps/web/src/app/styles/console.css
+++ b/apps/web/src/app/styles/console.css
@@ -709,30 +709,27 @@
.g-actid .g-dh .chips > * { animation: none; }
}
-/* ══ 二次确认改气泡弹层(2026-08-26 用户定:不要瘫在 box 下方的确认带)══
- 锚在页头右下、小三角指回触发钮;三必答从三栏收成竖排三行;
- 阴影只给弹层(§10),危险确认沿用非实心 danger 钮(§11),进场 180ms 从触发点展开(§13) */
-/* 注意:`.g-actvd/.g-actid > *` 的入场动画 forwards 填充会让每个区块长期保持独立层叠上下文,
- 气泡的 z-index 只在页头内部作数 —— 所以页头容器自己要有正 z-index,气泡才盖得住后面的区块 */
-.g-actvd .g-dhwrap, .g-actid .g-dhwrap { position: relative; z-index: 30; }
-.g-actvd .g-ask, .g-actid .g-ask { position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
- width: 372px; padding: 13px 15px 12px; gap: 10px;
+/* ══ 二次确认·轻气泡(2026-08-26 用户定:只问一句 + 确认/取消,不摆影响三必答)══
+ 锚在页头右下、小三角指回触发钮;点气泡外的透明罩即取消;
+ 阴影只给弹层(§10),确认钮 danger 非实心(§11),进场 180ms 从触发点展开(§13) */
+/* 正 z-index 必须有:`.g-actvd/.g-actid > *` 的入场动画 forwards 填充会让每个区块长期保持独立层叠上下文,
+ 没有它,气泡的 z-index 只在页头内部作数,盖不住后面的区块 */
+.g-actvd .g-pahead, .g-actid .g-pahead { position: relative; z-index: 30; }
+.g-actvd .g-popask-veil, .g-actid .g-popask-veil { position: fixed; inset: 0; z-index: 39; }
+.g-actvd .g-popask, .g-actid .g-popask { position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
+ width: 264px; padding: 13px 14px 12px; background: #fff;
border: 1px solid var(--tone-line, var(--cline)); border-radius: 12px;
box-shadow: 0 12px 32px rgba(22, 26, 34, .14), 0 2px 8px rgba(22, 26, 34, .08);
animation: actvd-ask .18s cubic-bezier(.3, 0, .2, 1) both; transform-origin: top right; }
@keyframes actvd-ask { from { opacity: 0; transform: translateY(-4px) scale(.97); } }
/* 小三角:指回右上方的触发钮(左/上两条边旋转 45° 拼出) */
-.g-actvd .g-ask::before, .g-actid .g-ask::before { content: ""; position: absolute; top: -6px; right: 60px;
+.g-actvd .g-popask::before, .g-actid .g-popask::before { content: ""; position: absolute; top: -6px; right: 60px;
width: 10px; height: 10px; background: #fff; transform: rotate(45deg);
border-left: 1px solid var(--tone-line, var(--cline)); border-top: 1px solid var(--tone-line, var(--cline)); }
-/* 三必答竖排:标签定宽左、内容右,气泡窄也读得顺 */
-.g-actvd .g-impact, .g-actid .g-impact { grid-template-columns: 1fr; border-radius: 8px; }
-.g-actvd .g-impact > div, .g-actid .g-impact > div { flex-direction: row; align-items: baseline; gap: 10px; padding: 7px 10px; }
-.g-actvd .g-impact em, .g-actid .g-impact em { flex: none; width: 52px; }
-.g-actvd .g-impact b, .g-actid .g-impact b { font-weight: 500; }
-.g-actvd .g-ask-a .g-btn, .g-actid .g-ask-a .g-btn { height: 28px; }
+.g-actvd .g-popask p, .g-actid .g-popask p { margin: 0; font-size: 13px; font-weight: 600; line-height: 1.55; color: var(--ink); }
+.g-actvd .g-popask-a, .g-actid .g-popask-a { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
@media (prefers-reduced-motion: reduce) {
- .g-actvd .g-ask, .g-actid .g-ask { animation: none; }
+ .g-actvd .g-popask, .g-actid .g-popask { animation: none; }
}
.g-fcard .mbtn { flex: none; width: 26px; height: 26px; display: grid; place-items: center; border: 1px solid transparent; border-radius: 7px; background: transparent; color: var(--ink-4); cursor: pointer; }
.g-fcard .mbtn:hover, .g-fcard .mbtn.on { border-color: var(--cline); background: #f3f6fa; color: var(--ink); }
diff --git a/apps/web/src/features/gov-task/internal/panel.tsx b/apps/web/src/features/gov-task/internal/panel.tsx
index 8b7807e..83981ec 100644
--- a/apps/web/src/features/gov-task/internal/panel.tsx
+++ b/apps/web/src/features/gov-task/internal/panel.tsx
@@ -283,6 +283,36 @@ function inTimeRange(at: string, from: string, to: string): boolean {
return true;
}
+/** 轻量二次确认的规格(2026-08-26 用户定:只问一句 + 确认/取消 —— 影响三必答(AskBand)是治理台重度动作的规格,禁用活动/终止实例是轻动作,不摆) */
+type PopAskSpec = { title: string; okLabel: string; danger?: boolean; onOk: () => void };
+
+/** 气泡式二次确认 —— 锚在页头右下的小泡泡:一句话问清,确认/取消两钮;点气泡外的透明罩即取消 */
+function PopAsk({ spec, onCancel }: { spec: PopAskSpec; onCancel: () => void }) {
+ return (
+ <>
+
+
+
{spec.title}
+
+
+
+
+
+ >
+ );
+}
+
/**
* L2 · 活动详情 —— 两个问题一屏答:「这个活动是什么」(定义,只读)
* 与「它跑得怎么样」(小统计 + 实例清单,点行进 L3)。
@@ -309,6 +339,8 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) {
const [fState, setFState] = useState('');
const [fFrom, setFFrom] = useState('');
const [fTo, setFTo] = useState('');
+ /* 轻量二次确认(2026-08-26 用户定):一句话 + 确认/取消的气泡,不走 AskBand 三必答 */
+ const [ask, setAsk] = useState(null);
if (a === undefined) return 活动不存在 —— 它可能已被调整,返回活动清单看看。;
const st = status ?? a.status;
const instances = rows ?? demoInstances(a.owner);
@@ -354,64 +386,66 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) {
return (
-
- {st === 'active' ? '激活中' : '已禁用'}
- {a.version !== undefined && v{a.version}}
- {/* 绑定的 LLM 上头版(2026-08-25 用户定):每个活动一个专属 agent */}
- {a.llm}
- {a.owner}
- 创建于 {a.createdAt}
- >
- }
- desc="活动定义只读 —— 输入与输出在创建时定死,演进走版本迭代;验收在工作台,这里只看状态。"
- acts={[
- st === 'active'
- ? {
- label: '禁用活动',
- ico: 'power',
- danger: true,
- ask: {
- title: '确认禁用这个活动?',
- note: '禁用后不能再为它创建实例;已在跑的实例不受影响。',
- impact: { who: '该活动及其后续实例创建', undo: '可恢复 —— 随时重新激活', when: '确认后立即生效(本期前端演示,接口对接后落库)' },
- okLabel: '确认禁用',
- onOk: () => {
- /* 规则(2026-08-25 用户定):有实例时接口会提示无法禁用 —— 本期前端先演出这个拦截 */
- if (a.instances > 0) {
- setBlocked(true);
- } else {
- setStatus('disabled');
- setBlocked(false);
- }
- },
+
+
+ {st === 'active' ? '激活中' : '已禁用'}
+ {a.version !== undefined && v{a.version}}
+ {/* 绑定的 LLM 上头版(2026-08-25 用户定):每个活动一个专属 agent */}
+ {a.llm}
+ {a.owner}
+ 创建于 {a.createdAt}
+ >
+ }
+ desc="活动定义只读 —— 输入与输出在创建时定死,演进走版本迭代;验收在工作台,这里只看状态。"
+ acts={[
+ st === 'active'
+ ? {
+ label: '禁用活动',
+ ico: 'power',
+ danger: true,
+ onClick: () =>
+ setAsk({
+ title: '确认禁用这个活动?',
+ okLabel: '确认禁用',
+ danger: true,
+ onOk: () => {
+ /* 规则(2026-08-25 用户定):有实例时接口会提示无法禁用 —— 本期前端先演出这个拦截 */
+ if (a.instances > 0) {
+ setBlocked(true);
+ } else {
+ setStatus('disabled');
+ setBlocked(false);
+ }
+ },
+ }),
+ }
+ : {
+ label: '激活活动',
+ ico: 'zap',
+ primary: true,
+ onClick: () =>
+ setAsk({
+ title: '确认激活这个活动?',
+ okLabel: '确认激活',
+ onOk: () => {
+ setStatus('active');
+ setBlocked(false);
+ },
+ }),
},
- }
- : {
- label: '激活活动',
- ico: 'zap',
- primary: true,
- ask: {
- title: '确认激活这个活动?',
- note: '激活后可以为它创建实例。',
- impact: { who: '该活动及其后续实例创建', undo: '可逆 —— 随时可以再次禁用', when: '确认后立即生效(本期前端演示,接口对接后落库)' },
- okLabel: '确认激活',
- onOk: () => {
- setStatus('active');
- setBlocked(false);
- },
- },
- },
- { label: '启动活动', ico: 'plus', disabled: true },
- /* 版本迭代入口先隐藏(2026-08-25 用户定):功能设计回头讨论,要做时把这行加回 acts
- { label: '版本迭代', ico: 'git-branch', disabled: true },
- */
- ]}
- />
+ { label: '启动活动', ico: 'plus', disabled: true },
+ /* 版本迭代入口先隐藏(2026-08-25 用户定):功能设计回头讨论,要做时把这行加回 acts
+ { label: '版本迭代', ico: 'git-branch', disabled: true },
+ */
+ ]}
+ />
+ {ask !== null && setAsk(null)} />}
+
{blocked && (
无法禁用:该活动还有 {a.instances} 个实例 —— 这条提示将来由接口返回(有实例时接口拒绝禁用),本期为前端占位演示。
@@ -735,6 +769,8 @@ export function ActiPage({ id }: { id: string }) {
/* 状态就地翻转(本期前端演示):终止暂落「失败」(归态接口语义未定),对接后走 mutation 由服务端重投影;
term 记下终止时刻 —— 终止的过程面与「自己跑挂」不同(当前步记「已终止」,不是「附件缺失」那种失败原因) */
const [term, setTerm] = useState(undefined);
+ /* 轻量二次确认(2026-08-26 用户定):一句话 + 确认/取消的气泡 */
+ const [ask, setAsk] = useState(null);
if (a === undefined || inst === undefined) {
return 实例不存在 —— 它不在该活动的清单里,从面包屑返回活动详情看看。;
}
@@ -754,7 +790,8 @@ export function ActiPage({ id }: { id: string }) {
return (
-
+ setTerm(fmtMinute(Date.now())),
- },
+ onClick: () =>
+ setAsk({
+ title: `确认终止实例 ${inst.id}?`,
+ okLabel: '确认终止',
+ danger: true,
+ onOk: () => setTerm(fmtMinute(Date.now())),
+ }),
} satisfies ActSpec,
]
: []),
]}
- />
+ />
+ {ask !== null && setAsk(null)} />}
+
{reason !== undefined && {reason}}
{state === '待执行' && 待执行:已排入队列,等待调度 —— 到点或由负责人手动起步。}
diff --git a/docs/活动模块重构需求-2026-08-25.md b/docs/活动模块重构需求-2026-08-25.md
index 666c8f3..13d117e 100644
--- a/docs/活动模块重构需求-2026-08-25.md
+++ b/docs/活动模块重构需求-2026-08-25.md
@@ -91,6 +91,7 @@ L1(模块首页 · 活动清单)需要一个**标签页**切换,因为活
- 2026-08-26 · **L2 历史活动加检索(用户定)**:列表上方检索条 —— 条件下拉四选(实例 / 负责人 / 状态 / 启动时间),控件随条件换:实例=按实例号模糊输入、负责人=人员下拉(候选取自现有实例,接接口后换人员接口)、状态=五态下拉、启动时间=日期段(含两端整日);筛中显示「筛出 X / 共 Y」并可一键清空;分页按筛出的集合走。
- 2026-08-26 · **L3 实例详情已落地(静态占位版,用户确认规划后实施)**:路由 `acti`(id = `活动id:实例号`),L2 行内「查看详情」接入。布局 = 主从 2:1(DESIGN-SPEC §9):左侧「动作过程记录」竖时间线是主角(五步节点流:输入接收 → 要素结构化 → 定价测算 → **负责人验收** → 输出归档 —— 验收永远留给人,对应人机分工),当前步呼吸高亮、未到达空心灰显、连接线成不间断竖轨;右栏输入(键值)/ 输出(文件行,下载随接口开放)。头部 = 状态丸 + 负责人/启动/结束·耗时/专属 LLM;驳回/失败出原因说明块;终止后的过程面与「自己跑挂」区分:当前步记「已终止」而非失败原因。数据仍是静态假数据。
- 2026-08-26 · **实例不可删除、不可编辑(用户定,推翻 L3 原规划)**:L3 的编辑/删除入口撤掉,唯一动作是进行中的「终止活动」(二次确认);六功能里实例的「编辑/删除」两项就此划掉,待确认问题里的两条(编辑指什么 / 删除是否放开)同时关闭。
+- 2026-08-26 · **二次确认的规格(用户定)**:活动模块的确认走**轻气泡**(锚在触发钮下方的小泡泡,只问一句 + 确认/取消,点罩即取消)—— 不摆影响三必答;三必答(AskBand)是治理台重度动作的规格,别处的确认带暂不动。
## 三、功能表(整理稿)
From fce60b1570b19c367a368c77a2ea14f20212ab6a Mon Sep 17 00:00:00 2001
From: shanqin <45168502@qq.com>
Date: Wed, 26 Aug 2026 20:49:18 +0800
Subject: [PATCH 6/8] =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E6=B0=94=E6=B3=A1?=
=?UTF-8?q?=E6=8C=AA=E5=88=B0=E8=A7=A6=E5=8F=91=E9=92=AE=E5=B7=A6=E4=BE=A7?=
=?UTF-8?q?(2026-08-26=20=E7=94=A8=E6=88=B7=E5=AE=9A:=E5=9C=A8=E6=8C=89?=
=?UTF-8?q?=E9=92=AE=E6=97=81=E8=BE=B9):=E5=9E=82=E7=9B=B4=E5=AF=B9?=
=?UTF-8?q?=E5=88=B0=E9=92=AE=E4=B8=AD=E7=BA=BF,=E5=B0=8F=E4=B8=89?=
=?UTF-8?q?=E8=A7=92=E4=BB=8E=E5=8F=B3=E7=BC=98=E6=8C=87=E5=9B=9E;L2/L3=20?=
=?UTF-8?q?=E5=90=84=E8=87=AA=E7=9A=84=20right=20=E8=AE=A9=E4=BD=8D?=
=?UTF-8?q?=E5=80=BC=E9=9A=8F=E9=92=AE=E6=95=B0=E5=AE=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web/src/app/styles/console.css | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/apps/web/src/app/styles/console.css b/apps/web/src/app/styles/console.css
index 7e98475..af59d72 100644
--- a/apps/web/src/app/styles/console.css
+++ b/apps/web/src/app/styles/console.css
@@ -710,22 +710,26 @@
}
/* ══ 二次确认·轻气泡(2026-08-26 用户定:只问一句 + 确认/取消,不摆影响三必答)══
- 锚在页头右下、小三角指回触发钮;点气泡外的透明罩即取消;
+ 气泡挨在触发钮**左边**(垂直对到钮中线),小三角从右缘指回钮;点透明罩即取消;
阴影只给弹层(§10),确认钮 danger 非实心(§11),进场 180ms 从触发点展开(§13) */
/* 正 z-index 必须有:`.g-actvd/.g-actid > *` 的入场动画 forwards 填充会让每个区块长期保持独立层叠上下文,
没有它,气泡的 z-index 只在页头内部作数,盖不住后面的区块 */
.g-actvd .g-pahead, .g-actid .g-pahead { position: relative; z-index: 30; }
.g-actvd .g-popask-veil, .g-actid .g-popask-veil { position: fixed; inset: 0; z-index: 39; }
-.g-actvd .g-popask, .g-actid .g-popask { position: absolute; top: calc(100% + 10px); right: 0; z-index: 40;
+/* right 的取值 = 页头右 padding + 触发钮右边那几枚钮的宽度和 + 10px 间隙 —— 钮文案改了这里要跟着量 */
+.g-actvd .g-popask, .g-actid .g-popask { position: absolute; top: 16px; z-index: 40;
width: 264px; padding: 13px 14px 12px; background: #fff;
border: 1px solid var(--tone-line, var(--cline)); border-radius: 12px;
box-shadow: 0 12px 32px rgba(22, 26, 34, .14), 0 2px 8px rgba(22, 26, 34, .08);
- animation: actvd-ask .18s cubic-bezier(.3, 0, .2, 1) both; transform-origin: top right; }
-@keyframes actvd-ask { from { opacity: 0; transform: translateY(-4px) scale(.97); } }
-/* 小三角:指回右上方的触发钮(左/上两条边旋转 45° 拼出) */
-.g-actvd .g-popask::before, .g-actid .g-popask::before { content: ""; position: absolute; top: -6px; right: 60px;
+ animation: actvd-ask .18s cubic-bezier(.3, 0, .2, 1) both; transform-origin: right center; }
+/* L2:触发钮右边还有「启动活动」占位钮 → 让出两枚钮的宽度;L3:触发钮自己就是最右枚 */
+.g-actvd .g-popask { right: 232px; }
+.g-actid .g-popask { right: 130px; }
+@keyframes actvd-ask { from { opacity: 0; transform: translateX(4px) scale(.97); } }
+/* 小三角:右缘中点指向触发钮(上/右两条边旋转 45° 拼出) */
+.g-actvd .g-popask::before, .g-actid .g-popask::before { content: ""; position: absolute; top: 50%; margin-top: -5px; right: -6px;
width: 10px; height: 10px; background: #fff; transform: rotate(45deg);
- border-left: 1px solid var(--tone-line, var(--cline)); border-top: 1px solid var(--tone-line, var(--cline)); }
+ border-top: 1px solid var(--tone-line, var(--cline)); border-right: 1px solid var(--tone-line, var(--cline)); }
.g-actvd .g-popask p, .g-actid .g-popask p { margin: 0; font-size: 13px; font-weight: 600; line-height: 1.55; color: var(--ink); }
.g-actvd .g-popask-a, .g-actid .g-popask-a { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
@media (prefers-reduced-motion: reduce) {
From a8797b8cc802d9b855c36646a98ecbf9625e29e9 Mon Sep 17 00:00:00 2001
From: shanqin <45168502@qq.com>
Date: Wed, 26 Aug 2026 21:20:25 +0800
Subject: [PATCH 7/8] =?UTF-8?q?L2=20=E6=B2=99=E7=AE=B1=E5=8D=A0=E4=BD=8D?=
=?UTF-8?q?=E9=A1=B5=E6=B8=85=E7=A9=BA=E6=BC=94=E7=A4=BA=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?(2026-08-26=20=E7=94=A8=E6=88=B7=E5=AE=9A):=E5=8F=AA=E7=95=99?=
=?UTF-8?q?=E9=AA=A8=E6=9E=B6=E5=BD=93=E7=BB=93=E6=9E=84=E5=8D=A0=E4=BD=8D?=
=?UTF-8?q?,=E7=A9=BA=E5=80=BC=E5=8F=A3=E5=BE=84=20-/=E6=9A=82=E6=97=A0?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../public/sandbox-demo/activity-view.html | 21 +++++++++----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/apps/web/public/sandbox-demo/activity-view.html b/apps/web/public/sandbox-demo/activity-view.html
index 7850cc9..41a036d 100644
--- a/apps/web/public/sandbox-demo/activity-view.html
+++ b/apps/web/public/sandbox-demo/activity-view.html
@@ -8,6 +8,7 @@
活动视图占位页(2026-08-25,L2 沙箱 v1):
模拟「服务器返回的 HTML」—— 由 iframe + sandbox="allow-scripts" 隔离渲染,
与宿主页面零样式/脚本互通;正式版由接口按活动定义返回。
+ 2026-08-26 用户定:演示数据全部清空,只留骨架当结构占位(空值口径:无数据显 `-` / 暂无数据)。
-->
-
-
-
-
-
-
-
询价接收与结构化
-
quote-intake · 活动视图(服务器返回 HTML · 沙箱隔离渲染)
-
-
-
-
-
今日接收-
-
待人工补全-
-
结构化成功-
-
平均耗时-
-
-
-
-
最近的询价原文
-
-
- | 来源 | 物料数 | 接收时间 | 结构化结果 |
-
-
- | 暂无数据 |
-
-
-
-
-
-