/* === Risk warning + SOP pages === */
/* global React, useToast, Icons */
const { useState: useWarnState, useMemo: useWarnMemo } = React;
const LEVELS = { high: "高", mid: "中", low: "低" };
const TAG_CLASS = { 预警: "red", 安抚: "yellow", 增值: "green" };
const safeStr = (s, defaultVal = "") => (typeof s === "string" ? s : defaultVal);
function PageRiskWarning({ openDrawer, onNav, watchState, setStage }) {
const { products, CATEGORIES } = window.IRDATA;
const [catFilter, setCatFilter] = useWarnState("all");
const [driverFilter, setDriverFilter] = useWarnState("all");
const list = watchState;
// Resolve category for each event - prefer explicit bigClass, else from product
const getCat = (e) => {
if (e.bigClass) return e.bigClass;
const p = products.find((pp) => pp.id === e.productId);
return p?.catId;
};
const filtered = useWarnMemo(() => list.filter((e) => {
const cat = getCat(e);
if (catFilter !== "all" && cat !== catFilter) return false;
if (driverFilter !== "all" && e.driver !== driverFilter) return false;
return true;
}), [list, catFilter, driverFilter]);
const counts = {
high: list.filter((e) => e.level === "high").length,
mid: list.filter((e) => e.level === "mid").length,
low: list.filter((e) => e.level === "low").length,
total: list.length
};
const catCounts = {};
CATEGORIES.forEach((c) => {catCounts[c.id] = list.filter((e) => getCat(e) === c.id).length;});
return (
{/* Top stat row */}
高等级预警
{counts.high}
需立即处置
预警总数
{counts.total}
含投研驱动 {list.filter((e) => safeStr(e.driver).includes("投研")).length}
{/* Watchlist */}
内部强关注列表
大类:
setCatFilter("all")}>
全部 {counts.total}
{CATEGORIES.map((c) =>
setCatFilter(c.id)}
style={catFilter === c.id ? { background: c.color, borderColor: c.color } : {}}>
{c.name}
{catCounts[c.id] || 0}
)}
驱动:
setDriverFilter("all")}>全部
setDriverFilter("产品驱动")}>产品驱动
setDriverFilter("投研驱动")}>投研驱动
| 产品 / 对象 |
大类 |
场景 |
驱动 |
标签 |
等级 |
触发原因 |
{filtered.map((e) => {
const p = products.find((pp) => pp.id === e.productId);
const cat = CATEGORIES.find((c) => c.id === getCat(e));
return (
|
{p?.name || e.productId}
{p?.code}
|
{cat && {cat.name}} |
{e.scene} |
{e.driver} |
{e.tag} |
{LEVELS[e.level]}
|
{e.reason} |
);
})}
{filtered.length === 0 &&
|
}
);
}
function PageSOP({ openDrawer, watchState, setStage }) {
const { products, STAGES, CATEGORIES } = window.IRDATA;
const toast = useToast();
const list = watchState;
const getCat = (e) => {
if (e.bigClass) return e.bigClass;
const p = products.find((pp) => pp.id === e.productId);
return p?.catId;
};
const counts = {
stage0: list.filter((e) => e.stage === 0).length,
stage1: list.filter((e) => e.stage === 1).length,
valueAdd: list.filter((e) => e.tag === "增值").length,
expire: 4
};
return (
{[
{ label: "待一线确认", val: counts.stage0, color: "var(--red)" },
{ label: "待客户触达", val: counts.stage1, color: "var(--orange)" },
{ label: "可营销机会", val: counts.valueAdd, color: "var(--green)" },
{ label: "今日到期任务", val: counts.expire, color: "var(--primary)" }].
map((m, i) =>
{m.label}
{m.val}
需在 24h 内处置
)}
{list.map((e) => {
const p = products.find((pp) => pp.id === e.productId);
const cat = CATEGORIES.find((c) => c.id === getCat(e));
return (
{p?.name || e.productId}
{p?.code} · {p?.manager}
{cat ?
{cat.name} :
}
{e.driver}
{e.tag}
{LEVELS[e.level]} · 等级
{STAGES.map((s, i) =>
i ? "done" : e.stage === i ? "active" : "")}>
{s.slice(0, 4)}
)}
{e.stage < STAGES.length - 1 ?
:
已完成
}
);
})}
);
}
function PageSystem() {
return (
{["账号与权限", "数据接入", "评分模型", "预警阈值", "通知偏好", "审计日志"].map((s, i) =>
{s}
)}
预警阈值配置
{[
{ label: "最大回撤阈值 (%)", val: "6.0" },
{ label: "净值单日下跌阈值 (%)", val: "1.5" },
{ label: "波动率上升触发倍数", val: "1.6" },
{ label: "夏普下降阈值", val: "0.5" },
{ label: "卡玛恶化连续月份", val: "2" },
{ label: "规模下降阈值 (%)", val: "15" }].
map((c, i) =>
)}
);
}
function PageWarnOverview({ onNav, watchState, setStage }) {
const { products, STAGES, CATEGORIES } = window.IRDATA;
const toast = useToast();
const list = watchState;
const [catFilter, setCatFilter] = useWarnState("all");
const [driverFilter, setDriverFilter] = useWarnState("all");
const getCat = (e) => {
if (e.bigClass) return e.bigClass;
const p = products.find((pp) => pp.id === e.productId);
return p?.catId;
};
const riskCounts = {
high: list.filter((e) => e.level === "high").length,
mid: list.filter((e) => e.level === "mid").length,
low: list.filter((e) => e.level === "low").length,
total: list.length,
};
const sopCounts = {
stage0: list.filter((e) => e.stage === 0).length,
stage1: list.filter((e) => e.stage === 1).length,
valueAdd: list.filter((e) => e.tag === "增值").length,
expire: 4,
};
// Pick top urgent items: high-level + stage 0/1
const urgent = list
.filter((e) => e.level === "high" || (e.level === "mid" && e.stage < 2))
.sort((a, b) => {
const order = { high: 0, mid: 1, low: 2 };
return order[a.level] - order[b.level] || a.stage - b.stage;
})
.slice(0, 6);
// Full watchlist with filters applied
const catCounts = {};
CATEGORIES.forEach((c) => { catCounts[c.id] = list.filter((e) => getCat(e) === c.id).length; });
const filtered = list.filter((e) => {
const cat = getCat(e);
if (catFilter !== "all" && cat !== catFilter) return false;
if (driverFilter !== "all" && e.driver !== driverFilter) return false;
return true;
});
return (
{/* === 风险事件预警 metrics === */}
风险事件预警 · 实时概况
onNav("warn-risk")}>
高等级预警
{riskCounts.high}
需立即处置
onNav("warn-risk")}>
中等级预警
{riskCounts.mid}
本周内跟进
onNav("warn-risk")}>
低等级预警
{riskCounts.low}
观察池
onNav("warn-risk")}>
预警总数
{riskCounts.total}
含投研驱动 {list.filter((e) => safeStr(e.driver).includes("投研")).length}
{/* === 售后 SOP metrics === */}
售后 SOP · 任务概况
{[
{ label: "待一线确认", val: sopCounts.stage0, color: "var(--red)", sub: "需 24h 内推进" },
{ label: "待客户触达", val: sopCounts.stage1, color: "var(--orange)", sub: "投顾团队跟进" },
{ label: "可营销机会", val: sopCounts.valueAdd, color: "var(--green)", sub: "增值事件" },
{ label: "今日到期任务", val: sopCounts.expire, color: "var(--primary)", sub: "SLA 临近" },
].map((m, i) => (
onNav("warn-sop")}>
{m.label}
{m.val}
{m.sub}
))}
{/* === 紧急处置清单 === */}
紧急处置清单
高等级或处于待确认/待触达阶段的事件 · 共 {urgent.length} 条
| 产品 / 对象 |
大类 |
场景 |
驱动 |
标签 |
等级 |
触发原因 |
状态 / 推进 |
{urgent.map((e) => {
const p = products.find((pp) => pp.id === e.productId);
const cat = CATEGORIES.find((c) => c.id === getCat(e));
return (
|
{p?.name || e.productId}
{p?.code}
|
{cat && {cat.name}} |
{e.scene} |
{e.driver} |
{e.tag} |
{LEVELS[e.level]}
|
{e.reason} |
{STAGES.map((s, i) => (
i ? "done" : e.stage === i ? "active" : "")}>
{s.slice(0, 4)}
))}
{e.stage < STAGES.length - 1 ? (
) : (
完成
)}
|
);
})}
{urgent.length === 0 && (
|
)}
{/* === 内部强关注列表 === */}
内部强关注列表
大类:
setCatFilter("all")}>
全部 {list.length}
{CATEGORIES.map((c) => (
setCatFilter(c.id)}
style={catFilter === c.id ? { background: c.color, borderColor: c.color } : {}}>
{c.name}
{catCounts[c.id] || 0}
))}
驱动:
setDriverFilter("all")}>全部
setDriverFilter("产品驱动")}>产品驱动
setDriverFilter("投研驱动")}>投研驱动
| 产品 / 对象 |
大类 |
场景 |
驱动 |
标签 |
等级 |
触发原因 |
{filtered.map((e) => {
const p = products.find((pp) => pp.id === e.productId);
const cat = CATEGORIES.find((c) => c.id === getCat(e));
return (
|
{p?.name || e.productId}
{p?.code}
|
{cat && {cat.name}} |
{e.scene} |
{e.driver} |
{e.tag} |
{LEVELS[e.level]}
|
{e.reason} |
);
})}
{filtered.length === 0 && (
|
)}
);
}
Object.assign(window, { PageRiskWarning, PageSOP, PageSystem, PageWarnOverview });