Open
Description
Version
5.6.0
Link to Minimal Reproduction
Steps to Reproduce
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>库存分配可视化 - ECharts版本</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.6.0/echarts.min.js"></script>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
padding: 20px;
}
.input-area {
margin: 20px 0;
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
}
textarea {
width: 100%;
height: 200px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px;
font-family: 'Courier New', monospace;
font-size: 12px;
}
.button {
padding: 12px 24px;
background: linear-gradient(45deg, #4CAF50, #45a049);
color: white;
border: none;
cursor: pointer;
border-radius: 6px;
font-size: 16px;
transition: all 0.3s ease;
}
.button:hover {
background: linear-gradient(45deg, #45a049, #4CAF50);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
#visualization {
margin-top: 20px;
height: 600px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background: white;
}
.metrics {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 20px;
}
.metric {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 15px 25px;
border-radius: 10px;
min-width: 180px;
font-size: 1.1em;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.metric:hover {
transform: translateY(-3px);
}
.metric.success {
background: linear-gradient(135deg, #d4edda 0%, #a8d8a8 100%);
}
.metric.warning {
background: linear-gradient(135deg, #fff3cd 0%, #ffd93d 100%);
}
.metric.danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
}
table {
width: 100%;
border-collapse: collapse;
margin: 10px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
border-radius: 8px;
overflow: hidden;
}
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-weight: bold;
}
tr:nth-child(even) {
background-color: #f8f9fa;
}
tr:hover {
background-color: #e3f2fd;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
}
h3 {
color: #34495e;
border-left: 4px solid #3498db;
padding-left: 15px;
margin-top: 30px;
}
</style>
</head>
<body>
<div class="container">
<h1>📦 库存分配可视化系统</h1>
<div class="input-area">
<h3>🔧 输入分配结果JSON:</h3>
<textarea id="jsonInput" placeholder="请粘贴AllocationResponse的JSON数据..."></textarea>
<button class="button" onclick="visualize()">🚀 生成可视化</button>
<div style="margin-top: 15px; background: #f0f8ff; padding: 15px; border-radius: 8px; border: 1px solid #d1e6ff;">
<h3 style="margin-top: 0;">📊 Sankey图布局调整</h3>
<div style="display: flex; flex-wrap: wrap; gap: 15px;">
<div style="flex: 1; min-width: 200px;">
<label for="nodeAlign">节点对齐方式:</label>
<select id="nodeAlign" style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc;">
<option value="left">左对齐</option>
<option value="right" selected>右对齐</option>
<option value="justify">两端对齐</option>
</select>
</div>
<div style="flex: 1; min-width: 200px;">
<label for="layoutOrientation">布局方向:</label>
<select id="layoutOrientation" style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc;">
<option value="horizontal" selected>水平</option>
<option value="vertical">垂直</option>
</select>
</div>
<div style="flex: 1; min-width: 200px;">
<label for="iterations">布局迭代次数 (32-128):</label>
<input type="number" id="iterations" value="64" min="32" max="128" step="8" style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc;">
</div>
</div>
<div style="margin-top: 10px; display: flex; flex-wrap: wrap; gap: 15px;">
<div style="flex: 1; min-width: 200px;">
<label for="waveFilter">波次筛选 (多选):</label>
<select id="waveFilter" multiple style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc; height: 100px;">
</select>
</div>
<div style="flex: 1; min-width: 200px;">
<label for="locationFilter">库位筛选 (多选):</label>
<select id="locationFilter" multiple style="width: 100%; padding: 8px; margin-top: 5px; border-radius: 4px; border: 1px solid #ccc; height: 100px;">
</select>
</div>
</div>
<div style="display: flex; justify-content: center; margin-top: 10px; gap: 15px;">
<button class="button" style="background: linear-gradient(45deg, #3498db, #2980b9);" onclick="updateLayout()">更新布局</button>
<button class="button" style="background: linear-gradient(45deg, #9b59b6, #8e44ad);" onclick="applyFilters()">应用筛选</button>
<button class="button" style="background: linear-gradient(45deg, #e74c3c, #c0392b);" onclick="resetFilters()">重置筛选</button>
</div>
</div>
</div>
<div id="metrics"></div>
<div id="visualization"></div>
<div id="tables"></div>
</div>
<script>
// 全局变量
let myChart = null;
let originalSankeyData = null; // 存储原始数据
// 示例数据
const sampleData = {
"emptyLocations": 1,
"multiUseLocations": 0,
"skuLevelData": {
"allPackages": [{"packageId": 1, "requestNum": 10, "skuId": 1001}],
"capacityByPriority": {1001: {0: 8}},
"locationsByPriority": {1001: {0: 1}},
"packageWaveMap": {"1": "WAVE_001"},
"skuLocationsByPriority": {
1001: {
0: [{"availableQty": 8, "locationId": 1, "priority": 0, "skuId": 1001}]
}
},
"skuTotalDemand": {1001: 10},
"totalCapacity": {1001: 8},
"totalLocations": {1001: 1},
"waveDemand": {"WAVE_001": 10},
"waveLocations": {"WAVE_001": [1]},
"wavePackages": {"WAVE_001": [1]}
},
"totalAllocated": 8,
"waveInfoRespList": [{
"packageInfoList": [{
"locationInfoList": [
{"locationId": 1, "packageId": 1, "allocatedQty": 8}
],
"packageId": 1,
"status": "1"
}],
"totalAllocated": 8,
"waveCode": "WAVE_001"
}]
};
// 页面加载完成时加载示例数据
window.onload = function() {
document.getElementById('jsonInput').value = JSON.stringify(sampleData, null, 2);
visualize();
};
// 主入口函数
function visualize() {
const jsonData = document.getElementById('jsonInput').value;
if (!jsonData) {
alert('请输入JSON数据');
return;
}
try {
// 修复 JSON 中的数字键问题
const fixedJson = jsonData.replace(/([\{,]\s*)(\d+)(\s*:)/g, '$1"$2"$3');
const data = JSON.parse(fixedJson);
// 填充过滤选择器
populateFilters(data);
// 1. 统计卡片
renderMetrics(data);
// 2. ECharts Sankey图
renderEChartsSankey(data);
// 3. 表格
renderTables(data);
} catch (e) {
alert('JSON格式错误或数据处理失败:' + e.message);
console.error('JSON解析错误:', e);
console.error('原始JSON:', jsonData);
}
}
// 填充筛选选择器
function populateFilters(data) {
const waveFilter = document.getElementById('waveFilter');
const locationFilter = document.getElementById('locationFilter');
// 清空现有选项
waveFilter.innerHTML = '';
locationFilter.innerHTML = '';
const skuData = data.skuLevelData || {};
const waves = Object.keys(skuData.waveDemand || {});
const locationSet = new Set();
// 收集所有的库位ID
for (const waveInfo of (data.waveInfoRespList || [])) {
for (const pkg of (waveInfo.packageInfoList || [])) {
for (const loc of (pkg.locationInfoList || [])) {
locationSet.add(loc.locationId);
}
}
}
// 按照数字大小排序的函数
const sortNumeric = (a, b) => {
// 提取数字部分
const numA = parseInt(a.replace(/\D/g, '')) || 0;
const numB = parseInt(b.replace(/\D/g, '')) || 0;
return numA - numB;
};
// 添加波次选项
waves.sort(sortNumeric).forEach(wave => {
const option = document.createElement('option');
option.value = wave;
option.textContent = wave;
waveFilter.appendChild(option);
});
// 添加库位选项
Array.from(locationSet).sort((a, b) => a - b).forEach(locId => {
const option = document.createElement('option');
option.value = locId;
option.textContent = `库位-${locId}`;
locationFilter.appendChild(option);
});
}
// 应用筛选
function applyFilters() {
const chartDom = document.getElementById('visualization');
if (!myChart || !originalSankeyData) {
return;
}
// 获取选中的波次和库位
const waveFilter = document.getElementById('waveFilter');
const locationFilter = document.getElementById('locationFilter');
const selectedWaves = Array.from(waveFilter.selectedOptions).map(opt => opt.value);
const selectedLocations = Array.from(locationFilter.selectedOptions).map(opt => parseInt(opt.value));
// 如果没有选择任何筛选条件,则保留所有数据
if (selectedWaves.length === 0 && selectedLocations.length === 0) {
resetFilters();
return;
}
// 深拷贝原始数据
const { nodes, links } = JSON.parse(JSON.stringify(originalSankeyData));
// 筛选链接
const filteredLinks = links.filter(link => {
// 波次筛选
if (selectedWaves.length > 0) {
const isWaveSource = selectedWaves.some(wave => link.source.includes(`波次-${wave}`));
if (isWaveSource) {
return true;
}
}
// 库位筛选
if (selectedLocations.length > 0) {
const isLocSource = selectedLocations.some(loc => link.source.includes(`库位-${loc}`));
const isLocTarget = selectedLocations.some(loc => link.target.includes(`库位-${loc}`));
if (isLocSource || isLocTarget) {
return true;
}
}
return selectedWaves.length === 0 && selectedLocations.length === 0;
});
// 获取过滤后的链接中涉及的所有节点名称
const remainingNodeNames = new Set();
filteredLinks.forEach(link => {
remainingNodeNames.add(link.source);
remainingNodeNames.add(link.target);
});
// 筛选节点
const filteredNodes = nodes.filter(node => remainingNodeNames.has(node.name));
// 更新图表
myChart.setOption({
series: [{
data: filteredNodes,
links: filteredLinks
}]
});
}
// 重置筛选
function resetFilters() {
// 清除选中状态
document.getElementById('waveFilter').selectedIndex = -1;
document.getElementById('locationFilter').selectedIndex = -1;
// 如果有原始数据和图表,恢复完整数据
if (originalSankeyData && myChart) {
myChart.setOption({
series: [{
data: originalSankeyData.nodes,
links: originalSankeyData.links
}]
});
}
}
// 统计卡片渲染
function renderMetrics(data) {
const totalAllocated = data.totalAllocated || 0;
const emptyLocations = data.emptyLocations || 0;
const multiUseLocations = data.multiUseLocations || 0;
const skuData = data.skuLevelData || {};
const totalDemand = Object.values(skuData.skuTotalDemand || {}).reduce((a, b) => a + b, 0);
const totalCapacity = Object.values(skuData.totalCapacity || {}).reduce((a, b) => a + b, 0);
const totalLocations = Object.values(skuData.totalLocations || {}).reduce((a, b) => a + b, 0);
const totalPackages = (skuData.allPackages || []).length;
const totalWaves = Object.keys(skuData.waveDemand || {}).length;
const allocationRate = totalDemand > 0 ? (totalAllocated / totalDemand * 100).toFixed(2) : 0;
const capacityUtilization = totalCapacity > 0 ? (totalAllocated / totalCapacity * 100).toFixed(2) : 0;
let html = `
<div class="metrics">
<div class="metric success"><strong>📊 分配总量:</strong> ${totalAllocated}</div>
<div class="metric success"><strong>🎯 总需求:</strong> ${totalDemand}</div>
<div class="metric success"><strong>📈 分配率:</strong> ${allocationRate}%</div>
<div class="metric"><strong>📦 总容量:</strong> ${totalCapacity}</div>
<div class="metric"><strong>⚡ 容量利用率:</strong> ${capacityUtilization}%</div>
<div class="metric"><strong>🏢 总库位数:</strong> ${totalLocations}</div>
<div class="metric warning"><strong>🗂️ 清空库位数:</strong> ${emptyLocations}</div>
<div class="metric warning"><strong>🔄 多波次库位数:</strong> ${multiUseLocations}</div>
<div class="metric"><strong>📋 总包裹数:</strong> ${totalPackages}</div>
<div class="metric"><strong>🌊 总波次数:</strong> ${totalWaves}</div>
</div>
`;
document.getElementById('metrics').innerHTML = html;
}
// ECharts Sankey图渲染
function renderEChartsSankey(data) {
const chartDom = document.getElementById('visualization');
if (myChart) {
myChart.dispose();
}
myChart = echarts.init(chartDom);
// 构建节点和链接数据
const { nodes, links } = buildSankeyData(data);
// 保存原始数据
originalSankeyData = { nodes: JSON.parse(JSON.stringify(nodes)), links: JSON.parse(JSON.stringify(links)) };
// 获取用户布局参数
const nodeAlign = document.getElementById('nodeAlign').value;
const layoutOrientation = document.getElementById('layoutOrientation').value;
const iterations = parseInt(document.getElementById('iterations').value) || 64;
const option = {
title: {
text: '库存分配流向图',
left: 'center',
textStyle: {
fontSize: 20,
fontWeight: 'bold',
color: '#2c3e50'
}
},
tooltip: {
trigger: 'item',
triggerOn: 'mousemove',
backgroundColor: 'rgba(50, 50, 50, 0.9)',
textStyle: {
color: '#fff'
},
formatter: function(params) {
if (params.dataType === 'node') {
return params.data.tooltip || params.data.name;
} else if (params.dataType === 'edge') {
return `${params.data.source} → ${params.data.target}<br/>分配量: ${params.data.value}`;
}
}
},
animation: true,
animationDuration: 1000,
animationEasing: 'cubicInOut',
series: [{
type: 'sankey',
data: nodes,
links: links,
emphasis: {
focus: 'adjacency'
},
orient: layoutOrientation,
levels: [
{
depth: 0,
itemStyle: {
color: '#ff7f7f'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
},
{
depth: 1,
itemStyle: {
color: '#87ceeb'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
},
{
depth: 2,
itemStyle: {
color: '#98fb98'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
}
],
lineStyle: {
curveness: 0.5,
opacity: 0.7,
color: 'gradient'
},
itemStyle: {
borderWidth: 1,
borderColor: '#aaa',
borderType: 'solid'
},
label: {
fontSize: 12,
fontWeight: 'bold'
},
nodeAlign: nodeAlign,
nodeGap: 30,
nodeWidth: 25,
layoutIterations: iterations
}]
};
myChart.setOption(option);
// 响应式调整
window.addEventListener('resize', function() {
if (myChart) {
myChart.resize();
}
});
}
// 构建Sankey数据
function buildSankeyData(data) {
const nodes = [];
const links = [];
const nodeMap = new Map();
const skuData = data.skuLevelData || {};
const locationPriorityMap = new Map();
const locationAvailableQtyMap = new Map();
const waveDemandMap = new Map();
const packageRequestMap = new Map();
const packageWaveMap = new Map();
// 预处理数据
for (const [wave, demand] of Object.entries(skuData.waveDemand || {})) {
waveDemandMap.set(wave, demand);
}
for (const pkg of (skuData.allPackages || [])) {
packageRequestMap.set(pkg.packageId, pkg.requestNum);
}
for (const [pkgId, wave] of Object.entries(skuData.packageWaveMap || {})) {
packageWaveMap.set(parseInt(pkgId), wave);
}
for (const [skuId, priorityMap] of Object.entries(skuData.skuLocationsByPriority || {})) {
for (const [priority, locations] of Object.entries(priorityMap)) {
for (const loc of locations) {
locationPriorityMap.set(loc.locationId, parseInt(priority));
locationAvailableQtyMap.set(loc.locationId, loc.availableQty);
}
}
}
// 添加节点的辅助函数
function addNode(name, tooltip) {
if (!nodeMap.has(name)) {
nodeMap.set(name, nodes.length);
nodes.push({ name: name, tooltip: tooltip });
}
return nodeMap.get(name);
}
// 构建节点和链接
const waveLocLinks = new Map();
const locPkgLinks = new Map();
(data.waveInfoRespList || []).forEach(wave => {
const waveLabel = `波次-${wave.waveCode}`;
const waveDemand = waveDemandMap.get(wave.waveCode) || 0;
const waveTooltip = `波次: ${wave.waveCode}<br/>需求量: ${waveDemand}`;
addNode(waveLabel, waveTooltip);
(wave.packageInfoList || []).forEach(pkg => {
(pkg.locationInfoList || []).forEach(loc => {
const locationLabel = `库位-${loc.locationId}`;
const priority = locationPriorityMap.get(loc.locationId) || -1;
const availableQty = locationAvailableQtyMap.get(loc.locationId) || 0;
const locationTooltip = `库位: ${loc.locationId}<br/>优先级: ${priority}<br/>可用量: ${availableQty}`;
addNode(locationLabel, locationTooltip);
const packageLabel = `包裹-${loc.packageId}`;
const requestNum = packageRequestMap.get(loc.packageId) || 0;
const packageWave = packageWaveMap.get(loc.packageId);
const packageTooltip = `包裹: ${loc.packageId}<br/>需求量: ${requestNum}<br/>所属波次: ${packageWave}`;
addNode(packageLabel, packageTooltip);
const qty = loc.allocatedQty || 0;
// 波次到库位的链接
const waveIdx = nodeMap.get(waveLabel);
const locationIdx = nodeMap.get(locationLabel);
const waveLocKey = `${waveIdx}_${locationIdx}`;
waveLocLinks.set(waveLocKey, (waveLocLinks.get(waveLocKey) || 0) + qty);
// 库位到包裹的链接
const packageIdx = nodeMap.get(packageLabel);
const locPkgKey = `${locationIdx}_${packageIdx}`;
locPkgLinks.set(locPkgKey, (locPkgLinks.get(locPkgKey) || 0) + qty);
});
});
});
// 添加波次到库位的链接
for (const [key, qty] of waveLocLinks.entries()) {
const [src, tgt] = key.split('_').map(Number);
links.push({
source: nodes[src].name,
target: nodes[tgt].name,
value: qty
});
}
// 添加库位到包裹的链接
for (const [key, qty] of locPkgLinks.entries()) {
const [src, tgt] = key.split('_').map(Number);
links.push({
source: nodes[src].name,
target: nodes[tgt].name,
value: qty
});
}
// 优化节点顺序,减少边交叉
sortSankey(nodes, links);
return { nodes, links };
}
// 优化Sankey图节点顺序,减少边交叉
function sortSankey(nodes, links) {
// 按照节点类型和连接情况排序
const nodeTypes = new Map();
const nodeConnections = new Map();
// 计算每个节点的连接数和类型
nodes.forEach((node, index) => {
const name = node.name;
if (name.startsWith('波次-')) {
nodeTypes.set(name, 0); // 波次类型
} else if (name.startsWith('库位-')) {
nodeTypes.set(name, 1); // 库位类型
} else if (name.startsWith('包裹-')) {
nodeTypes.set(name, 2); // 包裹类型
}
nodeConnections.set(name, 0);
});
// 计算连接数
links.forEach(link => {
nodeConnections.set(link.source, (nodeConnections.get(link.source) || 0) + link.value);
nodeConnections.set(link.target, (nodeConnections.get(link.target) || 0) + link.value);
});
// 按类型和连接数排序节点
nodes.sort((a, b) => {
const typeA = nodeTypes.get(a.name);
const typeB = nodeTypes.get(b.name);
// 首先按类型排序
if (typeA !== typeB) {
return typeA - typeB;
}
// 同类型按连接数降序排序
const connA = nodeConnections.get(a.name) || 0;
const connB = nodeConnections.get(b.name) || 0;
return connB - connA;
});
}
// 表格渲染
function renderTables(data) {
const skuData = data.skuLevelData || {};
let html = '';
const locationPriorityMap = new Map();
const locationAvailableQtyMap = new Map();
for (const [skuId, priorityMap] of Object.entries(skuData.skuLocationsByPriority || {})) {
for (const [priority, locations] of Object.entries(priorityMap)) {
for (const loc of locations) {
locationPriorityMap.set(loc.locationId, parseInt(priority));
locationAvailableQtyMap.set(loc.locationId, loc.availableQty);
}
}
}
// 1. SKU级别统计表格
html += `<h3>📈 SKU级别统计</h3>
<table>
<tr>
<th>SKU ID</th>
<th>总需求</th>
<th>总容量</th>
<th>总库位数</th>
<th>分配率</th>
<th>优先级0库位数</th>
<th>优先级1库位数</th>
</tr>`;
for (const [skuId, demand] of Object.entries(skuData.skuTotalDemand || {})) {
const capacity = skuData.totalCapacity?.[skuId] || 0;
const locations = skuData.totalLocations?.[skuId] || 0;
const allocationRate = demand > 0 ? (capacity / demand * 100).toFixed(2) : 0;
const priority0Locations = skuData.locationsByPriority?.[skuId]?.[0] || 0;
const priority1Locations = skuData.locationsByPriority?.[skuId]?.[1] || 0;
html += `<tr>
<td>${skuId}</td>
<td>${demand}</td>
<td>${capacity}</td>
<td>${locations}</td>
<td>${allocationRate}%</td>
<td>${priority0Locations}</td>
<td>${priority1Locations}</td>
</tr>`;
}
html += `</table>`;
// 2. 波次统计表格
html += `<h3>🌊 波次统计</h3>
<table>
<tr>
<th>波次</th>
<th>需求数量</th>
<th>包裹数量</th>
<th>使用库位数</th>
</tr>`;
for (const [wave, demand] of Object.entries(skuData.waveDemand || {})) {
const packages = skuData.wavePackages?.[wave]?.length || 0;
const locations = skuData.waveLocations?.[wave]?.length || 0;
html += `<tr>
<td>${wave}</td>
<td>${demand}</td>
<td>${packages}</td>
<td>${locations}</td>
</tr>`;
}
html += `</table>`;
// 3. 库位分配统计表格
html += `<h3>🏢 库位分配统计</h3>
<table>
<tr>
<th>库位ID</th>
<th>优先级</th>
<th>可用容量</th>
<th>分配总量</th>
<th>被使用次数</th>
<th>涉及包裹数</th>
<th>涉及波次数</th>
<th>是否多波次</th>
</tr>`;
const locationStats = {};
(data.waveInfoRespList || []).forEach(wave => {
(wave.packageInfoList || []).forEach(pkg => {
(pkg.locationInfoList || []).forEach(loc => {
const locId = loc.locationId;
if (!locationStats[locId]) {
locationStats[locId] = {
total: 0,
count: 0,
packages: new Set(),
waves: new Set(),
priority: locationPriorityMap.get(locId) || -1,
availableQty: locationAvailableQtyMap.get(locId) || 0
};
}
locationStats[locId].total += (loc.allocatedQty || 0);
locationStats[locId].count += 1;
locationStats[locId].packages.add(loc.packageId);
locationStats[locId].waves.add(wave.waveCode);
});
});
});
for (const locId in locationStats) {
const stat = locationStats[locId];
const isMultiWave = stat.waves.size > 1 ? '✅ 是' : '❌ 否';
html += `<tr>
<td>${locId}</td>
<td>${stat.priority}</td>
<td>${stat.availableQty}</td>
<td>${stat.total}</td>
<td>${stat.count}</td>
<td>${stat.packages.size}</td>
<td>${stat.waves.size}</td>
<td>${isMultiWave}</td>
</tr>`;
}
html += `</table>`;
document.getElementById('tables').innerHTML = html;
}
function updateLayout() {
const nodeAlign = document.getElementById('nodeAlign').value;
const layoutOrientation = document.getElementById('layoutOrientation').value;
const iterations = document.getElementById('iterations').value;
const chartDom = document.getElementById('visualization');
const chart = echarts.getInstanceByDom(chartDom);
if (chart) {
chart.setOption({
series: [{
type: 'sankey',
data: chart.getOption().series[0].data,
links: chart.getOption().series[0].links,
emphasis: {
focus: 'adjacency'
},
orient: layoutOrientation,
levels: [
{
depth: 0,
itemStyle: {
color: '#ff7f7f'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
},
{
depth: 1,
itemStyle: {
color: '#87ceeb'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
},
{
depth: 2,
itemStyle: {
color: '#98fb98'
},
lineStyle: {
color: 'source',
opacity: 0.6
}
}
],
lineStyle: {
curveness: 0.5,
opacity: 0.7,
color: 'gradient'
},
itemStyle: {
borderWidth: 1,
borderColor: '#aaa',
borderType: 'solid'
},
label: {
fontSize: 12,
fontWeight: 'bold'
},
nodeAlign: nodeAlign,
nodeGap: 30,
nodeWidth: 25,
layoutIterations: iterations
}]
});
}
}
</script>
</body>
</html>
{"emptyLocations":0,"multiUseLocations":1,"skuLevelData":{"allPackages":[{"packageId":1,"requestNum":4,"skuId":816324},{"packageId":2,"requestNum":4,"skuId":816324},{"packageId":3,"requestNum":4,"skuId":816324},{"packageId":4,"requestNum":4,"skuId":816324},{"packageId":5,"requestNum":4,"skuId":816324},{"packageId":57,"requestNum":4,"skuId":816324},{"packageId":58,"requestNum":4,"skuId":816324},{"packageId":59,"requestNum":4,"skuId":816324},{"packageId":60,"requestNum":4,"skuId":816324},{"packageId":61,"requestNum":4,"skuId":816324},{"packageId":113,"requestNum":4,"skuId":816324},{"packageId":114,"requestNum":4,"skuId":816324},{"packageId":115,"requestNum":4,"skuId":816324},{"packageId":116,"requestNum":4,"skuId":816324},{"packageId":117,"requestNum":4,"skuId":816324},{"packageId":118,"requestNum":4,"skuId":816324},{"packageId":119,"requestNum":4,"skuId":816324},{"packageId":120,"requestNum":4,"skuId":816324},{"packageId":121,"requestNum":4,"skuId":816324},{"packageId":122,"requestNum":4,"skuId":816324}],"capacityByPriority":{816324:{0:104,1:224}},"locationsByPriority":{816324:{0:1,1:2}},"packageWaveMap":{1:"WAVE_001",2:"WAVE_001",3:"WAVE_001",4:"WAVE_001",5:"WAVE_001",113:"WAVE_003",114:"WAVE_003",115:"WAVE_003",116:"WAVE_003",117:"WAVE_003",118:"WAVE_003",119:"WAVE_003",120:"WAVE_003",57:"WAVE_002",121:"WAVE_003",58:"WAVE_002",122:"WAVE_003",59:"WAVE_002",60:"WAVE_002",61:"WAVE_002"},"skuLocationsByPriority":{816324:{0:[{"availableQty":104,"locationId":1,"priority":0,"skuId":816324}],1:[{"availableQty":112,"locationId":2,"priority":1,"skuId":816324},{"availableQty":112,"locationId":3,"priority":1,"skuId":816324}]}},"skuTotalDemand":{816324:80},"totalCapacity":{816324:328},"totalLocations":{816324:3},"waveDemand":{"WAVE_002":20,"WAVE_003":40,"WAVE_001":20},"waveLocations":{"WAVE_002":[1,2,3],"WAVE_003":[1,2,3],"WAVE_001":[1,2,3]},"wavePackages":{"WAVE_002":[57,58,59,60,61],"WAVE_003":[113,114,115,116,117,118,119,120,121,122],"WAVE_001":[1,2,3,4,5]}},"totalAllocated":80,"waveInfoRespList":[{"packageInfoList":[{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":1,"skuId":816324}],"packageId":1,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":2,"skuId":816324}],"packageId":2,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":3,"skuId":816324}],"packageId":3,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":4,"skuId":816324}],"packageId":4,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":5,"skuId":816324}],"packageId":5,"status":"1"}],"totalAllocated":20,"waveCode":"WAVE_001"},{"packageInfoList":[{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":57,"skuId":816324}],"packageId":57,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":58,"skuId":816324}],"packageId":58,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":59,"skuId":816324}],"packageId":59,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":60,"skuId":816324}],"packageId":60,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":61,"skuId":816324}],"packageId":61,"status":"1"}],"totalAllocated":20,"waveCode":"WAVE_002"},{"packageInfoList":[{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":113,"skuId":816324}],"packageId":113,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":114,"skuId":816324}],"packageId":114,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":115,"skuId":816324}],"packageId":115,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":116,"skuId":816324}],"packageId":116,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":117,"skuId":816324}],"packageId":117,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":118,"skuId":816324}],"packageId":118,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":119,"skuId":816324}],"packageId":119,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":120,"skuId":816324}],"packageId":120,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":121,"skuId":816324}],"packageId":121,"status":"1"},{"locationInfoList":[{"allocatedQty":4,"locationId":1,"packageId":122,"skuId":816324}],"packageId":122,"status":"1"}],"totalAllocated":40,"waveCode":"WAVE_003"}]}
Current Behavior

Expected Behavior

Environment
- OS: OSX
- Browser: Chrome
- Framework: CDN use with htm&js
Any additional comments?
No response