--- a/src/components/kf-opportunity-card.js +++ b/src/components/kf-opportunity-card.js @@ -4,14 +4,12 @@ * 一屏读完五要素(任务书 3.1): * 1. 优先级 / 品类 / 阶段 2. 机会名 3. 一句话 * 4. 综合评分(多人平均)/ 证据强度 / 引用条数 - * 5. 决策记录 + 来源(谁 · 哪个 Agent · 什么方式 · 什么时候) + * 5. 来源(谁 · 哪个 Agent · 什么方式 · 什么时候) * * wc 层不做业务:评分平均、状态文字都由调用方把「已归一化的机会卡对象」传进来(.opp)。 - * 交互只派发事件,由 page 决定跳转 / 改状态: + * 交互只派发阅读事件,由 page 决定跳转: * card-open { id } 点整卡阅读报告 - * status-change { id, status } 状态按钮(悬停出现的快捷操作) - * - * 状态操作与决策记录不触发整卡跳转。 + * 决策操作、说明与历史统一在报告阅读页展示。 */ import { LitElement, html, css } from 'lit'; import { useTokens, define } from './base.js'; @@ -43,28 +41,17 @@ } static styles = css` - /* v11:高度只由内容决定(v11 10 的 padding 规则换掉了 face 内衬后,「100% + 栅格 stretch」会把三张卡拉成同一高、内容被纵向拉开) */ - :host { display: block; min-width: 0; } - /* 卡内三段:标题区(top / title / one,定高内容)/ 指标区 / 决策 + 来源 + 操作。 - 同一行的卡被拉成等高后,多出来的高度放进 .nums 与 .dec 之间的弹性间隙, - 而不是让来源行「沉底 + 中间一大块空」。标题、一句话、决策行各自都有定高(单 / 两行统一)。 - .dec 的 margin-top: auto 把 .dec / .src / .acts 三行贴着底边走 —— 同一排卡的「分隔线 / 按钮 / 来源行」落同一 y。 */ - /* v14 定稿:.wrap 是 slotted 到 的子元素,父级(宿主 kf-glass-card)高 389px, - 但 slotted 子元素默认 flex:0 1 auto,height:100% 在 flex item 上解析不出百分比 → 实测只有内容高 303px, - 导致无描述卡片的 .nums margin-top:auto 没有余量可分(底部差 53.6px)。用 box-sizing + min-height 撑满。 */ - .wrap { flex: 1 1 auto; min-height: 0; box-sizing: border-box; display: flex; flex-direction: column; gap: 12px; } + /* 决策区移入报告后,卡面按实际内容收缩,不预留空描述或操作区。 */ + :host { display: block; min-width: 0; align-self: start; } + .wrap { min-width: 0; box-sizing: border-box; display: flex; flex-direction: column; gap: 12px; } .top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .top .right { margin-left: auto; display: flex; gap: 6px; } /* 长标题不截断(5.6):折两行;正文行高 1.6、字距 0(1.5) */ .title { margin: 0; font-size: 16px; line-height: 1.35; color: var(--kf-ink); font-weight: 600; letter-spacing: var(--ls-cn-display); word-break: keep-all; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-width: 0; } /* 行盒修剪 + -webkit-box 行数截断会把行盒压成 ~1 行高(实测 11.6px);按两行高度收(2 × 1.35em) */ - .title { min-height: calc(2.7em + 0.25em); padding-block: 0.125em; } - .one { margin: 0; font-size: 14px; line-height: 1.6; letter-spacing: var(--ls-cn-body); color: var(--kf-ink-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: calc(2 * 1.6em); } - /* 卡面余量摊给无描述卡(v14 定稿):.one 不出文字但保留等高空块(aria-hidden,无文字节点,读屏与搜索都看不见「未填写」)。 - 实测约束——余量 53px,只在单缝做 margin:auto 那缝必 >24px;平分到多缝则 .dec/.acts 整体上移、与邻卡底边错位 53px。 - 保留等高空块是唯一能同时满足「不显示文字 / 卡内无大缝 / 底边逐 px 对齐」的做法。 */ - .one-empty { min-height: calc(2 * 1.6em); } + .title { padding-block: 0.125em; } + .one { margin: 0; font-size: 14px; line-height: 1.6; letter-spacing: var(--ls-cn-body); color: var(--kf-ink-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .nums { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } .n { min-width: 0; box-sizing: border-box; padding: 12px 10px; border: 1px solid #B4C1D5; border-radius: 8px; background: #EEF2F9; } /* 三个数值(1.2 单位分层):大数字 = Inter 数字 + 行盒修剪;单位 12px 灰,留 0.12em */ @@ -74,33 +61,13 @@ .n .l { font-size: 12px; color: var(--kf-ink-3); margin-top: 4px; letter-spacing: var(--ls-cn-small); } .n.score .v { color: var(--kf-accent-ink); } .n.score .v.none { color: var(--kf-ink-3); text-shadow: none; font-weight: 600; font-size: 16px; } - /* 决策行 = 状态 + 一句理由。理由最多两行,理由一长卡就被撑高、同排卡的分隔线跟着错位, - 所以这里定高两行(理由两行截断);「谁 · 何时」在标题上浮出全文,不靠这一行承载。 */ - /* v15:卡面不再被拉伸(fill-to 只用于需要等高铺满的调用方),这里不从 auto 取水平余量 */ - .dec { font-size: 14px; color: var(--kf-ink-2); margin-top: 4px; line-height: 1.6; min-height: calc(2 * 1.6em); max-height: calc(2 * 1.6em); overflow: hidden; - display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } - .dec .kf-muted { display: inline; } - .dec b { color: var(--kf-ink); font-weight: 600; } .src { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding-top: 12px; border-top: 1px solid var(--kf-line); font-size: 12px; line-height: 20px; letter-spacing: var(--ls-cn-small); color: var(--kf-ink-3); font-variant-numeric: tabular-nums; } .src .who { color: var(--kf-ink); font-weight: 600; } .src .agent { color: var(--kf-accent-ink); font-weight: 500; } .src .miss { color: var(--kf-warn-text); } - .acts { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; } - .decision-history { font-size: 12px; line-height: 1.7; color: var(--kf-ink-2); } - .decision-history summary { cursor: pointer; width: fit-content; padding: 4px 0; } - .decision-history ol { margin: 8px 0 0; padding-left: 18px; } - .decision-history li { margin-top: 6px; overflow-wrap: anywhere; } - .st { display: inline-flex; border: 1px solid var(--kf-line-strong); border-radius: var(--r-sm); overflow: hidden; background: var(--kf-surface); } - .st button { border: 0; background: transparent; font: 500 12px/1 var(--kf-font); color: var(--kf-ink-2); padding: 0 10px; height: 26px; cursor: pointer; transition: background var(--kf-dur) var(--kf-ease); } - .st button:hover { background: var(--kf-surface-btn); } - .st button[aria-pressed="true"]:hover { background: var(--kf-ink); } - .st button[aria-pressed="true"] { background: var(--kf-ink); color: #fff; } - .st button[aria-pressed="true"].ok { background: #0f5232; } - .st button[aria-pressed="true"].no { background: #8a1a14; } - .sample { font-size: 12px; line-height: 1; color: #5c4300; background: rgba(255,236,170,0.55); border: 1px solid rgba(170,130,0,0.35); border-radius: var(--r-sm); padding: 1px 6px; } :host([compact]) .title { font-size: 14px; } :host([compact]) .nums .v { font-size: 16px; } - /* 手机(任务书 1.3 / 1.5 / 2.4):标题 17/600、一句话 15/1.55、大数字 24/700、按钮 44 触摸高 */ + /* 手机保留指标布局,来源拆成两行。 */ @media (max-width: 767px) { .wrap { gap: 12px; } .title { font-size: 16px; font-weight: 600; line-height: 1.4; } @@ -115,13 +82,6 @@ .src .l1, .src .l2 { display: flex; gap: 6px; flex-wrap: wrap; } .src .l2 { color: var(--kf-ink-2); } .src .who, .src .agent { white-space: nowrap; } - .dec { font-size: 14px; } - .acts { gap: 8px; } - .st { height: 44px; } - .st button { height: 44px; padding: 0 14px; font-size: 14px; } - .st button:active { background: var(--kf-surface-btn); } - .decision-history summary { min-height: 44px; display: flex; align-items: center; } - .sample { font-size: 12px; padding: 3px 8px; } } @media (max-width: 359px) { .nums { gap: 6px; } @@ -137,43 +97,25 @@ this.dispatchEvent(new CustomEvent('card-open', { detail: { id: this.opp.id }, bubbles: true, composed: true })); }; - #status(status) { - return (e) => { - e.preventDefault(); - e.stopPropagation(); - this.dispatchEvent(new CustomEvent('status-change', { detail: { id: this.opp.id, status }, bubbles: true, composed: true })); - }; - } - render() { const o = this.opp; if (!o) return html`
`; - // v14 定稿:has-one 必须挂在宿主 上, - // 因为 :host(:not([has-one])) .nums 的选择器作用域是宿主自身; - // 早前挂在 上,属性与 :host 不在同一元素,规则永不命中(margin-top 恒为 0px)。 - if (o.oneLiner) { - if (!this.hasAttribute('has-one')) this.setAttribute('has-one', ''); - } else if (this.hasAttribute('has-one')) { - this.removeAttribute('has-one'); - } const s = o.stats || { avg: null, n: 0 }; const u = o.upload || {}; - const dec = o.decision; return html` - +
${o.priority ? html`` : ''} ${o.category ? html`` : ''} ${o.stageObj ? html`` : ''} - ${o.seedKey ? html`示例` : ''}

${o.title}

- ${o.oneLiner ? html`

${o.oneLiner}

` : html``} + ${o.oneLiner ? html`

${o.oneLiner}

` : ''}
@@ -190,17 +132,6 @@
-
- ${dec - ? html`${t('决策:')}${dec.label}${dec.reason ? html` · ${dec.reason}` : ''}${t(`(${dec.by || '未署名'} · ${fmtDate(dec.at, true)})`)}` - : html`${t('决策:尚无决策,')}${o.status === 'candidate' ? '等待评估' : '等待补充记录'}`} -
- ${o.decisionLog?.length ? html`
- ${t(`决策记录 ${o.decisionLog.length} 条(最近 3 条)`)} -
    ${o.decisionLog.slice(-3).reverse().map(x => html`
  1. ${x.label} ${x.reason} - ${t(`${x.by || '未署名'} · ${fmtDate(x.at, true)}`)}
  2. `)}
-
` : ''} -
${u.uploadedBy ? html`${u.uploadedBy}` : html`上传人未记录`} @@ -212,13 +143,6 @@
-
-
- - - -
-
`; --- a/src/pages/view-report.js +++ b/src/pages/view-report.js @@ -202,16 +202,20 @@ `; } + #selectedOpportunity() { + return this.artifactId ? reportOpportunity(this.db, this.artifactId, this.opportunityId) + : this.db.opportunityById?.[this.opportunityId]; + } + #rating() { const linked = this.artifactId ? opportunitiesForReport(this.db, this.artifactId) : []; - const selected = this.artifactId ? reportOpportunity(this.db, this.artifactId, this.opportunityId) - : this.db.opportunityById?.[this.opportunityId]; + const selected = this.#selectedOpportunity(); if (!linked.length && !selected && !this.opportunityId) return ''; return html`

多人评分

${linked.length > 1 || (this.artifactId && this.opportunityId && !selected) ? html` - - { const q = new URLSearchParams(this.routeQuery || {}); q.set('opportunity', e.target.value); navigate(`/report/${encodeURIComponent(this.artifactId)}?${q}`, { replace: true }); @@ -220,7 +224,36 @@ ` : ''} ${selected ? html`

${selected.title}

${keyed(selected.id, html``)}` - : html`

${this.opportunityId ? '当前机会与这份报告没有有效关联,请重新选择后评分。' : '这份报告关联多个机会,请先选择要评分的机会。'}

`} + : html`

${this.opportunityId ? '当前机会与这份报告没有有效关联,请重新选择后评分和决策。' : '这份报告关联多个机会,请先选择要评分和决策的机会。'}

`} +
`; + } + + #decision() { + const selected = this.#selectedOpportunity(); + if (!selected) return ''; + const decision = selected.decision; + const history = selected.decisionLog || []; + return html`
+

机会决策

+

${selected.title}

+
+ ${[['candidate', '候选'], ['approved', '立项'], ['rejected', '否决']].map(([status, label]) => html` + `)} +
+

决策说明

+ ${decision ? html`

${decision.label}

+ ${decision.reason ? html`

${decision.reason}

` : html`

未填写决策说明。

`} +

${decision.by || '未署名'} · ${fmtDate(decision.at, true) || '时间未记录'}

` + : html`

${selected.status === 'candidate' ? '尚无决策,等待评估。' : '尚无决策说明,等待补充记录。'}

`} +
+
+ 决策记录 ${history.length} 条 + ${history.length ? html`
    ${[...history].reverse().map(item => html`
  1. +

    ${item.label}

    ${item.reason ? html`

    ${item.reason}

    ` : ''} +

    ${item.by || '未署名'} · ${fmtDate(item.at, true) || '时间未记录'}

    +
  2. `)}
` : html`

暂无决策记录。

`} +
`; } @@ -239,14 +272,14 @@ return html`

${opportunity.title}

暂无完整报告

-

报告尚未收录

这张机会卡尚未关联可读取的报告正文。以下保留已提交的机会说明与来源,可先阅读并参与评分。

+

报告尚未收录

这张机会卡尚未关联可读取的报告正文。以下保留已提交的机会说明与来源,可先阅读并参与评分和决策。

${opportunity.oneLiner ? html`

机会说明

${opportunity.oneLiner}

` : ''}

提交来源

${opportunity.upload.uploadedBy || '提交人未记录'} · ${opportunity.upload.agentName || '数字人未记录'} · ${opportunity.upload.methodLabel || '方式未记录'} · ${fmtDate(opportunity.upload.uploadedAt, true) || '时间未记录'}

+
${this.#rating()}${this.#decision()} `; } @@ -367,6 +400,7 @@
${this.#rating()} + ${this.#decision()} --- a/src/pages/report-styles.js +++ b/src/pages/report-styles.js @@ -137,6 +137,25 @@ .rating-label { font-size:12px; color:var(--kf-ink-3); } .rating-card { scroll-margin-top:var(--report-anchor,84px); } .rating-card select { width:100%; min-width:0; min-height:40px; padding:8px; border:1px solid var(--kf-line-strong); border-radius:8px; background:var(--kf-surface); color:var(--kf-ink); font:12px var(--kf-font); } + .decision-heading { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; } + .decision-actions { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); border:1px solid var(--kf-line-strong); border-radius:8px; overflow:hidden; } + .decision-actions button { appearance:none; border:0; margin:0; min-width:0; min-height:40px; padding:8px; background:var(--kf-surface); color:var(--kf-ink-2); font:500 13px/1.5 var(--kf-font); cursor:pointer; } + .decision-actions button+button { border-left:1px solid var(--kf-line-strong); } + .decision-actions button:hover { background:var(--kf-surface-btn); } + .decision-actions button[aria-pressed="true"] { color:#fff; background:var(--kf-ink); } + .decision-actions button.approved[aria-pressed="true"] { background:#0f5232; } + .decision-actions button.rejected[aria-pressed="true"] { background:#8a1a14; } + .decision-actions button:focus-visible { outline:2px solid var(--kf-focus); outline-offset:-3px; } + .decision-summary { border-top:1px solid var(--kf-line); padding-top:14px; } + .decision-summary h3 { margin:0 0 8px; font-size:13px; font-weight:600; color:var(--kf-ink); } + .decision-label,.decision-reason,.decision-meta { margin:0; overflow-wrap:anywhere; } + .decision-label { font-size:13px; line-height:1.8; font-weight:600; color:var(--kf-ink); } + .decision-reason { margin-top:6px; font-size:13px; line-height:1.9; color:var(--kf-ink-2); white-space:pre-wrap; } + .decision-meta { margin-top:6px; font-size:12px; line-height:1.8; color:var(--kf-ink-3); } + .decision-history { border-top:1px solid var(--kf-line); padding-top:10px; } + .decision-history summary { cursor:pointer; min-height:30px; font-size:13px; line-height:1.8; color:var(--kf-ink-2); } + .rail .decision-history ol { gap:12px; margin-top:10px; } + .decision-history li { border:1px solid var(--kf-line); border-radius:8px; padding:12px; min-width:0; background:var(--kf-surface-btn); } .linked-evidence { border:0; padding:6px 0; background:transparent; color:var(--kf-accent-ink); text-align:left; font:12px/1.8 var(--kf-font); cursor:pointer; overflow-wrap:anywhere; } .rail ol { display:grid; gap:2px; list-style:none; margin:0; padding:0; } .rail a.jump { display:block; padding:9px 0; font-size:12px; line-height:1.9; color:var(--kf-ink-3); text-decoration:none; } @@ -147,7 +166,7 @@ .chain i { font-style:normal; font-size:11px; color:var(--kf-ink-3); } @media(min-width:1500px) { .cover { --kf-card-pad:32px; } article.ch { padding:32px; } } - @media(max-width:1100px) { .layout { grid-template-columns:1fr; } aside.rail { position:static; grid-template-columns:repeat(auto-fit,minmax(min(210px,100%),1fr)); } } + @media(max-width:1100px) { .layout { grid-template-columns:1fr; } aside.rail { position:static; grid-template-columns:repeat(auto-fit,minmax(min(210px,100%),1fr)); } .rail.with-rating .rating-card,.rail.with-rating .decision-card { grid-column:1/-1; } } @media(max-width:767px) { :host { --report-gap:18px; --report-radius:12px; } .cover { --kf-card-pad:20px; } @@ -183,6 +202,8 @@ .attachment-grid { grid-template-columns:1fr; } .file-entry { padding:16px; } .chain { grid-template-columns:repeat(4,1fr); } + .decision-actions button { min-height:44px; } + .decision-history summary { min-height:44px; display:flex; align-items:center; } } @media(max-width:360px) { .cover { --kf-card-pad:16px; } article.ch { padding:16px; } .cover h1 { font-size:21px; } .finding { padding:12px; } } @media print { --- a/src/pages/report-card.js +++ b/src/pages/report-card.js @@ -185,9 +185,9 @@ .cfoot .kf-btn { height: 28px; padding: 0 10px; font-size: 12px; } /* v15:工作台的机会卡(.rc.play)—— 比类型页的成果卡矮一档,只讲「是什么 + 三个数 + 状态」 */ - .rc.play { display: flex; flex-direction: column; gap: 10px; height: 100%; min-width: 0; } + .rc.play { display: flex; flex-direction: column; gap: 10px; min-width: 0; } .rc.play h3 { font-size: 16px; } - .rc.play .lead { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: calc(2 * 1.6em); } + .rc.play .lead { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .rc.play .top .right { margin-left: auto; } .rc.play .opp-st { font-size: 12px; line-height: 20px; padding: 0 8px; border-radius: var(--r-sm); border: 1px solid var(--kf-line-soft); color: var(--kf-ink-2); white-space: nowrap; } .rc.play .opp-st.st-approved { color: var(--kf-ok-text); border-color: rgba(21,92,58,0.35); background: rgba(21,92,58,0.06); } @@ -197,27 +197,6 @@ .rc.play .play-nums > div:first-child { border-left: 0; padding-left: 0; } .rc.play .play-nums b { display: block; font-size: 20px; font-weight: 600; line-height: 1.1; color: var(--kf-ink); font-variant-numeric: tabular-nums; white-space: nowrap; } .rc.play .play-nums span { display: block; margin-top: 2px; font-size: 12px; color: var(--kf-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - .rc.play .play-acts { display: flex; align-items: center; gap: 8px; margin-top: 2px; } - /* v16:只有按钮文字的样式放这里;布局(内边距 / 间隙 / 是否撑满)由页面给 —— 同一条 "> - 规则写进 CARD_CSS 后,工作台的 flex 页脚(空 span 把「详情 →」推到最右)会被 gap 撑出 84px。 */ - .card-play .quiet button { white-space: nowrap; } - .rc.play .quiet button { border: 0; background: none; padding: 0; height: 20px; font: 500 12px/1 var(--kf-font); color: var(--kf-ink-3); cursor: pointer; - text-box-trim: trim-both; text-box-edge: cap alphabetic; } - .rc.play .quiet button:hover { color: var(--kf-ink); } - .rc.play .quiet button[aria-pressed="true"] { color: var(--kf-accent-ink); font-weight: 600; box-shadow: inset 0 -1px 0 var(--kf-accent); } - .rc.play .sp { flex: 1; } - .rc.play .more { font-size: 12px; color: var(--kf-ink-3); } - @media(max-width:1023px){ - .rc.play .play-acts{flex-wrap:wrap;gap:6px} - .rc.play .quiet{display:flex;flex-wrap:wrap;gap:4px 10px;min-width:0} - .rc.play .sp{display:none} - .rc.play .more{margin-left:auto;white-space:nowrap} - } - @media(max-width:767px){ - .rc.play .quiet{gap:4px} - .rc.play .quiet button{min-height:44px;min-width:44px;height:auto;padding:8px 6px;touch-action:manipulation} - .rc.play .more{display:inline-flex;align-items:center;min-height:44px} - } `; /** 证据档位的文字(跟在数字后面当单位,1.2):2 → 「中」 */ @@ -308,14 +287,12 @@ /** * 工作台用的「安静」机会卡(v15):和卡池那张卡同源(同一个 normalized opportunity), - * 只留「这张卡是什么 + 关键数字 + 快捷决策」三块,整卡直接阅读报告。 + * 只留机会信息、关键数字与当前状态,整卡直接阅读报告;决策在报告侧栏操作。 * opp 机会卡对象(core normalizeOpportunity 的产物) - * status 当前状态(渲染方持有;点击只回 onStatus,由页面去写) - * onStatus (status) => void - */ -export function playCard({ opp: o, status, onStatus }) { + */ +export function playCard({ opp: o }) { const s = o.stats || { avg: null, n: 0 }; - const st = status || o.status; + const st = o.status; return html`
@@ -331,13 +308,6 @@
${o.evidenceLabel}证据
${o.quoteCount}条引用
- -
-
- ${['candidate', 'approved', 'rejected'].map((k) => html``)} -
-
`; } @@ -351,8 +321,7 @@ const f = cardFacts(e); const lead = String(e.lead || '').trim(); const q = (e.quotes || [])[0]; - /* 不编文案(v13 二):没有描述就不渲染描述区,`.nolead` 让卡内纵向重新均分。 - 注意 /pool 的机会卡不是这个组件(它在 kf-opportunity-card.js,一句话恒占两行高)。 */ + /* 没有描述就不渲染描述区,`.nolead` 调整卡内排布。 */ return html`
--- a/src/pages/view-pool.js +++ b/src/pages/view-pool.js @@ -99,7 +99,7 @@ .frow.toolbar .cfoot[data-foot] { margin: 0 0 0 4px; justify-content: flex-start; padding: 0 0 0 12px; border-top: 1px solid var(--kf-line); border-right: 0; border-bottom: 0; border-left: 1px solid var(--kf-line); } .sfield input[type="search"] { margin-left: 0; } - .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; } + .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 16px; align-items: start; } .cards kf-opportunity-card.is-focus { outline: 2px solid var(--kf-accent); outline-offset: 3px; border-radius: var(--r-md); } .count { font-size: 14px; color: var(--kf-ink-3); font-variant-numeric: tabular-nums; } .count b { color: var(--kf-ink); font-variant-numeric: tabular-nums; } @@ -182,11 +182,6 @@ const c = { ...this.#criteria, q }; // 搜索输入用 replace 节流:每敲一个字不新增历史 this.#apply(c); - } - - #changeStatus(e) { - e.stopPropagation(); - this.dispatchEvent(new CustomEvent('status-change', { detail: e.detail, bubbles: true, composed: true })); } render() { @@ -249,7 +244,7 @@ ${filtered.length ? html`
o.id).join(',')}> ${filtered.map((o) => html` { e.stopPropagation(); this.dispatchEvent(new CustomEvent('card-open', { detail: e.detail, bubbles: true, composed: true })); }} - @status-change=${this.#changeStatus}>`)} + >`)}
` : html` navigate('/pool', { replace: true })}>`} ${/* 筛选栏已收起页脚(排序行末尾:本页条数 + 回到顶部),卡片流之后不再重复 */ ''}
--- a/src/pages/view-workbench.js +++ b/src/pages/view-workbench.js @@ -192,11 +192,8 @@ .cards-main { grid-column: span 8; min-width: 0; } .cards-main .feed-head { margin-bottom: 0; } .cards-main .cards { margin-top: 12px; } - .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: stretch; } + .cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; } .cards > kf-glass-card { min-width: 0; } - /* 机会卡底部「候选 / 已立项 / 已否决」:静默文字行(20px、无底),页脚仍是「左段 + 详情 →」两端对齐 */ - .cards .quiet { display: inline-flex; gap: 14px; } - .cards .quiet button { height: 20px; } .side-stack { grid-column: span 4; min-width: 0; } .side-in { display: flex; flex-direction: column; height: 100%; gap: 8px; } .side-in [data-feed] { flex: 1 1 auto; } @@ -295,7 +292,7 @@
${t('最近动过')} 全部 ${pool.total} →
- ${play.length ? html`
${play.map((o) => html` this.#openCard(o.id)}>${playCard({ opp: o, status: o.status, onStatus: (st) => this.#setStatus({ id: o.id, status: st }) })}`)}
` + ${play.length ? html`
${play.map((o) => html` this.#openCard(o.id)}>${playCard({ opp: o })}`)}
` : html`

还没有机会卡

`}
@@ -319,11 +316,6 @@ /** 与卡池共用机会报告入口,不再经过详情弹窗。 */ #openCard(id) { this.dispatchEvent(new CustomEvent('opportunity-open', { detail: { id }, bubbles: true, composed: true })); - } - - /** 快捷决策:复用 app 的 status-change(带操作人校验与写入),与卡池同一路径 */ - #setStatus({ id, status }) { - this.dispatchEvent(new CustomEvent('status-change', { detail: { id, status }, bubbles: true, composed: true })); } #stagebar(rows, maxSub, picked) {