584 lines
25 KiB
JavaScript
584 lines
25 KiB
JavaScript
/* 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 "<strong>" + escapeHtml(name) + "</strong>" +
|
||
(description ? "<br>" + 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)) ? "<div style=\"margin-top:5px;color:#8fd8ff\">指标:" + escapeHtml(detail.value) + "</div>" : "";
|
||
popup.innerHTML = "<button type=\"button\" aria-label=\"关闭地图详情\" style=\"position:absolute;top:7px;right:9px;border:0;background:transparent;color:#b9dbec;font:700 20px/1 sans-serif;cursor:pointer\">×</button>" +
|
||
"<strong style=\"display:block;font-size:18px\">" + escapeHtml(name) + "</strong>" +
|
||
"<div style=\"margin-top:5px;color:#d9edf8\">" + escapeHtml(description) + "</div>" + 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("<strong>" + escapeHtml(marker.name || "地点") + "</strong>" +
|
||
(marker.description ? "<br>" + 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();
|
||
});
|
||
})();
|