/* views/pool.js —— 机会卡片池(组织资产)。 * * 这一页回答:"研发中心现在手上有哪些机会、值不值得做、谁在什么时候用什么方式送上来的。" * 卡片形态(不是列表行);可筛(优先级/阶段/品类/状态/上传人)、可排(优先级/评分/最新)、 * 可点评分(多人平均)、可改状态(候选/立项/否决,落库后刷新保持)。 * 筛选/排序/打开的卡都写进 URL;评分/状态落库,不做仅本地的假操作。 */ (function () { "use strict"; const T = window.KF_TAXONOMY; const U = window.KF_UTIL; const UI = window.KF_UI; const OPP = window.KF_OPP; const esc = U.esc; const STATUS_FILTER = ["candidate", "approved", "rejected"]; /* 数组.contains 有坑:CSS 里 .o-cat/.o-stage 是类名,这里绝不能把数组当字符串用。 * 统一用 Set,筛选是精确命中,不做子串匹配。 */ const has = (arr, v) => new Set(arr).has(v); function matches(o, p) { if (p.priority && p.priority.length && !has(p.priority, o.priority)) return false; if (p.stage && p.stage.length && !(o.stageObj && has(p.stage, o.stageObj.code))) return false; if (p.category && p.category.length && !has(p.category, o.category)) return false; if (p.status && p.status.length && !has(p.status, o.status)) return false; if (p.member && p.member.length && !has(p.member, o.upload.by)) return false; return true; } const list = (v) => (v ? String(v).split(",").filter(Boolean) : []); /* 概览条:池子总量 + 三个状态 + 平均分(给判断,不只给数字) */ function summary(all) { const n = (k) => all.filter((o) => o.status === k).length; const scored = all.filter((o) => o.stats.n); const avg = scored.length ? Math.round(scored.reduce((s, o) => s + o.stats.avg, 0) / scored.length * 10) / 10 : null; const top = OPP.sortCards(all, "score")[0]; return `
池中机会卡
${all.length}张
${n("candidate")} 候选 · ${n("approved")} 已立项 · ${n("rejected")} 已否决
池均综合分
${avg == null ? "—" : avg}
${scored.length} / ${all.length} 张已有人评分
最高分机会
${top ? esc(top.title) : "—"}
${top ? `综合 ${top.stats.avg} · ${top.stats.n} 人评 · ${esc(top.stage)}` : ""}
进池的人
${new Set(all.map((o) => o.upload.by).filter(Boolean)).size}人
${new Set(all.map((o) => o.upload.agentName).filter(Boolean)).size} 个 Agent 参与上传
`; } function facet(label, dim, values, active, counts, multi) { if (!values.length) return ""; const chips = values.map((v) => { const on = active.includes(v.value); const zero = !counts.get(v.value) && !on; return ``; }).join(""); return `
${esc(label)}${chips}
`; } function filterBar(all, c, n) { const rest = (dim) => all.filter((o) => matches(o, { ...c, [dim]: [] })); const count = (rows, fn) => U.countBy(rows, fn); const pc = count(rest("priority"), (o) => o.priority); const sc = count(rest("stage"), (o) => (o.stageObj ? o.stageObj.code : "")); const cc = count(rest("category"), (o) => o.category); const stc = count(rest("status"), (o) => o.status); const mc = count(rest("member"), (o) => o.upload.by); const cats = [...new Set(all.map((o) => o.category).filter(Boolean))].sort((a, b) => a.localeCompare(b, "zh")); const uploaders = [...mc.entries()].sort((a, b) => b[1] - a[1]).map(([v, k]) => ({ value: v, label: v })); const any = ["priority", "stage", "category", "status", "member"].some((k) => c[k].length); return `
${facet("优先级", "priority", T.PRIORITIES.map((v) => ({ value: v, label: v })), c.priority, pc)} ${facet("阶段", "stage", T.STAGES.map((s) => ({ value: s.code, label: s.code })), c.stage, sc)} ${facet("品类", "category", cats.map((v) => ({ value: v, label: v })), c.category, cc)} ${facet("状态", "status", T.OPP_STATUS.map((s) => ({ value: s.key, label: s.label })), c.status, stc)} ${facet("上传人", "member", uploaders, c.member, mc, true)}
符合当前条件 ${n} 张卡 ${any ? `` : ""}
`; } function render(db, params) { const all = db.opportunities; const c = { priority: list(params.priority), stage: list(params.stage), category: list(params.category), status: list(params.status).filter((s) => STATUS_FILTER.includes(s)), member: list(params.member) }; const sortKey = OPP.SORTS[params.sort] ? params.sort : "prio"; const rows = OPP.sortCards(all.filter((o) => matches(o, c)), sortKey); const head = `

机会卡片池

全员机会卡汇总入库:每张卡带评分、优先级与决策记录,构成研发中心的机会储备。卡片上的每一步都能追到「谁 · 哪个 Agent · 什么方式 · 什么时候」上传。

${OPP.sortSeg(sortKey, params.sort)}
`; const body = !all.length ? UI.empty({ title: "池子里还没有机会卡", body: "学员在 S3 机会识别阶段用主分身上传机会卡后,会自动进池。这里会按卡片形态排开,带评分、状态和决策记录。", actions: [{ label: "看阶段板块", href: "#/stages" }] }) : !rows.length ? UI.empty({ title: "当前条件下没有卡", body: `池中 ${all.length} 张卡,没有同时满足全部条件的。单独去掉某一项后会有卡出现。`, actions: [{ label: "清除全部筛选", act: "opp-clear" }] }) : ``; return `${head} ${summary(all)}
${filterBar(all, c, rows.length)}
${body}

共 ${all.length} 张 · ${rows.length} 张符合当前条件 · 点卡片看详情、评分、改状态;平均分由全部评分现算,改状态会记进决策日志。

`; } window.KF_VIEW_POOL = { render, matches }; })();