/* DeerFlow generated-HTML runtime. Browser ES5; no module loader required. */ (function () { "use strict"; function ready(fn) { if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", fn); else fn(); } function blocks(name) { return document.querySelectorAll('script[type="application/json"][' + name + "]"); } function parse(block, target) { try { return JSON.parse(block.textContent || "{}"); } catch (error) { showError(target, "图形数据格式错误:" + (error && error.message ? error.message : "无法解析 JSON")); return null; } } function showError(target, message) { var box = document.createElement("div"); box.className = "df-runtime-error"; box.setAttribute("role", "alert"); box.textContent = message; if (target && target.parentNode) target.parentNode.insertBefore(box, target.nextSibling); } function size(target) { if (target.clientHeight < 40) target.style.minHeight = "360px"; if (target.clientWidth < 40) target.style.width = "100%"; } // 多页面门户会用 display:none 隐藏非当前页;此时 G6/ECharts 以 0 宽初始化, // 即使稍后页面显示也只会在浏览器 resize 时才恢复。等容器真正可见后再 // 创建实例,并把之后的面板/iframe 尺寸变化同步给图表和流程图。 function closestLogicalPage(target) { var node = target; while (node && node !== document.body) { if (node.classList && (node.classList.contains("page") || node.classList.contains("view"))) { return node; } node = node.parentNode; } return null; } function whenVisible(target, callback) { var observer = null; var intersection = null; var attempts = 0; var timer = null; var completed = false; function cleanup() { if (observer) observer.disconnect(); if (intersection) intersection.disconnect(); if (timer) window.clearTimeout(timer); observer = null; intersection = null; timer = null; window.removeEventListener("hashchange", check); } function check() { if (completed || !target || target.clientWidth < 40 || target.clientHeight < 40) { return false; } completed = true; cleanup(); callback(); return true; } if (check()) return; if (window.ResizeObserver) { observer = new window.ResizeObserver(check); observer.observe(target); var page = closestLogicalPage(target); if (page) observer.observe(page); } if (window.IntersectionObserver) { intersection = new window.IntersectionObserver(function (entries) { for (var i = 0; i < entries.length; i += 1) { if (entries[i].isIntersecting) check(); } }, { threshold: 0 }); intersection.observe(target); } window.addEventListener("hashchange", check); if (window.ResizeObserver) return; (function poll() { if (check() || attempts >= 120) return; attempts += 1; timer = window.setTimeout(poll, 50); })(); } function bindCanvasResize(target, resize) { var frame = null; function schedule() { if (frame !== null) { window.cancelAnimationFrame(frame); } frame = window.requestAnimationFrame(function () { frame = null; if (target.clientWidth >= 40 && target.clientHeight >= 40) { resize(); } }); } if (window.ResizeObserver) { (new window.ResizeObserver(schedule)).observe(target); } window.addEventListener("resize", schedule); window.addEventListener("hashchange", schedule); } function bindEChartsResize(target, chart) { var frame = null; function resize() { if (frame !== null) { window.cancelAnimationFrame(frame); } frame = window.requestAnimationFrame(function () { frame = null; if (target.clientWidth >= 40 && target.clientHeight >= 40 && !chart.isDisposed()) { chart.resize(); } }); } bindCanvasResize(target, resize); } function escapeHtml(value) { return String(value == null ? "" : value).replace(/[&<>"']/g, function (ch) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch]; }); } function geoPoint(value) { if (!value || value.length < 2) return null; var lng = Number(value[0]); var lat = Number(value[1]); if (!isFinite(lng) || !isFinite(lat) || lng < -180 || lng > 180 || lat < -90 || lat > 90) return null; return [lng, lat]; } function leafletPoint(value) { var coordinate = geoPoint(value); return coordinate ? [coordinate[1], coordinate[0]] : null; } function initCharts() { var items = blocks("data-df-chart"); for (var i = 0; i < items.length; i += 1) { var block = items[i]; var target = document.getElementById(block.getAttribute("data-df-chart")); if (!target) continue; var option = parse(block, target); if (!option) continue; if (!window.echarts) { showError(target, "ECharts 运行库加载失败"); continue; } size(target); (function (chartTarget, chartOption) { whenVisible(chartTarget, function () { try { chartTarget.setAttribute("role", "img"); chartTarget.setAttribute("aria-label", chartOption.title && chartOption.title.text ? chartOption.title.text : "数据图表"); var chart = window.echarts.init(chartTarget); chart.setOption(chartOption); bindEChartsResize(chartTarget, chart); } catch (error) { showError(chartTarget, "图表渲染失败:" + (error && error.message ? error.message : "请检查图表数据")); } }); })(target, option); } } var geoMapCache = {}; var geoMapLoads = {}; var geoMapDefaultViews = { "us-states": { center: [-98, 38], zoom: 2.1 } }; function geoMapUrl(mapId) { var config = window.DEERFLOW_HTML_RUNTIME_CONFIG || {}; var maps = config.geoMaps || {}; return typeof maps[mapId] === "string" ? maps[mapId] : ""; } function loadGeoMap(mapId) { if (geoMapCache[mapId]) return Promise.resolve(geoMapCache[mapId]); if (geoMapLoads[mapId]) return geoMapLoads[mapId]; var url = geoMapUrl(mapId); if (!url) return Promise.reject(new Error("未配置地图资源:" + mapId)); if (!window.fetch) return Promise.reject(new Error("当前浏览器不支持加载本地 GeoJSON")); geoMapLoads[mapId] = window.fetch(url, { credentials: "omit" }).then(function (response) { if (!response.ok) throw new Error("地图资源加载失败(HTTP " + response.status + ")"); return response.json(); }).then(function (geoJson) { if (!geoJson || !geoJson.type) throw new Error("地图资源不是有效 GeoJSON"); normalizeGeoJsonNames(geoJson); geoMapCache[mapId] = geoJson; return geoJson; }).catch(function (error) { delete geoMapLoads[mapId]; throw error; }); return geoMapLoads[mapId]; } function normalizeGeoJsonNames(geoJson) { var features = geoJson && geoJson.features; if (!features || !features.length) return; for (var i = 0; i < features.length; i += 1) { var feature = features[i] || {}; var properties = feature.properties || (feature.properties = {}); if (!properties.name) { properties.name = properties.shapeName || properties.NAME_1 || properties.name_en || properties.id || "区域"; } } } function geoRegions(data) { var values = data.regions || []; var output = []; for (var i = 0; i < values.length; i += 1) { var region = values[i]; if (!region || !region.name) continue; var item = { name: String(region.name) }; if (isFinite(Number(region.value))) item.value = Number(region.value); if (region.color) item.itemStyle = { areaColor: String(region.color) }; if (region.description) item.description = String(region.description); if (region.selected === true) item.selected = true; output.push(item); } return output; } function geoMarkers(data) { var values = data.markers || []; var output = []; for (var i = 0; i < values.length; i += 1) { var marker = values[i]; var coordinate = marker && geoPoint(marker.coordinate); if (!coordinate) continue; output.push({ name: marker.name || "地点", value: coordinate.concat([Number(marker.value) || 1]), description: marker.description || "", itemStyle: marker.color ? { color: marker.color } : undefined }); } return output; } function geoLines(data) { var values = data.lines || []; var output = []; for (var i = 0; i < values.length; i += 1) { var line = values[i] || {}; var rawCoordinates = line.coordinates || []; var coordinates = []; for (var j = 0; j < rawCoordinates.length; j += 1) { var coordinate = geoPoint(rawCoordinates[j]); if (coordinate) coordinates.push(coordinate); } if (coordinates.length > 1) output.push({ name: line.name || "连接", coords: coordinates, description: line.description || "", lineStyle: line.color ? { color: line.color } : undefined }); } return output; } function geoTooltip(params, data) { var name = params.name || "地图区域"; var detail = geoDetail(data, name, params.data); var description = detail.description || ""; return "" + escapeHtml(name) + "" + (description ? "
" + escapeHtml(description) : ""); } function geoDetail(data, name, itemData) { var groups = [data.markers || [], data.regions || [], data.lines || []]; for (var groupIndex = 0; groupIndex < groups.length; groupIndex += 1) { var group = groups[groupIndex]; for (var itemIndex = 0; itemIndex < group.length; itemIndex += 1) { if (group[itemIndex] && group[itemIndex].name === name) return group[itemIndex]; } } return itemData || {}; } function showGeoPopup(target, params, data) { var name = params.name || "地图区域"; var detail = geoDetail(data, name, params.data); var popup = target.querySelector(".df-geo-popup"); if (!popup) { if (window.getComputedStyle && window.getComputedStyle(target).position === "static") { target.style.position = "relative"; } popup = document.createElement("section"); popup.className = "df-geo-popup"; popup.setAttribute("role", "dialog"); popup.setAttribute("aria-live", "polite"); popup.style.cssText = "position:absolute;z-index:10;max-width:280px;padding:16px 42px 16px 16px;border:1px solid #5aa8df;border-radius:12px;background:rgba(5,23,45,.96);box-shadow:0 16px 36px rgba(0,0,0,.34);color:#f8fbff;font:16px/1.55 -apple-system,BlinkMacSystemFont,'Segoe UI','Microsoft YaHei',sans-serif;"; target.appendChild(popup); } var description = detail.description || "暂无更多说明。"; var value = isFinite(Number(detail.value)) ? "
指标:" + escapeHtml(detail.value) + "
" : ""; popup.innerHTML = "" + "" + escapeHtml(name) + "" + "
" + escapeHtml(description) + "
" + value; var close = popup.querySelector("button"); if (close) close.onclick = function () { popup.style.display = "none"; }; var rawX = params.event && Number(params.event.offsetX); var rawY = params.event && Number(params.event.offsetY); var x = isFinite(rawX) ? rawX : target.clientWidth / 2; var y = isFinite(rawY) ? rawY : target.clientHeight / 2; popup.style.left = Math.max(12, Math.min(Math.max(12, target.clientWidth - 292), x + 14)) + "px"; popup.style.top = Math.max(12, Math.min(Math.max(12, target.clientHeight - 150), y + 14)) + "px"; popup.style.display = "block"; } function initGeoMaps() { var items = blocks("data-df-geo"); for (var i = 0; i < items.length; i += 1) { var block = items[i]; var target = document.getElementById(block.getAttribute("data-df-geo")); if (!target) continue; var data = parse(block, target); if (!data) continue; if (!window.echarts) { showError(target, "ECharts 运行库加载失败"); continue; } (function (mapTarget, mapData) { size(mapTarget); whenVisible(mapTarget, function () { var mapId = mapData.map || "china-provinces"; loadGeoMap(mapId).then(function (geoJson) { mapTarget.setAttribute("role", "application"); mapTarget.setAttribute("aria-label", mapData.title || "交互地图"); window.echarts.registerMap(mapId, geoJson); var background = mapData.mode === "background"; var labels = mapData.showLabels === true || (!background && mapData.showLabels !== false); var areaColor = mapData.areaColor || (background ? "#123b67" : "#d8ecff"); var borderColor = mapData.borderColor || (background ? "#4e8cbf" : "#5d8cbb"); var emphasisColor = mapData.emphasisColor || (background ? "#2578bc" : "#8ec5f0"); var regions = geoRegions(mapData); var defaultView = geoMapDefaultViews[mapId] || {}; var mapCenter = geoPoint(mapData.center) || defaultView.center; var configuredZoom = Number(mapData.zoom); var mapZoom = isFinite(configuredZoom) && configuredZoom > 0 ? configuredZoom : defaultView.zoom; var chart = window.echarts.init(mapTarget, null, { renderer: "canvas" }); chart.setOption({ animation: false, backgroundColor: "transparent", tooltip: { trigger: "item", backgroundColor: "rgba(8, 22, 45, 0.94)", borderColor: "#5aa8df", textStyle: { color: "#f8fbff", fontSize: 16 }, formatter: function (params) { return geoTooltip(params, mapData); } }, geo: { map: mapId, roam: mapData.roam !== false, layoutCenter: mapData.layoutCenter || ["50%", "50%"], layoutSize: mapData.layoutSize || (background ? "118%" : "100%"), center: mapCenter, zoom: mapZoom, label: { show: labels, color: background ? "#d7ecff" : "#183450", fontSize: 16 }, itemStyle: { areaColor: areaColor, borderColor: borderColor, borderWidth: 1.25 }, emphasis: { label: { show: labels }, itemStyle: { areaColor: emphasisColor } }, select: { label: { show: labels }, itemStyle: { areaColor: mapData.selectColor || "#1e6cac" } } }, series: [ { type: "map", map: mapId, geoIndex: 0, data: regions, selectedMode: "single", z: 1, itemStyle: { areaColor: areaColor, borderColor: borderColor, borderWidth: 1.25 }, emphasis: { label: { show: labels }, itemStyle: { areaColor: emphasisColor } }, select: { label: { show: labels }, itemStyle: { areaColor: mapData.selectColor || "#1e6cac" } } }, { type: "effectScatter", coordinateSystem: "geo", z: 4, data: geoMarkers(mapData), symbolSize: mapData.markerSize || 13, showEffectOn: "render", rippleEffect: { scale: 3.2, brushType: "stroke" }, label: { show: true, formatter: "{b}", position: "right", color: background ? "#f8fbff" : "#183450", fontSize: 16, fontWeight: 700 }, itemStyle: { color: mapData.markerColor || "#f4bd57", shadowBlur: 12, shadowColor: "rgba(244,189,87,0.55)" } }, { type: "lines", coordinateSystem: "geo", z: 3, data: geoLines(mapData), lineStyle: { color: mapData.lineColor || "#77d4ff", width: 2, opacity: 0.82, curveness: 0.16 }, effect: { show: true, period: 5, symbol: "arrow", symbolSize: 8, trailLength: 0.08 } } ] }); chart.on("click", function (params) { var detail = geoDetail(mapData, params.name || "", params.data); if (mapData.popup !== false) showGeoPopup(mapTarget, params, mapData); if (!window.CustomEvent) return; window.dispatchEvent(new window.CustomEvent("deerflow:geo-map-click", { detail: { targetId: mapTarget.id, name: params.name || "", kind: params.seriesType || "region", data: detail, description: detail.description || "" } })); }); bindEChartsResize(mapTarget, chart); }).catch(function (error) { showError(mapTarget, "地图渲染失败:" + (error && error.message ? error.message : "请检查地图数据")); }); }); })(target, data); } } function initMaps() { var items = blocks("data-df-map"); for (var i = 0; i < items.length; i += 1) { var block = items[i]; var target = document.getElementById(block.getAttribute("data-df-map")); if (!target) continue; var data = parse(block, target); if (!data) continue; if (!window.L) { showError(target, "Leaflet 运行库加载失败"); continue; } size(target); (function (mapTarget, mapData) { whenVisible(mapTarget, function () { try { mapTarget.setAttribute("role", "region"); mapTarget.setAttribute("aria-label", mapData.title || "地图"); var map = window.L.map(mapTarget, { zoomControl: true }); var config = window.DEERFLOW_HTML_RUNTIME_CONFIG || {}; var tileUrl = config.mapTileUrl; if (tileUrl) { var tileErrors = 0; window.L.tileLayer(tileUrl, { maxZoom: 20, attribution: /google/i.test(tileUrl) ? "Google" : "" }) .on("tileerror", function () { tileErrors += 1; if (tileErrors === 3) showError(mapTarget, "地图瓦片加载失败;点、线、面数据仍可查看"); }) .addTo(map); } var bounds = []; var markers = mapData.markers || []; for (var m = 0; m < markers.length; m += 1) { var marker = markers[m]; var markerPoint = leafletPoint(marker.coordinate); if (!markerPoint) continue; bounds.push(markerPoint); var dot = window.L.circleMarker(markerPoint, { radius: 7, color: "#1d4ed8", weight: 2, fillColor: "#3b82f6", fillOpacity: 0.9 }).addTo(map); dot.bindPopup("" + escapeHtml(marker.name || "地点") + "" + (marker.description ? "
" + escapeHtml(marker.description) : "")); dot.bindTooltip(escapeHtml(marker.name || "地点")); } var lines = mapData.lines || []; for (var l = 0; l < lines.length; l += 1) { var linePoints = []; var rawLine = lines[l].coordinates || []; for (var p = 0; p < rawLine.length; p += 1) { var linePoint = leafletPoint(rawLine[p]); if (linePoint) { linePoints.push(linePoint); bounds.push(linePoint); } } if (linePoints.length > 1) window.L.polyline(linePoints, { color: "#2563eb", weight: 3, opacity: 0.85 }).addTo(map); } var polygons = mapData.polygons || []; for (var g = 0; g < polygons.length; g += 1) { var polygonPoints = []; var rawPolygon = polygons[g].coordinates || []; for (var q = 0; q < rawPolygon.length; q += 1) { var polygonPoint = leafletPoint(rawPolygon[q]); if (polygonPoint) { polygonPoints.push(polygonPoint); bounds.push(polygonPoint); } } if (polygonPoints.length > 2) window.L.polygon(polygonPoints, { color: "#7c3aed", weight: 2, fillColor: "#8b5cf6", fillOpacity: 0.2 }).addTo(map); } var center = leafletPoint(mapData.center); if (center) map.setView(center, Number(mapData.zoom) || 6); else if (bounds.length) map.fitBounds(bounds, { padding: [28, 28], maxZoom: Number(mapData.zoom) || 10 }); else map.setView([35.8617, 104.1954], Number(mapData.zoom) || 4); bindCanvasResize(mapTarget, function () { map.invalidateSize(); }); } catch (error) { showError(mapTarget, "地图渲染失败:" + (error && error.message ? error.message : "请检查地图数据")); } }); })(target, data); } } function initFlows() { var items = blocks("data-df-flow"); for (var i = 0; i < items.length; i += 1) { var block = items[i]; var target = document.getElementById(block.getAttribute("data-df-flow")); if (!target) continue; var data = parse(block, target); if (!data) continue; if (!window.G6 || !window.G6.Graph) { showError(target, "AntV G6 运行库加载失败"); continue; } size(target); (function (flowTarget, flowData) { whenVisible(flowTarget, function () { try { var nodes = flowData.nodes || []; var edges = flowData.edges || []; var rankDirection = flowData.direction === "TB" ? "TB" : "LR"; var colors = { start: ["#dcfce7", "#22c55e", "#14532d"], end: ["#ede9fe", "#8b5cf6", "#4c1d95"], decision: ["#fef3c7", "#f59e0b", "#78350f"], risk: ["#fee2e2", "#ef4444", "#7f1d1d"], process: ["#dbeafe", "#3b82f6", "#1e3a8a"], stage: ["#e0e7ff", "#6366f1", "#312e81"], task: ["#f1f5f9", "#94a3b8", "#1e293b"] }; var typeNames = { start: "开始", end: "结束", decision: "判断", risk: "异常", process: "处理", stage: "阶段", task: "任务" }; var graphData = { nodes: [], edges: [] }; for (var n = 0; n < nodes.length; n += 1) { var node = nodes[n]; var pair = colors[node.type] || colors.task; graphData.nodes.push({ id: String(node.id), data: { label: node.label || node.id, description: node.description || "", typeName: typeNames[node.type] || typeNames.task, fill: node.color || pair[0], stroke: pair[1], textColor: pair[2], terminal: node.type === "start" || node.type === "end" }}); } for (var e = 0; e < edges.length; e += 1) { graphData.edges.push({ source: String(edges[e].source), target: String(edges[e].target), data: { label: edges[e].label || "" } }); } flowTarget.setAttribute("role", "img"); flowTarget.setAttribute("aria-label", (flowData.title || "流程图") + ",包含 " + nodes.length + " 个步骤和 " + edges.length + " 条连线"); var graph = new window.G6.Graph({ container: flowTarget, data: graphData, autoFit: "view", padding: 72, zoomRange: [0.1, 2], animation: false, layout: { type: "dagre", rankdir: rankDirection, nodesep: 44, ranksep: 60 }, node: { type: "rect", style: { size: function (d) { var width = Math.max(190, Math.min(260, 118 + String(d.data.label || "").length * 15)); return [width, 68]; }, radius: function (d) { return d.data.terminal ? 38 : 16; }, opacity: 1, fillOpacity: 1, strokeOpacity: 1, fill: function (d) { return d.data.fill; }, stroke: function (d) { return d.data.stroke; }, lineWidth: 2.5, shadowColor: "rgba(15,23,42,0.10)", shadowBlur: 12, shadowOffsetY: 4, labelText: function (d) { return d.data.typeName + " · " + d.data.label; }, labelPlacement: "center", labelFill: function (d) { return d.data.textColor; }, labelOpacity: 1, labelFontSize: 16, labelFontWeight: 700, labelWordWrap: true, labelMaxWidth: 220, cursor: "grab" }}, edge: { type: "polyline", style: { opacity: 1, strokeOpacity: 1, stroke: "#94a3b8", lineWidth: 2.25, radius: 16, endArrow: true, labelText: function (d) { return d.data.label; }, labelFill: "#334155", labelOpacity: 1, labelFontSize: 12, labelBackground: true, labelBackgroundFill: "#ffffff", labelBackgroundRadius: 6, labelPadding: [4, 7] }}, behaviors: ["drag-canvas", "zoom-canvas", "drag-element"] }); var rendered = graph.render(); if (rendered && rendered.catch) rendered.catch(function (error) { showError(flowTarget, "流程图渲染失败:" + error.message); }); bindCanvasResize(flowTarget, function () { if (graph.resize) graph.resize(); if (graph.fitView) graph.fitView(); }); } catch (error) { showError(flowTarget, "流程图渲染失败:" + (error && error.message ? error.message : "请检查流程数据")); } }); })(target, data); } } ready(function () { initFlows(); initCharts(); initGeoMaps(); initMaps(); }); })();