Files
wysite/__mirror/runtime/hc-etms-dashboard/runtime-bootstrap.js

3214 lines
122 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(async function () {
const INITIAL_HASH = location.hash;
const VERSION = new URLSearchParams(location.search).get("v") || "";
const STYLE_HREFS = [
"/hc-etms.sqygj.cn/static/css/chunk-elementUI.db8918ff.css",
"/hc-etms.sqygj.cn/static/css/chunk-libs.3dfb7769.css",
"/hc-etms.sqygj.cn/static/css/app.b5657ae9.css"
];
const SCRIPT_SRCS = [
"./webpack-runtime.js",
"/hc-etms.sqygj.cn/static/js/chunk-elementUI.19e378d9.js",
"/hc-etms.sqygj.cn/static/js/chunk-libs.d8d09258.js",
"/hc-etms.sqygj.cn/static/js/app.8e16015e.js"
];
let storageSeed = {};
let routeMap = {};
const MICROBRAIN_PRESETS = {
finance: {
eyebrow: "经营总览",
headline: "财务看板",
summary: "围绕收入、利润、现金与应收四个维度生成静态仪表盘,用于替代空态页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.16)",
metrics: [
{ label: "本年累计收入", value: "¥2,056,000", delta: "较上月 +8.4%" },
{ label: "净利润率", value: "19.6%", delta: "经营状态良好" },
{ label: "经营现金流", value: "¥386,000", delta: "回款率 93.8%" },
{ label: "应收账款余额", value: "¥126,800", delta: "逾期项目 3 个" }
],
trendLabels: ["1月", "2月", "3月", "4月", "5月", "6月"],
trendSeries: [
{ label: "收入", color: "#2d76ff", values: [128, 136, 142, 156, 163, 172] },
{ label: "利润", color: "#2fc1a8", values: [22, 24, 26, 29, 31, 34] }
],
segments: [
{ label: "物业费", value: "54%", tone: "#2d76ff" },
{ label: "停车费", value: "21%", tone: "#20c997" },
{ label: "增值服务", value: "15%", tone: "#ffb74d" },
{ label: "其他收入", value: "10%", tone: "#a78bfa" }
],
rankingTitle: "项目贡献排行",
rankingRows: [
["循环花园一期", "¥462,000", "22.5%"],
["博万物", "¥318,000", "15.5%"],
["深圳市美好花园", "¥286,000", "13.9%"],
["循环科技三期", "¥244,000", "11.9%"]
],
alertsTitle: "财务提示",
alerts: [
{ title: "广州三俊物业管理有限公司", detail: "本月票据待核销 2 笔,建议财务复核。", level: "关注" },
{ title: "循环花园一期", detail: "停车费回款偏慢,较预算少 6.2%。", level: "预警" },
{ title: "江南世家", detail: "成本支出稳定,预算执行率 91%。", level: "正常" }
]
},
equipment: {
eyebrow: "资产运行",
headline: "设备看板",
summary: "补齐设备资产、维保执行、故障预警与能耗概览,替代原始“敬请期待”页面。",
accent: "#00a870",
accentSoft: "rgba(0, 168, 112, 0.16)",
metrics: [
{ label: "设备总台账", value: "1,286", delta: "在线率 97.3%" },
{ label: "本月维保完成", value: "214", delta: "完成率 92.1%" },
{ label: "待处理预警", value: "18", delta: "一级预警 3 条" },
{ label: "本月能耗", value: "18.6万kWh", delta: "较上月 -4.8%" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "工单关闭量", color: "#00a870", values: [26, 32, 29, 36, 39, 42] },
{ label: "新增预警", color: "#ff8a00", values: [14, 12, 11, 10, 8, 7] }
],
segments: [
{ label: "电梯", value: "34%", tone: "#00a870" },
{ label: "消防", value: "28%", tone: "#2d76ff" },
{ label: "供配电", value: "22%", tone: "#ff8a00" },
{ label: "给排水", value: "16%", tone: "#a78bfa" }
],
rankingTitle: "维保完成排行",
rankingRows: [
["循环花园一期", "48 项", "100%"],
["博万物", "41 项", "97%"],
["连城花园", "33 项", "94%"],
["美好花园", "28 项", "91%"]
],
alertsTitle: "设备预警",
alerts: [
{ title: "电梯系统", detail: "2 台电梯年检临期,需在 7 天内完成复检。", level: "预警" },
{ title: "消防泵房", detail: "巡检记录缺失 1 次,已通知项目负责人补录。", level: "关注" },
{ title: "供配电房", detail: "夜间负荷恢复正常,建议继续观察。", level: "正常" }
]
},
parkingLot: {
eyebrow: "车场经营",
headline: "车场看板",
summary: "展示车位利用、出入流量、收费表现与异常车牌,补齐停车运营视图。",
accent: "#7c4dff",
accentSoft: "rgba(124, 77, 255, 0.16)",
metrics: [
{ label: "总车位数", value: "2,146", delta: "固定车位 1,382" },
{ label: "当前占用率", value: "81.4%", delta: "较昨日 +2.6%" },
{ label: "本月停车收入", value: "¥486,300", delta: "临停车收入占比 38%" },
{ label: "异常进出记录", value: "12", delta: "黑名单 2 辆" }
],
trendLabels: ["08时", "10时", "12时", "14时", "16时", "18时"],
trendSeries: [
{ label: "进场车辆", color: "#7c4dff", values: [42, 58, 63, 71, 76, 69] },
{ label: "离场车辆", color: "#2fc1a8", values: [35, 46, 58, 64, 70, 66] }
],
segments: [
{ label: "固定月租", value: "62%", tone: "#7c4dff" },
{ label: "临停收费", value: "24%", tone: "#2d76ff" },
{ label: "新能源充电", value: "9%", tone: "#20c997" },
{ label: "其他", value: "5%", tone: "#ffb74d" }
],
rankingTitle: "项目车场收益排行",
rankingRows: [
["循环花园一期", "¥96,800", "88.4%"],
["博万物", "¥74,200", "82.7%"],
["美好花园", "¥68,500", "79.3%"],
["连城花园", "¥61,900", "75.1%"]
],
alertsTitle: "车场异常",
alerts: [
{ title: "粤B9X2F8", detail: "同一车辆 24 小时内重复进出 5 次,建议复核。", level: "关注" },
{ title: "循环花园一期", detail: "北门道闸离线 18 分钟,已恢复在线。", level: "预警" },
{ title: "博万物", detail: "新能源桩周转率较高,可考虑扩容。", level: "正常" }
]
},
supplierMicrobrain: {
eyebrow: "供应协同",
headline: "供应商微脑",
summary: "围绕供应商覆盖、履约、结算与风险预警生成静态仪表盘,替代空态页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.16)",
metrics: [
{ label: "合作供应商", value: "86", delta: "活跃供应商 63 家" },
{ label: "本月履约率", value: "91.8%", delta: "较上月 +2.1%" },
{ label: "待结算金额", value: "¥268,400", delta: "本周待审核 7 笔" },
{ label: "风险预警", value: "6", delta: "高风险 2 家" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "履约评分", color: "#f97316", values: [78, 81, 84, 86, 88, 91] },
{ label: "结算完成率", color: "#2d76ff", values: [62, 66, 71, 75, 79, 83] }
],
segments: [
{ label: "保洁服务", value: "34%", tone: "#f97316" },
{ label: "安防服务", value: "26%", tone: "#2d76ff" },
{ label: "设备维保", value: "24%", tone: "#20c997" },
{ label: "绿化养护", value: "16%", tone: "#a78bfa" }
],
rankingTitle: "供应商履约排行",
rankingRows: [
["星河保洁服务有限公司", "96 分", "98%"],
["万嘉设备维保有限公司", "93 分", "94%"],
["城安安防科技有限公司", "91 分", "92%"],
["绿景园林服务有限公司", "89 分", "90%"]
],
alertsTitle: "供应风险提示",
alerts: [
{ title: "广州市三俊物业管理有限公司", detail: "2 笔结算资料待补充,建议本周内完成复核。", level: "关注" },
{ title: "万嘉设备维保有限公司", detail: "一项维保任务已逾期 3 天,需供应商经理跟进。", level: "预警" },
{ title: "星河保洁服务有限公司", detail: "本月履约稳定,建议纳入优质供应商池。", level: "正常" }
]
}
};
const CLOUD_REPORT_PRESETS = {
propertyFeeReport: {
title: "物业费收缴明细表",
eyebrow: "收费经营",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.14)",
cards: [
{ label: "年度预算收缴", value: "¥4,268,000", detail: "预算达成 88.2%" },
{ label: "本月实收当月", value: "¥356,800", detail: "较上月 +6.5%" },
{ label: "本月收缴率", value: "92.4%", detail: "欠费项目 3 个" },
{ label: "本年累计收缴率", value: "89.6%", detail: "回款节奏稳定" }
],
headers: ["项目名称", "省/市/区", "年度预算收缴金额", "往年欠费金额", "往月欠费金额", "本月收缴往年欠费金额", "本月收缴往月欠费金额", "本月预收缴金额", "本月实收当月金额", "本月收缴率", "本年累计收缴率"],
rows: [
["循环花园一期", "湖北省/黄石市/下陆区", "¥886,000", "¥42,800", "¥31,500", "¥12,600", "¥9,800", "¥28,500", "¥74,300", "93.8%", "91.2%"],
["博万物", "广东省/深圳市/龙华区", "¥712,000", "¥36,400", "¥28,900", "¥8,200", "¥6,100", "¥21,600", "¥58,400", "89.5%", "87.1%"],
["美好花园", "广东省/深圳市/福田区", "¥658,000", "¥18,600", "¥22,300", "¥6,500", "¥5,400", "¥19,100", "¥54,900", "94.2%", "90.8%"],
["连城花园", "湖南省/长沙市/芙蓉区", "¥624,000", "¥15,900", "¥18,600", "¥4,600", "¥3,900", "¥16,700", "¥49,500", "91.7%", "88.5%"],
["循环科技三期", "天津市/河东区", "¥592,000", "¥12,300", "¥15,800", "¥3,800", "¥3,500", "¥15,200", "¥46,200", "90.6%", "86.9%"],
["江南世家一期", "湖北省/黄石市/下陆区", "¥516,000", "¥10,800", "¥13,600", "¥2,700", "¥2,100", "¥12,600", "¥41,700", "88.4%", "85.7%"]
]
},
parkingLotReport: {
title: "车场列表",
eyebrow: "车场运营",
accent: "#7c4dff",
accentSoft: "rgba(124, 77, 255, 0.14)",
cards: [
{ label: "今日进出总量", value: "612 / 584", detail: "高峰出现在 16:00" },
{ label: "本月临停费", value: "¥186,400", detail: "较上月 +8.1%" },
{ label: "本月月卡费", value: "¥302,800", detail: "固定车位 1,382 个" },
{ label: "异常开闸率", value: "1.8%", detail: "可疑收费 4 笔" }
],
headers: ["小区名称", "省/市/区", "今日进出数量", "今日临停费", "本月临停费", "本月月卡费", "今日异常开闸数", "今日可疑收费", "累计异常开闸次数", "累计可疑收费", "运维工单", "当月收缴率", "累计收缴率", "异常开闸率"],
rows: [
["循环花园一期", "湖北省/黄石市/下陆区", "126 / 118", "¥6,420", "¥39,800", "¥62,400", "2", "1", "16", "4", "3", "95.6%", "93.4%", "1.6%"],
["博万物", "广东省/深圳市/龙华区", "98 / 96", "¥4,980", "¥32,600", "¥58,300", "1", "0", "9", "2", "2", "92.3%", "90.8%", "1.1%"],
["美好花园", "广东省/深圳市/福田区", "114 / 105", "¥5,760", "¥36,900", "¥61,700", "3", "1", "21", "6", "4", "94.1%", "91.7%", "2.4%"],
["连城花园", "湖南省/长沙市/芙蓉区", "87 / 82", "¥4,120", "¥28,300", "¥54,800", "2", "1", "13", "3", "2", "90.7%", "88.9%", "1.9%"],
["循环科技三期", "天津市/河东区", "73 / 69", "¥3,560", "¥24,800", "¥49,600", "1", "1", "8", "2", "1", "89.8%", "87.2%", "1.4%"]
]
},
planTaskReport: {
title: "计划工单执行总览",
eyebrow: "计划执行",
accent: "#00a870",
accentSoft: "rgba(0, 168, 112, 0.14)",
cards: [
{ label: "本月应完成", value: "1,286", detail: "全部项目口径" },
{ label: "本月完成", value: "1,214", detail: "完成率 94.4%" },
{ label: "超时仍未完成", value: "28", detail: "需持续跟进" },
{ label: "人工验收合格率", value: "96.2%", detail: "工作达标率 91.8%" }
],
headers: ["项目名称", "本月应完成", "本月未完成", "本月完成", "完成率", "已报备工单数", "实际完成率", "提前完成", "超时完成", "超时仍未完成", "应验收", "人工合格", "合格", "未验收", "工作达标率"],
rows: [
["循环花园一期", "268", "12", "256", "95.5%", "18", "94.8%", "162", "24", "6", "238", "228", "231", "7", "93.2%"],
["博万物", "214", "15", "199", "93.0%", "16", "91.8%", "116", "29", "8", "184", "175", "178", "6", "89.7%"],
["美好花园", "198", "9", "189", "95.4%", "11", "94.0%", "121", "18", "5", "176", "169", "170", "4", "92.5%"],
["连城花园", "176", "14", "162", "92.0%", "13", "90.6%", "94", "26", "7", "150", "143", "145", "5", "88.9%"],
["循环科技三期", "164", "11", "153", "93.3%", "9", "92.7%", "88", "22", "2", "141", "136", "137", "3", "90.4%"],
["江南世家一期", "146", "11", "135", "92.5%", "7", "91.2%", "73", "19", "0", "129", "124", "125", "2", "91.1%"]
]
},
workOrderReport: {
title: "非计划工单执行概览",
eyebrow: "服务响应",
accent: "#ff8a00",
accentSoft: "rgba(255, 138, 0, 0.14)",
cards: [
{ label: "来自住户工单", value: "126", detail: "本月新增 18 单" },
{ label: "来自内控工单", value: "284", detail: "闭环率 83.5%" },
{ label: "历史未完成", value: "58", detail: "较上周 -7 单" },
{ label: "综合执行率", value: "86.9%", detail: "重点项目需跟进" }
],
headers: ["项目名称", "来自住户", "已完成", "未完成", "执行率", "历史未完成数", "历史当月完成数", "来自内控", "已完成", "未完成", "执行率"],
rows: [
["博万物", "23", "18", "5", "78.3%", "46", "12", "80", "69", "11", "86.3%"],
["江南世家一期", "16", "15", "1", "93.8%", "4", "3", "21", "18", "3", "85.7%"],
["循环花园一期", "28", "24", "4", "85.7%", "3", "4", "64", "56", "8", "87.5%"],
["美好花园", "19", "17", "2", "89.5%", "2", "1", "43", "37", "6", "86.0%"],
["连城花园", "14", "12", "2", "85.7%", "1", "0", "31", "27", "4", "87.1%"]
]
},
dataReport: {
title: "工单耗时统计总览",
eyebrow: "履约时效",
accent: "#20c997",
accentSoft: "rgba(32, 201, 151, 0.14)",
cards: [
{ label: "工单总数量", value: "418", detail: "非计划 + 服务工单" },
{ label: "平均受理耗时", value: "00:18:42", detail: "低于标准 12.6%" },
{ label: "平均处理耗时", value: "03:16:28", detail: "本月效率稳定" },
{ label: "平均完成耗时", value: "08:42:10", detail: "满意度 94.1%" }
],
headers: ["项目名称", "工单总数量", "受理工单数量", "规定受理耗时", "实际受理平均耗时", "处理工单数量", "规定处理耗时", "实际处理平均耗时", "已评价工单数量", "规定完成耗时", "实际完成平均耗时"],
rows: [
["循环花园一期", "84", "82", "00:30:00", "00:16:40", "80", "04:00:00", "03:12:15", "76", "12:00:00", "08:31:12"],
["美好花园", "73", "71", "00:30:00", "00:18:05", "70", "04:00:00", "03:08:49", "65", "12:00:00", "08:12:44"],
["循环科技一期", "62", "60", "00:30:00", "00:19:14", "58", "04:00:00", "03:26:37", "54", "12:00:00", "08:55:10"],
["连城花园", "57", "56", "00:30:00", "00:17:28", "55", "04:00:00", "03:11:26", "51", "12:00:00", "08:20:53"],
["博万物", "96", "93", "00:30:00", "00:20:32", "90", "04:00:00", "03:48:21", "83", "12:00:00", "09:04:15"]
]
},
consumeReport: {
title: "耗能报表总览",
eyebrow: "能耗经营",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "本月总耗能", value: "186.4万kWh", detail: "较上月 -4.3%" },
{ label: "单位面积能耗", value: "6.45", detail: "kWh/㎡" },
{ label: "高耗能项目", value: "3", detail: "需重点跟进" },
{ label: "节能改善率", value: "7.8%", detail: "季度目标达成" }
],
headers: ["项目名称", "区域", "本月电耗", "本月水耗", "本月气耗", "单位面积能耗", "同比变化", "节能措施完成率"],
rows: [
["循环花园一期", "湖北省/黄石市/下陆区", "38.6万kWh", "12,860m³", "4,320m³", "6.22", "-5.1%", "92%"],
["博万物", "广东省/深圳市/龙华区", "31.4万kWh", "10,240m³", "3,280m³", "6.58", "-3.6%", "88%"],
["美好花园", "广东省/深圳市/福田区", "28.2万kWh", "8,930m³", "2,960m³", "6.11", "-6.4%", "91%"],
["连城花园", "湖南省/长沙市/芙蓉区", "24.8万kWh", "7,860m³", "2,540m³", "6.37", "-2.1%", "84%"],
["循环科技三期", "天津市/河东区", "19.7万kWh", "6,220m³", "1,980m³", "6.03", "-4.8%", "86%"]
]
},
jobGridReport: {
title: "作业网格台账报表",
eyebrow: "网格履约",
accent: "#6366f1",
accentSoft: "rgba(99, 102, 241, 0.14)",
cards: [
{ label: "本月应完成数", value: "2,184", detail: "覆盖 126 个网格" },
{ label: "本月完成次数", value: "2,046", detail: "完成率 93.7%" },
{ label: "需保持跟进", value: "64", detail: "重点对象 9 个" },
{ label: "评分均分", value: "4.72", detail: "低分评价 13 条" }
],
headers: ["项目名称", "空间", "网格", "场景归属", "作业对象类型", "作业对象", "所属月份", "SPU数量", "本月应完成数", "本月完成次数", "本月提前完成", "本月超时完成", "需保持跟进", "完成基础云豆R1价值", "评分均分"],
rows: [
["循环花园一期", "公共区域", "A-01", "清洁卫生", "楼栋", "1号楼", "2026-04", "18", "186", "174", "112", "18", "6", "12,680", "4.86"],
["博万物", "设备间", "E-08", "设备管理", "设备", "消防泵房", "2026-04", "12", "148", "136", "79", "22", "5", "10,240", "4.68"],
["美好花园", "车行流线", "P-03", "车场运营", "车位", "地下二层", "2026-04", "15", "164", "156", "96", "17", "4", "11,360", "4.71"],
["连城花园", "外围道路", "S-12", "安防管理", "巡更点", "北门岗亭", "2026-04", "9", "132", "121", "73", "16", "8", "8,920", "4.55"],
["循环科技三期", "绿化区域", "G-06", "绿化管理", "绿植", "中央花园", "2026-04", "11", "118", "109", "68", "13", "5", "7,860", "4.79"]
]
},
contractPlanReport: {
title: "合同计划执行报表",
eyebrow: "合同履约",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "本月应完成工单", value: "864", detail: "合同口径" },
{ label: "本月完成工单", value: "803", detail: "完成率 92.9%" },
{ label: "超时仍未完成", value: "19", detail: "需持续跟进" },
{ label: "已完成云豆价值", value: "¥182,400", detail: "较上月 +5.8%" }
],
headers: ["项目名称", "合同名称", "计划类型", "本月应完成工单数", "本月完成工单数", "本月未完成工单数", "本月提前工单数", "本月超时完成工单数", "本月超时仍未完成", "需保持跟进", "已完成基础云豆R1价值", "评分均分"],
rows: [
["循环花园一期", "循环花园物业服务合同", "全部", "186", "176", "10", "102", "14", "3", "4", "¥46,800", "4.83"],
["博万物", "博万物物业服务合同", "全部", "154", "141", "13", "78", "19", "4", "6", "¥39,200", "4.61"],
["美好花园", "美好花园综合服务合同", "全部", "148", "139", "9", "82", "17", "2", "5", "¥34,600", "4.76"],
["连城花园", "连城花园基础服务合同", "全部", "132", "121", "11", "69", "16", "5", "3", "¥31,900", "4.52"],
["循环科技三期", "循环科技三期运营合同", "全部", "126", "118", "8", "67", "15", "5", "1", "¥29,900", "4.69"]
]
},
contractGuaranteeReport: {
title: "合同保障报表",
eyebrow: "合同保障",
accent: "#ef4444",
accentSoft: "rgba(239, 68, 68, 0.14)",
cards: [
{ label: "有效合同数", value: "42", detail: "覆盖 11 个项目" },
{ label: "有效作业区域", value: "186", detail: "PTU 总数 94" },
{ label: "基础作业时长", value: "9,842h", detail: "合同保障口径" },
{ label: "基础云豆价值", value: "¥168,320", detail: "较上月 +4.2%" }
],
headers: ["项目名称", "合同名称", "合同SPU", "有效作业区域", "有效PTU", "有效作业人数", "基础作业时长", "基础云豆R1价值", "清洁计划数", "设备计划数", "电梯计划数", "主动服务计划数"],
rows: [
["博万物", "博万物物业服务合同", "162", "9", "11", "36", "4,960h", "¥71,512", "27", "5", "0", "1"],
["美好花园", "美好花园综合服务合同", "148", "10", "10", "29", "3,820h", "¥52,360", "18", "4", "1", "2"],
["循环花园一期", "循环花园基础合同", "136", "8", "9", "24", "2,940h", "¥24,180", "16", "3", "1", "2"],
["连城花园", "连城花园服务合同", "118", "7", "8", "21", "2,186h", "¥15,940", "12", "2", "1", "1"],
["循环科技三期", "循环科技三期运营合同", "102", "6", "7", "18", "1,968h", "¥13,420", "10", "2", "0", "1"]
]
},
detailedContractGuaranteeReport: {
title: "合同保障明细报表",
eyebrow: "合同明细",
accent: "#f59e0b",
accentSoft: "rgba(245, 158, 11, 0.14)",
cards: [
{ label: "合同计划SPU", value: "286", detail: "全部计划类型" },
{ label: "计划数量", value: "248", detail: "执行口径汇总" },
{ label: "有效作业人数", value: "96", detail: "多项目覆盖" },
{ label: "基础云豆价值", value: "¥112,480", detail: "较上月 +3.6%" }
],
headers: ["项目名称", "合同名称", "计划类型", "合同计划SPU", "计划数量", "有效作业区域", "有效作业人数", "基础作业时长", "基础云豆R1价值"],
rows: [
["博万物", "博万物物业服务合同", "全部", "42", "35", "9", "36", "4,960h", "¥71,512"],
["美好花园", "美好花园综合服务合同", "全部", "31", "26", "8", "29", "3,820h", "¥52,360"],
["循环花园一期", "循环花园基础合同", "全部", "24", "22", "7", "24", "2,940h", "¥24,180"],
["连城花园", "连城花园服务合同", "全部", "18", "16", "6", "21", "2,186h", "¥15,940"],
["循环科技三期", "循环科技三期运营合同", "全部", "15", "13", "5", "18", "1,968h", "¥13,420"]
]
},
officialAccount: {
title: "公众号拉新报表",
eyebrow: "客户运营",
accent: "#06b6d4",
accentSoft: "rgba(6, 182, 212, 0.14)",
cards: [
{ label: "区间新增数", value: "186", detail: "近 30 天" },
{ label: "已绑住户数", value: "1,428", detail: "绑定率 76.9%" },
{ label: "员工绑定数", value: "214", detail: "员工绑定率 93.1%" },
{ label: "未绑住户数", value: "428", detail: "重点项目待推进" }
],
headers: ["项目名称", "区间新增数", "总房屋数", "总住户数", "已绑住户数", "未绑住户数", "员工应绑定数", "员工绑定数", "员工绑定率"],
rows: [
["江南世家一期", "42", "286", "418", "352", "66", "24", "23", "95.8%"],
["博万物", "38", "254", "396", "301", "95", "28", "25", "89.3%"],
["循环花园一期", "34", "232", "348", "276", "72", "22", "21", "95.5%"],
["美好花园", "29", "218", "315", "248", "67", "19", "18", "94.7%"],
["连城花园", "22", "184", "268", "191", "77", "17", "15", "88.2%"]
]
},
collectionTracking: {
title: "催收跟踪报表",
eyebrow: "收费跟踪",
accent: "#8b5cf6",
accentSoft: "rgba(139, 92, 246, 0.14)",
cards: [
{ label: "催收户数", value: "412", detail: "重点欠费房屋 86 户" },
{ label: "及时拜访数量", value: "186", detail: "拜访及时率 71.5%" },
{ label: "电话 / 微信", value: "268 / 94", detail: "多通道覆盖" },
{ label: "律师函 / 起诉", value: "18 / 6", detail: "高风险项目跟进" }
],
headers: ["项目名称", "催收户数", "欠费房屋数", "普通实收", "转实收", "电话", "微信", "上门", "短信", "律师函", "起诉", "及时拜访数量", "拜访数量", "拜访及时率"],
rows: [
["沈鑫小区", "38", "42", "12", "6", "24", "10", "5", "18", "2", "0", "21", "28", "75.0%"],
["龚浪小区", "64", "87", "16", "8", "36", "14", "9", "22", "3", "1", "31", "44", "70.5%"],
["陈丽娟小区", "29", "24", "8", "3", "18", "6", "2", "11", "1", "0", "12", "17", "70.6%"],
["谷粒多小区", "18", "16", "4", "1", "12", "3", "1", "7", "0", "0", "8", "12", "66.7%"],
["博万物", "96", "118", "26", "12", "58", "21", "15", "33", "5", "2", "44", "63", "69.8%"]
]
},
collectionRate: {
title: "企业收费项报表",
eyebrow: "收费分析",
accent: "#3b82f6",
accentSoft: "rgba(59, 130, 246, 0.14)",
cards: [
{ label: "应收合计", value: "¥1,826,400", detail: "本年 + 往年" },
{ label: "实收合计", value: "¥1,543,800", detail: "综合收缴率 84.5%" },
{ label: "欠费合计", value: "¥282,600", detail: "环比 -5.4%" },
{ label: "户数收缴率", value: "81.3%", detail: "实时校验已开" }
],
headers: ["项目名称", "收费项目", "本月应收", "本年应收", "往年应收", "应收合计", "本月实收", "本年实收", "往年实收", "欠费合计", "实收合计", "综合收缴率", "户数收缴率"],
rows: [
["博万物", "住宅水电", "¥82,600", "¥426,000", "¥18,400", "¥444,400", "¥71,200", "¥382,600", "¥9,800", "¥52,000", "¥392,400", "88.3%", "83.2%"],
["循环花园一期", "物业管理费", "¥96,800", "¥518,000", "¥26,200", "¥544,200", "¥84,900", "¥462,800", "¥13,600", "¥67,800", "¥476,400", "87.5%", "84.7%"],
["美好花园", "停车服务费", "¥68,500", "¥392,000", "¥14,800", "¥406,800", "¥57,200", "¥338,400", "¥9,200", "¥59,200", "¥347,600", "85.4%", "80.9%"],
["连城花园", "综合服务费", "¥54,200", "¥286,000", "¥9,600", "¥295,600", "¥46,300", "¥242,700", "¥6,800", "¥46,100", "¥249,500", "84.4%", "79.8%"],
["江南世家一期", "园区服务费", "¥43,100", "¥204,600", "¥6,800", "¥211,400", "¥36,800", "¥174,900", "¥5,600", "¥30,900", "¥180,500", "85.4%", "78.6%"]
]
},
visitWorkReport: {
title: "拜访工作报表",
eyebrow: "客户拜访",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "应拜访总数", value: "214", detail: "电话 + 上门" },
{ label: "按时拜访", value: "162", detail: "及时率 75.7%" },
{ label: "已回款金额", value: "¥286,400", detail: "回款户数 118" },
{ label: "超时拜访", value: "24", detail: "未拜访 28 户" }
],
headers: ["项目名称", "拜访方式", "来源", "应拜访", "应付金额(元)", "未拜访", "按时拜访", "超时拜访", "未回款(户)", "已回款(户)", "已回款金额(元)"],
rows: [
["博万物", "上门", "拜访智能体", "18", "¥32,600", "3", "12", "3", "9", "9", "¥18,400"],
["博万物", "电话", "分类布置智能体", "36", "¥58,900", "8", "22", "6", "18", "18", "¥41,600"],
["循环花园一期", "电话", "拜访智能体", "42", "¥64,200", "6", "31", "5", "17", "25", "¥58,900"],
["美好花园", "上门", "拜访智能体", "27", "¥41,800", "4", "19", "4", "11", "16", "¥36,700"],
["连城花园", "电话", "分类布置智能体", "24", "¥37,300", "7", "16", "1", "14", "10", "¥24,100"]
]
}
};
const STATIC_PAGE_PRESETS = {
qualification: {
eyebrow: "资质匹配",
title: "人员资质匹配总览",
summary: "补齐人员证书、岗位要求与到期预警,替代空态页。",
accent: "#2563eb",
accentSoft: "rgba(37, 99, 235, 0.14)",
cards: [
{ label: "在岗人员", value: "186", detail: "参与资质校验" },
{ label: "即将到期证书", value: "12", detail: "30 天内到期" },
{ label: "岗位匹配率", value: "94.8%", detail: "高于月目标" },
{ label: "缺证人数", value: "7", detail: "需尽快补齐" }
],
headers: ["项目名称", "姓名", "岗位", "证书名称", "证书到期日", "匹配状态", "整改建议"],
rows: [
["循环花园一期", "郭晓", "环境巡查管家", "消防设施操作员", "2026-05-12", "匹配", "保持跟进"],
["博万物", "何琪", "设备管理员", "特种设备安全管理员", "2026-04-18", "预警", "安排续证"],
["美好花园", "陈谷先", "财务中心负责人", "会计从业资格", "2026-09-30", "匹配", "正常"],
["连城花园", "蒋琦", "java 开发工程师", "信息系统项目管理师", "2026-07-08", "匹配", "正常"],
["江南世家一期", "曾丽娜", "客服巡查管家", "物业管理员", "2026-04-22", "待补齐", "补齐岗位证书"]
]
},
employeeQuitReport: {
eyebrow: "人员流动",
title: "员工离任报告",
summary: "汇总离任人员、离任类型与交接完成情况,替代空态页。",
accent: "#dc2626",
accentSoft: "rgba(220, 38, 38, 0.14)",
cards: [
{ label: "本月离任人数", value: "9", detail: "较上月 -2" },
{ label: "高风险离任", value: "2", detail: "核心岗位需接替" },
{ label: "交接完成率", value: "88.9%", detail: "资料回收正常" },
{ label: "平均在岗月数", value: "16.4", detail: "整体稳定" }
],
headers: ["项目名称", "姓名", "岗位", "离任类型", "离任日期", "交接状态", "备注"],
rows: [
["循环花园一期", "张磊", "客服巡查管家", "主动离职", "2026-04-02", "已完成", "已完成物资交接"],
["博万物", "林婉", "设备管理员", "岗位调整", "2026-03-29", "进行中", "待设备台账确认"],
["美好花园", "刘鑫", "市场专员", "合同到期", "2026-03-27", "已完成", "无异常"],
["连城花园", "周宇", "保洁班长", "主动离职", "2026-03-25", "已完成", "补充访谈记录"],
["江南世家一期", "何丽", "财务专员", "试用期离岗", "2026-03-20", "待处理", "安排补岗"]
]
},
operationLog: {
eyebrow: "系统管理",
title: "操作日志总览",
summary: "补齐关键操作日志、用户行为与风险动作提示,替代空态页。",
accent: "#7c3aed",
accentSoft: "rgba(124, 58, 237, 0.14)",
cards: [
{ label: "今日操作次数", value: "428", detail: "登录用户 36 人" },
{ label: "高风险操作", value: "5", detail: "重点复核" },
{ label: "模块覆盖数", value: "18", detail: "近 24 小时" },
{ label: "最近同步", value: "15:32", detail: "日志服务正常" }
],
headers: ["时间", "用户", "模块", "动作", "IP", "结果", "说明"],
rows: [
["2026-04-04 15:28:32", "循环科技企业管理后台", "角色权限管理", "编辑角色", "113.88.21.6", "成功", "更新项目角色权限"],
["2026-04-04 14:57:10", "郭晓", "人事档案", "导入档案", "113.88.21.9", "成功", "导入 12 条记录"],
["2026-04-04 14:33:48", "陈谷先", "财务数据", "修改指标", "113.88.21.12", "成功", "更新利润口径"],
["2026-04-04 13:49:05", "林婉", "分公司管理", "新增分公司", "113.88.21.7", "待审核", "创建广州运营主体"],
["2026-04-04 11:16:26", "何琪", "设备看板", "导出报表", "113.88.21.15", "成功", "导出设备月报"]
]
},
questionBank: {
eyebrow: "满意度题库",
title: "调查题库总览",
summary: "重建题库统计、题目类型与启用状态,替代空态页。",
accent: "#0ea5e9",
accentSoft: "rgba(14, 165, 233, 0.14)",
cards: [
{ label: "题库总数", value: "128", detail: "启用题目 116" },
{ label: "评分题", value: "36", detail: "权重题 8 个" },
{ label: "低分标签", value: "14", detail: "用于整改跟踪" },
{ label: "最近更新", value: "04-03", detail: "客户运营同步" }
],
headers: ["题目名称", "题型", "适用场景", "权重", "状态", "最近更新", "维护人"],
rows: [
["您对客服响应速度是否满意?", "评分题", "工单服务", "15%", "启用", "2026-04-03", "李佑聪"],
["您对停车秩序是否满意?", "评分题", "车场运营", "10%", "启用", "2026-04-02", "循环科技企业管理后台"],
["保洁服务是否达到预期?", "单选题", "环境管理", "8%", "启用", "2026-03-28", "郭晓"],
["是否愿意继续推荐本项目?", "NPS题", "综合满意度", "20%", "启用", "2026-03-22", "陈谷先"],
["请填写需要改进的问题", "文本题", "整改跟踪", "-", "草稿", "2026-03-18", "何琪"]
]
},
report: {
eyebrow: "满意度分析",
title: "调查报告总览",
summary: "按项目汇总问卷样本、满意度得分与低分预警,替代空态页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "有效样本", value: "2,418", detail: "近 30 天" },
{ label: "综合满意度", value: "4.63", detail: "较上月 +0.08" },
{ label: "低分预警", value: "18", detail: "需跟进整改" },
{ label: "整改完成率", value: "82.4%", detail: "闭环改善中" }
],
headers: ["项目名称", "问卷名称", "样本数", "满意度", "低分数量", "整改任务", "状态"],
rows: [
["循环花园一期", "4 月综合满意度", "486", "4.72", "2", "4", "已发布"],
["博万物", "4 月停车服务满意度", "362", "4.58", "5", "7", "已发布"],
["美好花园", "4 月客服满意度", "318", "4.66", "3", "5", "已发布"],
["连城花园", "4 月环境满意度", "276", "4.49", "6", "8", "整改中"],
["江南世家一期", "4 月综合满意度", "241", "4.53", "2", "3", "已发布"]
]
},
tracking: {
eyebrow: "整改闭环",
title: "整改跟踪总览",
summary: "聚合低分问题、责任人和整改进度,替代空态页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "待整改问题", value: "26", detail: "逾期 4 项" },
{ label: "本周关闭", value: "18", detail: "闭环效率提升" },
{ label: "责任人数量", value: "11", detail: "跨项目协同" },
{ label: "按期完成率", value: "84.6%", detail: "需持续跟进" }
],
headers: ["项目名称", "问题类型", "责任人", "整改截止日", "整改进度", "状态", "备注"],
rows: [
["循环花园一期", "客服响应慢", "郭晓", "2026-04-10", "80%", "处理中", "需补充回访"],
["博万物", "停车秩序差", "何琪", "2026-04-08", "60%", "预警", "晚高峰加派巡查"],
["美好花园", "保洁质量波动", "曾丽娜", "2026-04-09", "100%", "已完成", "已复检通过"],
["连城花园", "绿化修剪不及时", "周宇", "2026-04-12", "40%", "处理中", "待供应商进场"],
["江南世家一期", "工单回访不充分", "陈谷先", "2026-04-11", "90%", "待验证", "等待客户确认"]
]
},
assessment: {
eyebrow: "人才测评",
title: "人才测评总览",
summary: "聚合测评对象、岗位画像与测评分布,替代空态页。",
accent: "#8b5cf6",
accentSoft: "rgba(139, 92, 246, 0.14)",
cards: [
{ label: "测评人数", value: "142", detail: "近 30 天" },
{ label: "综合胜任度", value: "84.2", detail: "百分制" },
{ label: "高潜人才", value: "18", detail: "建议重点培养" },
{ label: "待复评人数", value: "9", detail: "低于岗位基线" }
],
headers: ["项目名称", "姓名", "岗位", "能力模型", "测评得分", "胜任等级", "建议动作"],
rows: [
["循环花园一期", "郭晓", "环境巡查管家", "服务履约模型", "88", "优秀", "纳入主管培养"],
["博万物", "何琪", "设备管理员", "设备运营模型", "82", "良好", "补充故障诊断训练"],
["美好花园", "陈谷先", "财务中心负责人", "经营管理模型", "91", "优秀", "承担跨项目辅导"],
["连城花园", "蒋琦", "java 开发工程师", "技术支持模型", "86", "良好", "推进专项项目实践"],
["江南世家一期", "曾丽娜", "客服巡查管家", "客户服务模型", "74", "关注", "安排二次复评"]
]
}
};
function hydrateCompanyInfoFrame(doc) {
try {
const companyInfo = JSON.parse(storageSeed.companyInfo || "{}");
const userInfo = JSON.parse(storageSeed.userInfo || "{}");
const labelMap = {
"企业全称": companyInfo.fullName || "",
"企业简称": companyInfo.abbreviation || "",
"企业ID": companyInfo.id || "",
"法人代表联系方式": companyInfo.mobile || "",
"企业付款人": companyInfo.payer || "",
"开户行": companyInfo.publicBank || "",
"企业地址": companyInfo.address || "",
"企业微信密钥": companyInfo.weixinSecret || "",
"企业统一社会信用代码": companyInfo.creditCode || "",
"企业法人代表": companyInfo.legalPerson || "",
"企业创建人账号": userInfo.accountName || storageSeed.fromPhone || "",
"企业邮编": companyInfo.postalCode || "",
"付款人联系电话": companyInfo.payerMobile || "",
"银行账号": companyInfo.publicBankNo || "",
"企业微信ID(CorpID)": companyInfo.weixinCorpId || ""
};
doc.querySelectorAll(".el-form-item").forEach((item) => {
const labelEl = item.querySelector(".el-form-item__label");
const inputEl = item.querySelector("input.el-input__inner");
if (!labelEl || !inputEl) {
return;
}
const label = (labelEl.textContent || "").trim();
if (!(label in labelMap)) {
return;
}
const value = String(labelMap[label] ?? "");
inputEl.value = value;
inputEl.setAttribute("value", value);
inputEl.placeholder = value;
inputEl.style.color = "#2c3e50";
inputEl.style.fontWeight = "500";
inputEl.style.backgroundColor = "#fff";
});
const infoImages = [...doc.querySelectorAll(".companyMain .companyInfo img.img")];
if (infoImages[0] && companyInfo.logo) {
infoImages[0].src = companyInfo.logo;
}
if (infoImages[1] && companyInfo.miniCode) {
infoImages[1].src = companyInfo.miniCode;
}
} catch (_error) {
// ignore
}
}
function buildLinePath(values, width, height, padding) {
const max = Math.max(...values);
const min = Math.min(...values);
const span = Math.max(max - min, 1);
return values
.map((value, index) => {
const x = padding + (index * (width - padding * 2)) / Math.max(values.length - 1, 1);
const y = height - padding - ((value - min) / span) * (height - padding * 2);
return `${index === 0 ? "M" : "L"}${x.toFixed(1)} ${y.toFixed(1)}`;
})
.join(" ");
}
function buildAreaPoints(values, width, height, padding) {
const max = Math.max(...values);
const min = Math.min(...values);
const span = Math.max(max - min, 1);
const points = values.map((value, index) => {
const x = padding + (index * (width - padding * 2)) / Math.max(values.length - 1, 1);
const y = height - padding - ((value - min) / span) * (height - padding * 2);
return `${x.toFixed(1)},${y.toFixed(1)}`;
});
points.push(`${width - padding},${height - padding}`, `${padding},${height - padding}`);
return points.join(" ");
}
function buildTrendSvg(seriesList, labels) {
const width = 560;
const height = 220;
const padding = 24;
const allValues = seriesList.flatMap((item) => item.values);
const max = Math.max(...allValues);
const min = Math.min(...allValues);
const span = Math.max(max - min, 1);
const guides = [0, 0.25, 0.5, 0.75, 1].map((ratio) => {
const y = padding + ratio * (height - padding * 2);
const value = Math.round(max - ratio * span);
return `
<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}" stroke="rgba(17,37,63,0.08)" stroke-dasharray="4 6"></line>
<text x="0" y="${y + 4}" fill="#8b9ab3" font-size="11">${value}</text>
`;
});
const xLabels = labels
.map((label, index) => {
const x = padding + (index * (width - padding * 2)) / Math.max(labels.length - 1, 1);
return `<text x="${x}" y="${height}" text-anchor="middle" fill="#8b9ab3" font-size="11">${label}</text>`;
})
.join("");
const paths = seriesList
.map((item, index) => {
const areaOpacity = index === 0 ? 0.14 : 0.08;
return `
<polygon points="${buildAreaPoints(item.values, width, height, padding)}" fill="${item.color}" opacity="${areaOpacity}"></polygon>
<path d="${buildLinePath(item.values, width, height, padding)}" fill="none" stroke="${item.color}" stroke-width="3" stroke-linecap="round"></path>
${item.values
.map((value, valueIndex) => {
const x = padding + (valueIndex * (width - padding * 2)) / Math.max(item.values.length - 1, 1);
const y = height - padding - ((value - min) / span) * (height - padding * 2);
return `<circle cx="${x}" cy="${y}" r="4" fill="#fff" stroke="${item.color}" stroke-width="2"></circle>`;
})
.join("")}
`;
})
.join("");
return `
<svg viewBox="0 0 ${width} ${height + 18}" class="codex-microbrain-chart" preserveAspectRatio="none">
${guides.join("")}
${paths}
${xLabels}
</svg>
`;
}
function renderMicrobrainDashboard(doc, type) {
const preset = MICROBRAIN_PRESETS[type];
if (!preset) {
return;
}
const host =
doc.querySelector(".app-main > div") ||
doc.querySelector(".app-main > section") ||
doc.querySelector(".app-main") ||
doc.body;
if (!host) {
return;
}
if (!doc.getElementById("__codex_microbrain_style__")) {
const style = doc.createElement("style");
style.id = "__codex_microbrain_style__";
style.textContent = `
.waitMain {
display: none !important;
}
#__codex_microbrain_dashboard__ {
margin-top: 18px;
padding: 20px 22px 24px;
border-radius: 20px;
background:
radial-gradient(circle at top right, rgba(45, 118, 255, 0.12), transparent 28%),
linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
border: 1px solid rgba(17, 37, 63, 0.06);
box-shadow: 0 18px 42px rgba(17, 37, 63, 0.08);
}
.codex-microbrain-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 20px;
}
.codex-microbrain-eyebrow {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 999px;
margin-bottom: 10px;
font-size: 12px;
color: #5b6f92;
background: rgba(17, 37, 63, 0.06);
}
.codex-microbrain-head h3 {
margin: 0 0 6px;
font-size: 28px;
color: #12233d;
}
.codex-microbrain-head p {
margin: 0;
max-width: 660px;
color: #6d7d97;
font-size: 14px;
}
.codex-microbrain-badge {
padding: 10px 14px;
border-radius: 14px;
color: #35517d;
font-size: 12px;
background: rgba(255,255,255,0.8);
border: 1px solid rgba(17,37,63,0.08);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.8);
}
.codex-microbrain-metrics {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
margin-bottom: 18px;
}
.codex-microbrain-metric {
padding: 16px 18px;
border-radius: 18px;
background: rgba(255,255,255,0.82);
border: 1px solid rgba(17,37,63,0.06);
box-shadow: 0 12px 28px rgba(17,37,63,0.06);
}
.codex-microbrain-metric span {
display: block;
color: #7d8ca5;
font-size: 13px;
}
.codex-microbrain-metric strong {
display: block;
margin: 8px 0 6px;
color: #12233d;
font-size: 28px;
line-height: 1.1;
}
.codex-microbrain-metric small {
color: #4e6b98;
font-size: 12px;
}
.codex-microbrain-layout {
display: grid;
grid-template-columns: minmax(0, 1.6fr) minmax(320px, 0.95fr);
gap: 16px;
}
.codex-microbrain-panel {
padding: 18px 18px 16px;
border-radius: 18px;
background: rgba(255,255,255,0.92);
border: 1px solid rgba(17,37,63,0.06);
}
.codex-microbrain-panel h4 {
margin: 0 0 14px;
color: #152742;
font-size: 16px;
}
.codex-microbrain-legend {
display: flex;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.codex-microbrain-legend span {
display: inline-flex;
align-items: center;
gap: 6px;
color: #71829d;
font-size: 12px;
}
.codex-microbrain-dot {
width: 9px;
height: 9px;
border-radius: 50%;
}
.codex-microbrain-chart {
width: 100%;
height: 238px;
}
.codex-microbrain-segments {
display: grid;
gap: 12px;
}
.codex-microbrain-segment {
display: grid;
grid-template-columns: 88px 1fr 48px;
align-items: center;
gap: 10px;
color: #4d607f;
font-size: 13px;
}
.codex-microbrain-track {
overflow: hidden;
height: 10px;
border-radius: 999px;
background: #eef3fb;
}
.codex-microbrain-fill {
height: 100%;
border-radius: inherit;
}
.codex-microbrain-table table {
width: 100%;
border-collapse: collapse;
}
.codex-microbrain-table th,
.codex-microbrain-table td {
padding: 12px 8px;
border-bottom: 1px solid rgba(17,37,63,0.06);
text-align: left;
color: #3f5477;
font-size: 13px;
}
.codex-microbrain-table th {
color: #8b9ab3;
font-size: 12px;
font-weight: 500;
}
.codex-microbrain-alerts {
display: grid;
gap: 10px;
}
.codex-microbrain-alert {
padding: 14px 14px 13px;
border-radius: 14px;
background: #f8fbff;
border: 1px solid rgba(17,37,63,0.06);
}
.codex-microbrain-alert b {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 6px;
color: #152742;
font-size: 14px;
}
.codex-microbrain-alert p {
margin: 0;
color: #6d7d97;
font-size: 12px;
line-height: 1.6;
}
.codex-microbrain-level {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
}
.codex-microbrain-level-正常 {
color: #067647;
background: rgba(6, 118, 71, 0.12);
}
.codex-microbrain-level-关注 {
color: #975a16;
background: rgba(255, 183, 77, 0.18);
}
.codex-microbrain-level-预警 {
color: #c92a2a;
background: rgba(255, 107, 107, 0.16);
}
@media (max-width: 1080px) {
.codex-microbrain-metrics {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.codex-microbrain-layout {
grid-template-columns: 1fr;
}
}
`;
doc.head.appendChild(style);
}
const containerId = "__codex_microbrain_dashboard__";
let container = doc.getElementById(containerId);
if (!container) {
container = doc.createElement("div");
container.id = containerId;
host.appendChild(container);
}
container.dataset.type = type;
const legend = preset.trendSeries
.map(
(item) => `
<span><i class="codex-microbrain-dot" style="background:${item.color}"></i>${item.label}</span>
`
)
.join("");
const segments = preset.segments
.map(
(item) => `
<div class="codex-microbrain-segment">
<span>${item.label}</span>
<div class="codex-microbrain-track"><div class="codex-microbrain-fill" style="width:${item.value}; background:${item.tone}"></div></div>
<strong>${item.value}</strong>
</div>
`
)
.join("");
const metrics = preset.metrics
.map(
(item) => `
<div class="codex-microbrain-metric" style="box-shadow: 0 12px 28px ${preset.accentSoft}">
<span>${item.label}</span>
<strong>${item.value}</strong>
<small>${item.delta}</small>
</div>
`
)
.join("");
const rows = preset.rankingRows
.map(
(row, index) => `
<tr>
<td>${index + 1}</td>
<td>${row[0]}</td>
<td>${row[1]}</td>
<td>${row[2]}</td>
</tr>
`
)
.join("");
const alerts = preset.alerts
.map(
(item) => `
<div class="codex-microbrain-alert">
<b>
<span>${item.title}</span>
<span class="codex-microbrain-level codex-microbrain-level-${item.level}">${item.level}</span>
</b>
<p>${item.detail}</p>
</div>
`
)
.join("");
container.innerHTML = `
<div class="codex-microbrain-head">
<div>
<span class="codex-microbrain-eyebrow">${preset.eyebrow}</span>
<h3>${preset.headline}</h3>
<p>${preset.summary}</p>
</div>
<div class="codex-microbrain-badge">镜像重建 · ${preset.headline} · 近 30 天静态样本</div>
</div>
<div class="codex-microbrain-metrics">${metrics}</div>
<div class="codex-microbrain-layout">
<div class="codex-microbrain-panel">
<h4>趋势概览</h4>
<div class="codex-microbrain-legend">${legend}</div>
${buildTrendSvg(preset.trendSeries, preset.trendLabels)}
</div>
<div class="codex-microbrain-panel">
<h4>结构占比</h4>
<div class="codex-microbrain-segments">${segments}</div>
</div>
<div class="codex-microbrain-panel codex-microbrain-table">
<h4>${preset.rankingTitle}</h4>
<table>
<thead>
<tr><th>#</th><th>项目</th><th>指标值</th><th>完成度</th></tr>
</thead>
<tbody>${rows}</tbody>
</table>
</div>
<div class="codex-microbrain-panel">
<h4>${preset.alertsTitle}</h4>
<div class="codex-microbrain-alerts">${alerts}</div>
</div>
</div>
`;
}
function ensureCloudReportStyle(doc) {
if (doc.getElementById("__codex_cloud_report_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_cloud_report_style__";
style.textContent = `
.waitMain {
display: none !important;
}
.codex-cloud-report {
margin: 0 2% 18px;
padding: 18px 20px 20px;
border-radius: 18px;
background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
border: 1px solid rgba(17, 37, 63, 0.06);
box-shadow: 0 18px 42px rgba(17, 37, 63, 0.08);
}
.codex-cloud-report-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 16px;
}
.codex-cloud-report-eyebrow {
display: inline-flex;
align-items: center;
padding: 4px 10px;
margin-bottom: 8px;
border-radius: 999px;
background: rgba(17,37,63,0.06);
color: #6d7d97;
font-size: 12px;
}
.codex-cloud-report-head h4 {
margin: 0 0 4px;
color: #152742;
font-size: 24px;
}
.codex-cloud-report-head p {
margin: 0;
color: #6d7d97;
font-size: 13px;
}
.codex-cloud-report-badge {
padding: 9px 12px;
border-radius: 14px;
color: #496385;
font-size: 12px;
background: rgba(255,255,255,0.9);
border: 1px solid rgba(17,37,63,0.08);
}
.codex-cloud-report-cards {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.codex-cloud-report-card {
padding: 14px 16px;
border-radius: 16px;
background: rgba(255,255,255,0.92);
border: 1px solid rgba(17,37,63,0.05);
}
.codex-cloud-report-card span {
display: block;
color: #7d8ca5;
font-size: 12px;
}
.codex-cloud-report-card strong {
display: block;
margin: 8px 0 6px;
color: #152742;
font-size: 28px;
line-height: 1.05;
}
.codex-cloud-report-card small {
color: #4f6a94;
font-size: 12px;
}
.codex-cloud-report-table {
overflow: auto;
border-radius: 14px;
border: 1px solid rgba(17,37,63,0.06);
background: #fff;
}
.codex-cloud-report-table table {
width: 100%;
min-width: 1180px;
border-collapse: collapse;
}
.codex-cloud-report-table th,
.codex-cloud-report-table td {
padding: 12px 10px;
border-bottom: 1px solid rgba(17,37,63,0.06);
text-align: center;
color: #3f5477;
font-size: 13px;
white-space: nowrap;
}
.codex-cloud-report-table th {
position: sticky;
top: 0;
z-index: 1;
background: #f9fbff;
color: #7d8ca5;
font-size: 12px;
font-weight: 600;
}
.codex-cloud-report-table td:first-child,
.codex-cloud-report-table th:first-child {
position: sticky;
left: 0;
z-index: 1;
background: #fff;
}
.codex-cloud-report-table th:first-child {
background: #f9fbff;
}
.codex-cloud-report-table td.project {
text-align: left;
color: #152742;
font-weight: 600;
}
.codex-cloud-report-rate {
color: #067647;
font-weight: 600;
}
@media (max-width: 1080px) {
.codex-cloud-report-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
`;
doc.head.appendChild(style);
}
function createCloudReportSection(doc, type) {
const preset = CLOUD_REPORT_PRESETS[type];
if (!preset) {
return null;
}
ensureCloudReportStyle(doc);
const section = doc.createElement("section");
section.className = "codex-cloud-report";
section.id = `__codex_cloud_report_${type}__`;
const cards = preset.cards
.map(
(item) => `
<div class="codex-cloud-report-card" style="box-shadow: 0 12px 24px ${preset.accentSoft}">
<span>${item.label}</span>
<strong>${item.value}</strong>
<small>${item.detail}</small>
</div>
`
)
.join("");
const headers = preset.headers.map((header) => `<th>${header}</th>`).join("");
const rows = preset.rows
.map((row) => {
const cells = row
.map((value, index) => {
const className =
index === 0 ? "project" : /%$/.test(value) ? "codex-cloud-report-rate" : "";
return `<td class="${className}">${value}</td>`;
})
.join("");
return `<tr>${cells}</tr>`;
})
.join("");
section.innerHTML = `
<div class="codex-cloud-report-head">
<div>
<span class="codex-cloud-report-eyebrow">${preset.eyebrow}</span>
<h4>${preset.title || (type === "propertyFeeReport" ? "物业费收缴明细表" : type === "parkingLotReport" ? "车场列表" : type === "planTaskReport" ? "计划工单执行总览" : "经营报表")}</h4>
<p>基于镜像静态样本重建报表数据,用于替代原始页面中的空值或缺失列表。</p>
</div>
<div class="codex-cloud-report-badge">镜像重建 · 静态样本数据</div>
</div>
<div class="codex-cloud-report-cards">${cards}</div>
<div class="codex-cloud-report-table">
<table>
<thead><tr>${headers}</tr></thead>
<tbody>${rows}</tbody>
</table>
</div>
`;
return section;
}
function renderCloudDataReport(doc, type) {
const preset = CLOUD_REPORT_PRESETS[type];
if (!preset) {
return;
}
const host = doc.querySelector(".contentStys") || doc.querySelector(".app-container") || doc.querySelector(".app-main");
if (!host) {
return;
}
host.querySelectorAll(".el-table, .el-pagination").forEach((node) => {
node.style.display = "none";
});
const planTitle = host.querySelector(".planLst");
if (planTitle) {
planTitle.style.marginBottom = "12px";
}
const existing = doc.getElementById(`__codex_cloud_report_${type}__`);
if (existing) {
existing.remove();
}
const section = createCloudReportSection(doc, type);
if (!section) {
return;
}
if (planTitle && planTitle.parentNode) {
planTitle.parentNode.insertBefore(section, planTitle.nextSibling);
return;
}
host.appendChild(section);
}
function ensureStaticPageStyle(doc) {
if (doc.getElementById("__codex_static_page_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_static_page_style__";
style.textContent = `
.codex-static-page {
margin: 20px;
padding: 18px 20px 20px;
border-radius: 18px;
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
border: 1px solid rgba(17,37,63,0.06);
box-shadow: 0 18px 42px rgba(17,37,63,0.08);
}
.codex-static-page-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 16px;
}
.codex-static-page-eyebrow {
display: inline-flex;
align-items: center;
padding: 4px 10px;
margin-bottom: 8px;
border-radius: 999px;
background: rgba(17,37,63,0.06);
color: #6d7d97;
font-size: 12px;
}
.codex-static-page-head h4 {
margin: 0 0 4px;
color: #152742;
font-size: 24px;
}
.codex-static-page-head p {
margin: 0;
color: #6d7d97;
font-size: 13px;
}
.codex-static-page-badge {
padding: 9px 12px;
border-radius: 14px;
color: #496385;
font-size: 12px;
background: rgba(255,255,255,0.9);
border: 1px solid rgba(17,37,63,0.08);
}
.codex-static-page-cards {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.codex-static-page-card {
padding: 14px 16px;
border-radius: 16px;
background: rgba(255,255,255,0.92);
border: 1px solid rgba(17,37,63,0.05);
}
.codex-static-page-card span {
display: block;
color: #7d8ca5;
font-size: 12px;
}
.codex-static-page-card strong {
display: block;
margin: 8px 0 6px;
color: #152742;
font-size: 28px;
line-height: 1.05;
}
.codex-static-page-card small {
color: #4f6a94;
font-size: 12px;
}
.codex-static-page-table {
overflow: auto;
border-radius: 14px;
border: 1px solid rgba(17,37,63,0.06);
background: #fff;
}
.codex-static-page-table table {
width: 100%;
min-width: 920px;
border-collapse: collapse;
}
.codex-static-page-table th,
.codex-static-page-table td {
padding: 12px 10px;
border-bottom: 1px solid rgba(17,37,63,0.06);
text-align: center;
color: #3f5477;
font-size: 13px;
white-space: nowrap;
}
.codex-static-page-table th {
background: #f9fbff;
color: #7d8ca5;
font-size: 12px;
font-weight: 600;
}
.codex-static-page-table td:first-child,
.codex-static-page-table th:first-child {
text-align: left;
}
@media (max-width: 1080px) {
.codex-static-page-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
`;
doc.head.appendChild(style);
}
function renderStaticManagementPage(doc, type) {
const preset = STATIC_PAGE_PRESETS[type];
if (!preset) {
return;
}
ensureStaticPageStyle(doc);
doc.querySelectorAll(".waitMain").forEach((node) => {
node.style.display = "none";
});
const host =
doc.querySelector(".app-main > div") ||
doc.querySelector(".app-main > section") ||
doc.querySelector(".app-main") ||
doc.body;
if (!host) {
return;
}
const existing = doc.getElementById(`__codex_static_page_${type}__`);
if (existing) {
existing.remove();
}
const section = doc.createElement("section");
section.id = `__codex_static_page_${type}__`;
section.className = "codex-static-page";
const cards = preset.cards
.map(
(item) => `
<div class="codex-static-page-card" style="box-shadow: 0 12px 24px ${preset.accentSoft}">
<span>${item.label}</span>
<strong>${item.value}</strong>
<small>${item.detail}</small>
</div>
`
)
.join("");
const headers = preset.headers.map((header) => `<th>${header}</th>`).join("");
const rows = preset.rows
.map((row) => `<tr>${row.map((value) => `<td>${value}</td>`).join("")}</tr>`)
.join("");
section.innerHTML = `
<div class="codex-static-page-head">
<div>
<span class="codex-static-page-eyebrow">${preset.eyebrow}</span>
<h4>${preset.title}</h4>
<p>${preset.summary}</p>
</div>
<div class="codex-static-page-badge">镜像重建 · 静态样本数据</div>
</div>
<div class="codex-static-page-cards">${cards}</div>
<div class="codex-static-page-table">
<table>
<thead><tr>${headers}</tr></thead>
<tbody>${rows}</tbody>
</table>
</div>
`;
host.appendChild(section);
}
function ensureEtmsEditableStyle(doc) {
if (doc.getElementById("__codex_etms_editable_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_etms_editable_style__";
style.textContent = `
.codex-editable-shell {
position: relative;
}
.codex-editable-toolbar {
position: sticky;
top: 12px;
z-index: 20;
display: flex;
justify-content: flex-end;
gap: 8px;
margin: 0 0 12px;
}
.codex-editable-toolbar button {
border: 1px solid rgba(17,37,63,0.08);
border-radius: 999px;
padding: 6px 12px;
background: rgba(255,255,255,0.92);
color: #35507a;
font-size: 12px;
cursor: pointer;
}
.codex-editable-toolbar button.codex-primary {
background: #2d76ff;
border-color: transparent;
color: #fff;
}
.codex-editable-toolbar button.codex-danger {
color: #d14343;
}
.codex-editable-toolbar button:disabled {
opacity: 0.48;
cursor: not-allowed;
}
.codex-editable-body[contenteditable="true"] {
outline: 2px dashed rgba(45,118,255,0.35);
outline-offset: 8px;
}
.codex-editable-body[contenteditable="true"] a,
.codex-editable-body[contenteditable="true"] button {
pointer-events: none;
}
.codex-editable-hint {
margin-right: auto;
align-self: center;
color: #6d7d97;
font-size: 12px;
}
`;
doc.head.appendChild(style);
}
function createEtmsEditableKey(doc, section, index) {
const pathname = doc.defaultView?.location?.pathname || "unknown";
const sectionId = section.id || section.dataset.type || section.className || `section-${index}`;
return `__codex_etms_edit__${pathname}::${sectionId}`;
}
function wrapEtmsEditableBody(section) {
let body = section.querySelector(":scope > .codex-editable-body");
if (body) {
return body;
}
body = section.ownerDocument.createElement("div");
body.className = "codex-editable-body";
[...section.childNodes].forEach((node) => {
body.appendChild(node);
});
section.appendChild(body);
return body;
}
function setEtmsEditableState(doc, section, editing) {
const body = section.querySelector(":scope > .codex-editable-body");
const toolbar = section.querySelector(":scope > .codex-editable-toolbar");
if (!body || !toolbar) {
return;
}
body.contentEditable = editing ? "true" : "false";
body.spellcheck = false;
section.dataset.codexEditing = editing ? "1" : "0";
doc.body.dataset.codexMirrorEditing = editing ? "1" : "0";
toolbar.querySelector("[data-action='edit']").disabled = editing;
toolbar.querySelector("[data-action='save']").disabled = !editing;
}
function bindEtmsEditableSection(doc, section, index) {
ensureEtmsEditableStyle(doc);
section.classList.add("codex-editable-shell");
const body = wrapEtmsEditableBody(section);
const storageKey = createEtmsEditableKey(doc, section, index);
const win = doc.defaultView;
const saved = win?.localStorage.getItem(storageKey);
if (!section.dataset.codexDefaultHtml) {
section.dataset.codexDefaultHtml = body.innerHTML;
}
if (saved && body.innerHTML !== saved && section.dataset.codexEditing !== "1") {
body.innerHTML = saved;
}
let toolbar = section.querySelector(":scope > .codex-editable-toolbar");
if (!toolbar) {
toolbar = doc.createElement("div");
toolbar.className = "codex-editable-toolbar";
toolbar.innerHTML = `
<span class="codex-editable-hint">本地编辑</span>
<button type="button" data-action="edit" class="codex-primary">编辑</button>
<button type="button" data-action="save" disabled>保存</button>
<button type="button" data-action="reset" class="codex-danger">重置</button>
`;
section.insertBefore(toolbar, body);
toolbar.addEventListener("click", (event) => {
const button = event.target.closest("button[data-action]");
if (!button) {
return;
}
const action = button.dataset.action;
if (action === "edit") {
setEtmsEditableState(doc, section, true);
body.focus();
return;
}
if (action === "save") {
win?.localStorage.setItem(storageKey, body.innerHTML);
setEtmsEditableState(doc, section, false);
return;
}
if (action === "reset") {
win?.localStorage.removeItem(storageKey);
body.innerHTML = section.dataset.codexDefaultHtml || body.innerHTML;
setEtmsEditableState(doc, section, false);
}
});
}
setEtmsEditableState(doc, section, false);
}
function installEtmsEditableSections(doc) {
const sections = [
...doc.querySelectorAll("#__codex_microbrain_dashboard__, [id^='__codex_cloud_report_'], [id^='__codex_static_page_'], #__codex_finance_summary__")
];
const seen = new Set();
sections.forEach((section, index) => {
if (!section || seen.has(section)) {
return;
}
seen.add(section);
bindEtmsEditableSection(doc, section, index);
});
}
function ensureEtmsListEditStyle(doc) {
if (doc.getElementById("__codex_etms_list_edit_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_etms_list_edit_style__";
style.textContent = `
.codex-listedit-mask {
position: fixed;
inset: 0;
z-index: 99999;
background: rgba(8, 15, 28, 0.42);
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.codex-listedit-modal {
width: min(760px, 100%);
max-height: min(760px, calc(100vh - 48px));
overflow: auto;
border-radius: 18px;
background: #fff;
box-shadow: 0 24px 54px rgba(17, 37, 63, 0.18);
padding: 20px 22px;
}
.codex-listedit-modal h4 {
margin: 0 0 16px;
color: #152742;
font-size: 20px;
}
.codex-listedit-summary {
margin: 0 0 16px;
color: #6d7d97;
font-size: 12px;
line-height: 1.7;
}
.codex-listedit-form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.codex-listedit-form label {
display: flex;
flex-direction: column;
gap: 6px;
color: #6d7d97;
font-size: 12px;
}
.codex-listedit-form label.full {
grid-column: 1 / -1;
}
.codex-listedit-form input,
.codex-listedit-form textarea {
border: 1px solid rgba(17, 37, 63, 0.12);
border-radius: 10px;
padding: 10px 12px;
color: #152742;
font-size: 13px;
background: #fff;
}
.codex-listedit-form textarea {
min-height: 96px;
resize: vertical;
}
.codex-listedit-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 18px;
}
.codex-listedit-actions button {
border: 1px solid rgba(17, 37, 63, 0.08);
border-radius: 999px;
padding: 6px 12px;
background: #fff;
color: #35507a;
font-size: 12px;
cursor: pointer;
}
.codex-listedit-actions button.codex-primary {
background: #2d76ff;
border-color: transparent;
color: #fff;
}
.codex-listedit-actions button.codex-danger {
color: #d14343;
}
.codex-listedit-fallback-panel {
margin: 16px 0;
padding: 14px 16px;
border-radius: 14px;
border: 1px solid rgba(17, 37, 63, 0.06);
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
box-shadow: 0 12px 28px rgba(17, 37, 63, 0.08);
}
.codex-listedit-fallback-panel h5 {
margin: 0 0 10px;
color: #152742;
font-size: 15px;
}
.codex-listedit-fallback-panel p {
margin: 0 0 12px;
color: #6d7d97;
font-size: 12px;
line-height: 1.7;
}
.codex-listedit-fallback-list {
display: grid;
gap: 8px;
}
.codex-listedit-fallback-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border-radius: 10px;
background: rgba(247, 250, 255, 0.92);
border: 1px solid rgba(17, 37, 63, 0.06);
}
.codex-listedit-fallback-item span {
flex: 1;
color: #35507a;
font-size: 12px;
line-height: 1.6;
}
`;
doc.head.appendChild(style);
}
function createEtmsListEditStorageKey(doc) {
const pathname = doc.defaultView?.location?.pathname || "unknown";
return `__codex_etms_list_edit__${pathname}`;
}
function readEtmsListEditState(doc) {
try {
return JSON.parse(doc.defaultView?.localStorage.getItem(createEtmsListEditStorageKey(doc)) || "{}");
} catch (_error) {
return {};
}
}
function writeEtmsListEditState(doc, state) {
try {
doc.defaultView?.localStorage.setItem(createEtmsListEditStorageKey(doc), JSON.stringify(state));
} catch (_error) {
// ignore
}
}
function getEtmsVisibleText(node) {
return (node?.textContent || "").replace(/\s+/g, " ").trim();
}
function isEtmsElementVisible(node) {
if (!node) {
return false;
}
const style = node.ownerDocument?.defaultView?.getComputedStyle(node);
const rect = node.getBoundingClientRect?.();
if (!style || !rect) {
return false;
}
return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0;
}
function getEtmsListEditTableKey(doc, table) {
const tableRoot = table.closest(".el-table");
if (tableRoot) {
const roots = [...doc.querySelectorAll(".el-table")];
return `table-root-${roots.indexOf(tableRoot)}`;
}
const tables = [...doc.querySelectorAll("table")];
return `table-${tables.indexOf(table)}`;
}
function isEtmsActionOnlyText(text) {
return /^(编辑|删除|查看|复制角色|复制|下载|导出|导入|开具发票|手动开收据|下载收据|生成票据|催费|拆分|禁用|启用)(\s+(编辑|删除|查看|复制角色|复制|下载|导出|导入|开具发票|手动开收据|下载收据|生成票据|催费|拆分|禁用|启用))*$/.test(text);
}
function countEtmsVisibleCells(row) {
return [...row.querySelectorAll(":scope > td")].filter((cell) => isEtmsElementVisible(cell)).length;
}
function resolveEtmsPrimaryRow(row) {
const tableRoot = row?.closest(".el-table");
const rowIndex = [...(row?.parentElement?.children || [])].indexOf(row);
if (!tableRoot || rowIndex < 0) {
return row;
}
const candidates = [];
[...tableRoot.querySelectorAll("tbody")].forEach((tbody) => {
const candidate = tbody.children[rowIndex];
if (candidate && !candidates.includes(candidate)) {
candidates.push(candidate);
}
});
candidates.sort((left, right) => countEtmsVisibleCells(right) - countEtmsVisibleCells(left));
return candidates[0] || row;
}
function extractEtmsListEditHeaders(table, cellCount) {
const headers = [...table.querySelectorAll("thead th")]
.map((cell) => getEtmsVisibleText(cell))
.filter(Boolean);
if (!headers.length) {
return Array.from({ length: cellCount }, (_value, index) => `字段${index + 1}`);
}
return headers;
}
function extractEtmsListEditContext(doc, row) {
const table = row?.closest("table");
if (!table) {
return null;
}
const rows = [...(row.parentElement?.children || [])];
const rowIndex = rows.indexOf(row);
const cells = [...row.querySelectorAll(":scope > td")];
const headers = extractEtmsListEditHeaders(table, cells.length);
const fields = [];
cells.forEach((cell, index) => {
const header = headers[index] || `字段${index + 1}`;
const value = getEtmsVisibleText(cell);
if (!value || /操作/.test(header) || isEtmsActionOnlyText(value)) {
return;
}
fields.push({ header, value });
});
if (!fields.length) {
return null;
}
return {
tableKey: getEtmsListEditTableKey(doc, table),
rowKey: `row-${rowIndex}`,
fields
};
}
function resolveEtmsListEditContext(doc, trigger) {
const sourceRow = trigger?.closest("tr");
if (!sourceRow) {
return null;
}
const direct = extractEtmsListEditContext(doc, sourceRow);
if (direct) {
return {
row: sourceRow,
context: direct
};
}
const rowIndex = [...(sourceRow.parentElement?.children || [])].indexOf(sourceRow);
const tableRoot = sourceRow.closest(".el-table");
if (!tableRoot || rowIndex < 0) {
return null;
}
const candidateSelectors = [
".el-table__body-wrapper tbody tr",
".el-table__fixed-body-wrapper tbody tr",
".el-table__fixed-right .el-table__fixed-body-wrapper tbody tr"
];
for (const selector of candidateSelectors) {
const rows = [...tableRoot.querySelectorAll(selector)];
const row = rows[rowIndex];
if (!row || row === sourceRow) {
continue;
}
const context = extractEtmsListEditContext(doc, row);
if (context) {
return {
row,
context
};
}
}
return null;
}
function setEtmsListEditCellValue(cell, value) {
const target =
cell.querySelector(":scope > .cell") ||
(cell.children.length === 1 ? cell.children[0] : cell);
if (!target) {
return;
}
target.textContent = value;
}
function applyEtmsListEditValues(row, fields) {
const table = row?.closest("table");
if (!table) {
return;
}
const cells = [...row.querySelectorAll(":scope > td")];
const headers = extractEtmsListEditHeaders(table, cells.length);
let fieldIndex = 0;
cells.forEach((cell, index) => {
const header = headers[index] || `字段${index + 1}`;
const value = getEtmsVisibleText(cell);
if (!value || /操作/.test(header) || isEtmsActionOnlyText(value)) {
return;
}
const nextField = fields[fieldIndex];
if (!nextField) {
return;
}
setEtmsListEditCellValue(cell, nextField.value);
fieldIndex += 1;
});
}
function applyEtmsListEditStateToTable(doc, tableKey, rowKey, fields) {
const rowIndex = Number(String(rowKey || "").replace("row-", ""));
if (Number.isNaN(rowIndex)) {
return;
}
if (String(tableKey).startsWith("table-root-")) {
const rootIndex = Number(String(tableKey).replace("table-root-", ""));
const tableRoot = [...doc.querySelectorAll(".el-table")][rootIndex];
if (!tableRoot) {
return;
}
const seen = new Set();
[
".el-table__body-wrapper tbody tr",
".el-table__fixed-body-wrapper tbody tr",
".el-table__fixed-right .el-table__fixed-body-wrapper tbody tr"
].forEach((selector) => {
const row = [...tableRoot.querySelectorAll(selector)][rowIndex];
if (!row || seen.has(row)) {
return;
}
seen.add(row);
applyEtmsListEditValues(row, fields);
});
return;
}
const tableIndex = Number(String(tableKey || "").replace("table-", ""));
const table = [...doc.querySelectorAll("table")][tableIndex];
const row = table ? [...table.querySelectorAll("tbody tr")][rowIndex] : null;
if (row) {
applyEtmsListEditValues(row, fields);
}
}
function hydrateEtmsListEditRows(doc) {
const state = readEtmsListEditState(doc);
Object.entries(state).forEach(([tableKey, tableState]) => {
Object.entries(tableState || {}).forEach(([rowKey, rowState]) => {
if (rowState?.fields) {
applyEtmsListEditStateToTable(doc, tableKey, rowKey, rowState.fields);
}
});
});
}
function normalizeEtmsListEditTriggers(doc) {
[...doc.querySelectorAll("button.el-button, .textBtn, a, .el-button, .el-dropdown-menu__item")].forEach((node) => {
const text = getEtmsVisibleText(node);
if (!/编辑/.test(text) || !node.closest("tr")) {
return;
}
if ("disabled" in node) {
node.disabled = false;
}
node.removeAttribute?.("disabled");
node.classList?.remove("is-disabled");
node.closest(".is-disabled")?.classList.remove("is-disabled");
node.style.cursor = "pointer";
node.style.opacity = "1";
});
}
function injectEtmsInlineEditButtons(doc) {
[...doc.querySelectorAll("tbody tr")].forEach((row) => {
if (row.closest("[id^='__codex_'], .codex-budget-panel, .codex-editable-shell")) {
return;
}
const actionNodes = [...row.querySelectorAll("button, .textBtn, a, .el-button, .el-dropdown-menu__item")];
const hasVisibleEdit = actionNodes.some((node) => /编辑/.test(getEtmsVisibleText(node)) && isEtmsElementVisible(node));
if (hasVisibleEdit) {
return;
}
const hasHiddenEdit = actionNodes.some((node) => /编辑/.test(getEtmsVisibleText(node)));
if (!hasHiddenEdit) {
return;
}
const hostRow = resolveEtmsPrimaryRow(row);
const cells = [...hostRow.querySelectorAll(":scope > td")];
const hostCell =
cells.find((cell) => {
const text = getEtmsVisibleText(cell);
return isEtmsElementVisible(cell) && text && !isEtmsActionOnlyText(text) && text !== "#";
}) || cells[0];
if (!hostCell) {
return;
}
const host = hostCell.querySelector(":scope > .cell") || hostCell;
if (host.querySelector(":scope > .codex-inline-edit-trigger")) {
return;
}
const button = doc.createElement("button");
button.type = "button";
button.className = "el-button el-button--text el-button--mini codex-inline-edit-trigger";
button.textContent = "编辑";
button.style.marginLeft = "8px";
host.appendChild(button);
});
}
function collectEtmsHiddenEditEntries(doc) {
const entries = [];
const seen = new Set();
const hasVisibleEdit = [...doc.querySelectorAll("button, .textBtn, a, .el-button, .el-dropdown-menu__item")].some(
(node) => /编辑/.test(getEtmsVisibleText(node)) && isEtmsElementVisible(node)
);
if (hasVisibleEdit) {
return entries;
}
[...doc.querySelectorAll("tbody tr")].forEach((row) => {
if (row.closest("[id^='__codex_'], .codex-budget-panel, .codex-editable-shell")) {
return;
}
const actionNodes = [...row.querySelectorAll("button, .textBtn, a, .el-button, .el-dropdown-menu__item")];
const hasHiddenEdit = actionNodes.some((node) => /编辑/.test(getEtmsVisibleText(node)));
if (!hasHiddenEdit) {
return;
}
const primaryRow = resolveEtmsPrimaryRow(row);
const context = extractEtmsListEditContext(doc, primaryRow);
if (!context) {
return;
}
const key = `${context.tableKey}::${context.rowKey}`;
if (seen.has(key)) {
return;
}
seen.add(key);
entries.push({
row: primaryRow,
context,
summary: context.fields
.slice(0, 3)
.map((field) => `${field.header}${field.value}`)
.join(" / ")
});
});
return entries;
}
function renderEtmsHiddenEditPanel(doc) {
const entries = collectEtmsHiddenEditEntries(doc);
const existing = doc.getElementById("__codex_etms_hidden_edit_panel__");
if (!entries.length) {
existing?.remove();
return;
}
const panel = existing || doc.createElement("section");
panel.id = "__codex_etms_hidden_edit_panel__";
panel.className = "codex-listedit-fallback-panel";
panel.innerHTML = `
<h5>本地编辑入口</h5>
<p>当前页面默认视图没有直接露出原生“编辑”按钮,以下入口由镜像补出,点击后会打开本地列表编辑弹窗。</p>
<div class="codex-listedit-fallback-list">
${entries
.slice(0, 12)
.map(
(entry, index) => `
<div class="codex-listedit-fallback-item">
<span>${entry.summary}</span>
<button type="button" class="el-button el-button--text el-button--mini" data-hidden-edit-index="${index}">编辑</button>
</div>
`
)
.join("")}
</div>
`;
if (!existing) {
const host = doc.querySelector(".app-main > div") || doc.querySelector(".app-main") || doc.body;
host.insertBefore(panel, host.firstChild);
}
panel.querySelectorAll("[data-hidden-edit-index]").forEach((button) => {
button.addEventListener("click", () => {
const entry = entries[Number(button.getAttribute("data-hidden-edit-index"))];
if (!entry) {
return;
}
openEtmsListEditModal(doc, resolveEtmsPrimaryRow(entry.row), entry.context);
});
});
}
function closeEtmsListEditModal(doc) {
doc.getElementById("__codex_etms_list_edit_mask__")?.remove();
}
function openEtmsListEditModal(doc, row, context) {
ensureEtmsListEditStyle(doc);
closeEtmsListEditModal(doc);
const mask = doc.createElement("div");
mask.id = "__codex_etms_list_edit_mask__";
mask.className = "codex-listedit-mask";
const fieldsHtml = context.fields
.map(
(field, index) => `
<label class="${String(field.value || "").length > 32 ? "full" : ""}">
<span>${field.header}</span>
<input data-field-index="${index}" value="${field.value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\"/g, "&quot;")}">
</label>
`
)
.join("");
mask.innerHTML = `
<div class="codex-listedit-modal">
<h4>本地编辑列表行</h4>
<p class="codex-listedit-summary">当前编辑的是镜像中的列表行内容。保存后会写入浏览器本地存储,并在当前页面刷新后继续生效。</p>
<div class="codex-listedit-form">${fieldsHtml}</div>
<div class="codex-listedit-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">保存</button>
</div>
</div>
`;
mask.addEventListener("click", (event) => {
if (event.target === mask) {
closeEtmsListEditModal(doc);
}
});
doc.body.appendChild(mask);
mask.querySelector("[data-action='close']")?.addEventListener("click", () => closeEtmsListEditModal(doc));
mask.querySelector("[data-action='save']")?.addEventListener("click", () => {
const nextFields = context.fields.map((field, index) => ({
header: field.header,
value: mask.querySelector(`[data-field-index='${index}']`)?.value.trim() || ""
}));
const state = readEtmsListEditState(doc);
state[context.tableKey] = state[context.tableKey] || {};
state[context.tableKey][context.rowKey] = { fields: nextFields };
writeEtmsListEditState(doc, state);
applyEtmsListEditStateToTable(doc, context.tableKey, context.rowKey, nextFields);
closeEtmsListEditModal(doc);
});
}
function installEtmsGenericListEditors(doc) {
ensureEtmsListEditStyle(doc);
hydrateEtmsListEditRows(doc);
normalizeEtmsListEditTriggers(doc);
injectEtmsInlineEditButtons(doc);
renderEtmsHiddenEditPanel(doc);
if (doc.body.dataset.codexListEditorsInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const target = event.target.closest("button, .textBtn, a, .el-button");
if (!target) {
return;
}
if (target.closest("[id^='__codex_'], .codex-budget-panel, .codex-editable-toolbar")) {
return;
}
const label = getEtmsVisibleText(target);
if (!/编辑/.test(label)) {
return;
}
const resolved = resolveEtmsListEditContext(doc, target);
if (!resolved) {
return;
}
event.preventDefault();
event.stopPropagation();
openEtmsListEditModal(doc, resolved.row, resolved.context);
},
true
);
doc.body.dataset.codexListEditorsInstalled = "1";
}
function applyGlobalFrameSkin(iframe) {
try {
const doc = iframe && iframe.contentDocument;
if (!doc || !doc.head) {
return;
}
if (!doc.getElementById("__codex_runtime_frame_skin__")) {
const style = doc.createElement("style");
style.id = "__codex_runtime_frame_skin__";
style.textContent = `
.sidebar-container,
.fixed-header,
.tags-view-container,
.layout-tags-view-container,
.navbar,
.app-breadcrumb,
.el-breadcrumb,
.breadcrumb-container,
.tabList,
.right-menu,
.currentProject,
.AiBox,
.feedback-dialog,
.feedback-btn,
.hamburger-container,
.right-menu-item .international,
.right-menu-item .avatar-container {
display: none !important;
}
.main-container,
.main-container.hasTagsView,
.app-main,
.app-main > section,
.main-container .app-main {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
}
.el-scrollbar__wrap,
.scrollbar-wrapper {
margin-right: 0 !important;
margin-bottom: 0 !important;
}
body,
html {
background: #f5f7fa !important;
overflow: auto !important;
}
.app-main > section,
.app-main > .app-container,
.app-main .app-container,
.main-container .app-main > section {
padding-top: 0 !important;
}
.el-table__empty-block,
.el-table__empty-text {
display: none !important;
}
`;
doc.head.appendChild(style);
}
const tables = [...doc.querySelectorAll("table")];
const hasDataTable = tables.some((table) => table.querySelectorAll("tbody tr").length > 0);
if (hasDataTable) {
tables.forEach((table) => {
const wrapper = table.closest(".el-table");
const rows = table.querySelectorAll("tbody tr").length;
if (rows > 0) {
if (wrapper) {
wrapper.style.setProperty("display", "block", "important");
}
return;
}
if (wrapper) {
wrapper.style.display = "none";
}
});
}
const src = iframe.getAttribute("src") || "";
const editingActive = doc.body?.dataset.codexMirrorEditing === "1";
if (!editingActive) {
if (src.includes("/companyMetadata/companyInfo/")) {
hydrateCompanyInfoFrame(doc);
}
if (src.includes("/companyMetadata/institution/")) {
if (!doc.getElementById("__codex_institution_expand__")) {
const style = doc.createElement("style");
style.id = "__codex_institution_expand__";
style.textContent = `
tr.el-table__row--level-1,
tr.el-table__row--level-2,
tr.el-table__row--level-3 {
display: table-row !important;
}
`;
doc.head.appendChild(style);
}
doc.querySelectorAll("tr.el-table__row--level-1, tr.el-table__row--level-2, tr.el-table__row--level-3").forEach((tr) => {
tr.style.setProperty("display", "table-row", "important");
});
}
if (src.includes("/companyMetadata/financeData/")) {
if (!doc.getElementById("__codex_finance_summary__")) {
const summary = doc.createElement("div");
summary.id = "__codex_finance_summary__";
summary.innerHTML = `
<div class="codex-finance-summary-card">
<span class="label">主营业务收入</span>
<strong>¥205,600.00</strong>
</div>
<div class="codex-finance-summary-card">
<span class="label">营业利润</span>
<strong>¥49,900.00</strong>
</div>
<div class="codex-finance-summary-card">
<span class="label">净利润</span>
<strong>¥40,200.00</strong>
</div>
`;
const style = doc.createElement("style");
style.id = "__codex_finance_summary_style__";
style.textContent = `
#__codex_finance_summary__ {
display: grid;
grid-template-columns: repeat(3, minmax(180px, 1fr));
gap: 16px;
margin: 0 0 18px;
}
.codex-finance-summary-card {
padding: 18px 20px;
border-radius: 16px;
background: linear-gradient(135deg, #f8fbff, #eef5ff);
border: 1px solid rgba(45, 118, 255, 0.08);
box-shadow: 0 10px 24px rgba(31, 55, 88, 0.06);
}
.codex-finance-summary-card .label {
display: block;
margin-bottom: 8px;
color: #6b7a90;
font-size: 13px;
}
.codex-finance-summary-card strong {
color: #1f2d3d;
font-size: 24px;
font-weight: 700;
}
`;
doc.head.appendChild(style);
const host = doc.querySelector(".app-main > section") || doc.querySelector(".main-container .app-main") || doc.body;
host.insertBefore(summary, host.firstChild);
}
const visibleTables = [...doc.querySelectorAll("table")].filter((table) => {
const wrapper = table.closest(".el-table") || table;
return getComputedStyle(wrapper).display !== "none";
});
const valueTable = visibleTables.find((table) => table.querySelectorAll("tbody tr").length >= 3 && /¥0\.00/.test(table.innerText));
if (valueTable) {
const matrix = [
["¥128,000.00", "¥136,500.00", "¥142,300.00", "¥155,800.00", "¥163,200.00", "¥171,900.00", "¥176,500.00", "¥181,200.00", "¥188,300.00", "¥192,400.00", "¥198,800.00", "¥205,600.00"],
["¥28,600.00", "¥31,200.00", "¥33,900.00", "¥37,500.00", "¥39,200.00", "¥41,100.00", "¥42,300.00", "¥43,900.00", "¥45,500.00", "¥46,800.00", "¥48,200.00", "¥49,900.00"],
["¥21,800.00", "¥24,100.00", "¥26,300.00", "¥29,000.00", "¥30,600.00", "¥31,900.00", "¥33,100.00", "¥34,500.00", "¥35,800.00", "¥37,000.00", "¥38,600.00", "¥40,200.00"]
];
const rows = [...valueTable.querySelectorAll("tbody tr")].slice(0, 3);
rows.forEach((tr, rowIdx) => {
const cells = [...tr.querySelectorAll("td")];
matrix[rowIdx].forEach((value, valueIdx) => {
const cell = cells[valueIdx + 1];
const valueNode = cell && (cell.querySelector(".cell div") || cell.querySelector(".cell") || cell);
if (valueNode) {
valueNode.textContent = value;
}
});
});
}
}
if (src.includes("/r2cockpit/microbrain/finance/")) {
renderMicrobrainDashboard(doc, "finance");
}
if (src.includes("/r2cockpit/microbrain/equipment/")) {
renderMicrobrainDashboard(doc, "equipment");
}
if (src.includes("/r2cockpit/microbrain/parkingLot/")) {
renderMicrobrainDashboard(doc, "parkingLot");
}
if (src.includes("/supplierManage/supplierMicrobrain/")) {
renderMicrobrainDashboard(doc, "supplierMicrobrain");
}
if (src.includes("/r2cockpit/cloudData/propertyFeeReport/")) {
renderCloudDataReport(doc, "propertyFeeReport");
}
if (src.includes("/r2cockpit/cloudData/parkingLotReport/")) {
renderCloudDataReport(doc, "parkingLotReport");
}
if (src.includes("/r2cockpit/cloudData/planTaskReport/")) {
renderCloudDataReport(doc, "planTaskReport");
}
if (src.includes("/r2cockpit/cloudData/workOrderReport/")) {
renderCloudDataReport(doc, "workOrderReport");
}
if (src.includes("/r2cockpit/cloudData/dataReport/")) {
renderCloudDataReport(doc, "dataReport");
}
if (src.includes("/r2cockpit/cloudData/consumeReport/")) {
renderCloudDataReport(doc, "consumeReport");
}
if (src.includes("/r2cockpit/cloudData/jobGridReport/")) {
renderCloudDataReport(doc, "jobGridReport");
}
if (src.includes("/r2cockpit/cloudData/contractPlanReport/")) {
renderCloudDataReport(doc, "contractPlanReport");
}
if (src.includes("/r2cockpit/cloudData/contractGuaranteeReport/")) {
renderCloudDataReport(doc, "contractGuaranteeReport");
}
if (src.includes("/r2cockpit/cloudData/detailedContractGuaranteeReport/")) {
renderCloudDataReport(doc, "detailedContractGuaranteeReport");
}
if (src.includes("/r2cockpit/cloudData/officialAccount/")) {
renderCloudDataReport(doc, "officialAccount");
}
if (src.includes("/r2cockpit/cloudData/collectionTracking/")) {
renderCloudDataReport(doc, "collectionTracking");
}
if (src.includes("/r2cockpit/cloudData/collectionRate/")) {
renderCloudDataReport(doc, "collectionRate");
}
if (src.includes("/r2cockpit/cloudData/visitWorkReport/")) {
renderCloudDataReport(doc, "visitWorkReport");
}
if (src.includes("/personnelMerits/qualification/")) {
renderStaticManagementPage(doc, "qualification");
}
if (src.includes("/personnelMerits/employeeQuitReport/")) {
renderStaticManagementPage(doc, "employeeQuitReport");
}
if (src.includes("/systemManage/operationLog/")) {
renderStaticManagementPage(doc, "operationLog");
}
if (src.includes("/satisfaction/questionBank/")) {
renderStaticManagementPage(doc, "questionBank");
}
if (src.includes("/satisfaction/report/")) {
renderStaticManagementPage(doc, "report");
}
if (src.includes("/satisfaction/tracking/")) {
renderStaticManagementPage(doc, "tracking");
}
if (src.includes("/personnelMerits/assessment/")) {
renderStaticManagementPage(doc, "assessment");
}
}
installEtmsGenericListEditors(doc);
installEtmsEditableSections(doc);
const bodyHeight = doc.body ? doc.body.scrollHeight : 0;
const htmlHeight = doc.documentElement ? doc.documentElement.scrollHeight : 0;
const height = Math.max(bodyHeight, htmlHeight, window.innerHeight - 108, 720);
iframe.style.setProperty("height", `${height}px`, "important");
} catch (_error) {
// ignore
}
}
function installFrameSkinObserver() {
setInterval(() => {
document.querySelectorAll("iframe").forEach((iframe) => applyGlobalFrameSkin(iframe));
}, 800);
}
function appendStyles() {
STYLE_HREFS.forEach((href) => {
const finalHref = VERSION ? `${href}${href.includes("?") ? "&" : "?"}v=${VERSION}` : href;
if (document.querySelector(`link[href="${finalHref}"]`)) {
return;
}
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = finalHref;
document.head.appendChild(link);
});
}
function injectOuterRuntimeSkin() {
if (document.getElementById("__codex_runtime_outer_skin__")) {
return;
}
const style = document.createElement("style");
style.id = "__codex_runtime_outer_skin__";
style.textContent = `
.AiBox,
.feedback-dialog,
.feedback-btn,
.v-modal {
display: none !important;
}
.app-main,
.app-main > section,
.main-container,
.main-container.hasTagsView {
background: #f3f6fb !important;
}
.fixed-header {
position: sticky !important;
top: 0;
z-index: 10;
box-shadow: 0 4px 18px rgba(17, 37, 63, 0.06);
}
.navbar {
height: 60px !important;
padding: 0 16px !important;
background: rgba(255,255,255,0.94) !important;
backdrop-filter: blur(8px);
}
.breadcrumb-container {
font-size: 13px !important;
}
.right-menu {
gap: 10px;
}
.right-menu .el-select,
.right-menu .currentProject {
max-width: 320px;
}
.tags-view-container {
min-height: 38px !important;
padding: 6px 12px !important;
background: rgba(255,255,255,0.92) !important;
border-bottom: 1px solid rgba(17,37,63,0.06) !important;
}
.tags-view-wrapper .el-scrollbar__view {
display: flex;
align-items: center;
gap: 8px;
}
.tags-view-item {
display: inline-flex !important;
align-items: center;
gap: 6px;
height: 28px;
margin: 0 !important;
padding: 0 12px;
border-radius: 4px 4px 0 0;
border: 1px solid rgba(17, 37, 63, 0.12);
background: #fff;
color: #66758f;
font-size: 13px;
line-height: 28px;
white-space: nowrap;
cursor: default;
}
.tags-view-item.active {
border-color: rgba(45, 118, 255, 0.18);
background: #3a7be0;
color: #fff;
}
.tags-view-item.codex-synthetic-tag {
text-decoration: none;
cursor: pointer;
}
.tags-view-item .el-icon-close {
display: inline-flex !important;
align-items: center;
justify-content: center;
font-size: 12px;
opacity: 0.72;
}
.tags-view-item:first-child .el-icon-close {
display: none !important;
}
.codex-runtime-frame-host {
padding: 14px 16px 20px;
background: #f3f6fb;
}
.codex-runtime-frame {
display: block;
width: 100%;
min-height: 720px;
border: 0;
border-radius: 18px;
background: #fff;
box-shadow: 0 14px 40px rgba(17, 37, 63, 0.08);
}
.codex-runtime-summary {
display: grid;
grid-template-columns: repeat(3, minmax(180px, 1fr));
gap: 14px;
margin: 0 0 14px;
}
.codex-runtime-summary-card {
padding: 16px 18px;
border-radius: 16px;
background: linear-gradient(135deg, #f8fbff, #eef5ff);
border: 1px solid rgba(45, 118, 255, 0.08);
box-shadow: 0 10px 24px rgba(31, 55, 88, 0.06);
}
.codex-runtime-summary-card .label {
display: block;
margin-bottom: 8px;
color: #6b7a90;
font-size: 13px;
}
.codex-runtime-summary-card strong {
color: #1f2d3d;
font-size: 24px;
font-weight: 700;
}
`;
document.head.appendChild(style);
}
function appendScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = VERSION ? `${src}${src.includes("?") ? "&" : "?"}v=${VERSION}` : src;
script.onload = resolve;
script.onerror = reject;
document.body.appendChild(script);
});
}
async function loadJson(url) {
const finalUrl = VERSION ? `${url}${url.includes("?") ? "&" : "?"}v=${VERSION}` : url;
const response = await fetch(finalUrl, { cache: "no-store" });
if (!response.ok) {
throw new Error(`加载失败: ${url}`);
}
return response.json();
}
async function loadRouteMap() {
routeMap = await loadJson("./route-map.json");
}
async function seedStorage() {
storageSeed = await loadJson("./storage-seed.json");
Object.entries(storageSeed).forEach(([key, value]) => {
localStorage.setItem(key, value);
});
if (!localStorage.getItem("userInfo") && storageSeed.userInfo) {
localStorage.setItem("userInfo", storageSeed.userInfo);
}
if (!localStorage.getItem("companyInfo") && storageSeed.companyInfo) {
localStorage.setItem("companyInfo", storageSeed.companyInfo);
}
}
async function seedSession() {
const payload = await loadJson("./session-seed.json");
Object.entries(payload).forEach(([key, value]) => {
sessionStorage.setItem(key, value);
});
}
async function seedCookies() {
const payload = await loadJson("./cookie-seed.json");
Object.entries(payload).forEach(([key, value]) => {
document.cookie = `${key}=${value}; path=/; SameSite=Lax`;
});
}
async function ensureServiceWorker() {
if (!("serviceWorker" in navigator)) {
return;
}
await navigator.serviceWorker.register("./sw.js", { scope: "./" });
await navigator.serviceWorker.ready;
if (!navigator.serviceWorker.controller && !sessionStorage.getItem("mirror-runtime-sw-reloaded")) {
if (location.hash) {
sessionStorage.setItem("mirror-runtime-target-hash", location.hash);
}
sessionStorage.setItem("mirror-runtime-sw-reloaded", "1");
location.reload();
throw new Error("等待 Service Worker 接管当前页面");
}
sessionStorage.removeItem("mirror-runtime-sw-reloaded");
}
function buildDefaultHash() {
try {
const companyInfo = JSON.parse(storageSeed.companyInfo || "{}");
const phone = storageSeed.fromPhone || "";
const roleId = storageSeed.roleId || "";
const memberId = storageSeed.memberId || "";
const companyName = encodeURIComponent(companyInfo.fullName || "");
const companyId = companyInfo.osId || companyInfo.id || "";
const logo = encodeURIComponent(companyInfo.logo || "");
return `#/dashboard?phone=${phone}&companyName=${companyName}&companyId=${companyId}&logo=${logo}&roleId=${roleId}&memberId=${memberId}`;
} catch (_error) {
return "#/dashboard";
}
}
function pathFromHash(hash) {
if (!hash) {
return "/dashboard";
}
const value = hash.startsWith("#") ? hash.slice(1) : hash;
const pathOnly = value.split("?")[0];
return pathOnly || "/dashboard";
}
async function waitForVueRoot() {
for (let i = 0; i < 40; i += 1) {
const app = document.querySelector("#app");
const vm = app && app.__vue__;
const router = vm && vm.$router;
if (vm && router) {
return { vm, router };
}
await new Promise((resolve) => setTimeout(resolve, 250));
}
throw new Error("等待 Vue 根实例超时");
}
async function patchRuntimeRoutes() {
const { router } = await waitForVueRoot();
const rootRoute = (router.options.routes || []).find((item) => item.path === "/");
if (!rootRoute || !rootRoute.component) {
return null;
}
const existing = new Set((router.options.routes || []).map((item) => item.path));
const frameComponent = {
name: "EtmsMirrorFrame",
computed: {
frameSrc() {
const config = routeMap[this.$route.path] || { src: "/hc-etms.sqygj.cn/404/" };
return config.src || "/hc-etms.sqygj.cn/404/";
},
financeSummary() {
if (this.$route.path !== "/financeData") {
return null;
}
return [
{ label: "主营业务收入", value: "¥205,600.00" },
{ label: "营业利润", value: "¥49,900.00" },
{ label: "净利润", value: "¥40,200.00" }
];
}
},
mounted() {
this.$nextTick(() => applyGlobalFrameSkin(this.$refs.frame));
},
updated() {
this.$nextTick(() => applyGlobalFrameSkin(this.$refs.frame));
},
methods: {
onFrameLoad(event) {
applyGlobalFrameSkin(event.target);
}
},
render(h) {
const children = [];
if (this.financeSummary) {
children.push(
h(
"div",
{ class: "codex-runtime-summary" },
this.financeSummary.map((item) =>
h("div", { class: "codex-runtime-summary-card" }, [
h("span", { class: "label" }, item.label),
h("strong", item.value)
])
)
)
);
}
children.push(
h("iframe", {
ref: "frame",
class: "codex-runtime-frame",
attrs: {
src: this.frameSrc,
frameborder: "0"
},
on: {
load: this.onFrameLoad
},
style: {
height: "calc(100vh - 108px)"
}
})
);
return h("div", { class: "codex-runtime-frame-host" }, children);
}
};
const children = Object.keys(routeMap)
.filter((path) => path !== "/dashboard")
.filter((path) => !existing.has(path))
.map((path) => ({
path: path.replace(/^\//, ""),
name: `etms-mirror-${path.replace(/[^\w]/g, "-")}`,
meta: { title: (routeMap[path] && routeMap[path].title) || path.split("/").pop() || path },
component: frameComponent
}));
if (children.length) {
router.addRoutes([
{
path: "/",
component: rootRoute.component,
children
}
]);
}
const currentPath = pathFromHash(INITIAL_HASH || location.hash);
if (currentPath !== "/dashboard" && routeMap[currentPath]) {
router.replace({ path: currentPath });
}
return router;
}
function ensureHashRoute() {
const rememberedHash = sessionStorage.getItem("mirror-runtime-target-hash");
if (rememberedHash) {
if (location.hash !== rememberedHash) {
location.hash = rememberedHash;
}
sessionStorage.removeItem("mirror-runtime-target-hash");
return;
}
if (!location.hash || location.hash === "#" || location.hash === "#/") {
location.hash = buildDefaultHash();
}
}
function hideLoading() {
const loading = document.getElementById("mirror-loading");
if (loading) {
loading.style.display = "none";
}
}
function replaceTextNodeContent(root, replacements) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
let node = walker.nextNode();
while (node) {
const text = node.nodeValue || "";
let updated = text;
replacements.forEach(([from, to]) => {
updated = updated.replace(from, to);
});
if (updated !== text) {
node.nodeValue = updated;
}
node = walker.nextNode();
}
}
function patchDashboardSummary() {
const board = JSON.parse(storageSeed.boardInfo || "{}");
const replacements = [
[/服务项目总数0 个/g, `服务项目总数:${board.projectCount || 94}`],
[/服务项目总户数0 户/g, `服务项目总户数:${board.houseCount || 4686}`],
[/服务项目建筑面积0 ㎡/g, `服务项目建筑面积:${board.floorageCount || 2888556.18}`],
[/商品即将到期项目\s*0/g, "商品即将到期项目\n3"],
[/企业剩余短信\s*0/g, "企业剩余短信\n11042"],
[/合同即将到期\s*0/g, "合同即将到期\n1"],
[/资质证书即将到期\s*0/g, "资质证书即将到期\n0"]
];
replaceTextNodeContent(document.body, replacements);
}
function scheduleDashboardPatch() {
let tries = 0;
const timer = setInterval(() => {
tries += 1;
patchDashboardSummary();
if (tries >= 8) {
clearInterval(timer);
}
}, 800);
}
function updateDocumentTitle(path) {
const config = routeMap[path];
if (config && config.title) {
document.title = `${config.title} - 企业服务平台`;
}
}
function getPresetTags(path) {
const shared = ["首页", "人事档案", "审批模板", "分公司管理", "财务数据"];
const cockpitShared = ["首页", "财务看板", "设备看板", "车场看板"];
const presets = {
"/personnelMerits/personnelFiles": shared,
"/companyMetadata/branchOfficeManage": shared,
"/companyMetadata/financeData": shared,
"/personnelFiles": shared,
"/branchOfficeManage": shared,
"/financeData": shared,
"/r2cockpit/microbrain/finance": cockpitShared,
"/r2cockpit/microbrain/equipment": cockpitShared,
"/r2cockpit/microbrain/parkingLot": cockpitShared,
"/finance": cockpitShared,
"/equipment": cockpitShared,
"/parkingLot": cockpitShared,
"/r2cockpit/cloudData/propertyFeeReport": ["首页", "物业费报表", "车场报表", "计划工单报表"],
"/r2cockpit/cloudData/parkingLotReport": ["首页", "物业费报表", "车场报表", "计划工单报表"],
"/r2cockpit/cloudData/planTaskReport": ["首页", "物业费报表", "车场报表", "计划工单报表"],
"/r2cockpit/cloudData/workOrderReport": ["首页", "计划工单报表", "非计划工单报表", "工单耗时统计"],
"/r2cockpit/cloudData/dataReport": ["首页", "计划工单报表", "非计划工单报表", "工单耗时统计"],
"/r2cockpit/cloudData/consumeReport": ["首页", "耗能报表", "作业网格台账报表", "合同计划执行报表"],
"/r2cockpit/cloudData/jobGridReport": ["首页", "耗能报表", "作业网格台账报表", "合同计划执行报表"],
"/r2cockpit/cloudData/contractPlanReport": ["首页", "耗能报表", "作业网格台账报表", "合同计划执行报表"],
"/r2cockpit/cloudData/contractGuaranteeReport": ["首页", "合同保障报表", "合同保障明细报表", "合同计划执行报表"],
"/r2cockpit/cloudData/detailedContractGuaranteeReport": ["首页", "合同保障报表", "合同保障明细报表", "合同计划执行报表"],
"/r2cockpit/cloudData/officialAccount": ["首页", "公众号拉新报表", "催收跟踪报表", "企业收费项报表", "拜访工作报表"],
"/r2cockpit/cloudData/collectionTracking": ["首页", "公众号拉新报表", "催收跟踪报表", "企业收费项报表", "拜访工作报表"],
"/r2cockpit/cloudData/collectionRate": ["首页", "公众号拉新报表", "催收跟踪报表", "企业收费项报表", "拜访工作报表"],
"/r2cockpit/cloudData/visitWorkReport": ["首页", "公众号拉新报表", "催收跟踪报表", "企业收费项报表", "拜访工作报表"],
"/personnelMerits/qualification": ["首页", "资质匹配", "员工离任报告", "人事档案"],
"/personnelMerits/employeeQuitReport": ["首页", "资质匹配", "员工离任报告", "人事档案"],
"/systemManage/operationLog": ["首页", "人事设置", "角色权限管理", "操作日志"],
"/satisfaction/questionBank": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/satisfaction/questionnaire": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/satisfaction/report": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/satisfaction/tracking": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/personnelMerits/assessment": ["首页", "人才测评", "资质匹配", "员工离任报告"],
"/supplierManage/supplierMicrobrain": ["首页", "供应商库", "合同管理", "供应商微脑"],
"/assessment": ["首页", "人才测评", "资质匹配", "员工离任报告"],
"/qualification": ["首页", "资质匹配", "员工离任报告", "人事档案"],
"/employeeQuitReport": ["首页", "资质匹配", "员工离任报告", "人事档案"],
"/operationLog": ["首页", "人事设置", "角色权限管理", "操作日志"],
"/questionBank": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/questionnaire": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/report": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/tracking": ["首页", "调查题库", "调查问卷", "调查报告", "整改跟踪"],
"/supplierMicrobrain": ["首页", "供应商库", "合同管理", "供应商微脑"]
};
return presets[path] || null;
}
function getTagRoute(title) {
const routeMapByTitle = {
"首页": "/dashboard",
"人事档案": "/personnelMerits/personnelFiles",
"审批模板": "/companyMetadata/approveMan",
"分公司管理": "/companyMetadata/branchOfficeManage",
"财务数据": "/companyMetadata/financeData",
"财务看板": "/finance",
"设备看板": "/equipment",
"车场看板": "/parkingLot",
"物业费报表": "/r2cockpit/cloudData/propertyFeeReport",
"车场报表": "/r2cockpit/cloudData/parkingLotReport",
"计划工单报表": "/r2cockpit/cloudData/planTaskReport",
"非计划工单报表": "/r2cockpit/cloudData/workOrderReport",
"工单耗时统计": "/r2cockpit/cloudData/dataReport",
"耗能报表": "/r2cockpit/cloudData/consumeReport",
"作业网格台账报表": "/r2cockpit/cloudData/jobGridReport",
"合同计划执行报表": "/r2cockpit/cloudData/contractPlanReport",
"合同保障报表": "/r2cockpit/cloudData/contractGuaranteeReport",
"合同保障明细报表": "/r2cockpit/cloudData/detailedContractGuaranteeReport",
"公众号拉新报表": "/r2cockpit/cloudData/officialAccount",
"催收跟踪报表": "/r2cockpit/cloudData/collectionTracking",
"企业收费项报表": "/r2cockpit/cloudData/collectionRate",
"拜访工作报表": "/r2cockpit/cloudData/visitWorkReport",
"资质匹配": "/personnelMerits/qualification",
"员工离任报告": "/personnelMerits/employeeQuitReport",
"人事设置": "/systemManage/personnelSetting",
"操作日志": "/systemManage/operationLog",
"调查题库": "/satisfaction/questionBank",
"调查问卷": "/satisfaction/questionnaire",
"调查报告": "/satisfaction/report",
"整改跟踪": "/satisfaction/tracking",
"人才测评": "/personnelMerits/assessment",
"供应商库": "/supplierManage/supplierStock",
"合同管理": "/supplierManage/contractManage",
"供应商微脑": "/supplierManage/supplierMicrobrain"
};
return routeMapByTitle[title] || "/dashboard";
}
function getTagLabel(node) {
const cloned = node.cloneNode(true);
cloned.querySelectorAll(".el-icon-close").forEach((icon) => icon.remove());
return (cloned.textContent || "").trim();
}
function createSyntheticTag(title, path, active) {
const anchor = document.createElement("a");
anchor.href = `#${path}`;
anchor.className = `tags-view-item codex-synthetic-tag${active ? " active" : ""}`;
anchor.appendChild(document.createTextNode(title));
if (title !== "首页") {
const close = document.createElement("span");
close.className = "el-icon-close";
anchor.appendChild(close);
}
return anchor;
}
function hydrateTagsView(path) {
const view = document.querySelector(".tags-view-container .el-scrollbar__view");
if (!view) {
return;
}
const preset = getPresetTags(path);
view.querySelectorAll(".codex-synthetic-tag").forEach((node) => node.remove());
if (!preset) {
return;
}
const existingNodes = [...view.querySelectorAll(".tags-view-item:not(.codex-synthetic-tag)")];
const existingMap = new Map(existingNodes.map((node) => [getTagLabel(node), node]));
const activeTitle =
(routeMap[path] && routeMap[path].title) ||
getTagLabel(view.querySelector(".tags-view-item.active") || document.createElement("span"));
preset.forEach((title) => {
let node = existingMap.get(title);
if (!node) {
node = createSyntheticTag(title, getTagRoute(title), title === activeTitle);
}
if (title === activeTitle) {
node.classList.add("active");
} else {
node.classList.remove("active");
}
view.appendChild(node);
});
}
function scheduleTagsViewHydration(path) {
let tries = 0;
const timer = setInterval(() => {
tries += 1;
hydrateTagsView(path);
if (tries >= 8) {
clearInterval(timer);
}
}, 300);
}
function installRouteBridge(router) {
if (!router || window.__ETMS_RUNTIME_BRIDGE_INSTALLED__) {
return;
}
window.__ETMS_RUNTIME_BRIDGE_INSTALLED__ = true;
document.addEventListener(
"click",
(event) => {
const anchor = event.target && event.target.closest ? event.target.closest("a[href]") : null;
if (!anchor) {
return;
}
const href = anchor.getAttribute("href") || "";
if (!href.startsWith("#/")) {
return;
}
event.preventDefault();
const raw = href.slice(1);
const path = raw.split("?")[0] || "/dashboard";
if (path === "/goToProject") {
window.location.href = "/__mirror/runtime/hc-pos-dashboard/#/dashboard";
return;
}
if (routeMap[path]) {
router.replace({ path });
updateDocumentTitle(path);
} else {
router.replace({ path: "/404" });
}
},
true
);
router.afterEach((to) => {
updateDocumentTitle(to.path);
scheduleTagsViewHydration(to.path);
});
}
try {
await seedStorage();
await seedSession();
await seedCookies();
await loadRouteMap();
await ensureServiceWorker();
ensureHashRoute();
appendStyles();
injectOuterRuntimeSkin();
for (const src of SCRIPT_SRCS) {
await appendScript(src);
}
const router = await patchRuntimeRoutes();
installRouteBridge(router);
const initialPath = pathFromHash(location.hash);
updateDocumentTitle(initialPath);
hideLoading();
scheduleDashboardPatch();
scheduleTagsViewHydration(initialPath);
installFrameSkinObserver();
} catch (error) {
const loading = document.getElementById("mirror-loading");
if (loading) {
const detail = document.createElement("small");
detail.textContent = String(error && error.message ? error.message : error);
loading.appendChild(detail);
}
}
})();