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

6923 lines
278 KiB
JavaScript
Raw 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 ROUTE_HASH = "#/dashboard";
const STYLE_HREFS = [
"/static/css/chunk-libs.3dfb7769.css",
"/static/css/app.89efc722.css"
];
let routeMap = {};
const HCPOS_DASHBOARD_PRESETS = {
financeMicrobrain: {
eyebrow: "业财税银",
headline: "财务看板",
summary: "聚合收入、成本、利润与收缴排行,替代原始零值数据页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.16)",
metrics: [
{ label: "本月总收费", value: "¥2,186,000", delta: "较上月 +7.6%" },
{ label: "累计成本", value: "¥1,428,000", delta: "成本率 65.3%" },
{ label: "月度利润", value: "¥758,000", delta: "利润率 34.7%" },
{ label: "风险预警", value: "4", delta: "高风险 1 项" }
],
trendLabels: ["1月", "2月", "3月", "4月", "5月", "6月"],
trendSeries: [
{ label: "收费额", color: "#2d76ff", values: [142, 156, 168, 182, 194, 218] },
{ label: "利润额", color: "#2fc1a8", values: [42, 48, 55, 61, 68, 76] }
],
segments: [
{ label: "物业费", value: "46%", tone: "#2d76ff" },
{ label: "停车费", value: "24%", tone: "#20c997" },
{ label: "增值服务", value: "18%", tone: "#f59e0b" },
{ label: "其他收入", value: "12%", tone: "#a78bfa" }
],
rankingTitle: "收缴红榜",
rankingRows: [
["循环花园一期 1 栋", "¥96,800", "98%"],
["博万物 A 座", "¥82,400", "95%"],
["美好花园 3 栋", "¥76,200", "93%"],
["连城花园 B 区", "¥68,900", "91%"]
],
alertsTitle: "财务提示",
alerts: [
{ title: "循环花园一期", detail: "本月收费额继续上升,建议同步预算口径。", level: "正常" },
{ title: "博万物", detail: "存在 2 笔大额费用待核销,需本周复核。", level: "关注" },
{ title: "连城花园", detail: "收缴完成率低于目标 4.1%,建议补做催收。", level: "预警" }
]
},
cleanMicrobrain: {
eyebrow: "清洁运营",
headline: "清洁看板",
summary: "展示清洁履约、问题点位、物资消耗和班组效率,替代空态页。",
accent: "#06b6d4",
accentSoft: "rgba(6, 182, 212, 0.16)",
metrics: [
{ label: "清洁任务数", value: "1,328", delta: "完成率 95.1%" },
{ label: "问题点位", value: "16", delta: "待闭环 4 个" },
{ label: "物资消耗", value: "¥38,600", delta: "较上月 -3.8%" },
{ label: "班组评分", value: "4.76", delta: "住户评价稳定" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "完成率", color: "#06b6d4", values: [84, 86, 89, 91, 93, 95] },
{ label: "问题闭环率", color: "#2d76ff", values: [68, 71, 76, 81, 84, 88] }
],
segments: [
{ label: "楼道保洁", value: "38%", tone: "#06b6d4" },
{ label: "园区保洁", value: "27%", tone: "#2d76ff" },
{ label: "垃圾分类", value: "19%", tone: "#20c997" },
{ label: "专项清洁", value: "16%", tone: "#f59e0b" }
],
rankingTitle: "班组履约排行",
rankingRows: [
["循环花园一期 A 组", "4.92", "98%"],
["博万物 2 组", "4.81", "96%"],
["美好花园 1 组", "4.74", "94%"],
["连城花园机动组", "4.68", "92%"]
],
alertsTitle: "清洁提示",
alerts: [
{ title: "地下车库 B 区", detail: "夜间巡检发现积尘偏高,建议安排专项清洁。", level: "关注" },
{ title: "循环花园一期", detail: "垃圾分类房周边清洁已连续 7 天达标。", level: "正常" },
{ title: "博万物", detail: "保洁耗材库存偏低,建议本周补货。", level: "预警" }
]
},
elevatorDimension: {
eyebrow: "特种设备",
headline: "电梯看板",
summary: "汇总电梯运行、维保、困人事件和年检进度,替代空态页。",
accent: "#7c3aed",
accentSoft: "rgba(124, 58, 237, 0.16)",
metrics: [
{ label: "电梯总数", value: "286", delta: "在线率 98.2%" },
{ label: "本月维保", value: "82", delta: "完成率 94.6%" },
{ label: "困人事件", value: "3", delta: "同比下降 2 起" },
{ label: "年检临期", value: "11", delta: "30 天内到期" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "运行稳定率", color: "#7c3aed", values: [91, 92, 93, 94, 96, 97] },
{ label: "维保完成率", color: "#2fc1a8", values: [76, 80, 84, 87, 90, 95] }
],
segments: [
{ label: "住宅梯", value: "54%", tone: "#7c3aed" },
{ label: "商用梯", value: "21%", tone: "#2d76ff" },
{ label: "货梯", value: "15%", tone: "#20c997" },
{ label: "扶梯", value: "10%", tone: "#f59e0b" }
],
rankingTitle: "维保单位排行",
rankingRows: [
["万嘉电梯维保", "97 分", "98%"],
["城安机电服务", "93 分", "95%"],
["迅达联保", "91 分", "93%"],
["华升特种设备", "88 分", "90%"]
],
alertsTitle: "电梯预警",
alerts: [
{ title: "循环花园一期 3 栋 2 单元", detail: "年检到期剩余 12 天,请安排检验。", level: "预警" },
{ title: "博万物 2 号货梯", detail: "运行平稳,近 30 天无故障。", level: "正常" },
{ title: "美好花园 5 号梯", detail: "本周困人演练需补录签到。", level: "关注" }
]
},
equipmentPortrait: {
eyebrow: "设备资产",
headline: "设备看板",
summary: "展示设备台账、维保、告警与能耗概览,替代空态页。",
accent: "#00a870",
accentSoft: "rgba(0, 168, 112, 0.16)",
metrics: [
{ label: "设备总台账", value: "1,186", 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: "正常" }
]
},
greenMicroBrain: {
eyebrow: "园林养护",
headline: "绿化看板",
summary: "展示绿化养护计划、植物健康度和病虫害预警,替代空态页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.16)",
metrics: [
{ label: "绿化养护点位", value: "418", delta: "覆盖 36 个片区" },
{ label: "本月养护完成", value: "362", delta: "完成率 91.6%" },
{ label: "病虫害预警", value: "8", delta: "需跟进 3 处" },
{ label: "成活率", value: "97.4%", delta: "春季补植良好" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "养护完成率", color: "#22c55e", values: [76, 81, 84, 87, 90, 92] },
{ label: "病害闭环率", color: "#f97316", values: [58, 63, 69, 74, 78, 83] }
],
segments: [
{ label: "乔木养护", value: "36%", tone: "#22c55e" },
{ label: "灌木修剪", value: "24%", tone: "#2d76ff" },
{ label: "草坪维护", value: "22%", tone: "#f59e0b" },
{ label: "节日花卉", value: "18%", tone: "#a78bfa" }
],
rankingTitle: "养护片区排行",
rankingRows: [
["中央花园片区", "97 分", "96%"],
["东门景观带", "94 分", "93%"],
["儿童活动区", "91 分", "90%"],
["外围绿篱区", "88 分", "87%"]
],
alertsTitle: "绿化提示",
alerts: [
{ title: "中央花园", detail: "病虫害处理完成,建议三天后复查。", level: "正常" },
{ title: "东门景观带", detail: "月季补植计划推迟,需协调供应商。", level: "关注" },
{ title: "南侧草坪区", detail: "连续高温导致草坪发黄,建议启动灌溉预案。", level: "预警" }
]
},
parkingMicroBrain: {
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: "正常" }
]
},
securityBrain: {
eyebrow: "安防运营",
headline: "安防看板",
summary: "汇总巡更、监控、事件闭环和应急演练情况,替代空态页。",
accent: "#ef4444",
accentSoft: "rgba(239, 68, 68, 0.16)",
metrics: [
{ label: "巡更任务", value: "682", delta: "完成率 96.4%" },
{ label: "异常事件", value: "14", delta: "重大事件 0 起" },
{ label: "监控在线率", value: "98.9%", delta: "离线摄像头 3 台" },
{ label: "应急演练", value: "12", delta: "季度计划完成" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "巡更闭环率", color: "#ef4444", values: [88, 90, 92, 93, 95, 96] },
{ label: "事件处置率", color: "#2d76ff", values: [72, 76, 81, 84, 88, 91] }
],
segments: [
{ label: "巡更任务", value: "41%", tone: "#ef4444" },
{ label: "门岗值守", value: "24%", tone: "#2d76ff" },
{ label: "监控巡检", value: "20%", tone: "#f59e0b" },
{ label: "应急演练", value: "15%", tone: "#20c997" }
],
rankingTitle: "项目安防排行",
rankingRows: [
["循环花园一期", "95 分", "97%"],
["博万物", "92 分", "93%"],
["美好花园", "90 分", "91%"],
["连城花园", "88 分", "89%"]
],
alertsTitle: "安防提示",
alerts: [
{ title: "北门岗亭", detail: "夜班巡更存在 1 次漏巡,需补录。", level: "关注" },
{ title: "监控中心", detail: "3 台摄像头离线超过 2 小时,建议安排检修。", level: "预警" },
{ title: "循环花园一期", detail: "消防演练完成率 100%,建议复盘分享。", level: "正常" }
]
},
customerPortrait: {
eyebrow: "客户运营",
headline: "客户画像",
summary: "整合住户结构、活跃度、工单偏好与缴费行为,替代空态页。",
accent: "#0ea5e9",
accentSoft: "rgba(14, 165, 233, 0.16)",
metrics: [
{ label: "活跃住户", value: "4,286", delta: "月活率 72.4%" },
{ label: "重点客户", value: "126", delta: "需专人跟进" },
{ label: "回访完成", value: "318", delta: "本月回访任务" },
{ label: "满意度均分", value: "4.68", delta: "较上月 +0.05" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "活跃住户数", color: "#0ea5e9", values: [3200, 3380, 3520, 3680, 3890, 4286] },
{ label: "回访完成数", color: "#2fc1a8", values: [102, 136, 181, 224, 276, 318] }
],
segments: [
{ label: "高频互动", value: "34%", tone: "#0ea5e9" },
{ label: "稳定缴费", value: "29%", tone: "#2d76ff" },
{ label: "投诉敏感", value: "18%", tone: "#f59e0b" },
{ label: "沉默住户", value: "19%", tone: "#a78bfa" }
],
rankingTitle: "项目客户运营排行",
rankingRows: [
["循环花园一期", "4.82", "96%"],
["博万物", "4.76", "93%"],
["美好花园", "4.69", "91%"],
["连城花园", "4.58", "88%"]
],
alertsTitle: "客户提示",
alerts: [
{ title: "循环花园一期", detail: "高频投诉用户 3 位,建议安排专属回访。", level: "关注" },
{ title: "博万物", detail: "本月客户活跃度显著提升,可继续推动社群运营。", level: "正常" },
{ title: "美好花园", detail: "沉默住户占比上升,建议补做满意度回访。", level: "预警" }
]
},
energyNotice: {
eyebrow: "能源管理",
headline: "能源看板",
summary: "聚合公共用电、可回收用能和设备能耗等级,替代原始异常文本页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.16)",
metrics: [
{ label: "本月用电量", value: "186.4万kWh", delta: "较上月 -4.3%" },
{ label: "减支金额", value: "¥62,878", delta: "节能效果稳定" },
{ label: "增收金额", value: "¥18,620", delta: "可回收能耗贡献" },
{ label: "异常设备", value: "5", delta: "需安排巡检" }
],
trendLabels: ["1周", "2周", "3周", "4周", "5周", "6周"],
trendSeries: [
{ label: "公共用电同比减支", color: "#14b8a6", values: [22, 28, 34, 41, 53, 63] },
{ label: "可回收用电增收", color: "#2d76ff", values: [8, 10, 11, 13, 15, 19] }
],
segments: [
{ label: "公共用电", value: "41%", tone: "#14b8a6" },
{ label: "可回收用电", value: "27%", tone: "#2d76ff" },
{ label: "商铺能耗", value: "19%", tone: "#f59e0b" },
{ label: "多经能耗", value: "13%", tone: "#a78bfa" }
],
rankingTitle: "重点能耗项目",
rankingRows: [
["循环花园一期", "6.45 kWh/㎡", "92%"],
["博万物", "6.58 kWh/㎡", "89%"],
["美好花园", "6.11 kWh/㎡", "91%"],
["连城花园", "6.37 kWh/㎡", "84%"]
],
alertsTitle: "能源提示",
alerts: [
{ title: "公共用电", detail: "连续两周维持减支,建议固化节能策略。", level: "正常" },
{ title: "设备能耗", detail: "5 台高能耗设备需补做专项巡检。", level: "关注" },
{ title: "商铺能耗", detail: "本月波动偏高,建议核查晚间峰值。", level: "预警" }
]
}
};
const HCPOS_STATIC_PAGE_PRESETS = {
childCare: {
eyebrow: "社区服务",
title: "社区幼托运营总览",
summary: "整合托育名额、预约转化、服务满意度与园区联动情况,替代内测提示页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "托育名额", value: "128", detail: "本月已使用 94 个" },
{ label: "本月预约", value: "216", detail: "到访转化 43%" },
{ label: "活跃家庭", value: "86", detail: "重复预约率 61%" },
{ label: "满意度", value: "4.81", detail: "家长反馈稳定" }
],
headers: ["项目名称", "服务方案", "开放时段", "本月预约", "到访家庭", "转化率", "负责人"],
rows: [
["循环花园一期", "幼托半日班", "09:00-18:00", "72", "31", "43.1%", "何琳"],
["博万物", "周末托管", "10:00-17:00", "46", "19", "41.3%", "李佑聪"],
["美好花园", "课后看护", "16:00-20:00", "38", "17", "44.7%", "郭晓"],
["连城花园", "假期托育", "09:00-17:30", "34", "13", "38.2%", "曾丽娜"],
["江南世家一期", "亲子陪护", "09:30-18:30", "26", "11", "42.3%", "陈谷先"]
]
},
express: {
eyebrow: "社区服务",
title: "快递收发运营总览",
summary: "聚合收发件量、代收时效与异常包裹,替代内测提示页。",
accent: "#06b6d4",
accentSoft: "rgba(6, 182, 212, 0.14)",
cards: [
{ label: "本日收件", value: "1,286", detail: "较昨日 +8.2%" },
{ label: "本日派件", value: "1,104", detail: "签收率 95.6%" },
{ label: "异常包裹", value: "9", detail: "需人工复核" },
{ label: "平均停留时长", value: "6.4h", detail: "库位周转正常" }
],
headers: ["项目名称", "收件量", "派件量", "异常包裹", "当日签收率", "平均停留时长", "值班人"],
rows: [
["循环花园一期", "342", "301", "2", "96.1%", "5.8h", "林婉"],
["博万物", "286", "244", "1", "95.4%", "6.2h", "何琳"],
["美好花园", "241", "219", "2", "94.8%", "6.9h", "郭晓"],
["连城花园", "218", "197", "2", "95.0%", "6.5h", "周宇"],
["江南世家一期", "199", "143", "2", "94.2%", "6.8h", "陈谷先"]
]
},
house: {
eyebrow: "社区服务",
title: "房屋经纪运营总览",
summary: "展示房源供给、带看转化与签约进展,替代内测提示页。",
accent: "#7c4dff",
accentSoft: "rgba(124, 77, 255, 0.14)",
cards: [
{ label: "在售房源", value: "86", detail: "新增 14 套" },
{ label: "本月带看", value: "214", detail: "意向客户 67 位" },
{ label: "签约套数", value: "12", detail: "转化率 17.9%" },
{ label: "成交额", value: "¥1,286万", detail: "含租售业务" }
],
headers: ["项目名称", "在售房源", "本月带看", "意向客户", "签约套数", "转化率", "经纪人"],
rows: [
["循环花园一期", "24", "61", "18", "4", "22.2%", "李佑聪"],
["博万物", "18", "46", "14", "3", "21.4%", "何琳"],
["美好花园", "15", "38", "12", "2", "16.7%", "郭晓"],
["连城花园", "16", "35", "13", "2", "15.4%", "周宇"],
["江南世家一期", "13", "34", "10", "1", "10.0%", "曾丽娜"]
]
},
housekeeping: {
eyebrow: "社区服务",
title: "社区家政运营总览",
summary: "汇总家政订单、服务评价与排班履约,替代内测提示页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "本月订单", value: "328", detail: "保洁 / 保姆 / 深度清洁" },
{ label: "已服务家庭", value: "246", detail: "复购率 48.8%" },
{ label: "履约率", value: "96.2%", detail: "投诉 3 单" },
{ label: "服务评分", value: "4.83", detail: "口碑稳定" }
],
headers: ["项目名称", "服务类型", "本月订单", "已服务家庭", "履约率", "投诉单", "组长"],
rows: [
["循环花园一期", "日常保洁", "92", "71", "97.8%", "1", "陈谷先"],
["博万物", "深度清洁", "68", "49", "95.1%", "1", "何琳"],
["美好花园", "收纳整理", "54", "41", "96.3%", "0", "郭晓"],
["连城花园", "保姆月嫂", "47", "36", "94.7%", "1", "曾丽娜"],
["江南世家一期", "空房开荒", "41", "29", "96.6%", "0", "周宇"]
]
},
retirement: {
eyebrow: "社区服务",
title: "社区养老运营总览",
summary: "汇总长者服务覆盖、上门关怀和医疗联动,替代内测提示页。",
accent: "#ef4444",
accentSoft: "rgba(239, 68, 68, 0.14)",
cards: [
{ label: "服务长者", value: "214", detail: "高龄长者 38 人" },
{ label: "本月上门关怀", value: "486", detail: "医养联动 62 次" },
{ label: "健康预警", value: "11", detail: "重点跟进对象" },
{ label: "满意度", value: "4.88", detail: "家属反馈良好" }
],
headers: ["项目名称", "服务长者", "本月关怀", "医养联动", "健康预警", "满意度", "负责人"],
rows: [
["循环花园一期", "56", "128", "18", "3", "4.92", "郭晓"],
["博万物", "44", "103", "14", "2", "4.85", "李佑聪"],
["美好花园", "39", "92", "12", "2", "4.81", "何琳"],
["连城花园", "37", "86", "10", "2", "4.79", "曾丽娜"],
["江南世家一期", "38", "77", "8", "2", "4.84", "陈谷先"]
]
},
bankEnterprise: {
eyebrow: "业财税银",
title: "银企直联总览",
summary: "展示银行账户接入、支付通道状态与对账进度,替代空态页。",
accent: "#0ea5e9",
accentSoft: "rgba(14, 165, 233, 0.14)",
cards: [
{ label: "已接银行账户", value: "18", detail: "覆盖 9 个项目" },
{ label: "本月支付笔数", value: "1,286", detail: "对账完成 96.8%" },
{ label: "待处理回单", value: "12", detail: "需财务复核" },
{ label: "支付成功率", value: "99.2%", detail: "通道状态稳定" }
],
headers: ["项目名称", "开户行", "账户类型", "本月支付笔数", "支付金额", "对账完成率", "状态"],
rows: [
["循环花园一期", "中国银行", "基本户", "286", "¥684,200", "98.6%", "正常"],
["博万物", "建设银行", "一般户", "241", "¥512,800", "96.2%", "正常"],
["美好花园", "工商银行", "基本户", "214", "¥468,600", "95.8%", "正常"],
["连城花园", "农业银行", "一般户", "198", "¥392,400", "94.7%", "待复核"],
["江南世家一期", "招商银行", "基本户", "173", "¥318,900", "97.9%", "正常"]
]
},
financialAccount: {
eyebrow: "业财税银",
title: "财务核算总览",
summary: "展示核算主体、账套状态与月度结账进度,替代空态页。",
accent: "#8b5cf6",
accentSoft: "rgba(139, 92, 246, 0.14)",
cards: [
{ label: "核算主体", value: "11", detail: "账套均在线" },
{ label: "本月结账率", value: "81.8%", detail: "9/11 主体已完成" },
{ label: "待审核凭证", value: "42", detail: "需本周内清理" },
{ label: "异常科目", value: "6", detail: "需复核映射关系" }
],
headers: ["项目名称", "核算主体", "账套状态", "本月凭证数", "待审核凭证", "结账进度", "负责人"],
rows: [
["循环花园一期", "循环花园物业服务主体", "正常", "128", "6", "100%", "陈谷先"],
["博万物", "博万物运营主体", "正常", "116", "8", "92%", "何琳"],
["美好花园", "美好花园服务主体", "正常", "104", "9", "88%", "郭晓"],
["连城花园", "连城花园管理主体", "关注", "97", "11", "74%", "曾丽娜"],
["江南世家一期", "江南世家服务主体", "正常", "82", "8", "79%", "周宇"]
]
},
financialVoucher: {
eyebrow: "业财税银",
title: "财务凭证总览",
summary: "汇总月度凭证流转、审核效率与异常凭证,替代空态页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "本月凭证", value: "468", detail: "自动生成占比 62%" },
{ label: "已审核", value: "426", detail: "审核率 91.0%" },
{ label: "异常凭证", value: "14", detail: "需人工校正" },
{ label: "平均审核时长", value: "3.2h", detail: "较上月 -0.6h" }
],
headers: ["项目名称", "本月凭证", "自动生成", "已审核", "异常凭证", "审核率", "平均审核时长"],
rows: [
["循环花园一期", "112", "74", "106", "2", "94.6%", "2.6h"],
["博万物", "98", "63", "89", "4", "90.8%", "3.1h"],
["美好花园", "91", "58", "82", "3", "90.1%", "3.4h"],
["连城花园", "86", "52", "75", "3", "87.2%", "3.8h"],
["江南世家一期", "81", "43", "74", "2", "91.4%", "3.0h"]
]
},
taxCoordination: {
eyebrow: "业财税银",
title: "税务统筹总览",
summary: "展示申报进度、风险事项与税负变化,替代空态页。",
accent: "#ef4444",
accentSoft: "rgba(239, 68, 68, 0.14)",
cards: [
{ label: "本月申报主体", value: "11", detail: "已申报 9 个" },
{ label: "税负率", value: "5.86%", detail: "较上月 -0.22%" },
{ label: "风险事项", value: "4", detail: "逾期 0 项" },
{ label: "待补资料", value: "7", detail: "需市场侧配合" }
],
headers: ["项目名称", "申报状态", "本月税额", "税负率", "风险事项", "待补资料", "负责人"],
rows: [
["循环花园一期", "已申报", "¥82,400", "5.42%", "0", "1", "陈谷先"],
["博万物", "已申报", "¥74,800", "5.91%", "1", "2", "何琳"],
["美好花园", "已申报", "¥68,200", "5.76%", "1", "1", "郭晓"],
["连城花园", "待复核", "¥63,900", "6.11%", "1", "2", "曾丽娜"],
["江南世家一期", "已申报", "¥58,700", "6.03%", "1", "1", "周宇"]
]
},
serviceProvider: {
eyebrow: "社区治理",
title: "服务商管理总览",
summary: "汇总服务商覆盖、合作状态与履约评分,替代空态页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "合作服务商", value: "42", detail: "有效合作 36 家" },
{ label: "本月履约率", value: "92.6%", detail: "较上月 +1.7%" },
{ label: "待签约", value: "5", detail: "需合同跟进" },
{ label: "平均评分", value: "4.74", detail: "投诉 2 单" }
],
headers: ["项目名称", "服务商", "服务类型", "合作状态", "履约评分", "本月工单", "负责人"],
rows: [
["循环花园一期", "星河保洁服务有限公司", "保洁服务", "合作中", "4.92", "86", "郭晓"],
["博万物", "万嘉设备维保有限公司", "设备维保", "合作中", "4.81", "72", "何琳"],
["美好花园", "城安安防科技有限公司", "安防服务", "合作中", "4.73", "64", "李佑聪"],
["连城花园", "绿景园林服务有限公司", "园林养护", "待续签", "4.66", "58", "曾丽娜"],
["江南世家一期", "社邻家政服务有限公司", "社区家政", "待签约", "4.58", "41", "陈谷先"]
]
},
cleanAssessmentTraining: {
eyebrow: "清洁管理",
title: "清洁考核培训总览",
summary: "汇总保洁班组培训计划、考核得分与复训状态,替代空态页。",
accent: "#06b6d4",
accentSoft: "rgba(6, 182, 212, 0.14)",
cards: [
{ label: "培训计划", value: "42", detail: "本月新增 6 场" },
{ label: "参训人数", value: "186", detail: "到课率 94.1%" },
{ label: "平均得分", value: "88.6", detail: "高于目标线" },
{ label: "待复训", value: "8", detail: "需下周安排" }
],
headers: ["项目名称", "培训主题", "参训人数", "考核得分", "复训状态", "负责人"],
rows: [
["循环花园一期", "夜间保洁标准化", "36", "92.4", "已完成", "郭晓"],
["博万物", "地下车库清洁规范", "28", "88.1", "待复训", "何琳"],
["美好花园", "垃圾分类流程", "31", "87.6", "已完成", "曾丽娜"],
["连城花园", "专项清洁作业", "24", "84.9", "进行中", "周宇"],
["江南世家一期", "应急保洁演练", "19", "89.3", "已完成", "陈谷先"]
]
},
cleanSpareParts: {
eyebrow: "清洁管理",
title: "清洁备品备件总览",
summary: "展示清洁耗材库存、补货周期与异常库存,替代空态页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "SKU 数量", value: "86", detail: "低库存 7 项" },
{ label: "本月领用", value: "¥38,600", detail: "较上月 -3.8%" },
{ label: "待补货", value: "12", detail: "3 项紧急" },
{ label: "周转天数", value: "18.4", detail: "库存结构稳定" }
],
headers: ["项目名称", "物料名称", "库存数量", "月消耗", "安全库存", "状态", "库管员"],
rows: [
["循环花园一期", "清洁剂", "128", "36", "80", "正常", "郭晓"],
["博万物", "垃圾袋", "92", "48", "60", "关注", "何琳"],
["美好花园", "拖布头", "74", "22", "40", "正常", "曾丽娜"],
["连城花园", "消毒液", "38", "19", "30", "预警", "周宇"],
["江南世家一期", "尘推布", "56", "18", "32", "正常", "陈谷先"]
]
},
elevatorAssessmentTraining: {
eyebrow: "电梯管理",
title: "电梯考核培训总览",
summary: "汇总维保培训、困人演练与考核得分,替代空态页。",
accent: "#7c3aed",
accentSoft: "rgba(124, 58, 237, 0.14)",
cards: [
{ label: "培训批次", value: "18", detail: "季度滚动计划" },
{ label: "参训人数", value: "74", detail: "到课率 96.2%" },
{ label: "演练通过率", value: "93.4%", detail: "困人演练良好" },
{ label: "待复训", value: "4", detail: "新员工为主" }
],
headers: ["项目名称", "培训主题", "参训人数", "得分", "演练结果", "负责人"],
rows: [
["循环花园一期", "困人救援演练", "18", "94.8", "通过", "何琳"],
["博万物", "年检流程培训", "12", "91.2", "通过", "郭晓"],
["美好花园", "维保质量复训", "14", "89.6", "待复训", "曾丽娜"],
["连城花园", "设备安全管理", "11", "87.3", "通过", "周宇"],
["江南世家一期", "电梯巡检规范", "9", "92.4", "通过", "陈谷先"]
]
},
elevatorSpareParts: {
eyebrow: "电梯管理",
title: "电梯备品备件总览",
summary: "展示电梯备件库存、使用频率与关键缺件,替代空态页。",
accent: "#f59e0b",
accentSoft: "rgba(245, 158, 11, 0.14)",
cards: [
{ label: "备件种类", value: "54", detail: "关键件 12 类" },
{ label: "本月领用", value: "32", detail: "较上月 +4" },
{ label: "低库存", value: "6", detail: "需本周补货" },
{ label: "完好率", value: "97.1%", detail: "仓储正常" }
],
headers: ["项目名称", "备件名称", "库存数量", "月领用", "安全库存", "状态", "库管员"],
rows: [
["循环花园一期", "门机皮带", "24", "6", "12", "正常", "何琳"],
["博万物", "层门锁触点", "18", "4", "10", "正常", "郭晓"],
["美好花园", "按钮面板", "9", "3", "8", "关注", "曾丽娜"],
["连城花园", "轿厢风扇", "6", "2", "6", "预警", "周宇"],
["江南世家一期", "编码器", "11", "1", "5", "正常", "陈谷先"]
]
},
equipmentAssessmentTraining: {
eyebrow: "设备管理",
title: "设备考核培训总览",
summary: "汇总设备维保培训、巡检演练与得分,替代空态页。",
accent: "#00a870",
accentSoft: "rgba(0, 168, 112, 0.14)",
cards: [
{ label: "培训场次", value: "26", detail: "覆盖 8 类设备" },
{ label: "参训人数", value: "96", detail: "到课率 95.3%" },
{ label: "平均得分", value: "90.8", detail: "维保体系稳定" },
{ label: "待整改项", value: "7", detail: "需复盘" }
],
headers: ["项目名称", "培训主题", "参训人数", "得分", "整改项", "负责人"],
rows: [
["循环花园一期", "消防系统培训", "22", "93.1", "1", "何琳"],
["博万物", "供配电巡检培训", "18", "90.4", "2", "郭晓"],
["美好花园", "泵房维护复训", "17", "89.6", "1", "曾丽娜"],
["连城花园", "弱电设备培训", "14", "88.8", "2", "周宇"],
["江南世家一期", "设施保养培训", "13", "92.0", "1", "陈谷先"]
]
},
equipmentSpareParts: {
eyebrow: "设备管理",
title: "设备备品备件总览",
summary: "展示设备备件库存、消耗与缺件预警,替代空态页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.14)",
cards: [
{ label: "备件种类", value: "138", detail: "关键件 28 类" },
{ label: "本月消耗", value: "¥46,200", detail: "较上月 -2.7%" },
{ label: "低库存", value: "11", detail: "紧急 3 项" },
{ label: "完好率", value: "96.4%", detail: "库存状态可控" }
],
headers: ["项目名称", "备件名称", "库存数量", "月消耗", "安全库存", "状态", "库管员"],
rows: [
["循环花园一期", "消防探测器", "62", "18", "30", "正常", "何琳"],
["博万物", "压力开关", "41", "12", "20", "正常", "郭晓"],
["美好花园", "接触器", "24", "8", "12", "关注", "曾丽娜"],
["连城花园", "轴承组件", "17", "6", "10", "正常", "周宇"],
["江南世家一期", "信号模块", "9", "4", "8", "预警", "陈谷先"]
]
},
greenAssessmentTraining: {
eyebrow: "绿化管理",
title: "绿化考核培训总览",
summary: "汇总绿化养护培训、病虫害演练与考核得分,替代空态页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "培训场次", value: "21", detail: "覆盖 6 类养护主题" },
{ label: "参训人数", value: "84", detail: "到课率 93.8%" },
{ label: "平均得分", value: "89.2", detail: "高于月目标" },
{ label: "待复训", value: "5", detail: "多为新员工" }
],
headers: ["项目名称", "培训主题", "参训人数", "得分", "复训状态", "负责人"],
rows: [
["循环花园一期", "乔木修剪规范", "18", "91.4", "已完成", "郭晓"],
["博万物", "病虫害识别", "16", "88.9", "待复训", "何琳"],
["美好花园", "草坪养护标准", "15", "90.2", "已完成", "曾丽娜"],
["连城花园", "季节性补植培训", "13", "87.3", "进行中", "周宇"],
["江南世家一期", "灌溉系统操作", "11", "88.1", "已完成", "陈谷先"]
]
},
greenSpareParts: {
eyebrow: "绿化管理",
title: "绿化备品备件总览",
summary: "展示绿化工具、药剂和补植物资库存,替代空态页。",
accent: "#84cc16",
accentSoft: "rgba(132, 204, 22, 0.14)",
cards: [
{ label: "物资种类", value: "72", detail: "关键项 14 类" },
{ label: "本月消耗", value: "¥24,800", detail: "较上月 +3.2%" },
{ label: "低库存", value: "9", detail: "需补货 2 项" },
{ label: "完好率", value: "95.2%", detail: "仓储正常" }
],
headers: ["项目名称", "物资名称", "库存数量", "月消耗", "安全库存", "状态", "库管员"],
rows: [
["循环花园一期", "复合肥", "48", "12", "20", "正常", "郭晓"],
["博万物", "修剪刀", "26", "6", "10", "正常", "何琳"],
["美好花园", "杀菌剂", "18", "5", "8", "关注", "曾丽娜"],
["连城花园", "草籽", "12", "4", "6", "预警", "周宇"],
["江南世家一期", "滴灌接头", "24", "3", "10", "正常", "陈谷先"]
]
},
securityAssessmentTraining: {
eyebrow: "安防管理",
title: "安防考核培训总览",
summary: "汇总巡更、门岗和值守演练培训,替代空态页。",
accent: "#ef4444",
accentSoft: "rgba(239, 68, 68, 0.14)",
cards: [
{ label: "培训批次", value: "24", detail: "本月滚动培训" },
{ label: "参训人数", value: "102", detail: "到课率 95.1%" },
{ label: "平均得分", value: "91.3", detail: "安防体系稳定" },
{ label: "待复训", value: "6", detail: "夜班岗为主" }
],
headers: ["项目名称", "培训主题", "参训人数", "得分", "复训状态", "负责人"],
rows: [
["循环花园一期", "门岗值守规范", "22", "93.5", "已完成", "何琳"],
["博万物", "监控巡检流程", "19", "90.4", "进行中", "郭晓"],
["美好花园", "消防联动演练", "18", "92.1", "已完成", "曾丽娜"],
["连城花园", "夜间巡更培训", "16", "88.8", "待复训", "周宇"],
["江南世家一期", "突发事件上报", "14", "91.7", "已完成", "陈谷先"]
]
},
securitySpareParts: {
eyebrow: "安防管理",
title: "安防备品备件总览",
summary: "展示安防设备备件、耗材和缺件预警,替代空态页。",
accent: "#f43f5e",
accentSoft: "rgba(244, 63, 94, 0.14)",
cards: [
{ label: "备件种类", value: "66", detail: "摄像头 / 门禁 / 对讲" },
{ label: "本月消耗", value: "¥31,400", detail: "较上月 +1.6%" },
{ label: "低库存", value: "8", detail: "紧急 2 项" },
{ label: "完好率", value: "97.8%", detail: "库存状态可控" }
],
headers: ["项目名称", "备件名称", "库存数量", "月消耗", "安全库存", "状态", "库管员"],
rows: [
["循环花园一期", "摄像头模组", "34", "8", "12", "正常", "何琳"],
["博万物", "门禁卡", "186", "42", "80", "正常", "郭晓"],
["美好花园", "对讲电源", "16", "4", "8", "关注", "曾丽娜"],
["连城花园", "硬盘录像机盘位", "7", "2", "5", "预警", "周宇"],
["江南世家一期", "报警探测器", "22", "6", "10", "正常", "陈谷先"]
]
},
parkingAssessmentTraining: {
eyebrow: "车场管理",
title: "车场考核培训总览",
summary: "汇总车场收费、道闸处理和现场演练培训,替代空态页。",
accent: "#7c4dff",
accentSoft: "rgba(124, 77, 255, 0.14)",
cards: [
{ label: "培训批次", value: "16", detail: "收费岗与巡查岗" },
{ label: "参训人数", value: "58", detail: "到课率 96.7%" },
{ label: "平均得分", value: "90.1", detail: "车场运营稳定" },
{ label: "待复训", value: "3", detail: "新岗补训" }
],
headers: ["项目名称", "培训主题", "参训人数", "得分", "复训状态", "负责人"],
rows: [
["循环花园一期", "道闸异常处理", "14", "92.6", "已完成", "何琳"],
["博万物", "收费岗交接培训", "12", "89.7", "已完成", "郭晓"],
["美好花园", "夜间来访登记", "11", "88.4", "待复训", "曾丽娜"],
["连城花园", "车牌识别演练", "10", "90.8", "已完成", "周宇"],
["江南世家一期", "临停收费规范", "11", "89.0", "进行中", "陈谷先"]
]
},
balanceSheet: {
eyebrow: "财务报表",
title: "资产负债表总览",
summary: "基于镜像静态样本重建资产、负债与净资产概览,替代空态页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.14)",
cards: [
{ label: "资产总计", value: "¥8,426,000", detail: "较年初 +6.8%" },
{ label: "负债合计", value: "¥3,286,000", detail: "负债率 39.0%" },
{ label: "所有者权益", value: "¥5,140,000", detail: "权益结构稳定" },
{ label: "流动比率", value: "1.92", detail: "短期偿债正常" }
],
headers: ["项目名称", "流动资产", "非流动资产", "资产总计", "流动负债", "非流动负债", "负债合计", "所有者权益"],
rows: [
["循环花园一期", "¥1,286,000", "¥842,000", "¥2,128,000", "¥624,000", "¥182,000", "¥806,000", "¥1,322,000"],
["博万物", "¥1,164,000", "¥736,000", "¥1,900,000", "¥592,000", "¥168,000", "¥760,000", "¥1,140,000"],
["美好花园", "¥1,082,000", "¥704,000", "¥1,786,000", "¥548,000", "¥154,000", "¥702,000", "¥1,084,000"],
["连城花园", "¥968,000", "¥642,000", "¥1,610,000", "¥486,000", "¥136,000", "¥622,000", "¥988,000"],
["江南世家一期", "¥594,000", "¥408,000", "¥1,002,000", "¥284,000", "¥112,000", "¥396,000", "¥606,000"]
]
},
cashFlowStatement: {
eyebrow: "财务报表",
title: "现金流量表总览",
summary: "重建经营、投资、筹资现金流概览,替代空态页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "经营净现金流", value: "¥1,286,000", detail: "核心业务稳定" },
{ label: "投资净现金流", value: "-¥326,000", detail: "设备投入增加" },
{ label: "筹资净现金流", value: "¥182,000", detail: "贷款偿付平稳" },
{ label: "现金净增加额", value: "¥1,142,000", detail: "资金安全" }
],
headers: ["项目名称", "经营现金流入", "经营现金流出", "经营净现金流", "投资净现金流", "筹资净现金流", "现金净增加额"],
rows: [
["循环花园一期", "¥2,486,000", "¥1,618,000", "¥868,000", "-¥126,000", "¥52,000", "¥794,000"],
["博万物", "¥2,084,000", "¥1,428,000", "¥656,000", "-¥94,000", "¥48,000", "¥610,000"],
["美好花园", "¥1,876,000", "¥1,294,000", "¥582,000", "-¥72,000", "¥36,000", "¥546,000"],
["连城花园", "¥1,624,000", "¥1,142,000", "¥482,000", "-¥21,000", "¥28,000", "¥489,000"],
["江南世家一期", "¥1,208,000", "¥962,000", "¥246,000", "-¥13,000", "¥18,000", "¥251,000"]
]
},
incomeStatement: {
eyebrow: "财务报表",
title: "全年收入报表总览",
summary: "按项目汇总收入、成本、毛利与净利,替代空态页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "全年收入", value: "¥12,680,000", detail: "较上年 +9.4%" },
{ label: "营业成本", value: "¥8,940,000", detail: "成本率 70.5%" },
{ label: "毛利润", value: "¥3,740,000", detail: "毛利率 29.5%" },
{ label: "净利润", value: "¥2,286,000", detail: "净利率 18.0%" }
],
headers: ["项目名称", "全年收入", "营业成本", "毛利润", "管理费用", "净利润", "净利率"],
rows: [
["循环花园一期", "¥3,186,000", "¥2,186,000", "¥1,000,000", "¥318,000", "¥642,000", "20.2%"],
["博万物", "¥2,648,000", "¥1,862,000", "¥786,000", "¥264,000", "¥486,000", "18.4%"],
["美好花园", "¥2,214,000", "¥1,614,000", "¥600,000", "¥208,000", "¥356,000", "16.1%"],
["连城花园", "¥1,986,000", "¥1,434,000", "¥552,000", "¥186,000", "¥318,000", "16.0%"],
["江南世家一期", "¥1,646,000", "¥1,132,000", "¥514,000", "¥148,000", "¥284,000", "17.3%"]
]
},
profitSurface: {
eyebrow: "财务报表",
title: "利润表总览",
summary: "按项目展示利润结构、费用与利润率,替代空态页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "营业利润", value: "¥2,948,000", detail: "较上月 +5.3%" },
{ label: "利润率", value: "23.2%", detail: "经营状态良好" },
{ label: "费用率", value: "9.6%", detail: "可继续优化" },
{ label: "亏损项目", value: "0", detail: "本月无亏损项目" }
],
headers: ["项目名称", "营业收入", "营业成本", "销售费用", "管理费用", "营业利润", "利润率"],
rows: [
["循环花园一期", "¥1,286,000", "¥824,000", "¥42,000", "¥86,000", "¥334,000", "26.0%"],
["博万物", "¥1,084,000", "¥712,000", "¥38,000", "¥74,000", "¥260,000", "24.0%"],
["美好花园", "¥986,000", "¥664,000", "¥32,000", "¥68,000", "¥222,000", "22.5%"],
["连城花园", "¥842,000", "¥598,000", "¥28,000", "¥61,000", "¥155,000", "18.4%"],
["江南世家一期", "¥714,000", "¥486,000", "¥21,000", "¥48,000", "¥159,000", "22.3%"]
]
},
projectIncome: {
eyebrow: "财务报表",
title: "项目收支表总览",
summary: "聚合项目收入、支出、预算执行与收支结余,替代空态页。",
accent: "#8b5cf6",
accentSoft: "rgba(139, 92, 246, 0.14)",
cards: [
{ label: "项目收入", value: "¥8,864,000", detail: "本月累计" },
{ label: "项目支出", value: "¥6,428,000", detail: "预算执行 91.2%" },
{ label: "收支结余", value: "¥2,436,000", detail: "结余率 27.5%" },
{ label: "超预算项目", value: "3", detail: "需专项复盘" }
],
headers: ["项目名称", "项目收入", "项目支出", "收支结余", "预算执行率", "本月回款", "超预算项"],
rows: [
["循环花园一期", "¥2,186,000", "¥1,548,000", "¥638,000", "92.4%", "¥246,000", "0"],
["博万物", "¥1,946,000", "¥1,382,000", "¥564,000", "89.8%", "¥214,000", "1"],
["美好花园", "¥1,782,000", "¥1,296,000", "¥486,000", "90.6%", "¥198,000", "1"],
["连城花园", "¥1,612,000", "¥1,168,000", "¥444,000", "91.1%", "¥176,000", "0"],
["江南世家一期", "¥1,338,000", "¥1,034,000", "¥304,000", "93.5%", "¥152,000", "1"]
]
},
laborQuota: {
eyebrow: "业财税银",
title: "劳动定额总览",
summary: "汇总定额岗位、人工配置与执行偏差,替代等待页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.14)",
cards: [
{ label: "定额岗位", value: "28", detail: "覆盖保洁/安防/工程" },
{ label: "标准工时", value: "4,820h", detail: "月度核定口径" },
{ label: "执行偏差", value: "3.6%", detail: "较上月收敛" },
{ label: "超编项目", value: "2", detail: "需复核岗位配置" }
],
headers: ["项目名称", "岗位类型", "核定人数", "标准工时", "执行偏差", "负责人"],
rows: [
["循环花园一期", "环境巡查", "12", "1,260h", "2.8%", "郭晓"],
["博万物", "设备运维", "9", "980h", "4.1%", "何琳"],
["江南世家一期", "安防值守", "8", "860h", "3.3%", "陈谷先"]
]
},
resourceManage: {
eyebrow: "业财税银",
title: "资源管理总览",
summary: "展示资源档案、利用率与收益分布,替代等待页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "资源档案", value: "86", detail: "场地/广告位/配套资源" },
{ label: "利用率", value: "91.4%", detail: "本月稳定提升" },
{ label: "资源收益", value: "¥268,400", detail: "较上月 +5.2%" },
{ label: "待上架", value: "6", detail: "均在补资料" }
],
headers: ["项目名称", "资源类型", "档案数量", "利用率", "本月收益", "负责人"],
rows: [
["循环花园一期", "广告位", "28", "94.2%", "¥82,600", "郭晓"],
["博万物", "共享场地", "17", "88.6%", "¥64,800", "何琳"],
["江南世家一期", "便民柜机", "9", "92.1%", "¥28,300", "陈谷先"]
]
}
};
const HCPOS_FINANCE_REPORT_PRESETS = {
balanceSheet: {
eyebrow: "财务报表",
title: "资产负债表总览",
summary: "基于镜像静态样本重建资产、负债与净资产概览,替代空态页。",
accent: "#2d76ff",
accentSoft: "rgba(45, 118, 255, 0.14)",
cards: [
{ label: "资产总计", value: "¥8,426,000", detail: "较年初 +6.8%" },
{ label: "负债合计", value: "¥3,286,000", detail: "负债率 39.0%" },
{ label: "所有者权益", value: "¥5,140,000", detail: "权益结构稳定" },
{ label: "流动比率", value: "1.92", detail: "短期偿债正常" }
],
headers: ["项目名称", "流动资产", "非流动资产", "资产总计", "流动负债", "非流动负债", "负债合计", "所有者权益"],
rows: [
["循环花园一期", "¥1,286,000", "¥842,000", "¥2,128,000", "¥624,000", "¥182,000", "¥806,000", "¥1,322,000"],
["博万物", "¥1,164,000", "¥736,000", "¥1,900,000", "¥592,000", "¥168,000", "¥760,000", "¥1,140,000"],
["美好花园", "¥1,082,000", "¥704,000", "¥1,786,000", "¥548,000", "¥154,000", "¥702,000", "¥1,084,000"],
["连城花园", "¥968,000", "¥642,000", "¥1,610,000", "¥486,000", "¥136,000", "¥622,000", "¥988,000"],
["江南世家一期", "¥594,000", "¥408,000", "¥1,002,000", "¥284,000", "¥112,000", "¥396,000", "¥606,000"]
]
},
cashFlowStatement: {
eyebrow: "财务报表",
title: "现金流量表总览",
summary: "重建经营、投资、筹资现金流概览,替代空态页。",
accent: "#14b8a6",
accentSoft: "rgba(20, 184, 166, 0.14)",
cards: [
{ label: "经营净现金流", value: "¥1,286,000", detail: "核心业务稳定" },
{ label: "投资净现金流", value: "-¥326,000", detail: "设备投入增加" },
{ label: "筹资净现金流", value: "¥182,000", detail: "贷款偿付平稳" },
{ label: "现金净增加额", value: "¥1,142,000", detail: "资金安全" }
],
headers: ["项目名称", "经营现金流入", "经营现金流出", "经营净现金流", "投资净现金流", "筹资净现金流", "现金净增加额"],
rows: [
["循环花园一期", "¥2,486,000", "¥1,618,000", "¥868,000", "-¥126,000", "¥52,000", "¥794,000"],
["博万物", "¥2,084,000", "¥1,428,000", "¥656,000", "-¥94,000", "¥48,000", "¥610,000"],
["美好花园", "¥1,876,000", "¥1,294,000", "¥582,000", "-¥72,000", "¥36,000", "¥546,000"],
["连城花园", "¥1,624,000", "¥1,142,000", "¥482,000", "-¥21,000", "¥28,000", "¥489,000"],
["江南世家一期", "¥1,208,000", "¥962,000", "¥246,000", "-¥13,000", "¥18,000", "¥251,000"]
]
},
incomeStatement: {
eyebrow: "财务报表",
title: "全年收入报表总览",
summary: "按项目汇总收入、成本、毛利与净利,替代空态页。",
accent: "#22c55e",
accentSoft: "rgba(34, 197, 94, 0.14)",
cards: [
{ label: "全年收入", value: "¥12,680,000", detail: "较上年 +9.4%" },
{ label: "营业成本", value: "¥8,940,000", detail: "成本率 70.5%" },
{ label: "毛利润", value: "¥3,740,000", detail: "毛利率 29.5%" },
{ label: "净利润", value: "¥2,286,000", detail: "净利率 18.0%" }
],
headers: ["项目名称", "全年收入", "营业成本", "毛利润", "管理费用", "净利润", "净利率"],
rows: [
["循环花园一期", "¥3,186,000", "¥2,186,000", "¥1,000,000", "¥318,000", "¥642,000", "20.2%"],
["博万物", "¥2,648,000", "¥1,862,000", "¥786,000", "¥264,000", "¥486,000", "18.4%"],
["美好花园", "¥2,214,000", "¥1,614,000", "¥600,000", "¥208,000", "¥356,000", "16.1%"],
["连城花园", "¥1,986,000", "¥1,434,000", "¥552,000", "¥186,000", "¥318,000", "16.0%"],
["江南世家一期", "¥1,646,000", "¥1,132,000", "¥514,000", "¥148,000", "¥284,000", "17.3%"]
]
},
profitSurface: {
eyebrow: "财务报表",
title: "利润表总览",
summary: "按项目展示利润结构、费用与利润率,替代空态页。",
accent: "#f97316",
accentSoft: "rgba(249, 115, 22, 0.14)",
cards: [
{ label: "营业利润", value: "¥2,948,000", detail: "较上月 +5.3%" },
{ label: "利润率", value: "23.2%", detail: "经营状态良好" },
{ label: "费用率", value: "9.6%", detail: "可继续优化" },
{ label: "亏损项目", value: "0", detail: "本月无亏损项目" }
],
headers: ["项目名称", "营业收入", "营业成本", "销售费用", "管理费用", "营业利润", "利润率"],
rows: [
["循环花园一期", "¥1,286,000", "¥824,000", "¥42,000", "¥86,000", "¥334,000", "26.0%"],
["博万物", "¥1,084,000", "¥712,000", "¥38,000", "¥74,000", "¥260,000", "24.0%"],
["美好花园", "¥986,000", "¥664,000", "¥32,000", "¥68,000", "¥222,000", "22.5%"],
["连城花园", "¥842,000", "¥598,000", "¥28,000", "¥61,000", "¥155,000", "18.4%"],
["江南世家一期", "¥714,000", "¥486,000", "¥21,000", "¥48,000", "¥159,000", "22.3%"]
]
},
projectIncome: {
eyebrow: "财务报表",
title: "项目收支表总览",
summary: "聚合项目收入、支出、预算执行与收支结余,替代空态页。",
accent: "#8b5cf6",
accentSoft: "rgba(139, 92, 246, 0.14)",
cards: [
{ label: "项目收入", value: "¥8,864,000", detail: "本月累计" },
{ label: "项目支出", value: "¥6,428,000", detail: "预算执行 91.2%" },
{ label: "收支结余", value: "¥2,436,000", detail: "结余率 27.5%" },
{ label: "超预算项目", value: "3", detail: "需专项复盘" }
],
headers: ["项目名称", "项目收入", "项目支出", "收支结余", "预算执行率", "本月回款", "超预算项"],
rows: [
["循环花园一期", "¥2,186,000", "¥1,548,000", "¥638,000", "92.4%", "¥246,000", "0"],
["博万物", "¥1,946,000", "¥1,382,000", "¥564,000", "89.8%", "¥214,000", "1"],
["美好花园", "¥1,782,000", "¥1,296,000", "¥486,000", "90.6%", "¥198,000", "1"],
["连城花园", "¥1,612,000", "¥1,168,000", "¥444,000", "91.1%", "¥176,000", "0"],
["江南世家一期", "¥1,338,000", "¥1,034,000", "¥304,000", "93.5%", "¥152,000", "1"]
]
}
};
function buildHcPosSoftAccent(hex, alpha) {
const normalized = (hex || "").replace("#", "");
const full = normalized.length === 3 ? normalized.split("").map((item) => `${item}${item}`).join("") : normalized;
const value = Number.parseInt(full || "2d76ff", 16);
const r = (value >> 16) & 255;
const g = (value >> 8) & 255;
const b = value & 255;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
function createHcPosStaticPreset(preset) {
return {
...preset,
accentSoft: preset.accentSoft || buildHcPosSoftAccent(preset.accent || "#2d76ff", 0.14)
};
}
const HCPOS_WAIT_PAGE_PRESETS = {
earlyPartnership: createHcPosStaticPreset({
eyebrow: "前介管理",
title: "入伙管理总览",
summary: "汇总入伙交付、钥匙移交与缺陷闭环,替代等待页。",
accent: "#2d76ff",
cards: [
{ label: "待交付楼栋", value: "6", detail: "本月计划 3 栋" },
{ label: "已验房户数", value: "186", detail: "完成率 92.5%" },
{ label: "钥匙移交", value: "172", detail: "待领取 14 户" },
{ label: "整改闭环", value: "94.1%", detail: "遗留缺陷 11 项" }
],
headers: ["项目名称", "楼栋", "本周交付", "钥匙移交", "整改闭环", "负责人"],
rows: [
["循环花园一期", "1-3 栋", "42 户", "39 户", "96%", "郭晓"],
["博万物", "A/B 座", "31 户", "28 户", "92%", "何琳"],
["江南世家一期", "5-6 栋", "26 户", "24 户", "89%", "陈谷先"]
]
}),
earlySalesCooperation: createHcPosStaticPreset({
eyebrow: "前介管理",
title: "售楼配合总览",
summary: "展示样板间巡检、看房接待与销售协同进度,替代等待页。",
accent: "#14b8a6",
cards: [
{ label: "本周接待", value: "128", detail: "到访转化 37.5%" },
{ label: "样板间巡检", value: "42", detail: "问题已闭环 38 项" },
{ label: "销售协同单", value: "23", detail: "超时 2 单" },
{ label: "满意度", value: "4.86", detail: "客户反馈稳定" }
],
headers: ["项目名称", "配合场景", "本周接待", "协同事项", "闭环率", "接口人"],
rows: [
["循环花园一期", "样板间接待", "48", "9", "100%", "郭晓"],
["博万物", "销售说辞配合", "36", "8", "87%", "何琳"],
["江南世家一期", "交付展示支持", "22", "6", "83%", "陈谷先"]
]
}),
earlyUndertakeInspection: createHcPosStaticPreset({
eyebrow: "前介管理",
title: "承接查验总览",
summary: "汇总查验批次、问题整改与移交节点,替代等待页。",
accent: "#8b5cf6",
cards: [
{ label: "查验批次", value: "18", detail: "本月新增 4 批" },
{ label: "查验问题", value: "126", detail: "已闭环 109 项" },
{ label: "待移交区域", value: "5", detail: "涉及公区与设备房" },
{ label: "闭环率", value: "86.5%", detail: "重点问题 7 项" }
],
headers: ["项目名称", "查验区域", "问题总数", "已闭环", "待移交", "负责人"],
rows: [
["循环花园一期", "公区楼栋", "46", "41", "1", "郭晓"],
["博万物", "地下车库", "38", "31", "2", "何琳"],
["江南世家一期", "设备机房", "27", "22", "2", "陈谷先"]
]
}),
elevatorYearly: createHcPosStaticPreset({
eyebrow: "电梯管理",
title: "电梯年检总览",
summary: "汇总年检到期、检验状态与整改进度,替代等待页。",
accent: "#7c3aed",
cards: [
{ label: "年检设备", value: "48", detail: "30 天内到期 9 台" },
{ label: "已送检", value: "39", detail: "通过率 91.7%" },
{ label: "待整改", value: "6", detail: "均已派单" },
{ label: "复检排期", value: "3", detail: "本周完成" }
],
headers: ["项目名称", "设备编号", "年检到期", "检验状态", "整改项", "维保单位"],
rows: [
["循环花园一期", "DT-01", "2026-04-18", "已通过", "0", "万嘉电梯"],
["博万物", "DT-11", "2026-04-22", "整改中", "2", "城安机电"],
["江南世家一期", "DT-07", "2026-04-29", "待送检", "1", "华升特设"]
]
}),
energyMeterReadingCharge: createHcPosStaticPreset({
eyebrow: "能源管控",
title: "抄表收费总览",
summary: "展示抄表户数、收费金额与收缴进度,替代等待页。",
accent: "#0ea5e9",
cards: [
{ label: "抄表户数", value: "1,286", detail: "本月完成率 97.3%" },
{ label: "应收金额", value: "¥482,600", detail: "已收 91.2%" },
{ label: "待复核账单", value: "12", detail: "集中在商铺户" },
{ label: "异常读数", value: "5", detail: "已转人工核查" }
],
headers: ["项目名称", "抄表户数", "应收金额", "已收金额", "收缴率", "负责人"],
rows: [
["循环花园一期", "386", "¥146,800", "¥138,900", "94.6%", "郭晓"],
["博万物", "312", "¥118,600", "¥106,200", "89.5%", "何琳"],
["江南世家一期", "241", "¥86,300", "¥79,400", "92.0%", "陈谷先"]
]
}),
equipmentEnergySaving: createHcPosStaticPreset({
eyebrow: "设备管理",
title: "节能降耗总览",
summary: "聚合设备节电措施、节能收益与异常耗能点位,替代等待页。",
accent: "#22c55e",
cards: [
{ label: "节能项目", value: "27", detail: "覆盖泵房 / 照明 / 风机" },
{ label: "本月节电", value: "18.4万kWh", detail: "同比下降 6.2%" },
{ label: "节能收益", value: "¥74,200", detail: "成本持续下降" },
{ label: "异常点位", value: "4", detail: "需本周复核" }
],
headers: ["项目名称", "节能措施", "本月节电", "同比变化", "状态", "负责人"],
rows: [
["循环花园一期", "照明联控", "6.8万kWh", "-7.1%", "稳定", "郭晓"],
["博万物", "泵房变频", "5.1万kWh", "-5.4%", "观察", "何琳"],
["江南世家一期", "风机调度", "3.9万kWh", "-6.8%", "稳定", "陈谷先"]
]
}),
fullCleaning: createHcPosStaticPreset({
eyebrow: "全员行动",
title: "全员保洁总览",
summary: "展示集中保洁行动、参与规模与问题闭环,替代等待页。",
accent: "#06b6d4",
cards: [
{ label: "行动场次", value: "14", detail: "本月新增 3 场" },
{ label: "参与人数", value: "186", detail: "覆盖 9 个项目" },
{ label: "整治点位", value: "328", detail: "闭环率 94.8%" },
{ label: "住户好评", value: "96%", detail: "环境反馈明显提升" }
],
headers: ["项目名称", "行动主题", "参与人数", "整治点位", "闭环率", "负责人"],
rows: [
["循环花园一期", "雨后公区清洁", "46", "82", "97%", "郭晓"],
["博万物", "地下车库专项保洁", "38", "71", "93%", "何琳"],
["江南世家一期", "楼道整治提升", "31", "56", "92%", "陈谷先"]
]
}),
fullPatrol: createHcPosStaticPreset({
eyebrow: "全员行动",
title: "全员巡查总览",
summary: "汇总集中巡查发现问题、闭环率与重点隐患,替代等待页。",
accent: "#f97316",
cards: [
{ label: "巡查场次", value: "11", detail: "覆盖重点区域 26 个" },
{ label: "发现问题", value: "94", detail: "已闭环 82 项" },
{ label: "重点隐患", value: "7", detail: "均已派发整改" },
{ label: "闭环率", value: "87.2%", detail: "本周继续跟进" }
],
headers: ["项目名称", "巡查主题", "参与人数", "发现问题", "闭环率", "负责人"],
rows: [
["循环花园一期", "设备与环境联合巡查", "28", "31", "90%", "郭晓"],
["博万物", "高空坠物风险巡查", "24", "26", "85%", "何琳"],
["江南世家一期", "夜间安防巡查", "19", "18", "89%", "陈谷先"]
]
}),
materialScrapDisposal: createHcPosStaticPreset({
eyebrow: "物资管理",
title: "报废处理总览",
summary: "汇总报废物资、审批进度与处置去向,替代等待页。",
accent: "#ef4444",
cards: [
{ label: "报废单数", value: "26", detail: "本月新增 5 单" },
{ label: "报废物资", value: "318 件", detail: "设备件占比 42%" },
{ label: "审批通过", value: "21", detail: "待复核 3 单" },
{ label: "回收金额", value: "¥18,600", detail: "较上月 +11.4%" }
],
headers: ["项目名称", "报废物资", "数量", "审批状态", "处置方式", "负责人"],
rows: [
["循环花园一期", "废旧探测器", "46", "已通过", "回收入库", "郭晓"],
["博万物", "老旧门禁卡", "128", "待复核", "集中销毁", "何琳"],
["江南世家一期", "损坏工具箱", "22", "已通过", "废品回收", "陈谷先"]
]
}),
parkingAppointmentVisit: createHcPosStaticPreset({
eyebrow: "车场运营",
title: "预约来访总览",
summary: "展示来访预约、放行效率与高峰时段表现,替代等待页。",
accent: "#7c4dff",
cards: [
{ label: "本日预约", value: "186", detail: "较昨日 +12" },
{ label: "已放行", value: "172", detail: "放行率 92.5%" },
{ label: "超时等待", value: "6", detail: "均已处理" },
{ label: "高峰时段", value: "18:00-20:00", detail: "需加岗 1 人" }
],
headers: ["项目名称", "来访类型", "预约数", "已到访", "通过率", "岗亭"],
rows: [
["循环花园一期", "访客车辆", "72", "67", "93.1%", "北门岗"],
["博万物", "商户来访", "58", "51", "87.9%", "西门岗"],
["江南世家一期", "施工车辆", "24", "22", "91.7%", "南门岗"]
]
}),
parkingSuspiciousRecords: createHcPosStaticPreset({
eyebrow: "车场运营",
title: "异常管理总览",
summary: "汇总异常进出、黑名单车辆与核查进度,替代等待页。",
accent: "#ef4444",
cards: [
{ label: "异常记录", value: "42", detail: "重复进出 18 条" },
{ label: "待核查", value: "9", detail: "黑名单 2 辆" },
{ label: "已处置", value: "31", detail: "闭环率 88.6%" },
{ label: "高风险时段", value: "23:00-02:00", detail: "需加强巡查" }
],
headers: ["项目名称", "异常类型", "本月记录", "已闭环", "风险级别", "负责人"],
rows: [
["循环花园一期", "重复进出", "15", "13", "中", "郭晓"],
["博万物", "车牌异常", "12", "9", "高", "何琳"],
["江南世家一期", "逃费嫌疑", "8", "7", "中", "陈谷先"]
]
}),
parkingTemporaryControl: createHcPosStaticPreset({
eyebrow: "车场运营",
title: "临停管控总览",
summary: "展示临停车位利用、收费完成与管控效果,替代等待页。",
accent: "#14b8a6",
cards: [
{ label: "临停车位", value: "486", detail: "高峰利用率 84%" },
{ label: "日均车次", value: "1,128", detail: "周末更高" },
{ label: "临停收费", value: "¥62,400", detail: "本月累计" },
{ label: "异常拦截", value: "17", detail: "已复核完成" }
],
headers: ["项目名称", "临停车位", "日均车次", "异常拦截", "收费率", "负责人"],
rows: [
["循环花园一期", "186", "426", "6", "95.4%", "郭晓"],
["博万物", "144", "358", "5", "91.8%", "何琳"],
["江南世家一期", "102", "221", "3", "93.7%", "陈谷先"]
]
}),
parkingPhysical: createHcPosStaticPreset({
eyebrow: "车场运营",
title: "车场测评总览",
summary: "汇总车场测评得分、整改项与复检状态,替代等待页。",
accent: "#8b5cf6",
cards: [
{ label: "测评批次", value: "12", detail: "覆盖 7 个项目" },
{ label: "平均得分", value: "89.6", detail: "较上月 +1.4" },
{ label: "整改项", value: "16", detail: "待复检 4 项" },
{ label: "优秀率", value: "58.3%", detail: "A 级占比" }
],
headers: ["项目名称", "测评主题", "本月测评", "平均分", "整改项", "负责人"],
rows: [
["循环花园一期", "出入口秩序", "4", "92.4", "2", "郭晓"],
["博万物", "岗亭服务", "3", "88.7", "3", "何琳"],
["江南世家一期", "车场动线", "2", "87.9", "1", "陈谷先"]
]
}),
parkingOwnerArchives: createHcPosStaticPreset({
eyebrow: "车场运营",
title: "车主档案总览",
summary: "汇总车主建档、认证状态与月卡结构,替代等待页。",
accent: "#2d76ff",
cards: [
{ label: "车主档案", value: "2,148", detail: "本月新增 64 份" },
{ label: "已认证", value: "1,986", detail: "认证率 92.5%" },
{ label: "待补资料", value: "42", detail: "集中在租户车主" },
{ label: "月卡车主", value: "1,284", detail: "固定车主占比高" }
],
headers: ["项目名称", "车主档案", "已认证", "待补资料", "月卡车主", "负责人"],
rows: [
["循环花园一期", "726", "683", "12", "438", "郭晓"],
["博万物", "542", "491", "18", "336", "何琳"],
["江南世家一期", "314", "287", "7", "184", "陈谷先"]
]
}),
renovationDeclaration: createHcPosStaticPreset({
eyebrow: "装修管理",
title: "装修报建总览",
summary: "展示装修申请、审批进度与违规预警,替代等待页。",
accent: "#f59e0b",
cards: [
{ label: "报建申请", value: "86", detail: "本月新增 18 单" },
{ label: "已审批", value: "72", detail: "通过率 83.7%" },
{ label: "在审中", value: "9", detail: "平均 1.8 天" },
{ label: "违规预警", value: "3", detail: "均已通知整改" }
],
headers: ["项目名称", "报建申请", "已审批", "在审中", "违规项", "负责人"],
rows: [
["循环花园一期", "28", "23", "3", "1", "郭晓"],
["博万物", "21", "18", "2", "1", "何琳"],
["江南世家一期", "14", "12", "1", "0", "陈谷先"]
]
}),
renovationCheck: createHcPosStaticPreset({
eyebrow: "装修管理",
title: "装修验收总览",
summary: "汇总验收批次、通过率与复检情况,替代等待页。",
accent: "#14b8a6",
cards: [
{ label: "验收批次", value: "52", detail: "本月新增 11 批" },
{ label: "通过率", value: "88.5%", detail: "复检中 5 单" },
{ label: "整改项", value: "18", detail: "噪音粉尘为主" },
{ label: "待复检", value: "5", detail: "均已排期" }
],
headers: ["项目名称", "验收批次", "通过率", "整改项", "复检中", "负责人"],
rows: [
["循环花园一期", "18", "91.6%", "5", "1", "郭晓"],
["博万物", "14", "85.7%", "7", "2", "何琳"],
["江南世家一期", "9", "88.9%", "2", "1", "陈谷先"]
]
}),
renovationFeeManage: createHcPosStaticPreset({
eyebrow: "装修管理",
title: "装修收费管理总览",
summary: "展示装修押金、垃圾清运费与欠费情况,替代等待页。",
accent: "#2d76ff",
cards: [
{ label: "收费单数", value: "74", detail: "本月新增 16 单" },
{ label: "应收金额", value: "¥428,600", detail: "已收 93.4%" },
{ label: "押金冻结", value: "¥216,000", detail: "待退 9 单" },
{ label: "欠费单", value: "4", detail: "均已提醒" }
],
headers: ["项目名称", "收费单数", "应收金额", "已收金额", "欠费单", "负责人"],
rows: [
["循环花园一期", "24", "¥138,000", "¥132,000", "1", "郭晓"],
["博万物", "18", "¥106,400", "¥98,600", "2", "何琳"],
["江南世家一期", "12", "¥68,200", "¥66,900", "0", "陈谷先"]
]
}),
renovationPatrol: createHcPosStaticPreset({
eyebrow: "装修管理",
title: "装修巡查总览",
summary: "汇总日常巡查、违规装修与闭环情况,替代等待页。",
accent: "#ef4444",
cards: [
{ label: "巡查频次", value: "164", detail: "日均 5.4 次" },
{ label: "发现问题", value: "28", detail: "已闭环 24 项" },
{ label: "违规装修", value: "6", detail: "集中在超时施工" },
{ label: "闭环率", value: "85.7%", detail: "需继续跟进" }
],
headers: ["项目名称", "巡查频次", "发现问题", "已闭环", "违规装修", "负责人"],
rows: [
["循环花园一期", "58", "11", "10", "2", "郭晓"],
["博万物", "46", "9", "7", "3", "何琳"],
["江南世家一期", "28", "4", "4", "0", "陈谷先"]
]
}),
satisfactionQuestionnaire: createHcPosStaticPreset({
eyebrow: "客户满意",
title: "调查问卷总览",
summary: "展示问卷发放、回收率与满意度趋势,替代等待页。",
accent: "#8b5cf6",
cards: [
{ label: "问卷主题", value: "18", detail: "覆盖保洁 / 车场 / 安防" },
{ label: "发放数量", value: "3,286", detail: "本月累计" },
{ label: "回收数量", value: "2,614", detail: "回收率 79.5%" },
{ label: "平均满意度", value: "4.73", detail: "较上月 +0.04" }
],
headers: ["项目名称", "问卷主题", "发放数量", "回收数量", "回收率", "负责人"],
rows: [
["循环花园一期", "公区服务体验", "986", "812", "82.4%", "郭晓"],
["博万物", "车场秩序满意度", "742", "568", "76.5%", "何琳"],
["江南世家一期", "安防服务评价", "418", "339", "81.1%", "陈谷先"]
]
}),
securityMonitoringCenter: createHcPosStaticPreset({
eyebrow: "安防管理",
title: "监控中心总览",
summary: "展示监控在线率、离线设备与告警处置,替代等待页。",
accent: "#ef4444",
cards: [
{ label: "在线设备", value: "1,286", detail: "在线率 98.7%" },
{ label: "离线设备", value: "17", detail: "已派检修 9 台" },
{ label: "本月告警", value: "68", detail: "已处置 61 条" },
{ label: "处置率", value: "89.7%", detail: "夜间告警偏多" }
],
headers: ["项目名称", "在线设备", "离线设备", "异常告警", "处置率", "值班长"],
rows: [
["循环花园一期", "426", "4", "18", "94.4%", "郭晓"],
["博万物", "318", "7", "23", "82.6%", "何琳"],
["江南世家一期", "204", "2", "9", "88.9%", "陈谷先"]
]
}),
securityEmergencyManagement: createHcPosStaticPreset({
eyebrow: "安防管理",
title: "应急处理总览",
summary: "汇总应急预案、演练频次与响应时长,替代等待页。",
accent: "#f97316",
cards: [
{ label: "应急预案", value: "24", detail: "覆盖火警 / 停电 / 水浸" },
{ label: "演练次数", value: "18", detail: "季度计划完成" },
{ label: "平均响应", value: "6.8 分钟", detail: "同比缩短 0.9 分钟" },
{ label: "待整改", value: "5", detail: "集中在物资准备" }
],
headers: ["项目名称", "预案类型", "演练次数", "响应时长", "待整改", "负责人"],
rows: [
["循环花园一期", "火警处置", "6", "6.2 分钟", "1", "郭晓"],
["博万物", "停电应急", "5", "7.1 分钟", "2", "何琳"],
["江南世家一期", "水浸处置", "3", "6.9 分钟", "1", "陈谷先"]
]
}),
securityArchives: createHcPosStaticPreset({
eyebrow: "安防管理",
title: "安防档案总览",
summary: "汇总安防档案、证照临期与资料完备度,替代等待页。",
accent: "#2d76ff",
cards: [
{ label: "安防档案", value: "86", detail: "设备 / 人员 / 证照" },
{ label: "完备率", value: "96.5%", detail: "缺失资料 3 份" },
{ label: "临期证照", value: "6", detail: "需本月更新" },
{ label: "更新状态", value: "稳定", detail: "本周新增 4 份" }
],
headers: ["项目名称", "安防档案", "完备率", "临期证照", "更新状态", "负责人"],
rows: [
["循环花园一期", "32", "100%", "1", "正常", "郭晓"],
["博万物", "24", "91.7%", "3", "补录中", "何琳"],
["江南世家一期", "11", "90.9%", "1", "正常", "陈谷先"]
]
}),
securityInternetOfThings: createHcPosStaticPreset({
eyebrow: "安全生产",
title: "物联网总览",
summary: "展示联网设备、在线状态与告警处置,替代等待页。",
accent: "#14b8a6",
cards: [
{ label: "联网设备", value: "624", detail: "覆盖烟感 / 水压 / 门磁" },
{ label: "在线率", value: "97.9%", detail: "离线 13 台" },
{ label: "本月告警", value: "54", detail: "已处置 49 条" },
{ label: "处置率", value: "90.7%", detail: "重点关注夜间告警" }
],
headers: ["项目名称", "联网设备", "在线率", "告警数", "已处置", "负责人"],
rows: [
["循环花园一期", "218", "98.6%", "16", "15", "郭晓"],
["博万物", "176", "96.8%", "21", "18", "何琳"],
["江南世家一期", "114", "97.4%", "9", "8", "陈谷先"]
]
})
};
const HCPOS_WAIT_PAGE_ROUTE_PRESETS = {
"/propertySMG/earlyManagement/partnership/": "earlyPartnership",
"/propertySMG/earlyManagement/salesCooperation/": "earlySalesCooperation",
"/propertySMG/earlyManagement/undertakeInspection/": "earlyUndertakeInspection",
"/propertySMG/elevatorManage/elevatorYearly/": "elevatorYearly",
"/propertySMG/energySourceOperat/publicAreaControl/meterReadingCharge/": "energyMeterReadingCharge",
"/propertySMG/equipmentManage/useManagement/energySaving/": "equipmentEnergySaving",
"/propertySMG/fullForceAssaultSMG/fullCleaning/": "fullCleaning",
"/propertySMG/fullForceAssaultSMG/fullPatrol/": "fullPatrol",
"/propertySMG/materialManage/scrapDisposal/": "materialScrapDisposal",
"/propertySMG/parkingOperation/dailyManage/appointmentVisit/": "parkingAppointmentVisit",
"/propertySMG/parkingOperation/dailyManage/suspiciousRecords/": "parkingSuspiciousRecords",
"/propertySMG/parkingOperation/dailyManage/temporaryControl/": "parkingTemporaryControl",
"/propertySMG/parkingOperation/parkingPhysical/": "parkingPhysical",
"/propertySMG/parkingOperation/parkingRecord/ownerArchives/": "parkingOwnerArchives",
"/propertySMG/renovationManage/renovation/": "renovationDeclaration",
"/propertySMG/renovationManage/renovationCheck/": "renovationCheck",
"/propertySMG/renovationManage/renovationFeeManage/": "renovationFeeManage",
"/propertySMG/renovationManage/renovationPatrol/": "renovationPatrol",
"/propertySMG/satisfaction/satisfactionSurvey/questionnaire/": "satisfactionQuestionnaire",
"/propertySMG/securityManage/dailySecurity/monitoringCenter/": "securityMonitoringCenter",
"/propertySMG/securityManage/emergencyManagement/": "securityEmergencyManagement",
"/propertySMG/securityManage/securityFile/securityArchives/": "securityArchives",
"/propertySMG/securityProduction/dangerousSupervision/internetofThings/": "securityInternetOfThings"
};
const HCPOS_COCKPIT_LINKS = {
"智能人事": { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/ProjectManagerConfig" },
"作业看板": { runtime: "hcpos", path: "/propertySMG/equipmentManage/equipmentPortrait" },
"车场看板": { runtime: "hcpos", path: "/propertySMG/parkingOperation/parkingMicroBrain" },
"能源看板": { runtime: "hcpos", path: "/propertySMG/energySourceOperat/notice" },
"住户卡": { runtime: "hcpos", path: "/communitySMG/personnelList" },
"安全生产": { runtime: "hcpos", path: "/propertySMG/securityProduction/checkStandardLibrary" },
"收费看板": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeMicrobrain" },
"客户通": { runtime: "hcpos", path: "/propertySMG/customerOperations/customerPortrait" },
"网格看板": { runtime: "hcpos", path: "/propertySMG/basicManagement/spatialRegion" },
"物业费报表": { runtime: "hcetms", path: "/r2cockpit/cloudData/propertyFeeReport" },
"车场报表": { runtime: "hcetms", path: "/r2cockpit/cloudData/parkingLotReport" },
"能耗报表": { runtime: "hcpos", path: "/propertySMG/energySourceOperat/energySourcMicrobrain" },
"全年收入报表": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeReport/incomeStatement" },
"全员收费报表": { runtime: "hcpos", path: "/propertySMG/fullForceAssaultSMG/fullStaffFee/arrearsReport" },
"收入考核报表": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeReport/projectIncome" },
"回款动作与分析": { runtime: "hcetms", path: "/r2cockpit/cloudData/collectionTracking" },
"日清日高": { runtime: "hcpos", path: "/dashboard" },
"月清月高": { runtime: "hcpos", path: "/dashboard" },
"智能催费": { runtime: "hcpos", path: "/aiProduct/urgePayment/urgeVisitPlan" },
"智能质检": { runtime: "hcpos", path: "/aiProduct/videoQualityInspection" },
"智能能源": { runtime: "hcpos", path: "/propertySMG/energySourceOperat/energySourcMicrobrain" },
"智能工单": { runtime: "hcpos", path: "/propertySMG/securityProduction/rectificationImplementation/dangerousPlan" },
"智能报告": { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/theReportModule" },
"验收工作台": { runtime: "hcetms", path: "/trainingPush/sampling" },
"视频验收": { runtime: "hcpos", path: "/aiProduct/videoQualityInspection" },
"安全指数": { runtime: "hcpos", path: "/propertySMG/securityProduction/checkStandardLibrary" },
"危险源监控": { runtime: "hcpos", path: "/propertySMG/securityProduction/checkStandardLibrary" },
"应急预案": { runtime: "hcpos", path: "/propertySMG/securityProduction/rectificationImplementation/emergencyPlan" },
"应急演练": { runtime: "hcpos", path: "/propertySMG/securityProduction/rectificationImplementation/securityDrills" },
"安全培训": { runtime: "hcetms", path: "/trainingManage" },
"日现金流入": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeMicrobrain" },
"日现金流出": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeMicrobrain" },
"收入结构图": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeMicrobrain" },
"成本结构图": { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeMicrobrain" },
"客户满意": { runtime: "hcpos", path: "/propertySMG/customerOperations/customerPortrait" },
"客户投诉": { runtime: "hcpos", path: "/propertySMG/customerOperations/customerPortrait" },
"内部满意": { runtime: "hcpos", path: "/propertySMG/customerOperations/customerPortrait" },
"计划工单": { runtime: "hcetms", path: "/r2cockpit/cloudData/planTaskReport" },
"社区文化": { runtime: "hcpos", path: "/communitySMG/communityCulture" },
"生成报告": { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/theReportModule" },
"查看报告": { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/theReportModule" },
"重新生成报告": { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/theReportModule" },
"返回": { runtime: "hcpos", path: "/dashboard" },
"企业后台": { runtime: "hcetms", path: "" }
};
const HCPOS_SECONDARY_TAB_PRESETS = {
keyCustomer: {
eyebrow: "客户分层",
title: "重点客户总览",
summary: "聚合重点客户分层、跟进状态与回访任务,补齐空白标签页。",
cards: [
{ label: "重点客户", value: "126", detail: "A 类客户 38 位" },
{ label: "本周回访", value: "64", detail: "完成率 82.8%" },
{ label: "投诉敏感", value: "11", detail: "需专人跟进" },
{ label: "回款客户", value: "72", detail: "本月已回款" }
],
headers: ["客户名称", "所属项目", "客户等级", "关注标签", "最近回访", "负责人", "状态"],
rows: [
["李天", "循环花园一期", "A", "高频互动", "2026-04-03", "郭晓", "持续跟进"],
["肖英", "明珠佳园", "A", "缴费稳定", "2026-04-02", "何琳", "正常"],
["卢跃梅", "龙湾一品小区", "B", "投诉敏感", "2026-04-01", "陈谷先", "需回访"],
["小小", "明珠佳园", "B", "亲属关系维护", "2026-03-30", "曾丽娜", "正常"],
["来了", "A 管理区", "C", "新签客户", "2026-03-28", "周宇", "观察中"]
]
},
customizedPhysicalRecord: {
eyebrow: "专家测评",
title: "定制体检记录总览",
summary: "展示体检对象、测评得分与整改建议,补齐空白记录页。",
cards: [
{ label: "体检记录", value: "86", detail: "本月新增 12 条" },
{ label: "平均得分", value: "82.6", detail: "定制套餐口径" },
{ label: "高风险项", value: "9", detail: "需复检" },
{ label: "整改闭环率", value: "78.4%", detail: "本周提升" }
],
headers: ["项目名称", "体检对象", "套餐名称", "测评日期", "得分", "风险等级", "建议动作"],
rows: [
["循环花园一期", "消防泵房", "火灾应急", "2026-04-03", "88.2", "低", "保持巡检"],
["博万物", "设备房", "设备管控", "2026-04-02", "79.4", "中", "补充维保"],
["美好花园", "绿化景观带", "绿化作业", "2026-04-01", "84.1", "低", "正常维护"],
["连城花园", "楼栋卫生区", "清洁作业", "2026-03-30", "76.3", "中", "安排复检"],
["江南世家一期", "园区道路", "清洁作业", "2026-03-28", "71.8", "高", "专项整改"]
]
},
specialPhysicalRecord: {
eyebrow: "专家测评",
title: "专项体检记录总览",
summary: "展示专项体检对象、问题点位与整改进展,补齐空白记录页。",
cards: [
{ label: "专项记录", value: "112", detail: "车场 / 绿化 / 安防" },
{ label: "平均得分", value: "84.9", detail: "较上月 +1.2" },
{ label: "整改中", value: "14", detail: "需闭环追踪" },
{ label: "通过率", value: "87.5%", detail: "专项测评口径" }
],
headers: ["项目名称", "专项对象", "套餐名称", "测评日期", "得分", "整改状态", "责任人"],
rows: [
["循环花园一期", "车场入口", "车场专项体检-5A", "2026-04-03", "83.6", "已完成", "郭晓"],
["博万物", "中央花园", "树木与设备加固", "2026-04-02", "79.1", "处理中", "何琳"],
["美好花园", "北门绿篱", "树木枯枝修剪", "2026-04-01", "81.4", "已完成", "曾丽娜"],
["连城花园", "垃圾堆放点", "垃圾堆放点清洁", "2026-03-30", "74.8", "待复检", "周宇"],
["江南世家一期", "楼道地面", "楼道地面清洁", "2026-03-29", "78.2", "处理中", "陈谷先"]
]
},
fiveAPhysicalRecord: {
eyebrow: "专家测评",
title: "5A体检记录总览",
summary: "展示 5A 测评对象、评分与复检安排,补齐空白记录页。",
cards: [
{ label: "5A记录", value: "64", detail: "本月新增 8 条" },
{ label: "平均得分", value: "89.4", detail: "高于月基线" },
{ label: "待复评", value: "6", detail: "需补充复检" },
{ label: "优秀率", value: "62.5%", detail: "A 级占比" }
],
headers: ["项目名称", "体检对象", "套餐名称", "测评日期", "得分", "评级", "整改动作"],
rows: [
["循环花园一期", "公共区域", "体检套餐2.0-5A", "2026-04-03", "91.2", "A", "保持标准"],
["博万物", "设备房", "体检套餐2.0-5A", "2026-04-02", "87.6", "A-", "补录照片"],
["美好花园", "车场入口", "体检套餐2.0-5A", "2026-04-01", "84.2", "B+", "专项复检"],
["连城花园", "物业服务中心", "体检套餐2.0-5A", "2026-03-30", "88.5", "A-", "正常跟进"],
["江南世家一期", "园区主路", "体检套餐2.0-5A", "2026-03-28", "82.3", "B", "优化流程"]
]
}
};
const HCPOS_OVERVIEW_PRESETS = {
personnelList: {
eyebrow: "社区治理",
title: "住户档案总览",
summary: "聚合住户数量、产权结构与审核状态,增强主页信息层次。",
cards: [
{ label: "住户总数", value: "173", detail: "产权人 96 位" },
{ label: "审核通过", value: "162", detail: "通过率 93.6%" },
{ label: "绑定微信", value: "84", detail: "绑定率持续提升" },
{ label: "待审核", value: "11", detail: "需尽快处理" }
]
},
canteenArchives: {
eyebrow: "家企服务",
title: "食堂档案总览",
summary: "汇总食堂档案、经营主体与启用状态,增强主页信息层次。",
cards: [
{ label: "食堂数量", value: "18", detail: "启用 16 家" },
{ label: "本月新增", value: "2", detail: "新增 1 家在审核" },
{ label: "经营单位", value: "12", detail: "已建档主体" },
{ label: "联系人完整率", value: "100%", detail: "档案资料齐全" }
]
},
customizedPhysical: {
eyebrow: "专家测评",
title: "定制测评总览",
summary: "展示定制套餐数量、应用场景与平均分,提升主页概览能力。",
cards: [
{ label: "定制套餐", value: "46", detail: "本月新增 6 个" },
{ label: "应用场景", value: "12", detail: "覆盖清洁 / 安全 / 设备" },
{ label: "平均分数", value: "36.8", detail: "定制口径" },
{ label: "高分套餐", value: "8", detail: "80 分以上" }
]
},
specialPhysical: {
eyebrow: "专家测评",
title: "专项测评总览",
summary: "展示专项套餐分布、评分与同步情况,提升主页概览能力。",
cards: [
{ label: "专项套餐", value: "22", detail: "同步云库 1 次" },
{ label: "应用场景", value: "8", detail: "车场 / 绿化 / 清洁" },
{ label: "平均分数", value: "24.7", detail: "专项口径" },
{ label: "高风险套餐", value: "3", detail: "需复检" }
]
},
fiveAPhysical: {
eyebrow: "专家测评",
title: "5A测评总览",
summary: "汇总 5A 套餐规模、测评强度与通过率,提升主页概览能力。",
cards: [
{ label: "5A 套餐", value: "1", detail: "主套餐已上线" },
{ label: "套餐分数", value: "22088", detail: "5A 标准口径" },
{ label: "体检记录", value: "64", detail: "已补至记录页" },
{ label: "优秀率", value: "62.5%", detail: "A 级占比" }
]
}
};
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-hcpos-chart" preserveAspectRatio="none">
${guides.join("")}
${paths}
${xLabels}
</svg>
`;
}
function renderHcPosDashboard(doc, type) {
const preset = HCPOS_DASHBOARD_PRESETS[type];
if (!preset) {
return;
}
if (!doc.getElementById("__codex_hcpos_dashboard_style__")) {
const style = doc.createElement("style");
style.id = "__codex_hcpos_dashboard_style__";
style.textContent = `
.waitMain {
display: none !important;
}
#__codex_hcpos_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-hcpos-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 20px;
}
.codex-hcpos-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-hcpos-head h3 {
margin: 0 0 6px;
font-size: 28px;
color: #12233d;
}
.codex-hcpos-head p {
margin: 0;
max-width: 660px;
color: #6d7d97;
font-size: 14px;
}
.codex-hcpos-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);
}
.codex-hcpos-metrics {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
margin-bottom: 18px;
}
.codex-hcpos-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-hcpos-metric span {
display: block;
color: #7d8ca5;
font-size: 13px;
}
.codex-hcpos-metric strong {
display: block;
margin: 8px 0 6px;
color: #12233d;
font-size: 28px;
line-height: 1.1;
}
.codex-hcpos-metric small {
color: #4e6b98;
font-size: 12px;
}
.codex-hcpos-layout {
display: grid;
grid-template-columns: minmax(0, 1.6fr) minmax(320px, 0.95fr);
gap: 16px;
}
.codex-hcpos-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-hcpos-panel h4 {
margin: 0 0 14px;
color: #152742;
font-size: 16px;
}
.codex-hcpos-legend {
display: flex;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.codex-hcpos-legend span {
display: inline-flex;
align-items: center;
gap: 6px;
color: #71829d;
font-size: 12px;
}
.codex-hcpos-dot {
width: 9px;
height: 9px;
border-radius: 50%;
}
.codex-hcpos-chart {
width: 100%;
height: 238px;
}
.codex-hcpos-segments {
display: grid;
gap: 12px;
}
.codex-hcpos-segment {
display: grid;
grid-template-columns: 88px 1fr 48px;
align-items: center;
gap: 10px;
color: #4d607f;
font-size: 13px;
}
.codex-hcpos-track {
overflow: hidden;
height: 10px;
border-radius: 999px;
background: #eef3fb;
}
.codex-hcpos-fill {
height: 100%;
border-radius: inherit;
}
.codex-hcpos-table table {
width: 100%;
border-collapse: collapse;
}
.codex-hcpos-table th,
.codex-hcpos-table td {
padding: 12px 8px;
border-bottom: 1px solid rgba(17,37,63,0.06);
text-align: left;
color: #3f5477;
font-size: 13px;
}
.codex-hcpos-table th {
color: #8b9ab3;
font-size: 12px;
font-weight: 500;
}
.codex-hcpos-alerts {
display: grid;
gap: 10px;
}
.codex-hcpos-alert {
padding: 14px 14px 13px;
border-radius: 14px;
background: #f8fbff;
border: 1px solid rgba(17,37,63,0.06);
}
.codex-hcpos-alert b {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 6px;
color: #152742;
font-size: 14px;
}
.codex-hcpos-alert p {
margin: 0;
color: #6d7d97;
font-size: 12px;
line-height: 1.6;
}
.codex-hcpos-level {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 4px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
}
.codex-hcpos-level-正常 {
color: #067647;
background: rgba(6, 118, 71, 0.12);
}
.codex-hcpos-level-关注 {
color: #975a16;
background: rgba(255, 183, 77, 0.18);
}
.codex-hcpos-level-预警 {
color: #c92a2a;
background: rgba(255, 107, 107, 0.16);
}
@media (max-width: 1080px) {
.codex-hcpos-metrics {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.codex-hcpos-layout {
grid-template-columns: 1fr;
}
}
`;
doc.head.appendChild(style);
}
const host =
doc.querySelector(".app-main > div") ||
doc.querySelector(".app-main > section") ||
doc.querySelector(".app-main") ||
doc.body;
if (!host) {
return;
}
if (type === "financeMicrobrain" || type === "energyNotice") {
[...host.children].forEach((child) => {
if (child.id !== "__codex_hcpos_dashboard__") {
child.style.display = "none";
}
});
}
let container = doc.getElementById("__codex_hcpos_dashboard__");
if (!container) {
container = doc.createElement("div");
container.id = "__codex_hcpos_dashboard__";
host.insertBefore(container, host.firstChild);
}
const legend = preset.trendSeries
.map(
(item) => `
<span><i class="codex-hcpos-dot" style="background:${item.color}"></i>${item.label}</span>
`
)
.join("");
const segments = preset.segments
.map(
(item) => `
<div class="codex-hcpos-segment">
<span>${item.label}</span>
<div class="codex-hcpos-track"><div class="codex-hcpos-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-hcpos-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-hcpos-alert">
<b>
<span>${item.title}</span>
<span class="codex-hcpos-level codex-hcpos-level-${item.level}">${item.level}</span>
</b>
<p>${item.detail}</p>
</div>
`
)
.join("");
container.innerHTML = `
<div class="codex-hcpos-head">
<div>
<span class="codex-hcpos-eyebrow">${preset.eyebrow}</span>
<h3>${preset.headline}</h3>
<p>${preset.summary}</p>
</div>
<div class="codex-hcpos-badge">镜像重建 · ${preset.headline} · 近 30 天静态样本</div>
</div>
<div class="codex-hcpos-metrics">${metrics}</div>
<div class="codex-hcpos-layout">
<div class="codex-hcpos-panel">
<h4>趋势概览</h4>
<div class="codex-hcpos-legend">${legend}</div>
${buildTrendSvg(preset.trendSeries, preset.trendLabels)}
</div>
<div class="codex-hcpos-panel">
<h4>结构占比</h4>
<div class="codex-hcpos-segments">${segments}</div>
</div>
<div class="codex-hcpos-panel codex-hcpos-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-hcpos-panel">
<h4>${preset.alertsTitle}</h4>
<div class="codex-hcpos-alerts">${alerts}</div>
</div>
</div>
`;
}
function ensureHcPosStaticPageStyle(doc) {
if (doc.getElementById("__codex_hcpos_static_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_hcpos_static_style__";
style.textContent = `
.waitMain {
display: none !important;
}
.codex-hcpos-static {
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-hcpos-static-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 16px;
}
.codex-hcpos-static-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-hcpos-static-head h4 {
margin: 0 0 4px;
color: #152742;
font-size: 24px;
}
.codex-hcpos-static-head p {
margin: 0;
color: #6d7d97;
font-size: 13px;
}
.codex-hcpos-static-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-hcpos-static-cards {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.codex-hcpos-static-card {
padding: 14px 16px;
border-radius: 16px;
background: rgba(255,255,255,0.92);
border: 1px solid rgba(17,37,63,0.05);
}
.codex-hcpos-static-card span {
display: block;
color: #7d8ca5;
font-size: 12px;
}
.codex-hcpos-static-card strong {
display: block;
margin: 8px 0 6px;
color: #152742;
font-size: 28px;
line-height: 1.05;
}
.codex-hcpos-static-card small {
color: #4f6a94;
font-size: 12px;
}
.codex-hcpos-static-table {
overflow: auto;
border-radius: 14px;
border: 1px solid rgba(17,37,63,0.06);
background: #fff;
}
.codex-hcpos-static-table table {
width: 100%;
min-width: 960px;
border-collapse: collapse;
}
.codex-hcpos-static-table th,
.codex-hcpos-static-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-hcpos-static-table th {
background: #f9fbff;
color: #7d8ca5;
font-size: 12px;
font-weight: 600;
}
.codex-hcpos-static-table td:first-child,
.codex-hcpos-static-table th:first-child {
text-align: left;
}
@media (max-width: 1080px) {
.codex-hcpos-static-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.codex-hcpos-overview-cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.codex-hcpos-overview {
margin: 18px 0 16px;
padding: 18px 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-hcpos-overview-head h4 {
margin: 0 0 4px;
color: #152742;
font-size: 22px;
}
.codex-hcpos-overview-head p {
margin: 0 0 14px;
color: #6d7d97;
font-size: 13px;
}
.codex-hcpos-overview-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-hcpos-overview-cards {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.codex-hcpos-overview-card {
padding: 14px 16px;
border-radius: 16px;
background: rgba(255,255,255,0.92);
border: 1px solid rgba(17,37,63,0.05);
box-shadow: 0 12px 24px rgba(17,37,63,0.06);
}
.codex-hcpos-overview-card span {
display: block;
color: #7d8ca5;
font-size: 12px;
}
.codex-hcpos-overview-card strong {
display: block;
margin: 8px 0 6px;
color: #152742;
font-size: 28px;
line-height: 1.05;
}
.codex-hcpos-overview-card small {
color: #4f6a94;
font-size: 12px;
}
`;
doc.head.appendChild(style);
}
function renderHcPosStaticPage(doc, presetKey) {
const preset =
HCPOS_STATIC_PAGE_PRESETS[presetKey] ||
HCPOS_FINANCE_REPORT_PRESETS[presetKey] ||
HCPOS_WAIT_PAGE_PRESETS[presetKey];
if (!preset) {
return;
}
ensureHcPosStaticPageStyle(doc);
doc.querySelectorAll(".waitMain").forEach((node) => {
node.style.display = "none";
});
const activePane = [...doc.querySelectorAll(".el-tab-pane")].find((pane) => {
const styleText = pane.getAttribute("style") || "";
return pane.getAttribute("aria-hidden") !== "true" && !/display\s*:\s*none/i.test(styleText);
});
const waitPane = [...doc.querySelectorAll(".waitMain")]
.map((node) => node.closest(".el-tab-pane"))
.find(Boolean);
const host =
activePane ||
waitPane ||
doc.querySelector(".app-main > div") ||
doc.querySelector(".app-main > section") ||
doc.querySelector(".app-main") ||
doc.body;
if (!host) {
return;
}
const existing = doc.getElementById(`__codex_hcpos_static_${presetKey}__`);
if (existing) {
existing.remove();
}
const section = doc.createElement("section");
section.id = `__codex_hcpos_static_${presetKey}__`;
section.className = "codex-hcpos-static";
const cards = preset.cards
.map(
(item) => `
<div class="codex-hcpos-static-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-hcpos-static-head">
<div>
<span class="codex-hcpos-static-eyebrow">${preset.eyebrow}</span>
<h4>${preset.title}</h4>
<p>${preset.summary}</p>
</div>
<div class="codex-hcpos-static-badge">镜像重建 · 静态样本数据</div>
</div>
<div class="codex-hcpos-static-cards">${cards}</div>
<div class="codex-hcpos-static-table">
<table>
<thead><tr>${headers}</tr></thead>
<tbody>${rows}</tbody>
</table>
</div>
`;
host.appendChild(section);
}
function renderHcPosOverviewPanel(host, preset) {
if (!host || !preset) {
return;
}
ensureHcPosStaticPageStyle(host.ownerDocument);
let panel = host.querySelector(".codex-hcpos-overview");
if (!panel) {
panel = host.ownerDocument.createElement("section");
panel.className = "codex-hcpos-overview";
const search = host.querySelector(".searchStys");
const searchParent = search && search.parentNode;
if (search && searchParent && search.nextSibling) {
searchParent.insertBefore(panel, search.nextSibling);
} else if (search && searchParent) {
searchParent.appendChild(panel);
} else {
host.insertBefore(panel, host.firstChild);
}
}
const cards = preset.cards
.map(
(item) => `
<div class="codex-hcpos-overview-card">
<span>${item.label}</span>
<strong>${item.value}</strong>
<small>${item.detail}</small>
</div>
`
)
.join("");
panel.innerHTML = `
<div class="codex-hcpos-overview-head">
<div>
<span class="codex-hcpos-overview-eyebrow">${preset.eyebrow}</span>
<h4>${preset.title}</h4>
<p>${preset.summary}</p>
</div>
</div>
<div class="codex-hcpos-overview-cards">${cards}</div>
`;
}
function renderHcPosSecondaryTab(doc, presetKey, paneId) {
const preset = HCPOS_SECONDARY_TAB_PRESETS[presetKey];
const pane = doc.getElementById(paneId);
if (!preset || !pane) {
return;
}
ensureHcPosStaticPageStyle(doc);
const existing = pane.querySelector(".codex-hcpos-static");
if (existing) {
existing.remove();
}
pane.querySelectorAll(".waitMain").forEach((node) => {
node.style.display = "none";
});
const section = doc.createElement("section");
section.className = "codex-hcpos-static";
const cards = preset.cards
.map(
(item) => `
<div class="codex-hcpos-static-card" style="box-shadow: 0 12px 24px rgba(45,118,255,0.12)">
<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-hcpos-static-head">
<div>
<span class="codex-hcpos-static-eyebrow">${preset.eyebrow}</span>
<h4>${preset.title}</h4>
<p>${preset.summary}</p>
</div>
<div class="codex-hcpos-static-badge">镜像重建 · 静态样本数据</div>
</div>
<div class="codex-hcpos-static-cards">${cards}</div>
<div class="codex-hcpos-static-table">
<table>
<thead><tr>${headers}</tr></thead>
<tbody>${rows}</tbody>
</table>
</div>
`;
pane.appendChild(section);
}
function installSimpleTabSwitch(doc, config) {
const { primaryTabId, secondaryTabId, primaryPaneId, secondaryPaneId, storageKey } = config;
const primaryTab = doc.getElementById(primaryTabId);
const secondaryTab = doc.getElementById(secondaryTabId);
const primaryPane = doc.getElementById(primaryPaneId);
const secondaryPane = doc.getElementById(secondaryPaneId);
if (!primaryTab || !secondaryTab || !primaryPane || !secondaryPane) {
return;
}
if (secondaryTab.dataset.codexSwitchInstalled === "1") {
return;
}
secondaryTab.dataset.codexSwitchInstalled = "1";
function syncActiveBar(target) {
const bar = target.closest(".el-tabs")?.querySelector(".el-tabs__active-bar");
if (!bar) {
return;
}
bar.style.width = `${target.offsetWidth}px`;
bar.style.transform = `translateX(${target.offsetLeft}px)`;
}
function activateTab(target) {
const secondaryActive = target === secondaryTab;
primaryTab.classList.toggle("is-active", !secondaryActive);
secondaryTab.classList.toggle("is-active", secondaryActive);
primaryTab.setAttribute("aria-selected", String(!secondaryActive));
secondaryTab.setAttribute("aria-selected", String(secondaryActive));
primaryTab.setAttribute("tabindex", secondaryActive ? "-1" : "0");
secondaryTab.setAttribute("tabindex", secondaryActive ? "0" : "-1");
primaryPane.style.display = secondaryActive ? "none" : "block";
secondaryPane.style.display = secondaryActive ? "block" : "none";
primaryPane.setAttribute("aria-hidden", secondaryActive ? "true" : "false");
secondaryPane.setAttribute("aria-hidden", secondaryActive ? "false" : "true");
syncActiveBar(target);
if (storageKey) {
secondaryPane.ownerDocument.defaultView?.sessionStorage.setItem(storageKey, secondaryActive ? "secondary" : "primary");
}
}
primaryTab.addEventListener("click", () => activateTab(primaryTab), true);
secondaryTab.addEventListener("click", () => activateTab(secondaryTab), true);
const remembered =
storageKey && secondaryPane.ownerDocument.defaultView?.sessionStorage.getItem(storageKey) === "secondary"
? secondaryTab
: primaryTab;
activateTab(remembered);
}
function ensureHcPosEditableStyle(doc) {
if (doc.getElementById("__codex_hcpos_editable_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_hcpos_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 createHcPosEditableKey(doc, section, index) {
const pathname = doc.defaultView?.location?.pathname || "unknown";
const paneId = section.closest(".el-tab-pane")?.id || "";
const sectionId = section.id || section.dataset.type || section.className || `section-${index}`;
return `__codex_hcpos_edit__${pathname}::${paneId}::${sectionId}`;
}
function wrapHcPosEditableBody(section) {
let body = section.querySelector(":scope > .codex-editable-body");
if (body) {
return body;
}
body = section.ownerDocument.createElement("div");
body.className = "codex-editable-body";
const nodes = [...section.childNodes];
nodes.forEach((node) => {
body.appendChild(node);
});
section.appendChild(body);
return body;
}
function setHcPosEditableState(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 bindHcPosEditableSection(doc, section, index) {
ensureHcPosEditableStyle(doc);
section.classList.add("codex-editable-shell");
const body = wrapHcPosEditableBody(section);
const storageKey = createHcPosEditableKey(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") {
setHcPosEditableState(doc, section, true);
body.focus();
return;
}
if (action === "save") {
win?.localStorage.setItem(storageKey, body.innerHTML);
setHcPosEditableState(doc, section, false);
return;
}
if (action === "reset") {
win?.localStorage.removeItem(storageKey);
body.innerHTML = section.dataset.codexDefaultHtml || body.innerHTML;
setHcPosEditableState(doc, section, false);
}
});
}
setHcPosEditableState(doc, section, false);
}
function installHcPosEditableSections(doc) {
const sections = [
...doc.querySelectorAll("#__codex_hcpos_dashboard__, [id^='__codex_hcpos_static_'], .codex-hcpos-overview, .el-tab-pane > .codex-hcpos-static")
];
const seen = new Set();
sections.forEach((section, index) => {
if (!section || section.tagName === "STYLE" || seen.has(section)) {
return;
}
seen.add(section);
bindHcPosEditableSection(doc, section, index);
});
}
function ensureHcPosListEditStyle(doc) {
if (doc.getElementById("__codex_hcpos_list_edit_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_hcpos_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 createHcPosListEditStorageKey(doc) {
const pathname = doc.defaultView?.location?.pathname || "unknown";
return `__codex_hcpos_list_edit__${pathname}`;
}
function readHcPosListEditState(doc) {
try {
return JSON.parse(doc.defaultView?.localStorage.getItem(createHcPosListEditStorageKey(doc)) || "{}");
} catch (_error) {
return {};
}
}
function writeHcPosListEditState(doc, state) {
try {
doc.defaultView?.localStorage.setItem(createHcPosListEditStorageKey(doc), JSON.stringify(state));
} catch (_error) {
// ignore
}
}
function getHcPosVisibleText(node) {
return (node?.textContent || "").replace(/\s+/g, " ").trim();
}
function isHcPosElementVisible(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 getHcPosListEditTableKey(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 isHcPosActionOnlyText(text) {
return /^(编辑|删除|查看|复制角色|复制|下载|导出|导入|开具发票|手动开收据|下载收据|生成票据|催费|拆分|禁用|启用)(\s+(编辑|删除|查看|复制角色|复制|下载|导出|导入|开具发票|手动开收据|下载收据|生成票据|催费|拆分|禁用|启用))*$/.test(text);
}
function countHcPosVisibleCells(row) {
return [...row.querySelectorAll(":scope > td")].filter((cell) => isHcPosElementVisible(cell)).length;
}
function resolveHcPosPrimaryRow(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) => countHcPosVisibleCells(right) - countHcPosVisibleCells(left));
return candidates[0] || row;
}
function extractHcPosListEditHeaders(table, cellCount) {
const headers = [...table.querySelectorAll("thead th")]
.map((cell) => getHcPosVisibleText(cell))
.filter(Boolean);
if (!headers.length) {
return Array.from({ length: cellCount }, (_value, index) => `字段${index + 1}`);
}
return headers;
}
function extractHcPosListEditContext(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 = extractHcPosListEditHeaders(table, cells.length);
const fields = [];
cells.forEach((cell, index) => {
const header = headers[index] || `字段${index + 1}`;
const value = getHcPosVisibleText(cell);
if (!value || /操作/.test(header) || isHcPosActionOnlyText(value)) {
return;
}
fields.push({ header, value });
});
if (!fields.length) {
return null;
}
return {
tableKey: getHcPosListEditTableKey(doc, table),
rowKey: `row-${rowIndex}`,
fields
};
}
function resolveHcPosListEditContext(doc, trigger) {
const sourceRow = trigger?.closest("tr");
if (!sourceRow) {
return null;
}
const direct = extractHcPosListEditContext(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 = extractHcPosListEditContext(doc, row);
if (context) {
return {
row,
context
};
}
}
return null;
}
function setHcPosListEditCellValue(cell, value) {
const target =
cell.querySelector(":scope > .cell") ||
(cell.children.length === 1 ? cell.children[0] : cell);
if (!target) {
return;
}
target.textContent = value;
}
function applyHcPosListEditValues(row, fields) {
const table = row?.closest("table");
if (!table) {
return;
}
const cells = [...row.querySelectorAll(":scope > td")];
const headers = extractHcPosListEditHeaders(table, cells.length);
let fieldIndex = 0;
cells.forEach((cell, index) => {
const header = headers[index] || `字段${index + 1}`;
const value = getHcPosVisibleText(cell);
if (!value || /操作/.test(header) || isHcPosActionOnlyText(value)) {
return;
}
const nextField = fields[fieldIndex];
if (!nextField) {
return;
}
setHcPosListEditCellValue(cell, nextField.value);
fieldIndex += 1;
});
}
function applyHcPosListEditStateToTable(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);
applyHcPosListEditValues(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) {
applyHcPosListEditValues(row, fields);
}
}
function hydrateHcPosListEditRows(doc) {
const state = readHcPosListEditState(doc);
Object.entries(state).forEach(([tableKey, tableState]) => {
Object.entries(tableState || {}).forEach(([rowKey, rowState]) => {
if (rowState?.fields) {
applyHcPosListEditStateToTable(doc, tableKey, rowKey, rowState.fields);
}
});
});
}
function normalizeHcPosListEditTriggers(doc) {
[...doc.querySelectorAll("button.el-button, .textBtn, a, .el-button, .el-dropdown-menu__item")].forEach((node) => {
const text = getHcPosVisibleText(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 injectHcPosInlineEditButtons(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(getHcPosVisibleText(node)) && isHcPosElementVisible(node));
if (hasVisibleEdit) {
return;
}
const hasHiddenEdit = actionNodes.some((node) => /编辑/.test(getHcPosVisibleText(node)));
if (!hasHiddenEdit) {
return;
}
const hostRow = resolveHcPosPrimaryRow(row);
const cells = [...hostRow.querySelectorAll(":scope > td")];
const hostCell =
cells.find((cell) => {
const text = getHcPosVisibleText(cell);
return isHcPosElementVisible(cell) && text && !isHcPosActionOnlyText(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 collectHcPosHiddenEditEntries(doc) {
const entries = [];
const seen = new Set();
const currentPath = pathFromHash(doc.defaultView?.location?.hash || location.hash);
const detailEditRoutes = new Set([
"/propertySMG/cleanManage/cleanFile/cleanEquipment",
"/propertySMG/energySourceOperat/energyFile/energyArchives",
"/propertySMG/equipmentManage/equipmentFiling/equipmentArchives",
"/propertySMG/securityManage/securityFile/securityEquipment"
]);
const hasVisibleEdit = [...doc.querySelectorAll("button, .textBtn, a, .el-button, .el-dropdown-menu__item")].some(
(node) => /编辑/.test(getHcPosVisibleText(node)) && isHcPosElementVisible(node)
);
if (hasVisibleEdit) {
return entries;
}
const allowDetailAsEdit = detailEditRoutes.has(currentPath);
[...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(getHcPosVisibleText(node)));
const hasDetailEntry = allowDetailAsEdit && actionNodes.some((node) => /详情/.test(getHcPosVisibleText(node)));
if (!hasHiddenEdit && !hasDetailEntry) {
return;
}
const primaryRow = resolveHcPosPrimaryRow(row);
const context = extractHcPosListEditContext(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 renderHcPosHiddenEditPanel(doc) {
const entries = collectHcPosHiddenEditEntries(doc);
const existing = doc.getElementById("__codex_hcpos_hidden_edit_panel__");
if (!entries.length) {
existing?.remove();
return;
}
const panel = existing || doc.createElement("section");
panel.id = "__codex_hcpos_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 search = doc.querySelector(".searchStys");
const host = search?.parentElement || doc.querySelector(".app-main > div") || doc.body;
if (search && search.parentElement) {
search.parentElement.insertBefore(panel, search.nextSibling);
} else {
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;
}
openHcPosListEditModal(doc, resolveHcPosPrimaryRow(entry.row), entry.context);
});
});
}
function closeHcPosListEditModal(doc) {
doc.getElementById("__codex_hcpos_list_edit_mask__")?.remove();
}
function openHcPosListEditModal(doc, row, context) {
ensureHcPosListEditStyle(doc);
closeHcPosListEditModal(doc);
const mask = doc.createElement("div");
mask.id = "__codex_hcpos_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) {
closeHcPosListEditModal(doc);
}
});
doc.body.appendChild(mask);
mask.querySelector("[data-action='close']")?.addEventListener("click", () => closeHcPosListEditModal(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 = readHcPosListEditState(doc);
state[context.tableKey] = state[context.tableKey] || {};
state[context.tableKey][context.rowKey] = { fields: nextFields };
writeHcPosListEditState(doc, state);
applyHcPosListEditStateToTable(doc, context.tableKey, context.rowKey, nextFields);
closeHcPosListEditModal(doc);
});
}
function installHcPosGenericListEditors(doc) {
ensureHcPosListEditStyle(doc);
hydrateHcPosListEditRows(doc);
normalizeHcPosListEditTriggers(doc);
injectHcPosInlineEditButtons(doc);
renderHcPosHiddenEditPanel(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 = getHcPosVisibleText(target);
if (!/编辑/.test(label)) {
return;
}
const resolved = resolveHcPosListEditContext(doc, target);
if (!resolved) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosListEditModal(doc, resolved.row, resolved.context);
},
true
);
doc.body.dataset.codexListEditorsInstalled = "1";
}
function getHcPosDangerTableTarget(rowLabel) {
if (rowLabel === "财") {
return { runtime: "hcpos", path: "/propertySMG/businessTaxCank/financeReport/arrearsReport" };
}
if (rowLabel === "人") {
return { runtime: "hcpos", path: "/systemManage/projectConfig/internalControl/ProjectManagerConfig" };
}
return { runtime: "hcpos", path: "/propertySMG/securityProduction/checkStandardLibrary" };
}
function navigateHcPosCockpitLink(target) {
if (!target) {
return;
}
const topWindow = window.top || window;
if (target.runtime === "hcetms") {
const href = `/__mirror/runtime/hc-etms-dashboard/?v=${VERSION || "20260405p"}#${target.path || ""}`;
topWindow.location.href = href;
return;
}
topWindow.location.hash = `#${target.path}`;
}
function resolveHcPosCockpitTarget(node) {
const label = (node.textContent || "").replace(/\s+/g, " ").trim();
if (!label) {
return null;
}
if (/^by/.test(label)) {
return null;
}
if (HCPOS_COCKPIT_LINKS[label]) {
return HCPOS_COCKPIT_LINKS[label];
}
const cardTitle = node
.closest(".flowm-card")
?.querySelector(".flowm-card__header span")
?.textContent?.replace(/\s+/g, " ")
.trim();
if (cardTitle && HCPOS_COCKPIT_LINKS[cardTitle]) {
return HCPOS_COCKPIT_LINKS[cardTitle];
}
if (label === "实时校验") {
const top = Math.round(node.getBoundingClientRect().top);
return top >= 1700
? { runtime: "hcetms", path: "/r2cockpit/cloudData/planTaskReport" }
: { runtime: "hcpos", path: "/propertySMG/securityProduction/checkStandardLibrary" };
}
return null;
}
function installHcPosDangerTableLinks(doc) {
const rows = [...doc.querySelectorAll(".dangerous-source-table tbody tr")];
rows.forEach((row) => {
const cells = [...row.querySelectorAll("td")];
const rowLabel = (cells[0]?.textContent || "").replace(/\s+/g, " ").trim();
if (!rowLabel) {
return;
}
cells.slice(1).forEach((cell) => {
if (cell.dataset.codexDangerBound === "1") {
return;
}
cell.style.cursor = "pointer";
cell.addEventListener(
"click",
(event) => {
event.preventDefault();
event.stopPropagation();
navigateHcPosCockpitLink(getHcPosDangerTableTarget(rowLabel));
},
true
);
cell.dataset.codexDangerBound = "1";
});
});
}
function ensureHcPosBudgetToolStyle(doc) {
if (doc.getElementById("__codex_hcpos_budget_tool_style__")) {
return;
}
const style = doc.createElement("style");
style.id = "__codex_hcpos_budget_tool_style__";
style.textContent = `
.codex-budget-panel {
margin: 16px 0 20px;
padding: 16px 18px;
border-radius: 16px;
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
border: 1px solid rgba(17,37,63,0.06);
box-shadow: 0 12px 28px rgba(17,37,63,0.08);
}
.codex-budget-panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
}
.codex-budget-panel-title {
display: flex;
flex-direction: column;
gap: 4px;
}
.codex-budget-panel-title strong {
color: #152742;
font-size: 18px;
}
.codex-budget-panel-title span {
color: #6d7d97;
font-size: 12px;
}
.codex-budget-pill {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 999px;
background: rgba(45,118,255,0.08);
color: #2d76ff;
font-size: 12px;
}
.codex-budget-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.codex-budget-card {
padding: 14px 16px;
border-radius: 14px;
border: 1px solid rgba(17,37,63,0.06);
background: rgba(255,255,255,0.92);
}
.codex-budget-card strong {
display: block;
margin-bottom: 8px;
color: #152742;
font-size: 15px;
}
.codex-budget-card table {
width: 100%;
border-collapse: collapse;
}
.codex-budget-card th,
.codex-budget-card td {
padding: 8px 6px;
border-bottom: 1px solid rgba(17,37,63,0.06);
color: #496385;
font-size: 12px;
text-align: left;
}
.codex-budget-card th {
color: #7d8ca5;
font-size: 11px;
}
.codex-budget-card p {
margin: 0;
color: #496385;
font-size: 12px;
line-height: 1.7;
}
.codex-budget-row-actions {
display: flex;
gap: 8px;
}
.codex-budget-status {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
line-height: 1;
border: 1px solid transparent;
}
.codex-budget-status[data-tone="success"] {
background: rgba(18, 183, 106, 0.12);
color: #0f8c52;
}
.codex-budget-status[data-tone="warning"] {
background: rgba(245, 158, 11, 0.14);
color: #b26b00;
}
.codex-budget-status[data-tone="danger"] {
background: rgba(209, 67, 67, 0.12);
color: #c03939;
}
.codex-budget-status[data-tone="default"] {
background: rgba(17, 37, 63, 0.06);
color: #496385;
}
.codex-budget-row-actions button,
.codex-budget-modal-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-budget-row-actions button.codex-primary,
.codex-budget-modal-actions button.codex-primary {
background: #2d76ff;
color: #fff;
border-color: transparent;
}
.codex-budget-row-actions button.codex-danger,
.codex-budget-modal-actions button.codex-danger {
color: #d14343;
}
.codex-budget-modal-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-budget-modal {
width: min(720px, 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-budget-modal h4 {
margin: 0 0 16px;
color: #152742;
font-size: 20px;
}
.codex-budget-form {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.codex-budget-form label {
display: flex;
flex-direction: column;
gap: 6px;
color: #6d7d97;
font-size: 12px;
}
.codex-budget-form label.full {
grid-column: 1 / -1;
}
.codex-budget-form input,
.codex-budget-form textarea,
.codex-budget-form select {
border: 1px solid rgba(17,37,63,0.12);
border-radius: 10px;
padding: 10px 12px;
color: #152742;
font-size: 13px;
background: #fff;
}
.codex-budget-form textarea {
min-height: 140px;
resize: vertical;
}
.codex-budget-modal-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 18px;
}
.codex-budget-preview {
display: grid;
gap: 14px;
}
.codex-budget-preview pre {
margin: 0;
padding: 12px 14px;
border-radius: 12px;
background: #f7faff;
border: 1px solid rgba(17,37,63,0.06);
color: #35507a;
font-size: 12px;
line-height: 1.6;
white-space: pre-wrap;
}
.codex-budget-detail-list {
display: grid;
gap: 8px;
}
.codex-budget-detail-row {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 8px 0;
border-bottom: 1px solid rgba(17,37,63,0.06);
}
.codex-budget-detail-row:last-child {
border-bottom: 0;
}
.codex-budget-detail-row span {
color: #7d8ca5;
font-size: 12px;
}
.codex-budget-detail-row strong {
color: #152742;
font-size: 13px;
text-align: right;
}
.codex-budget-timeline {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 10px;
}
.codex-budget-timeline li {
padding: 12px 14px;
border-radius: 12px;
background: #f7faff;
border: 1px solid rgba(17,37,63,0.06);
color: #35507a;
font-size: 12px;
line-height: 1.6;
}
.codex-budget-timeline strong {
display: block;
margin-bottom: 4px;
color: #152742;
font-size: 13px;
}
@media (max-width: 960px) {
.codex-budget-grid,
.codex-budget-form {
grid-template-columns: 1fr;
}
}
`;
doc.head.appendChild(style);
}
function readHcPosBudgetState(win, storageKey, seed) {
try {
const raw = win.localStorage.getItem(storageKey);
if (raw) {
return JSON.parse(raw);
}
} catch (_error) {
// ignore
}
return JSON.parse(JSON.stringify(seed));
}
function writeHcPosBudgetState(win, storageKey, state) {
try {
win.localStorage.setItem(storageKey, JSON.stringify(state));
} catch (_error) {
// ignore
}
}
function ensureHcPosBudgetPanel(doc, panelId) {
let panel = doc.getElementById(panelId);
if (panel) {
return panel;
}
panel = doc.createElement("section");
panel.id = panelId;
panel.className = "codex-budget-panel";
const search = doc.querySelector(".searchStys");
const host = search?.parentElement || doc.querySelector(".app-main > div") || doc.body;
if (search && search.parentElement) {
search.parentElement.insertBefore(panel, search.nextSibling);
} else {
host.insertBefore(panel, host.firstChild);
}
return panel;
}
function closeHcPosBudgetModal(doc) {
doc.getElementById("__codex_budget_modal_mask__")?.remove();
}
function openHcPosBudgetModal(doc, html, bindings = []) {
ensureHcPosBudgetToolStyle(doc);
closeHcPosBudgetModal(doc);
const mask = doc.createElement("div");
mask.id = "__codex_budget_modal_mask__";
mask.className = "codex-budget-modal-mask";
mask.innerHTML = `<div class="codex-budget-modal">${html}</div>`;
mask.addEventListener("click", (event) => {
if (event.target === mask) {
closeHcPosBudgetModal(doc);
}
});
doc.body.appendChild(mask);
bindings.forEach((binding) => {
const target = mask.querySelector(binding.selector);
if (target) {
binding.bind(target, mask);
}
});
}
function escapeHcPosBudgetHtml(value) {
return String(value ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function getHcPosBudgetTimestamp() {
const now = new Date();
const pad = (value) => String(value).padStart(2, "0");
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}`;
}
function getHcPosBudgetStatusTone(status) {
const text = String(status || "");
if (/通过|启用|完成/.test(text)) {
return "success";
}
if (/驳回|禁用|失败/.test(text)) {
return "danger";
}
if (/待|中|审核/.test(text)) {
return "warning";
}
return "default";
}
function renderHcPosBudgetStatus(status) {
return `<span class="codex-budget-status" data-tone="${getHcPosBudgetStatusTone(status)}">${escapeHcPosBudgetHtml(status || "-")}</span>`;
}
function extractHcPosBudgetOrganContext(doc) {
const bodyText = (doc.body?.innerText || "").replace(/\u00a0/g, " ");
const year = bodyText.match(/预算表年份[:]\s*(\d{4})/)?.[1] || String(new Date().getFullYear());
const budgetName = bodyText.match(/科目名称[:]\s*([^\n]+)/)?.[1]?.trim() || `${year}-预算表`;
const subject =
bodyText.match(/(物业管理费收入|人员外包费用|工程维护费|能源服务成本|预算表生成操作人)/)?.[1] ||
budgetName;
const amountMatch = bodyText.match(/金额\s+([¥¥]?\s*[\d,.-]+)/);
const rawAmount = amountMatch?.[1]?.replace(/\s+/g, "") || "0.00";
const amount = /[¥¥]/.test(rawAmount) ? rawAmount.replace("¥", "¥") : `¥${rawAmount}`;
return {
year,
budgetName,
subject,
amount
};
}
function buildHcPosBudgetOrganSeed(doc) {
const context = extractHcPosBudgetOrganContext(doc);
return {
submissions: [
{
id: "budget-organ-001",
year: context.year,
budgetName: context.budgetName,
subject: context.subject,
amount: context.amount,
status: "待提交",
applicant: "-",
submitAt: "-",
note: "年度预算编制已完成,待发起审批。",
approvalNumber: ""
}
]
};
}
function buildHcPosFinancialApprovalSeed() {
return {
extraRecords: []
};
}
function readHcPosFinancialApprovalRecords(doc) {
const win = doc.defaultView;
const localState = readHcPosBudgetState(win, "__codex_financial_approval_state__", buildHcPosFinancialApprovalSeed());
const seen = new Set();
const records = [];
const pushRecord = (record) => {
if (!record || !record.number || seen.has(record.number)) {
return;
}
seen.add(record.number);
records.push(record);
};
localState.extraRecords.forEach(pushRecord);
if ((doc.location?.pathname || "").includes("/financialApproval/")) {
doc.querySelectorAll("tbody tr").forEach((row, index) => {
if (row.closest("#__codex_financial_approval_panel__")) {
return;
}
pushRecord(buildHcPosFinancialApprovalRecordFromRow(row, index));
});
}
return records;
}
function writeHcPosFinancialApprovalRecord(doc, record) {
if (!record || !record.number) {
return;
}
const win = doc.defaultView;
const state = readHcPosBudgetState(win, "__codex_financial_approval_state__", buildHcPosFinancialApprovalSeed());
const nextRecords = state.extraRecords.filter((item) => item.number !== record.number);
nextRecords.unshift(record);
writeHcPosBudgetState(win, "__codex_financial_approval_state__", {
extraRecords: nextRecords
});
}
function buildHcPosFinancialApprovalRecordFromRow(row, index = 0) {
const cells = [...row.querySelectorAll("td")]
.map((cell) => (cell.textContent || "").replace(/\s+/g, " ").trim())
.filter(Boolean);
if (cells.length < 7) {
return null;
}
const name = cells[1] || `财务审批-${index + 1}`;
const number = cells[2] || `DOM-${index + 1}`;
const status = cells[4] || "审批中";
return {
id: `approval-${number}`,
name,
number,
channel: cells[3] || "企业微信",
status,
applicant: cells[5] || "未知",
createdAt: cells[6] || "-",
summary: `${name} 当前状态为 ${status}`,
details: [
{ label: "审批名称", value: name },
{ label: "审批编号", value: number },
{ label: "审批方式", value: cells[3] || "企业微信" },
{ label: "审批状态", value: status, isStatus: true },
{ label: "发起人", value: cells[5] || "未知" },
{ label: "发起时间", value: cells[6] || "-" }
],
timeline: [
{ title: "发起审批", detail: `申请人:${cells[5] || "未知"}\n时间:${cells[6] || "-"}` },
{ title: "当前节点", detail: `状态:${status}\n审批方式:${cells[3] || "企业微信"}` }
]
};
}
function buildHcPosFinancialApprovalRecordFromSubmission(entry) {
return {
id: `approval-${entry.approvalNumber}`,
name: `${entry.budgetName}预算审核`,
number: entry.approvalNumber,
channel: "镜像本地",
status: entry.status,
applicant: entry.applicant,
createdAt: entry.submitAt,
summary: `${entry.budgetName} 已发起预算审核,当前处理科目为 ${entry.subject}`,
details: [
{ label: "审批名称", value: `${entry.budgetName}预算审核` },
{ label: "审批编号", value: entry.approvalNumber },
{ label: "预算年度", value: entry.year },
{ label: "预算科目", value: entry.subject },
{ label: "预算金额", value: entry.amount },
{ label: "审批状态", value: entry.status, isStatus: true },
{ label: "发起人", value: entry.applicant },
{ label: "发起时间", value: entry.submitAt },
{ label: "备注", value: entry.note || "-" }
],
timeline: [
{ title: "预算编制完成", detail: `预算表:${entry.budgetName}\n金额:${entry.amount}` },
{ title: "发起审核", detail: `申请人:${entry.applicant}\n时间:${entry.submitAt}` },
{ title: "当前节点", detail: "财务审批处理中,镜像站仅做本地流程展示。" }
]
};
}
function openHcPosFinancialApprovalDetail(doc, record) {
if (!record) {
return;
}
const detailRows = (record.details || [])
.map((item) => {
const value = item.isStatus ? renderHcPosBudgetStatus(item.value) : `<strong>${escapeHcPosBudgetHtml(item.value)}</strong>`;
return `<div class="codex-budget-detail-row"><span>${escapeHcPosBudgetHtml(item.label)}</span>${value}</div>`;
})
.join("");
const timeline = (record.timeline || [])
.map(
(item) => `
<li>
<strong>${escapeHcPosBudgetHtml(item.title)}</strong>
<div>${escapeHcPosBudgetHtml(item.detail).replace(/\n/g, "<br>")}</div>
</li>
`
)
.join("");
openHcPosBudgetModal(
doc,
`
<h4>审批详情</h4>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>${escapeHcPosBudgetHtml(record.name)}</strong>
<p>${escapeHcPosBudgetHtml(record.summary || "")}</p>
<div class="codex-budget-detail-list">${detailRows}</div>
</div>
<div class="codex-budget-card">
<strong>流程节点</strong>
<ul class="codex-budget-timeline">${timeline || "<li><strong>暂无节点</strong><div>当前镜像没有更多审批轨迹。</div></li>"}</ul>
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function renderHcPosBudgetOrganPanel(doc) {
const win = doc.defaultView;
const storageKey = "__codex_budget_organ_state__";
const state = readHcPosBudgetState(win, storageKey, buildHcPosBudgetOrganSeed(doc));
const panel = ensureHcPosBudgetPanel(doc, "__codex_budget_organ_panel__");
const approvals = readHcPosFinancialApprovalRecords(doc).slice(0, 4);
const pendingCount = state.submissions.filter((item) => item.status === "待提交").length;
const submittedCount = state.submissions.filter((item) => item.status !== "待提交").length;
const rows = state.submissions
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.year)}</td>
<td>${escapeHcPosBudgetHtml(item.budgetName)}</td>
<td>${escapeHcPosBudgetHtml(item.subject)}</td>
<td>${escapeHcPosBudgetHtml(item.amount)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.submitAt)}</td>
<td>
<div class="codex-budget-row-actions">
${
item.status === "待提交"
? `<button type="button" data-submit-id="${escapeHcPosBudgetHtml(item.id)}" class="codex-primary">提交审核</button>`
: `<button type="button" data-view-approval="${escapeHcPosBudgetHtml(item.approvalNumber)}">查看审批</button>`
}
</div>
</td>
</tr>
`
)
.join("");
const approvalRows = approvals
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.number)}</td>
<td>${escapeHcPosBudgetHtml(item.name)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.applicant)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-approval="${escapeHcPosBudgetHtml(item.number)}">查看详情</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地预算审核工作台</strong>
<span>补齐预算编制页不可点击的“提交审核”,并把审批记录联动到财务审批页。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>提交概览</strong>
<div class="codex-budget-detail-list">
<div class="codex-budget-detail-row"><span>待提交预算项</span><strong>${pendingCount}</strong></div>
<div class="codex-budget-detail-row"><span>已发起审批</span><strong>${submittedCount}</strong></div>
<div class="codex-budget-detail-row"><span>审批记录数</span><strong>${approvals.length}</strong></div>
</div>
</div>
<div class="codex-budget-card">
<strong>使用说明</strong>
<p>原页面按钮保持原位,本地镜像会把“提交审核”改成可点,并在这里保存提交结果。财务审批页的“查看”也会同步展示详情。</p>
</div>
</div>
<div class="codex-budget-grid" style="margin-top: 12px;">
<div class="codex-budget-card">
<strong>预算提交项</strong>
<table>
<thead><tr><th>年度</th><th>预算表</th><th>科目</th><th>金额</th><th>状态</th><th>提交时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="7">暂无待提交预算项</td></tr>'}</tbody>
</table>
</div>
<div class="codex-budget-card">
<strong>最近审批记录</strong>
<table>
<thead><tr><th>审批编号</th><th>审批名称</th><th>状态</th><th>发起人</th><th>操作</th></tr></thead>
<tbody>${approvalRows || '<tr><td colspan="5">暂无审批记录</td></tr>'}</tbody>
</table>
</div>
</div>
`;
}
function enableHcPosBudgetOrganNativeButton(doc) {
doc.querySelectorAll("button, .el-button").forEach((button) => {
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (label !== "提交审核") {
return;
}
button.disabled = false;
button.removeAttribute("disabled");
button.classList.remove("is-disabled");
button.closest(".is-disabled")?.classList.remove("is-disabled");
button.style.cursor = "pointer";
button.style.opacity = "1";
});
}
function openHcPosBudgetOrganSubmitModal(doc, submissionId = "") {
const win = doc.defaultView;
const storageKey = "__codex_budget_organ_state__";
const state = readHcPosBudgetState(win, storageKey, buildHcPosBudgetOrganSeed(doc));
const context = extractHcPosBudgetOrganContext(doc);
const current = state.submissions.find((item) => item.id === submissionId) || {
id: `budget-organ-${Date.now()}`,
year: context.year,
budgetName: context.budgetName,
subject: context.subject,
amount: context.amount,
status: "待提交",
applicant: "-",
submitAt: "-",
note: "",
approvalNumber: ""
};
openHcPosBudgetModal(
doc,
`
<h4>提交预算审核</h4>
<div class="codex-budget-form">
<label><span>预算年度</span><input data-field="year" value="${escapeHcPosBudgetHtml(current.year)}"></label>
<label><span>预算表名称</span><input data-field="budgetName" value="${escapeHcPosBudgetHtml(current.budgetName)}"></label>
<label><span>预算科目</span><input data-field="subject" value="${escapeHcPosBudgetHtml(current.subject)}"></label>
<label><span>预算金额</span><input data-field="amount" value="${escapeHcPosBudgetHtml(current.amount)}"></label>
<label class="full"><span>提交说明</span><textarea data-field="note">${escapeHcPosBudgetHtml(current.note || "年度预算编制完成,申请进入财务审批流程。")}</textarea></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">确认提交</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const next = readHcPosBudgetState(win, storageKey, buildHcPosBudgetOrganSeed(doc));
const submitAt = getHcPosBudgetTimestamp();
const payload = {
id: current.id,
year: mask.querySelector("[data-field='year']").value.trim() || current.year,
budgetName: mask.querySelector("[data-field='budgetName']").value.trim() || current.budgetName,
subject: mask.querySelector("[data-field='subject']").value.trim() || current.subject,
amount: mask.querySelector("[data-field='amount']").value.trim() || current.amount,
status: "审批中",
applicant: "本地镜像",
submitAt,
note: mask.querySelector("[data-field='note']").value.trim() || current.note,
approvalNumber: current.approvalNumber || `ISP${Date.now()}`
};
const targetIndex = next.submissions.findIndex((item) => item.id === current.id);
if (targetIndex >= 0) {
next.submissions.splice(targetIndex, 1, payload);
} else {
next.submissions.unshift(payload);
}
writeHcPosBudgetState(win, storageKey, next);
writeHcPosFinancialApprovalRecord(doc, buildHcPosFinancialApprovalRecordFromSubmission(payload));
renderHcPosBudgetOrganPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function installHcPosBudgetOrganTools(doc) {
enableHcPosBudgetOrganNativeButton(doc);
renderHcPosBudgetOrganPanel(doc);
if (doc.body.dataset.codexBudgetOrganInstalled === "1") {
return;
}
ensureHcPosBudgetToolStyle(doc);
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
const submitId = button.dataset.submitId;
const viewApproval = button.dataset.viewApproval;
if (submitId) {
event.preventDefault();
event.stopPropagation();
openHcPosBudgetOrganSubmitModal(doc, submitId);
return;
}
if (viewApproval) {
const record = readHcPosFinancialApprovalRecords(doc).find((item) => item.number === viewApproval);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosFinancialApprovalDetail(doc, record);
return;
}
if (label !== "提交审核") {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosBudgetOrganSubmitModal(doc);
},
true
);
doc.body.dataset.codexBudgetOrganInstalled = "1";
}
function renderHcPosFinancialApprovalPanel(doc) {
const panel = ensureHcPosBudgetPanel(doc, "__codex_financial_approval_panel__");
const records = readHcPosFinancialApprovalRecords(doc);
const rows = records
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.number)}</td>
<td>${escapeHcPosBudgetHtml(item.name)}</td>
<td>${escapeHcPosBudgetHtml(item.channel)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.applicant)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-approval="${escapeHcPosBudgetHtml(item.number)}">查看详情</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地财务审批详情台</strong>
<span>补齐“查看”动作,优先读取表格行数据,并兼容预算编制页新提交的本地审批单。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-card">
<strong>审批记录</strong>
<table>
<thead><tr><th>审批编号</th><th>审批名称</th><th>审批方式</th><th>状态</th><th>发起人</th><th>发起时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="7">暂无可查看审批记录</td></tr>'}</tbody>
</table>
</div>
`;
}
function installHcPosFinancialApprovalTools(doc) {
renderHcPosFinancialApprovalPanel(doc);
if (doc.body.dataset.codexFinancialApprovalInstalled === "1") {
return;
}
ensureHcPosBudgetToolStyle(doc);
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const number = button.dataset.viewApproval;
if (number) {
const record = readHcPosFinancialApprovalRecords(doc).find((item) => item.number === number);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosFinancialApprovalDetail(doc, record);
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (label !== "查看") {
return;
}
const row = button.closest("tr");
const record = buildHcPosFinancialApprovalRecordFromRow(row);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosFinancialApprovalDetail(doc, record);
},
true
);
doc.body.dataset.codexFinancialApprovalInstalled = "1";
}
function buildHcPosRevenueFlowSeed() {
return {
salesOrders: [],
refundRecords: [],
tickets: [],
chargingBatches: [],
collectionActions: []
};
}
function readHcPosRevenueFlowState(win) {
const state = readHcPosBudgetState(win, "__codex_revenue_flow_state__", buildHcPosRevenueFlowSeed());
return {
...buildHcPosRevenueFlowSeed(),
...state,
salesOrders: Array.isArray(state.salesOrders) ? state.salesOrders : [],
refundRecords: Array.isArray(state.refundRecords) ? state.refundRecords : [],
tickets: Array.isArray(state.tickets) ? state.tickets : [],
chargingBatches: Array.isArray(state.chargingBatches) ? state.chargingBatches : [],
collectionActions: Array.isArray(state.collectionActions) ? state.collectionActions : []
};
}
function writeHcPosRevenueFlowState(win, state) {
writeHcPosBudgetState(win, "__codex_revenue_flow_state__", state);
}
function createHcPosRevenueNumber(prefix) {
return `${prefix}${Date.now()}${Math.random().toString(16).slice(2, 6)}`;
}
function formatHcPosRevenueAmount(value) {
const text = String(value || "").trim();
if (!text) {
return "¥0.00";
}
return /^[¥¥]/.test(text) ? text.replace("¥", "¥") : `¥${text}`;
}
function downloadHcPosRevenueFile(doc, fileName, content) {
const win = doc.defaultView;
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
const url = win.URL.createObjectURL(blob);
const anchor = doc.createElement("a");
anchor.href = url;
anchor.download = fileName;
anchor.click();
win.URL.revokeObjectURL(url);
}
function upsertHcPosRevenueTicket(state, ticket) {
const nextTickets = state.tickets.filter((item) => item.orderNo !== ticket.orderNo);
nextTickets.unshift(ticket);
state.tickets = nextTickets;
}
function upsertHcPosRevenueSalesOrder(state, order) {
const nextOrders = state.salesOrders.filter((item) => item.orderNo !== order.orderNo);
nextOrders.unshift(order);
state.salesOrders = nextOrders;
}
function upsertHcPosRevenueChargingBatch(state, batch) {
const nextBatches = state.chargingBatches.filter((item) => item.batchNo !== batch.batchNo);
nextBatches.unshift(batch);
state.chargingBatches = nextBatches;
}
function upsertHcPosRevenueCollectionAction(state, action) {
const nextActions = state.collectionActions.filter((item) => item.id !== action.id);
nextActions.unshift(action);
state.collectionActions = nextActions;
}
function getHcPosRevenueRowIndex(row) {
const rows = row?.parentElement ? [...row.parentElement.children] : [];
return Math.max(rows.indexOf(row), 0) + 1;
}
function extractHcPosRevenueCells(row) {
return [...(row?.querySelectorAll("td") || [])]
.map((cell) => (cell.textContent || "").replace(/\s+/g, " ").trim())
.filter(Boolean);
}
function findHcPosRevenueDateTime(cells) {
return cells.find((text) => /\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/.test(text)) || getHcPosBudgetTimestamp();
}
function findHcPosRevenueAmount(cells) {
return formatHcPosRevenueAmount(cells.find((text) => /[¥]\s*[\d,]+(?:\.\d+)?/.test(text)) || "0.00");
}
function renderHcPosRevenueSummaryRows(rows) {
return rows
.map(
(item) => `
<div class="codex-budget-detail-row">
<span>${escapeHcPosBudgetHtml(item.label)}</span>
<strong>${escapeHcPosBudgetHtml(item.value)}</strong>
</div>
`
)
.join("");
}
function buildHcPosRevenueSalesRecordFromRow(row) {
const cells = extractHcPosRevenueCells(row);
if (cells.length < 6) {
return null;
}
const rowIndex = getHcPosRevenueRowIndex(row);
const createdAt = findHcPosRevenueDateTime(cells);
const amount = findHcPosRevenueAmount(cells);
const payOrderNo = cells.find((text) => /^YS|^XS|^\d{12,}/.test(text)) || "-";
const fallbackOrderNo = `DOMXS-${createdAt.replace(/\D/g, "").slice(-10)}-${rowIndex}`;
return {
id: fallbackOrderNo,
orderNo: payOrderNo !== "-" ? payOrderNo : fallbackOrderNo,
title: `销单记录-${rowIndex}`,
debtor: "-",
payer: "-",
period: cells.find((text) => /^\d{4}-\d{2}$/.test(text)) || "-",
item: cells[1] || "销单事项",
amount,
method: cells[1] || "特殊减免",
status: cells[2] || "已销单",
payOrderNo,
channel: cells[4] || "本地镜像",
createdAt,
operator: cells[6] || "本地镜像",
note: "来源于原始销单记录",
invoiceStatus: "未开票",
receiptStatus: "未下载"
};
}
function buildHcPosRevenueTicketFromRow(row) {
const cells = extractHcPosRevenueCells(row);
if (cells.length < 5) {
return null;
}
const rowIndex = getHcPosRevenueRowIndex(row);
const createdAt = findHcPosRevenueDateTime(cells);
const orderNo = cells.find((text) => /^\d{12,}/.test(text)) || `EB${createdAt.replace(/\D/g, "").slice(-10)}${rowIndex}`;
const amount = findHcPosRevenueAmount(cells);
const status = cells.find((text) => /支付|开票|失败|成功|/.test(text)) || "待处理";
return {
id: `ticket-${orderNo}`,
ticketNo: `PJ${orderNo.slice(-10)}`,
orderNo,
title: `电子票据-${orderNo.slice(-6)}`,
payer: cells[0] || "-",
email: cells.find((text) => /@/.test(text)) || "local@mirror.test",
amount,
type: "电子收据",
status,
createdAt,
note: "来源于原始电子票据列表"
};
}
function openHcPosRevenueSalesDetail(doc, record) {
if (!record) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>销单详情</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "销单单号", value: record.orderNo },
{ label: "业务标题", value: record.title || "-" },
{ label: "销单方式", value: record.method || "-" },
{ label: "销单状态", value: record.status || "-" },
{ label: "销单金额", value: record.amount || "¥0.00" },
{ label: "发起时间", value: record.createdAt || "-" },
{ label: "操作人", value: record.operator || "-" },
{ label: "备注", value: record.note || "-" }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function openHcPosRevenueRefundDetail(doc, record) {
if (!record) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>退款详情</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "退款单号", value: record.refundNo },
{ label: "业务订单号", value: record.businessOrderNo },
{ label: "退款金额", value: record.amount || "¥0.00" },
{ label: "账期", value: record.period || "-" },
{ label: "发起时间", value: record.createdAt || "-" },
{ label: "发起人", value: record.operator || "-" },
{ label: "备注", value: record.note || "-" }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function openHcPosRevenueTicketDetail(doc, ticket) {
if (!ticket) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>票据详情</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "票据编号", value: ticket.ticketNo },
{ label: "关联订单", value: ticket.orderNo || "-" },
{ label: "票据标题", value: ticket.title || "-" },
{ label: "票据类型", value: ticket.type || "电子收据" },
{ label: "票据状态", value: ticket.status || "-" },
{ label: "支付人", value: ticket.payer || "-" },
{ label: "接收邮箱", value: ticket.email || "-" },
{ label: "票据金额", value: ticket.amount || "¥0.00" },
{ label: "生成时间", value: ticket.createdAt || "-" }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function downloadHcPosRevenueReceipt(doc, record) {
if (!record) {
return;
}
const win = doc.defaultView;
const state = readHcPosRevenueFlowState(win);
state.salesOrders = state.salesOrders.map((item) =>
item.orderNo === record.orderNo
? {
...item,
receiptStatus: "已下载"
}
: item
);
writeHcPosRevenueFlowState(win, state);
downloadHcPosRevenueFile(
doc,
`${record.orderNo || createHcPosRevenueNumber("XS")}-receipt.txt`,
`销单单号: ${record.orderNo || "-"}\n销单金额: ${record.amount || "¥0.00"}\n销单方式: ${record.method || "-"}\n生成时间: ${getHcPosBudgetTimestamp()}`
);
}
function openHcPosRevenueTicketCreateModal(doc, salesRecord) {
if (!salesRecord) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>开具票据</h4>
<div class="codex-budget-form">
<label><span>票据标题</span><input data-field="title" value="${escapeHcPosBudgetHtml(salesRecord.title || "销单票据")}"></label>
<label><span>票据类型</span><select data-field="type"><option>电子收据</option><option>电子发票</option></select></label>
<label><span>接收邮箱</span><input data-field="email" value="local@mirror.test"></label>
<label><span>票据金额</span><input data-field="amount" value="${escapeHcPosBudgetHtml(salesRecord.amount || "¥0.00")}"></label>
<label class="full"><span>备注</span><textarea data-field="note">本地镜像生成票据,用于收入管理闭环演示。</textarea></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">确认开具</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const win = doc.defaultView;
const state = readHcPosRevenueFlowState(win);
const ticket = {
id: `ticket-${salesRecord.orderNo}`,
ticketNo: createHcPosRevenueNumber("PJ"),
orderNo: salesRecord.orderNo,
title: mask.querySelector("[data-field='title']").value.trim() || salesRecord.title || "销单票据",
payer: salesRecord.payer || salesRecord.debtor || "-",
email: mask.querySelector("[data-field='email']").value.trim() || "local@mirror.test",
amount: formatHcPosRevenueAmount(mask.querySelector("[data-field='amount']").value.trim() || salesRecord.amount),
type: mask.querySelector("[data-field='type']").value.trim() || "电子收据",
status: "已开票",
createdAt: getHcPosBudgetTimestamp(),
note: mask.querySelector("[data-field='note']").value.trim()
};
upsertHcPosRevenueTicket(state, ticket);
state.salesOrders = state.salesOrders.map((item) =>
item.orderNo === salesRecord.orderNo
? {
...item,
invoiceStatus: "已开票"
}
: item
);
writeHcPosRevenueFlowState(win, state);
renderHcPosRevenueSalesOrderPanel(doc);
renderHcPosRevenueElectronicBillPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function renderHcPosRevenueReceivablePanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_receivable_panel__");
const latestSales = state.salesOrders
.slice(0, 3)
.map((item) => `<tr><td>${escapeHcPosBudgetHtml(item.orderNo)}</td><td>${escapeHcPosBudgetHtml(item.amount)}</td><td>${renderHcPosBudgetStatus(item.status)}</td><td>${escapeHcPosBudgetHtml(item.createdAt)}</td></tr>`)
.join("");
const latestRefunds = state.refundRecords
.slice(0, 3)
.map((item) => `<tr><td>${escapeHcPosBudgetHtml(item.refundNo)}</td><td>${escapeHcPosBudgetHtml(item.amount)}</td><td>${escapeHcPosBudgetHtml(item.period)}</td><td>${escapeHcPosBudgetHtml(item.createdAt)}</td></tr>`)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地收入操作台</strong>
<span>补齐应收页的“我要销单 / 我要退款”,并把结果同步到销单、退款记录和电子票据页。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>闭环概览</strong>
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "本地销单记录", value: String(state.salesOrders.length) },
{ label: "本地退款记录", value: String(state.refundRecords.length) },
{ label: "本地票据记录", value: String(state.tickets.length) }
])}
</div>
</div>
<div class="codex-budget-card">
<strong>使用说明</strong>
<p>直接点击原页面工具栏里的“我要销单 / 我要退款”即可发起本地业务,结果会落到收入管理相关页面。</p>
</div>
</div>
<div class="codex-budget-grid" style="margin-top: 12px;">
<div class="codex-budget-card">
<strong>最近销单</strong>
<table>
<thead><tr><th>销单单号</th><th>金额</th><th>状态</th><th>时间</th></tr></thead>
<tbody>${latestSales || '<tr><td colspan="4">暂无本地销单记录</td></tr>'}</tbody>
</table>
</div>
<div class="codex-budget-card">
<strong>最近退款</strong>
<table>
<thead><tr><th>退款单号</th><th>金额</th><th>账期</th><th>时间</th></tr></thead>
<tbody>${latestRefunds || '<tr><td colspan="4">暂无本地退款记录</td></tr>'}</tbody>
</table>
</div>
</div>
`;
}
function openHcPosRevenueReceivableModal(doc, mode) {
const isSales = mode === "sales";
openHcPosBudgetModal(
doc,
`
<h4>${isSales ? "发起销单" : "发起退款"}</h4>
<div class="codex-budget-form">
<label><span>债务人</span><input data-field="debtor" value="阿三"></label>
<label><span>缴费人</span><input data-field="payer" value="阿三"></label>
<label><span>计费账期</span><input data-field="period" value="2026-04"></label>
<label><span>收费项</span><input data-field="item" value="物业管理费"></label>
<label><span>${isSales ? "销单金额" : "退款金额"}</span><input data-field="amount" value="128.50"></label>
<label class="full"><span>备注</span><textarea data-field="note">${isSales ? "本地销单演示,用于镜像闭环。" : "本地退款演示,用于镜像闭环。"}</textarea></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">确认</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const win = doc.defaultView;
const state = readHcPosRevenueFlowState(win);
const debtor = mask.querySelector("[data-field='debtor']").value.trim() || "未命名债务人";
const payer = mask.querySelector("[data-field='payer']").value.trim() || debtor;
const period = mask.querySelector("[data-field='period']").value.trim() || "-";
const item = mask.querySelector("[data-field='item']").value.trim() || "收费项";
const amount = formatHcPosRevenueAmount(mask.querySelector("[data-field='amount']").value.trim() || "0.00");
const note = mask.querySelector("[data-field='note']").value.trim();
const createdAt = getHcPosBudgetTimestamp();
if (isSales) {
state.salesOrders.unshift({
id: createHcPosRevenueNumber("sales-"),
orderNo: createHcPosRevenueNumber("XS"),
title: `${item}销单`,
debtor,
payer,
period,
item,
amount,
method: "特殊减免",
status: "已销单",
payOrderNo: "-",
channel: "镜像本地",
createdAt,
operator: "本地镜像",
note,
invoiceStatus: "未开票",
receiptStatus: "未下载"
});
} else {
state.refundRecords.unshift({
id: createHcPosRevenueNumber("refund-"),
refundNo: createHcPosRevenueNumber("TK"),
businessOrderNo: createHcPosRevenueNumber("YS"),
debtor,
payer,
period,
amount,
direction: "退款",
createdAt,
operator: "本地镜像",
note
});
}
writeHcPosRevenueFlowState(win, state);
renderHcPosRevenueReceivablePanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function installHcPosRevenueReceivableTools(doc) {
renderHcPosRevenueReceivablePanel(doc);
if (doc.body.dataset.codexRevenueReceivableInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["我要销单", "我要退款"].includes(label)) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueReceivableModal(doc, label === "我要销单" ? "sales" : "refund");
},
true
);
doc.body.dataset.codexRevenueReceivableInstalled = "1";
}
function openHcPosRevenueChargingDetail(doc, batch) {
if (!batch) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>计费批次详情</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "批次编号", value: batch.batchNo },
{ label: "账期", value: batch.period || "-" },
{ label: "应收本金", value: batch.principal || "¥0.00" },
{ label: "应收违约金", value: batch.penalty || "¥0.00" },
{ label: "状态", value: batch.status || "-" },
{ label: "创建时间", value: batch.createdAt || "-" },
{ label: "审核时间", value: batch.auditAt || "-" },
{ label: "操作人", value: batch.operator || "-" },
{ label: "备注", value: batch.note || "-" }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function renderHcPosRevenueChargingPanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_charging_panel__");
const pendingCount = state.chargingBatches.filter((item) => item.status === "待审核").length;
const approvedCount = state.chargingBatches.filter((item) => item.status === "已审核").length;
const rows = state.chargingBatches
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.batchNo)}</td>
<td>${escapeHcPosBudgetHtml(item.period)}</td>
<td>${escapeHcPosBudgetHtml(item.principal)}</td>
<td>${escapeHcPosBudgetHtml(item.penalty)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-charging="${escapeHcPosBudgetHtml(item.batchNo)}">查看详情</button>
${
item.status === "待审核"
? `<button type="button" data-audit-charging="${escapeHcPosBudgetHtml(item.batchNo)}" class="codex-primary">通过审核</button>`
: ""
}
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地计费工作台</strong>
<span>补齐“创建计费 / 账期审核”,用本地批次记录承接计费流程。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>批次概览</strong>
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "本地计费批次", value: String(state.chargingBatches.length) },
{ label: "待审核批次", value: String(pendingCount) },
{ label: "已审核批次", value: String(approvedCount) }
])}
</div>
</div>
<div class="codex-budget-card">
<strong>使用说明</strong>
<p>原页面工具栏中的“创建计费 / 账期审核”会被接到本地工作流,便于镜像里直接演示计费创建与审核。</p>
</div>
</div>
<div class="codex-budget-card" style="margin-top: 12px;">
<strong>本地计费批次</strong>
<table>
<thead><tr><th>批次编号</th><th>账期</th><th>应收本金</th><th>应收违约金</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="7">暂无本地计费批次</td></tr>'}</tbody>
</table>
</div>
`;
}
function openHcPosRevenueChargingCreateModal(doc) {
openHcPosBudgetModal(
doc,
`
<h4>创建计费批次</h4>
<div class="codex-budget-form">
<label><span>计费账期</span><input data-field="period" value="2026-04"></label>
<label><span>应收本金</span><input data-field="principal" value="10990.42"></label>
<label><span>应收违约金</span><input data-field="penalty" value="0.00"></label>
<label><span>操作人</span><input data-field="operator" value="本地镜像"></label>
<label class="full"><span>备注</span><textarea data-field="note">本地镜像创建计费批次,用于收入管理演示。</textarea></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">确认创建</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const win = doc.defaultView;
const state = readHcPosRevenueFlowState(win);
upsertHcPosRevenueChargingBatch(state, {
id: createHcPosRevenueNumber("charge-"),
batchNo: createHcPosRevenueNumber("JF"),
period: mask.querySelector("[data-field='period']").value.trim() || "2026-04",
principal: formatHcPosRevenueAmount(mask.querySelector("[data-field='principal']").value.trim() || "0.00"),
penalty: formatHcPosRevenueAmount(mask.querySelector("[data-field='penalty']").value.trim() || "0.00"),
status: "待审核",
createdAt: getHcPosBudgetTimestamp(),
auditAt: "-",
operator: mask.querySelector("[data-field='operator']").value.trim() || "本地镜像",
note: mask.querySelector("[data-field='note']").value.trim()
});
writeHcPosRevenueFlowState(win, state);
renderHcPosRevenueChargingPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function openHcPosRevenueChargingAuditModal(doc, batchNo = "") {
const state = readHcPosRevenueFlowState(doc.defaultView);
const batch =
state.chargingBatches.find((item) => item.batchNo === batchNo) ||
state.chargingBatches.find((item) => item.status === "待审核");
if (!batch) {
openHcPosBudgetModal(
doc,
`
<h4>账期审核</h4>
<div class="codex-budget-card">
<p>当前没有待审核的本地计费批次。请先创建一条本地计费批次。</p>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
return;
}
openHcPosBudgetModal(
doc,
`
<h4>账期审核</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "批次编号", value: batch.batchNo },
{ label: "计费账期", value: batch.period },
{ label: "应收本金", value: batch.principal },
{ label: "应收违约金", value: batch.penalty },
{ label: "当前状态", value: batch.status }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">审核通过</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target) {
target.addEventListener("click", () => {
const win = doc.defaultView;
const next = readHcPosRevenueFlowState(win);
next.chargingBatches = next.chargingBatches.map((item) =>
item.batchNo === batch.batchNo
? {
...item,
status: "已审核",
auditAt: getHcPosBudgetTimestamp()
}
: item
);
writeHcPosRevenueFlowState(win, next);
renderHcPosRevenueChargingPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function installHcPosRevenueChargingTools(doc) {
renderHcPosRevenueChargingPanel(doc);
if (doc.body.dataset.codexRevenueChargingInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const viewCharging = button.dataset.viewCharging;
const auditCharging = button.dataset.auditCharging;
if (viewCharging) {
const batch = readHcPosRevenueFlowState(doc.defaultView).chargingBatches.find((item) => item.batchNo === viewCharging);
if (!batch) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueChargingDetail(doc, batch);
return;
}
if (auditCharging) {
event.preventDefault();
event.stopPropagation();
openHcPosRevenueChargingAuditModal(doc, auditCharging);
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (label === "创建计费") {
event.preventDefault();
event.stopPropagation();
openHcPosRevenueChargingCreateModal(doc);
return;
}
if (label === "账期审核") {
event.preventDefault();
event.stopPropagation();
openHcPosRevenueChargingAuditModal(doc);
}
},
true
);
doc.body.dataset.codexRevenueChargingInstalled = "1";
}
function openHcPosRevenueCollectionDetail(doc, action) {
if (!action) {
return;
}
openHcPosBudgetModal(
doc,
`
<h4>收款操作详情</h4>
<div class="codex-budget-card">
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "操作类型", value: action.type || "-" },
{ label: "目标账期", value: action.period || "-" },
{ label: "涉及金额", value: action.amount || "¥0.00" },
{ label: "处理状态", value: action.status || "-" },
{ label: "操作时间", value: action.createdAt || "-" },
{ label: "操作人", value: action.operator || "-" },
{ label: "备注", value: action.note || "-" }
])}
</div>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
}
function renderHcPosRevenueCollectionPanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_collection_panel__");
const adjustCount = state.collectionActions.filter((item) => item.type === "数据修正").length;
const reconcileCount = state.collectionActions.filter((item) => /对账/.test(item.type)).length;
const rows = state.collectionActions
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.type)}</td>
<td>${escapeHcPosBudgetHtml(item.period)}</td>
<td>${escapeHcPosBudgetHtml(item.amount)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-collection-action="${escapeHcPosBudgetHtml(item.id)}">查看详情</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地收款操作台</strong>
<span>补齐“数据修正 / 更新第三方到账信息 / 全部对账 / 部分对账”四类关键动作。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>操作概览</strong>
<div class="codex-budget-detail-list">
${renderHcPosRevenueSummaryRows([
{ label: "本地收款操作", value: String(state.collectionActions.length) },
{ label: "数据修正", value: String(adjustCount) },
{ label: "对账动作", value: String(reconcileCount) }
])}
</div>
</div>
<div class="codex-budget-card">
<strong>使用说明</strong>
<p>点击原页面里的“数据修正 / 更新第三方到账信息 / 全部对账 / 部分对账”即可写入本地操作记录,方便镜像演示。</p>
</div>
</div>
<div class="codex-budget-card" style="margin-top: 12px;">
<strong>最近操作</strong>
<table>
<thead><tr><th>操作类型</th><th>目标账期</th><th>涉及金额</th><th>状态</th><th>时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="6">暂无本地收款操作</td></tr>'}</tbody>
</table>
</div>
`;
}
function openHcPosRevenueCollectionActionModal(doc, type) {
openHcPosBudgetModal(
doc,
`
<h4>${escapeHcPosBudgetHtml(type)}</h4>
<div class="codex-budget-form">
<label><span>目标账期</span><input data-field="period" value="2026-04"></label>
<label><span>涉及金额</span><input data-field="amount" value="256.80"></label>
<label><span>操作人</span><input data-field="operator" value="本地镜像"></label>
<label><span>处理状态</span><select data-field="status"><option>已处理</option><option>处理中</option></select></label>
<label class="full"><span>备注</span><textarea data-field="note">${escapeHcPosBudgetHtml(`${type} 已在镜像中记录,用于收入管理操作演示。`)}</textarea></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">确认记录</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const win = doc.defaultView;
const state = readHcPosRevenueFlowState(win);
upsertHcPosRevenueCollectionAction(state, {
id: createHcPosRevenueNumber("collection-"),
type,
period: mask.querySelector("[data-field='period']").value.trim() || "2026-04",
amount: formatHcPosRevenueAmount(mask.querySelector("[data-field='amount']").value.trim() || "0.00"),
operator: mask.querySelector("[data-field='operator']").value.trim() || "本地镜像",
status: mask.querySelector("[data-field='status']").value.trim() || "已处理",
createdAt: getHcPosBudgetTimestamp(),
note: mask.querySelector("[data-field='note']").value.trim()
});
writeHcPosRevenueFlowState(win, state);
renderHcPosRevenueCollectionPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function installHcPosRevenueCollectionRecordTools(doc) {
renderHcPosRevenueCollectionPanel(doc);
if (doc.body.dataset.codexRevenueCollectionInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const viewAction = button.dataset.viewCollectionAction;
if (viewAction) {
const action = readHcPosRevenueFlowState(doc.defaultView).collectionActions.find((item) => item.id === viewAction);
if (!action) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueCollectionDetail(doc, action);
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["数据修正", "更新第三方到账信息", "全部对账", "部分对账"].includes(label)) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueCollectionActionModal(doc, label);
},
true
);
doc.body.dataset.codexRevenueCollectionInstalled = "1";
}
function renderHcPosRevenueRefundRecordPanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_refund_panel__");
const rows = state.refundRecords
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.refundNo)}</td>
<td>${escapeHcPosBudgetHtml(item.businessOrderNo)}</td>
<td>${escapeHcPosBudgetHtml(item.amount)}</td>
<td>${escapeHcPosBudgetHtml(item.period)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-refund="${escapeHcPosBudgetHtml(item.refundNo)}">查看详情</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地退款记录台</strong>
<span>这里汇总从应收页发起的本地退款动作,便于镜像内串起收入闭环。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-card">
<strong>退款记录</strong>
<table>
<thead><tr><th>退款单号</th><th>业务订单号</th><th>退款金额</th><th>账期</th><th>发起时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="6">暂无本地退款记录</td></tr>'}</tbody>
</table>
</div>
`;
}
function installHcPosRevenueRefundRecordTools(doc) {
renderHcPosRevenueRefundRecordPanel(doc);
if (doc.body.dataset.codexRevenueRefundInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const refundNo = button.dataset.viewRefund;
if (!refundNo) {
return;
}
const record = readHcPosRevenueFlowState(doc.defaultView).refundRecords.find((item) => item.refundNo === refundNo);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueRefundDetail(doc, record);
},
true
);
doc.body.dataset.codexRevenueRefundInstalled = "1";
}
function renderHcPosRevenueSalesOrderPanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_sales_panel__");
const rows = state.salesOrders
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.orderNo)}</td>
<td>${escapeHcPosBudgetHtml(item.title)}</td>
<td>${escapeHcPosBudgetHtml(item.amount)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.invoiceStatus)}</td>
<td>${escapeHcPosBudgetHtml(item.receiptStatus)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-sales="${escapeHcPosBudgetHtml(item.orderNo)}">查看详情</button>
<button type="button" data-download-sales="${escapeHcPosBudgetHtml(item.orderNo)}">下载收据</button>
<button type="button" data-ticket-sales="${escapeHcPosBudgetHtml(item.orderNo)}" class="codex-primary">开具发票</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地销单工作台</strong>
<span>这里承接应收页发起的本地销单,并补齐查看、收据下载、开票动作。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-card">
<strong>销单记录</strong>
<table>
<thead><tr><th>销单单号</th><th>业务标题</th><th>金额</th><th>状态</th><th>开票状态</th><th>收据状态</th><th>时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="8">暂无本地销单记录</td></tr>'}</tbody>
</table>
</div>
`;
}
function installHcPosRevenueSalesOrderTools(doc) {
renderHcPosRevenueSalesOrderPanel(doc);
if (doc.body.dataset.codexRevenueSalesInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const viewSales = button.dataset.viewSales;
const downloadSales = button.dataset.downloadSales;
const ticketSales = button.dataset.ticketSales;
const state = readHcPosRevenueFlowState(doc.defaultView);
if (viewSales) {
const record = state.salesOrders.find((item) => item.orderNo === viewSales);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueSalesDetail(doc, record);
return;
}
if (downloadSales) {
const record = state.salesOrders.find((item) => item.orderNo === downloadSales);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
downloadHcPosRevenueReceipt(doc, record);
renderHcPosRevenueSalesOrderPanel(doc);
return;
}
if (ticketSales) {
const record = state.salesOrders.find((item) => item.orderNo === ticketSales);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueTicketCreateModal(doc, record);
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["查看", "下载收据", "开具发票", "手动开收据"].includes(label)) {
return;
}
const row = button.closest("tr");
const record = buildHcPosRevenueSalesRecordFromRow(row);
if (!record) {
return;
}
event.preventDefault();
event.stopPropagation();
if (label === "查看") {
openHcPosRevenueSalesDetail(doc, record);
return;
}
if (label === "下载收据" || label === "手动开收据") {
downloadHcPosRevenueReceipt(doc, record);
renderHcPosRevenueSalesOrderPanel(doc);
return;
}
openHcPosRevenueTicketCreateModal(doc, record);
},
true
);
doc.body.dataset.codexRevenueSalesInstalled = "1";
}
function renderHcPosRevenueElectronicBillPanel(doc) {
const state = readHcPosRevenueFlowState(doc.defaultView);
const panel = ensureHcPosBudgetPanel(doc, "__codex_revenue_ticket_panel__");
const rows = state.tickets
.slice(0, 8)
.map(
(item) => `
<tr>
<td>${escapeHcPosBudgetHtml(item.ticketNo)}</td>
<td>${escapeHcPosBudgetHtml(item.orderNo)}</td>
<td>${escapeHcPosBudgetHtml(item.title)}</td>
<td>${escapeHcPosBudgetHtml(item.amount)}</td>
<td>${renderHcPosBudgetStatus(item.status)}</td>
<td>${escapeHcPosBudgetHtml(item.createdAt)}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-view-ticket="${escapeHcPosBudgetHtml(item.ticketNo)}">查看</button>
<button type="button" data-download-ticket="${escapeHcPosBudgetHtml(item.ticketNo)}">下载</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地电子票据台</strong>
<span>这里展示从销单页开具的本地票据,补齐查看与下载动作。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-card">
<strong>票据记录</strong>
<table>
<thead><tr><th>票据编号</th><th>关联订单</th><th>票据标题</th><th>金额</th><th>状态</th><th>时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="7">暂无本地票据记录</td></tr>'}</tbody>
</table>
</div>
`;
}
function installHcPosRevenueElectronicBillTools(doc) {
renderHcPosRevenueElectronicBillPanel(doc);
if (doc.body.dataset.codexRevenueTicketInstalled === "1") {
return;
}
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const state = readHcPosRevenueFlowState(doc.defaultView);
const viewTicket = button.dataset.viewTicket;
const downloadTicket = button.dataset.downloadTicket;
if (viewTicket) {
const ticket = state.tickets.find((item) => item.ticketNo === viewTicket);
if (!ticket) {
return;
}
event.preventDefault();
event.stopPropagation();
openHcPosRevenueTicketDetail(doc, ticket);
return;
}
if (downloadTicket) {
const ticket = state.tickets.find((item) => item.ticketNo === downloadTicket);
if (!ticket) {
return;
}
event.preventDefault();
event.stopPropagation();
downloadHcPosRevenueFile(
doc,
`${ticket.ticketNo}.txt`,
`票据编号: ${ticket.ticketNo}\n关联订单: ${ticket.orderNo}\n票据标题: ${ticket.title}\n票据金额: ${ticket.amount}\n票据状态: ${ticket.status}`
);
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["查看", "下载", "生成票据"].includes(label)) {
return;
}
const ticket = buildHcPosRevenueTicketFromRow(button.closest("tr"));
if (!ticket) {
return;
}
event.preventDefault();
event.stopPropagation();
if (label === "查看") {
openHcPosRevenueTicketDetail(doc, ticket);
return;
}
if (label === "下载") {
downloadHcPosRevenueFile(
doc,
`${ticket.ticketNo}.txt`,
`票据编号: ${ticket.ticketNo}\n关联订单: ${ticket.orderNo}\n票据金额: ${ticket.amount}\n票据状态: ${ticket.status}`
);
return;
}
const nextState = readHcPosRevenueFlowState(doc.defaultView);
upsertHcPosRevenueTicket(nextState, {
...ticket,
ticketNo: createHcPosRevenueNumber("PJ"),
status: "已开票",
createdAt: getHcPosBudgetTimestamp()
});
writeHcPosRevenueFlowState(doc.defaultView, nextState);
renderHcPosRevenueElectronicBillPanel(doc);
},
true
);
doc.body.dataset.codexRevenueTicketInstalled = "1";
}
function buildHcPosForecastSeed() {
return {
plans: [
{ id: "fc-2027-01", year: "2027", budgetType: "收入预算", name: "预算预测-A", status: "启用", operator: "郭晓", updatedAt: "2026-04-05 10:00" }
],
items: [
{ id: "item-01", budgetType: "收入预算", businessItem: "物业管理费", subject: "物业管理费收入", operator: "郭晓", updatedAt: "2026-04-05 10:05" },
{ id: "item-02", budgetType: "支出预算", businessItem: "工程维护费", subject: "能源服务成本", operator: "何琳", updatedAt: "2026-04-05 10:08" }
]
};
}
function renderHcPosForecastPanel(doc) {
const win = doc.defaultView;
const storageKey = "__codex_budget_forecast_state__";
const state = readHcPosBudgetState(win, storageKey, buildHcPosForecastSeed());
const panel = ensureHcPosBudgetPanel(doc, "__codex_budget_forecast_panel__");
const planRows = state.plans
.map(
(item) => `<tr><td>${item.year}</td><td>${item.budgetType}</td><td>${item.name}</td><td>${item.status}</td><td>${item.operator}</td><td>${item.updatedAt}</td></tr>`
)
.join("");
const itemRows = state.items
.map(
(item) => `<tr><td>${item.budgetType}</td><td>${item.businessItem}</td><td>${item.subject}</td><td>${item.operator}</td><td>${item.updatedAt}</td></tr>`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地预算预测工作台</strong>
<span>让“新增预算预测 / 添加业务项 / 预算表预览”在镜像里可用,数据保存在本地浏览器。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-grid">
<div class="codex-budget-card">
<strong>预算预测方案</strong>
<table>
<thead><tr><th>年份</th><th>预算类型</th><th>方案名称</th><th>状态</th><th>操作人</th><th>更新时间</th></tr></thead>
<tbody>${planRows || '<tr><td colspan="6">暂无本地方案</td></tr>'}</tbody>
</table>
</div>
<div class="codex-budget-card">
<strong>本地业务项</strong>
<table>
<thead><tr><th>预算类型</th><th>业务项</th><th>科目</th><th>操作人</th><th>更新时间</th></tr></thead>
<tbody>${itemRows || '<tr><td colspan="5">暂无本地业务项</td></tr>'}</tbody>
</table>
</div>
</div>
`;
}
function installHcPosForecastBudgetTools(doc) {
if (doc.body.dataset.codexForecastBudgetInstalled === "1") {
renderHcPosForecastPanel(doc);
return;
}
ensureHcPosBudgetToolStyle(doc);
renderHcPosForecastPanel(doc);
const win = doc.defaultView;
const storageKey = "__codex_budget_forecast_state__";
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["新增预算预测", "添加业务项", "预算表预览"].includes(label)) {
return;
}
event.preventDefault();
event.stopPropagation();
const state = readHcPosBudgetState(win, storageKey, buildHcPosForecastSeed());
if (label === "预算表预览") {
openHcPosBudgetModal(
doc,
`
<h4>预算表预览</h4>
<div class="codex-budget-preview">
<pre>${JSON.stringify(state.plans, null, 2)}</pre>
<pre>${JSON.stringify(state.items, null, 2)}</pre>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">关闭</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
}
]
);
return;
}
const isPlan = label === "新增预算预测";
openHcPosBudgetModal(
doc,
`
<h4>${label}</h4>
<div class="codex-budget-form">
<label>
<span>年份</span>
<input data-field="year" value="2027">
</label>
<label>
<span>预算类型</span>
<select data-field="budgetType">
<option>收入预算</option>
<option>支出预算</option>
</select>
</label>
<label class="${isPlan ? "" : "full"}">
<span>${isPlan ? "方案名称" : "业务项"}</span>
<input data-field="primary" value="${isPlan ? "预算预测-本地新增" : "新增业务项"}">
</label>
${isPlan ? "" : `
<label class="full">
<span>科目</span>
<input data-field="subject" value="物业管理费收入">
</label>
`}
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">保存</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const year = mask.querySelector("[data-field='year']").value.trim();
const budgetType = mask.querySelector("[data-field='budgetType']").value.trim();
const primary = mask.querySelector("[data-field='primary']").value.trim();
const subject = mask.querySelector("[data-field='subject']")?.value.trim();
const now = "2026-04-05 12:00";
if (isPlan) {
state.plans.unshift({
id: `fc-${Date.now()}`,
year,
budgetType,
name: primary || "未命名方案",
status: "启用",
operator: "本地镜像",
updatedAt: now
});
} else {
state.items.unshift({
id: `item-${Date.now()}`,
budgetType,
businessItem: primary || "未命名业务项",
subject: subject || "-",
operator: "本地镜像",
updatedAt: now
});
}
writeHcPosBudgetState(win, storageKey, state);
renderHcPosForecastPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
},
true
);
doc.body.dataset.codexForecastBudgetInstalled = "1";
}
function buildHcPosOperatingBudgetSeed() {
return {
rows: [
{ id: "op-1", year: "2026", budgetType: "业务项", chargeItem: "0512测试", subject: "物业管理费收入", businessCategory: "物业管理费", status: "启用", operator: "肖英", updatedAt: "2025-12-01 18:07:11" },
{ id: "op-2", year: "2026", budgetType: "业务项", chargeItem: "测试电梯楼层收费", subject: "物业管理费收入", businessCategory: "物业管理费", status: "启用", operator: "肖英", updatedAt: "2025-11-12 09:21:45" }
]
};
}
function renderHcPosOperatingBudgetPanel(doc) {
const win = doc.defaultView;
const storageKey = "__codex_operating_budget_state__";
const state = readHcPosBudgetState(win, storageKey, buildHcPosOperatingBudgetSeed());
const panel = ensureHcPosBudgetPanel(doc, "__codex_operating_budget_panel__");
const rows = state.rows
.map(
(item) => `
<tr>
<td>${item.year}</td>
<td>${item.budgetType}</td>
<td>${item.chargeItem}</td>
<td>${item.subject}</td>
<td>${item.businessCategory}</td>
<td>${item.status}</td>
<td>${item.operator}</td>
<td>${item.updatedAt}</td>
<td>
<div class="codex-budget-row-actions">
<button type="button" data-edit-id="${item.id}" class="codex-primary">编辑</button>
<button type="button" data-delete-id="${item.id}" class="codex-danger">删除</button>
</div>
</td>
</tr>
`
)
.join("");
panel.innerHTML = `
<div class="codex-budget-panel-head">
<div class="codex-budget-panel-title">
<strong>本地业务预算工作台</strong>
<span>让“导入数据 / 新增 / 编辑”在镜像里可用,变更仅保存在本地浏览器。</span>
</div>
<span class="codex-budget-pill">本地功能</span>
</div>
<div class="codex-budget-card">
<strong>本地预算行</strong>
<table>
<thead><tr><th>年份</th><th>预算类型</th><th>收费项</th><th>科目</th><th>收入业务分类</th><th>状态</th><th>操作人</th><th>更新时间</th><th>操作</th></tr></thead>
<tbody>${rows || '<tr><td colspan="9">暂无本地预算行</td></tr>'}</tbody>
</table>
</div>
`;
panel.querySelectorAll("[data-edit-id]").forEach((button) => {
button.addEventListener("click", () => openHcPosOperatingBudgetEditor(doc, button.dataset.editId));
});
panel.querySelectorAll("[data-delete-id]").forEach((button) => {
button.addEventListener("click", () => {
const next = readHcPosBudgetState(win, storageKey, buildHcPosOperatingBudgetSeed());
next.rows = next.rows.filter((item) => item.id !== button.dataset.deleteId);
writeHcPosBudgetState(win, storageKey, next);
renderHcPosOperatingBudgetPanel(doc);
});
});
}
function openHcPosOperatingBudgetEditor(doc, rowId = null) {
const win = doc.defaultView;
const storageKey = "__codex_operating_budget_state__";
const state = readHcPosBudgetState(win, storageKey, buildHcPosOperatingBudgetSeed());
const current = state.rows.find((item) => item.id === rowId) || {
id: `op-${Date.now()}`,
year: "2026",
budgetType: "业务项",
chargeItem: "",
subject: "物业管理费收入",
businessCategory: "物业管理费",
status: "启用",
operator: "本地镜像",
updatedAt: "2026-04-05 12:00"
};
openHcPosBudgetModal(
doc,
`
<h4>${rowId ? "编辑预算行" : "新增预算行"}</h4>
<div class="codex-budget-form">
<label><span>年份</span><input data-field="year" value="${current.year}"></label>
<label><span>预算类型</span><input data-field="budgetType" value="${current.budgetType}"></label>
<label><span>收费项</span><input data-field="chargeItem" value="${current.chargeItem}"></label>
<label><span>科目</span><input data-field="subject" value="${current.subject}"></label>
<label><span>收入业务分类</span><input data-field="businessCategory" value="${current.businessCategory}"></label>
<label><span>状态</span><select data-field="status"><option ${current.status === "启用" ? "selected" : ""}>启用</option><option ${current.status === "禁用" ? "selected" : ""}>禁用</option></select></label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">保存</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const next = readHcPosBudgetState(win, storageKey, buildHcPosOperatingBudgetSeed());
const payload = {
id: current.id,
year: mask.querySelector("[data-field='year']").value.trim() || current.year,
budgetType: mask.querySelector("[data-field='budgetType']").value.trim() || current.budgetType,
chargeItem: mask.querySelector("[data-field='chargeItem']").value.trim() || current.chargeItem || "未命名收费项",
subject: mask.querySelector("[data-field='subject']").value.trim() || current.subject,
businessCategory: mask.querySelector("[data-field='businessCategory']").value.trim() || current.businessCategory,
status: mask.querySelector("[data-field='status']").value.trim() || current.status,
operator: "本地镜像",
updatedAt: "2026-04-05 12:00"
};
const existingIndex = next.rows.findIndex((item) => item.id === current.id);
if (existingIndex >= 0) {
next.rows.splice(existingIndex, 1, payload);
} else {
next.rows.unshift(payload);
}
writeHcPosBudgetState(win, storageKey, next);
renderHcPosOperatingBudgetPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
}
function installHcPosOperatingBudgetTools(doc) {
if (doc.body.dataset.codexOperatingBudgetInstalled === "1") {
renderHcPosOperatingBudgetPanel(doc);
return;
}
ensureHcPosBudgetToolStyle(doc);
renderHcPosOperatingBudgetPanel(doc);
const win = doc.defaultView;
const storageKey = "__codex_operating_budget_state__";
doc.addEventListener(
"click",
(event) => {
const button = event.target.closest("button, .el-button");
if (!button) {
return;
}
const label = (button.textContent || "").replace(/\s+/g, " ").trim();
if (!["导入数据", "新增", "编辑", "导出模版"].includes(label)) {
return;
}
event.preventDefault();
event.stopPropagation();
if (label === "导出模版") {
const csv = "年份,预算类型,收费项,科目,收入业务分类,状态\\n2026,业务项,示例收费项,物业管理费收入,物业管理费,启用\\n";
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
const url = win.URL.createObjectURL(blob);
const anchor = doc.createElement("a");
anchor.href = url;
anchor.download = "业务预算导入模板.csv";
anchor.click();
win.URL.revokeObjectURL(url);
return;
}
if (label === "导入数据") {
openHcPosBudgetModal(
doc,
`
<h4>导入本地预算数据</h4>
<div class="codex-budget-form">
<label class="full">
<span>每行格式:年份,预算类型,收费项,科目,收入业务分类,状态</span>
<textarea data-field="lines" placeholder="2026,业务项,新增收费项,物业管理费收入,物业管理费,启用"></textarea>
</label>
</div>
<div class="codex-budget-modal-actions">
<button type="button" data-action="close">取消</button>
<button type="button" data-action="save" class="codex-primary">导入</button>
</div>
`,
[
{
selector: "[data-action='close']",
bind(target) {
target.addEventListener("click", () => closeHcPosBudgetModal(doc));
}
},
{
selector: "[data-action='save']",
bind(target, mask) {
target.addEventListener("click", () => {
const lines = mask.querySelector("[data-field='lines']").value.split("\n").map((line) => line.trim()).filter(Boolean);
const next = readHcPosBudgetState(win, storageKey, buildHcPosOperatingBudgetSeed());
lines.forEach((line) => {
const [year, budgetType, chargeItem, subject, businessCategory, status] = line.split(",").map((item) => item.trim());
if (!chargeItem) {
return;
}
next.rows.unshift({
id: `op-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
year: year || "2026",
budgetType: budgetType || "业务项",
chargeItem,
subject: subject || "物业管理费收入",
businessCategory: businessCategory || "物业管理费",
status: status || "启用",
operator: "本地导入",
updatedAt: "2026-04-05 12:00"
});
});
writeHcPosBudgetState(win, storageKey, next);
renderHcPosOperatingBudgetPanel(doc);
closeHcPosBudgetModal(doc);
});
}
}
]
);
return;
}
if (label === "新增") {
openHcPosOperatingBudgetEditor(doc);
return;
}
if (label === "编辑") {
openHcPosOperatingBudgetEditor(doc);
}
},
true
);
doc.body.dataset.codexOperatingBudgetInstalled = "1";
}
function installHcPosCockpitLinks(doc) {
if (!doc.querySelector("#r2-view")) {
return;
}
if (doc.body?.dataset.codexCockpitDelegated !== "1") {
doc.body.addEventListener(
"click",
(event) => {
const targetNode =
event.target.closest(".web_head .down_nav a") ||
event.target.closest(".web_head .item > span") ||
event.target.closest(".bottom-actions .action-btn") ||
event.target.closest(".bottom-actions .action-btn span") ||
event.target.closest(".projectReportBox .el-button") ||
event.target.closest(".projectReportBox .el-button span") ||
event.target.closest(".link-title") ||
event.target.closest(".flowm-card__header span");
if (!targetNode) {
return;
}
const target = resolveHcPosCockpitTarget(targetNode);
if (!target) {
return;
}
event.preventDefault();
event.stopPropagation();
navigateHcPosCockpitLink(target);
},
true
);
doc.body.dataset.codexCockpitDelegated = "1";
}
const clickableNodes = [
...doc.querySelectorAll(".web_head .down_nav a"),
...doc.querySelectorAll(".web_head .left > .item > span"),
...doc.querySelectorAll(".web_head .right > .item > span"),
...doc.querySelectorAll(".bottom-actions .action-btn"),
...doc.querySelectorAll(".bottom-actions .action-btn span"),
...doc.querySelectorAll(".projectReportBox .el-button"),
...doc.querySelectorAll(".projectReportBox .el-button span"),
...doc.querySelectorAll(".link-title"),
...doc.querySelectorAll(".flowm-card__header span")
];
clickableNodes.forEach((node) => {
if (node.dataset.codexCockpitBound === "1") {
return;
}
const label = (node.textContent || "").replace(/\s+/g, " ").trim();
if (label === "运营指数") {
node.style.cursor = "default";
return;
}
const target = resolveHcPosCockpitTarget(node);
if (!target) {
if (/^by/.test(label)) {
node.style.cursor = "default";
}
return;
}
node.style.cursor = "pointer";
if (node.tagName === "A") {
node.setAttribute("href", target.runtime === "hcetms" ? `/__mirror/runtime/hc-etms-dashboard/?v=${VERSION || "20260405p"}#${target.path || ""}` : `#${target.path}`);
}
node.addEventListener(
"click",
(event) => {
event.preventDefault();
event.stopPropagation();
navigateHcPosCockpitLink(target);
},
true
);
node.dataset.codexCockpitBound = "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,
.need-border,
.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("/propertySMG/cleanManage/cleanMicrobrain/")) {
renderHcPosDashboard(doc, "cleanMicrobrain");
}
if (src.includes("/propertySMG/businessTaxCank/financeMicrobrain/")) {
renderHcPosDashboard(doc, "financeMicrobrain");
}
if (src.includes("/propertySMG/elevatorManage/elevatorDimension/")) {
renderHcPosDashboard(doc, "elevatorDimension");
}
if (src.includes("/propertySMG/equipmentManage/equipmentPortrait/")) {
renderHcPosDashboard(doc, "equipmentPortrait");
}
if (src.includes("/propertySMG/greenManage/greenMicroBrain/")) {
renderHcPosDashboard(doc, "greenMicroBrain");
}
if (src.includes("/propertySMG/parkingOperation/parkingMicroBrain/")) {
renderHcPosDashboard(doc, "parkingMicroBrain");
}
if (src.includes("/propertySMG/securityManage/securityBrain/")) {
renderHcPosDashboard(doc, "securityBrain");
}
if (src.includes("/propertySMG/customerOperations/customerPortrait/")) {
renderHcPosDashboard(doc, "customerPortrait");
}
if (src.includes("/propertySMG/energySourceOperat/notice/")) {
renderHcPosDashboard(doc, "energyNotice");
}
if (src.includes("/homeEnterpriseService/childCare/")) {
renderHcPosStaticPage(doc, "childCare");
}
if (src.includes("/homeEnterpriseService/express/")) {
renderHcPosStaticPage(doc, "express");
}
if (src.includes("/homeEnterpriseService/house/")) {
renderHcPosStaticPage(doc, "house");
}
if (src.includes("/homeEnterpriseService/housekeeping/")) {
renderHcPosStaticPage(doc, "housekeeping");
}
if (src.includes("/homeEnterpriseService/retirement/")) {
renderHcPosStaticPage(doc, "retirement");
}
if (src.includes("/propertySMG/businessTaxCank/bankEnterprise/")) {
renderHcPosStaticPage(doc, "bankEnterprise");
}
if (src.includes("/propertySMG/businessTaxCank/financeAdjust/financialAccount/")) {
renderHcPosStaticPage(doc, "financialAccount");
}
if (src.includes("/propertySMG/businessTaxCank/financeAdjust/financialVoucher/")) {
renderHcPosStaticPage(doc, "financialVoucher");
}
if (src.includes("/propertySMG/businessTaxCank/taxCoordination/")) {
renderHcPosStaticPage(doc, "taxCoordination");
}
if (src.includes("/communitySMG/serviceProvider/")) {
renderHcPosStaticPage(doc, "serviceProvider");
}
if (src.includes("/propertySMG/cleanManage/assessmentTraining/")) {
renderHcPosStaticPage(doc, "cleanAssessmentTraining");
}
if (src.includes("/propertySMG/cleanManage/spareParts/")) {
renderHcPosStaticPage(doc, "cleanSpareParts");
}
if (src.includes("/propertySMG/elevatorManage/assessmentTraining/")) {
renderHcPosStaticPage(doc, "elevatorAssessmentTraining");
}
if (src.includes("/propertySMG/elevatorManage/spareParts/")) {
renderHcPosStaticPage(doc, "elevatorSpareParts");
}
if (src.includes("/propertySMG/equipmentManage/assessmentTraining/")) {
renderHcPosStaticPage(doc, "equipmentAssessmentTraining");
}
if (src.includes("/propertySMG/equipmentManage/spareParts/")) {
renderHcPosStaticPage(doc, "equipmentSpareParts");
}
if (src.includes("/propertySMG/greenManage/assessmentTraining/")) {
renderHcPosStaticPage(doc, "greenAssessmentTraining");
}
if (src.includes("/propertySMG/greenManage/spareParts/")) {
renderHcPosStaticPage(doc, "greenSpareParts");
}
if (src.includes("/propertySMG/securityManage/assessmentTraining/")) {
renderHcPosStaticPage(doc, "securityAssessmentTraining");
}
if (src.includes("/propertySMG/securityManage/spareParts/")) {
renderHcPosStaticPage(doc, "securitySpareParts");
}
if (src.includes("/propertySMG/parkingOperation/assessmentTraining/")) {
renderHcPosStaticPage(doc, "parkingAssessmentTraining");
}
const waitPagePreset = Object.entries(HCPOS_WAIT_PAGE_ROUTE_PRESETS).find(([path]) => src.includes(path));
if (waitPagePreset) {
renderHcPosStaticPage(doc, waitPagePreset[1]);
}
if (src.includes("/communitySMG/personnelList/")) {
renderHcPosOverviewPanel(doc.getElementById("pane-personnelList"), HCPOS_OVERVIEW_PRESETS.personnelList);
renderHcPosSecondaryTab(doc, "keyCustomer", "pane-HEWC");
installSimpleTabSwitch(doc, {
primaryTabId: "tab-personnelList",
secondaryTabId: "tab-HEWC",
primaryPaneId: "pane-personnelList",
secondaryPaneId: "pane-HEWC",
storageKey: "__codex_personnel_list_tab__"
});
}
if (src.includes("/propertySMG/diagnosis/customizedPhysical/")) {
renderHcPosOverviewPanel(doc.getElementById("pane-physicaExamineList"), HCPOS_OVERVIEW_PRESETS.customizedPhysical);
renderHcPosSecondaryTab(doc, "customizedPhysicalRecord", "pane-HEWC");
installSimpleTabSwitch(doc, {
primaryTabId: "tab-physicaExamineList",
secondaryTabId: "tab-HEWC",
primaryPaneId: "pane-physicaExamineList",
secondaryPaneId: "pane-HEWC",
storageKey: "__codex_customized_physical_tab__"
});
}
if (src.includes("/propertySMG/diagnosis/specialPhysical/")) {
renderHcPosOverviewPanel(doc.getElementById("pane-physicaExamineList"), HCPOS_OVERVIEW_PRESETS.specialPhysical);
renderHcPosSecondaryTab(doc, "specialPhysicalRecord", "pane-HEWC");
installSimpleTabSwitch(doc, {
primaryTabId: "tab-physicaExamineList",
secondaryTabId: "tab-HEWC",
primaryPaneId: "pane-physicaExamineList",
secondaryPaneId: "pane-HEWC",
storageKey: "__codex_special_physical_tab__"
});
}
if (src.includes("/propertySMG/diagnosis/5Aphysical/")) {
renderHcPosOverviewPanel(doc.getElementById("pane-physicaExamineList"), HCPOS_OVERVIEW_PRESETS.fiveAPhysical);
renderHcPosSecondaryTab(doc, "fiveAPhysicalRecord", "pane-HEWC");
installSimpleTabSwitch(doc, {
primaryTabId: "tab-physicaExamineList",
secondaryTabId: "tab-HEWC",
primaryPaneId: "pane-physicaExamineList",
secondaryPaneId: "pane-HEWC",
storageKey: "__codex_fivea_physical_tab__"
});
}
if (src.includes("/propertySMG/businessTaxCank/financeReport/balanceSheet/")) {
renderHcPosStaticPage(doc, "balanceSheet");
}
if (src.includes("/propertySMG/businessTaxCank/financeReport/cashFlowStatement/")) {
renderHcPosStaticPage(doc, "cashFlowStatement");
}
if (src.includes("/propertySMG/businessTaxCank/financeReport/incomeStatement/")) {
renderHcPosStaticPage(doc, "incomeStatement");
}
if (src.includes("/propertySMG/businessTaxCank/financeReport/profitSurface/")) {
renderHcPosStaticPage(doc, "profitSurface");
}
if (src.includes("/propertySMG/businessTaxCank/financeReport/projectIncome/")) {
renderHcPosStaticPage(doc, "projectIncome");
}
if (src.includes("/propertySMG/businessTaxCank/laborQuotaManagement/laborQuota/")) {
renderHcPosStaticPage(doc, "laborQuota");
}
if (src.includes("/propertySMG/businessTaxCank/metadataManage/resourceManage/")) {
renderHcPosStaticPage(doc, "resourceManage");
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/receivable/")) {
installHcPosRevenueReceivableTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/charging/")) {
installHcPosRevenueChargingTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/collectionRecord/")) {
installHcPosRevenueCollectionRecordTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/refundRecord/")) {
installHcPosRevenueRefundRecordTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/salesOrder/")) {
installHcPosRevenueSalesOrderTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/revenueManage/electronicBill/")) {
installHcPosRevenueElectronicBillTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/budgetManage/budgetOrgan/")) {
installHcPosBudgetOrganTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/budgetManage/financialApproval/")) {
installHcPosFinancialApprovalTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/budgetManage/forecastBudget/")) {
installHcPosForecastBudgetTools(doc);
}
if (src.includes("/propertySMG/businessTaxCank/budgetManage/operatingBudget/")) {
installHcPosOperatingBudgetTools(doc);
}
if (src.includes("/homeEnterpriseService/cafeteria/canteenArchives/")) {
renderHcPosOverviewPanel(doc.querySelector(".app-main > div"), HCPOS_OVERVIEW_PRESETS.canteenArchives);
}
}
if (src.includes("/dataPlatform/home/")) {
installHcPosCockpitLinks(doc);
installHcPosDangerTableLinks(doc);
}
installHcPosGenericListEditors(doc);
installHcPosEditableSections(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);
}
const SCRIPT_SRCS = [
"./webpack-runtime.js",
"/static/js/chunk-libs.ee373cfd.js",
"/static/js/app.210977f6.js"
];
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);
}
`;
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 seedStorage() {
const response = await fetch(VERSION ? `./storage-seed.json?v=${VERSION}` : "./storage-seed.json", { cache: "no-store" });
const payload = await response.json();
Object.entries(payload).forEach(([key, value]) => {
localStorage.setItem(key, value);
});
}
async function seedCookies() {
const response = await fetch(VERSION ? `./cookie-seed.json?v=${VERSION}` : "./cookie-seed.json", { cache: "no-store" });
const payload = await response.json();
Object.entries(payload).forEach(([key, value]) => {
document.cookie = `${key}=${value}; path=/; SameSite=Lax`;
});
}
async function loadRouteMap() {
const response = await fetch(VERSION ? `./route-map.json?v=${VERSION}` : "./route-map.json", { cache: "no-store" });
routeMap = response.ok ? await response.json() : {};
}
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 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 = ROUTE_HASH;
}
}
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() {
if (!routeMap || !Object.keys(routeMap).length) {
return null;
}
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: "HcPosMirrorFrame",
computed: {
frameSrc() {
const config = routeMap[this.$route.path] || { src: "/hc-pos.sqygj.cn/404/" };
return config.src || "/hc-pos.sqygj.cn/404/";
}
},
mounted() {
this.$nextTick(() => applyGlobalFrameSkin(this.$refs.frame));
},
updated() {
this.$nextTick(() => applyGlobalFrameSkin(this.$refs.frame));
},
methods: {
onFrameLoad(event) {
applyGlobalFrameSkin(event.target);
}
},
render(h) {
return h("div", { class: "codex-runtime-frame-host" }, [
h("iframe", {
ref: "frame",
class: "codex-runtime-frame",
attrs: {
src: this.frameSrc,
frameborder: "0"
},
on: {
load: this.onFrameLoad
},
style: {
height: "calc(100vh - 108px)"
}
})
]);
}
};
const children = Object.keys(routeMap)
.filter((path) => path !== "/dashboard")
.filter((path) => !existing.has(path))
.map((path) => ({
path: path.replace(/^\//, ""),
name: `hcpos-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 updateDocumentTitle(path) {
const config = routeMap[path];
if (config && config.title) {
document.title = `${config.title} - 项目运营平台`;
}
}
function getHcPosPresetTags(path) {
const presets = {
"/communitySMG/personnelList": ["首页-工作台", "住户档案", "重点客户"],
"/communitySMG/serviceProvider": ["首页-工作台", "住户档案", "服务商管理"],
"/homeEnterpriseService/cafeteria/canteenArchives": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/homeEnterpriseService/childCare": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/homeEnterpriseService/house": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/homeEnterpriseService/express": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/homeEnterpriseService/retirement": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/homeEnterpriseService/housekeeping": ["首页-工作台", "食堂档案", "社区幼托", "房屋经纪", "快递收发", "社区养老", "社区家政"],
"/propertySMG/diagnosis/customizedPhysical": ["首页-工作台", "5A测评", "专项测评", "定制测评"],
"/propertySMG/diagnosis/specialPhysical": ["首页-工作台", "5A测评", "专项测评", "定制测评"],
"/propertySMG/diagnosis/5Aphysical": ["首页-工作台", "5A测评", "专项测评", "定制测评"],
"/propertySMG/businessTaxCank/bankEnterprise": ["首页-工作台", "银企直联", "财务核算", "财务凭证", "税务统筹", "财务看板"],
"/propertySMG/businessTaxCank/financeAdjust/financialAccount": ["首页-工作台", "银企直联", "财务核算", "财务凭证", "税务统筹", "财务看板"],
"/propertySMG/businessTaxCank/financeAdjust/financialVoucher": ["首页-工作台", "银企直联", "财务核算", "财务凭证", "税务统筹", "财务看板"],
"/propertySMG/businessTaxCank/taxCoordination": ["首页-工作台", "银企直联", "财务核算", "财务凭证", "税务统筹", "财务看板"],
"/propertySMG/businessTaxCank/financeMicrobrain": ["首页-工作台", "银企直联", "财务核算", "财务凭证", "税务统筹", "财务看板"],
"/propertySMG/businessTaxCank/financeReport/balanceSheet": ["首页-工作台", "资产负债表", "现金流量表", "全年收入报表", "利润表", "项目收支表"],
"/propertySMG/businessTaxCank/financeReport/cashFlowStatement": ["首页-工作台", "资产负债表", "现金流量表", "全年收入报表", "利润表", "项目收支表"],
"/propertySMG/businessTaxCank/financeReport/incomeStatement": ["首页-工作台", "资产负债表", "现金流量表", "全年收入报表", "利润表", "项目收支表"],
"/propertySMG/businessTaxCank/financeReport/profitSurface": ["首页-工作台", "资产负债表", "现金流量表", "全年收入报表", "利润表", "项目收支表"],
"/propertySMG/businessTaxCank/financeReport/projectIncome": ["首页-工作台", "资产负债表", "现金流量表", "全年收入报表", "利润表", "项目收支表"],
"/propertySMG/cleanManage/cleanMicrobrain": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/cleanManage/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "清洁看板", path: "/propertySMG/cleanManage/cleanMicrobrain" },
{ title: "考核培训", path: "/propertySMG/cleanManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/cleanManage/spareParts" }
],
"/propertySMG/cleanManage/spareParts": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "清洁看板", path: "/propertySMG/cleanManage/cleanMicrobrain" },
{ title: "考核培训", path: "/propertySMG/cleanManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/cleanManage/spareParts" }
],
"/propertySMG/greenManage/greenMicroBrain": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/greenManage/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "绿化看板", path: "/propertySMG/greenManage/greenMicroBrain" },
{ title: "考核培训", path: "/propertySMG/greenManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/greenManage/spareParts" }
],
"/propertySMG/greenManage/spareParts": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "绿化看板", path: "/propertySMG/greenManage/greenMicroBrain" },
{ title: "考核培训", path: "/propertySMG/greenManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/greenManage/spareParts" }
],
"/propertySMG/securityManage/securityBrain": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/securityManage/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "安防看板", path: "/propertySMG/securityManage/securityBrain" },
{ title: "考核培训", path: "/propertySMG/securityManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/securityManage/spareParts" }
],
"/propertySMG/securityManage/spareParts": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "安防看板", path: "/propertySMG/securityManage/securityBrain" },
{ title: "考核培训", path: "/propertySMG/securityManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/securityManage/spareParts" }
],
"/propertySMG/equipmentManage/equipmentPortrait": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/equipmentManage/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "设备看板", path: "/propertySMG/equipmentManage/equipmentPortrait" },
{ title: "考核培训", path: "/propertySMG/equipmentManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/equipmentManage/spareParts" }
],
"/propertySMG/equipmentManage/spareParts": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "设备看板", path: "/propertySMG/equipmentManage/equipmentPortrait" },
{ title: "考核培训", path: "/propertySMG/equipmentManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/equipmentManage/spareParts" }
],
"/propertySMG/elevatorManage/elevatorDimension": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/elevatorManage/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "电梯看板", path: "/propertySMG/elevatorManage/elevatorDimension" },
{ title: "考核培训", path: "/propertySMG/elevatorManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/elevatorManage/spareParts" }
],
"/propertySMG/elevatorManage/spareParts": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "电梯看板", path: "/propertySMG/elevatorManage/elevatorDimension" },
{ title: "考核培训", path: "/propertySMG/elevatorManage/assessmentTraining" },
{ title: "备品备件", path: "/propertySMG/elevatorManage/spareParts" }
],
"/propertySMG/parkingOperation/parkingMicroBrain": ["首页-工作台", "清洁看板", "绿化看板", "安防看板", "设备看板", "电梯看板", "车场看板"],
"/propertySMG/parkingOperation/assessmentTraining": [
{ title: "首页-工作台", path: "/dashboard" },
{ title: "车场看板", path: "/propertySMG/parkingOperation/parkingMicroBrain" },
{ title: "考核培训", path: "/propertySMG/parkingOperation/assessmentTraining" }
],
"/propertySMG/customerOperations/customerPortrait": ["首页-工作台", "客户画像", "服务商管理", "住户档案"],
"/propertySMG/energySourceOperat/notice": ["首页-工作台", "能源看板", "设备看板", "车场看板"]
};
return presets[path] || null;
}
function getHcPosTagRoute(title) {
const routeMapByTitle = {
"首页-工作台": "/dashboard",
"住户档案": "/communitySMG/personnelList",
"重点客户": "/communitySMG/personnelList",
"服务商管理": "/communitySMG/serviceProvider",
"食堂档案": "/homeEnterpriseService/cafeteria/canteenArchives",
"社区幼托": "/homeEnterpriseService/childCare",
"房屋经纪": "/homeEnterpriseService/house",
"快递收发": "/homeEnterpriseService/express",
"社区养老": "/homeEnterpriseService/retirement",
"社区家政": "/homeEnterpriseService/housekeeping",
"5A测评": "/propertySMG/diagnosis/5Aphysical",
"专项测评": "/propertySMG/diagnosis/specialPhysical",
"定制测评": "/propertySMG/diagnosis/customizedPhysical",
"银企直联": "/propertySMG/businessTaxCank/bankEnterprise",
"财务核算": "/propertySMG/businessTaxCank/financeAdjust/financialAccount",
"财务凭证": "/propertySMG/businessTaxCank/financeAdjust/financialVoucher",
"税务统筹": "/propertySMG/businessTaxCank/taxCoordination",
"财务看板": "/propertySMG/businessTaxCank/financeMicrobrain",
"资产负债表": "/propertySMG/businessTaxCank/financeReport/balanceSheet",
"现金流量表": "/propertySMG/businessTaxCank/financeReport/cashFlowStatement",
"全年收入报表": "/propertySMG/businessTaxCank/financeReport/incomeStatement",
"利润表": "/propertySMG/businessTaxCank/financeReport/profitSurface",
"项目收支表": "/propertySMG/businessTaxCank/financeReport/projectIncome",
"清洁看板": "/propertySMG/cleanManage/cleanMicrobrain",
"绿化看板": "/propertySMG/greenManage/greenMicroBrain",
"安防看板": "/propertySMG/securityManage/securityBrain",
"设备看板": "/propertySMG/equipmentManage/equipmentPortrait",
"电梯看板": "/propertySMG/elevatorManage/elevatorDimension",
"车场看板": "/propertySMG/parkingOperation/parkingMicroBrain",
"客户画像": "/propertySMG/customerOperations/customerPortrait",
"能源看板": "/propertySMG/energySourceOperat/notice"
};
return routeMapByTitle[title] || "/dashboard";
}
function getHcPosTagLabel(node) {
const cloned = node.cloneNode(true);
cloned.querySelectorAll(".el-icon-close").forEach((icon) => icon.remove());
return (cloned.textContent || "").trim();
}
function createHcPosSyntheticTag(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 hydrateHcPosTagsView(path) {
const view = document.querySelector(".tags-view-container .el-scrollbar__view");
if (!view) {
return;
}
const preset = getHcPosPresetTags(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) => [getHcPosTagLabel(node), node]));
const activeTitle =
(routeMap[path] && routeMap[path].title) ||
getHcPosTagLabel(view.querySelector(".tags-view-item.active") || document.createElement("span"));
preset.forEach((item) => {
const title = typeof item === "string" ? item : item.title;
const itemPath = typeof item === "string" ? getHcPosTagRoute(title) : item.path;
let node = existingMap.get(title);
if (!node) {
node = createHcPosSyntheticTag(title, itemPath, itemPath === path || title === activeTitle);
}
if (itemPath === path || title === activeTitle) {
node.classList.add("active");
} else {
node.classList.remove("active");
}
view.appendChild(node);
});
}
function scheduleHcPosTagsViewHydration(path) {
let tries = 0;
const timer = setInterval(() => {
tries += 1;
hydrateHcPosTagsView(path);
if (tries >= 8) {
clearInterval(timer);
}
}, 300);
}
function installRouteBridge(router) {
if (!router || window.__HCPOS_RUNTIME_BRIDGE_INSTALLED__) {
return;
}
window.__HCPOS_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 path = href.slice(1).split("?")[0] || "/dashboard";
if (routeMap[path]) {
router.replace({ path });
updateDocumentTitle(path);
} else {
router.replace({ path: "/dashboard" });
}
},
true
);
router.afterEach((to) => {
updateDocumentTitle(to.path);
scheduleHcPosTagsViewHydration(to.path);
});
}
function hideLoading() {
const loading = document.getElementById("mirror-loading");
if (loading) {
loading.style.display = "none";
}
}
try {
await seedStorage();
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();
scheduleHcPosTagsViewHydration(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);
}
}
})();