v1.0.10
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* dashboard.js - 首页逻辑
|
||||
* 8个指标卡 + 区域分布(国内=中国地图按省份 / 全球=世界地图按国家,各含企业/人员Tab)
|
||||
* + 行业分布(企业/人员)+ 需求关键词词云(占位)
|
||||
*/
|
||||
$(function () {
|
||||
renderShell('综合概览', '首页 / 数据看板');
|
||||
|
||||
initPage('dashboard', function (user) {
|
||||
loadStats();
|
||||
});
|
||||
|
||||
// 图表状态(Tab 选中态与数据维度)
|
||||
var pageData = null;
|
||||
var regionState = { china: { dim: 'company' }, world: { dim: 'company' } };
|
||||
var industryState = { dim: 'company' };
|
||||
|
||||
/** 数值安全转换:NaN / 空值一律转 0 */
|
||||
function toNum(v) {
|
||||
var n = parseInt(v, 10);
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
|
||||
function loadStats() {
|
||||
httpGet('dashboard/stats.php').then(function (data) {
|
||||
renderMetricCards(data.metrics || {});
|
||||
pageData = data;
|
||||
renderRegionChart('china');
|
||||
renderRegionChart('world');
|
||||
renderIndustryChart();
|
||||
renderWordCloud(data.wordcloud || []);
|
||||
});
|
||||
}
|
||||
|
||||
/* ============ 8 个指标卡 + 图表容器 ============ */
|
||||
function renderMetricCards(m) {
|
||||
var cards = [
|
||||
{ label: '累计企业', num: toNum(m.company_total), cls: 'c-blue' },
|
||||
{ label: '累计人员', num: toNum(m.person_total), cls: 'c-green' },
|
||||
{ label: '昨日新增数据(企业/人员)', num: toNum(m.company_new) + '/' + toNum(m.person_new), cls: 'c-orange' },
|
||||
{ label: '待处理数据碎片', num: toNum(m.preliminary), cls: 'c-red' },
|
||||
{ label: '覆盖行业', num: toNum(m.industry_count), cls: 'c-purple' },
|
||||
{ label: '生产型企业', num: toNum(m.manufacturer), cls: 'c-blue' },
|
||||
{ label: '集成商/代理商', num: toNum(m.integrator), cls: 'c-cyan' },
|
||||
{ label: '行业媒体资源', num: toNum(m.media_resource), cls: 'c-teal' }
|
||||
];
|
||||
var html = '';
|
||||
cards.forEach(function (c) {
|
||||
html += '<div class="stat-card ' + c.cls + '">' +
|
||||
'<div class="num">' + (c.num === undefined ? '-' : c.num) + '</div>' +
|
||||
'<div class="label">' + c.label + '</div></div>';
|
||||
});
|
||||
$('#page-content').html(
|
||||
'<div class="stat-cards">' + html + '</div>' +
|
||||
'<div class="chart-grid">' +
|
||||
' <div class="chart-box"><div class="chart-title">区域分布(国内)' +
|
||||
' <span class="tabs" data-group="region-china">' +
|
||||
' <a class="tab' + (regionState.china.dim === 'company' ? ' active' : '') + '" data-val="company" onclick="switchRegionDim(\'china\',\'company\')">企业</a>' +
|
||||
' <a class="tab' + (regionState.china.dim === 'person' ? ' active' : '') + '" data-val="person" onclick="switchRegionDim(\'china\',\'person\')">人员</a>' +
|
||||
' </span></div><div class="chart" id="chart-region-china"></div></div>' +
|
||||
' <div class="chart-box"><div class="chart-title">区域分布(全球)' +
|
||||
' <span class="tabs" data-group="region-world">' +
|
||||
' <a class="tab' + (regionState.world.dim === 'company' ? ' active' : '') + '" data-val="company" onclick="switchRegionDim(\'world\',\'company\')">企业</a>' +
|
||||
' <a class="tab' + (regionState.world.dim === 'person' ? ' active' : '') + '" data-val="person" onclick="switchRegionDim(\'world\',\'person\')">人员</a>' +
|
||||
' </span></div><div class="chart" id="chart-region-world"></div></div>' +
|
||||
' <div class="chart-box"><div class="chart-title">行业分布' +
|
||||
' <span class="tabs" data-group="industry">' +
|
||||
' <a class="tab' + (industryState.dim === 'company' ? ' active' : '') + '" data-val="company" onclick="switchIndustryDim(\'company\')">企业</a>' +
|
||||
' <a class="tab' + (industryState.dim === 'person' ? ' active' : '') + '" data-val="person" onclick="switchIndustryDim(\'person\')">人员</a>' +
|
||||
' </span></div><div class="chart" id="chart-industry"></div></div>' +
|
||||
' <div class="chart-box"><div class="chart-title">需求关键词(词云,简化提取,后续接入分词服务)</div><div class="chart" id="chart-wordcloud"></div></div>' +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
|
||||
/* ============ Tab 切换(内联 onclick,数据属性精确匹配选中态) ============ */
|
||||
window.switchRegionDim = function (box, dim) {
|
||||
regionState[box].dim = dim;
|
||||
syncTabs();
|
||||
renderRegionChart(box);
|
||||
};
|
||||
window.switchIndustryDim = function (dim) {
|
||||
industryState.dim = dim;
|
||||
syncTabs();
|
||||
renderIndustryChart();
|
||||
};
|
||||
|
||||
/** 同步所有 Tab 的选中色块 */
|
||||
function syncTabs() {
|
||||
$('.chart-title .tabs').each(function () {
|
||||
var $tabs = $(this);
|
||||
var group = $tabs.data('group');
|
||||
$tabs.find('.tab').each(function () {
|
||||
var $t = $(this);
|
||||
var val = $t.data('val');
|
||||
var active = false;
|
||||
if (group === 'region-china') active = (regionState.china.dim === val);
|
||||
else if (group === 'region-world') active = (regionState.world.dim === val);
|
||||
else if (group === 'industry') active = (industryState.dim === val);
|
||||
$t.toggleClass('active', active);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ============ 区域分布(china=中国地图按省份 / world=世界地图按国家) ============ */
|
||||
function renderRegionChart(box) {
|
||||
var el = document.getElementById('chart-region-' + box);
|
||||
if (!el || !window.echarts) return;
|
||||
try {
|
||||
var dim = regionState[box].dim;
|
||||
var rows = ((pageData.region || {})[dim] || {})[box] || [];
|
||||
rows = rows.map(function (r) {
|
||||
return { name: r.name, value: toNum(r.value) };
|
||||
});
|
||||
var chart = echarts.getInstanceByDom(el) || echarts.init(el);
|
||||
var mapName = box === 'china' ? 'china' : 'world';
|
||||
var hasMap = !!(echarts.getMap && echarts.getMap(mapName));
|
||||
|
||||
// 世界地图:ECharts5 不支持 series.nameMap,需把中文国家名转英文(地图用英文名匹配);中文名保留给 tooltip
|
||||
if (box === 'world') {
|
||||
rows = rows.map(function (r) {
|
||||
var en = CN_TO_EN[r.name] || r.name;
|
||||
return { name: en, value: r.value, displayName: r.name };
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasMap || !rows.length) {
|
||||
// 地图未加载或无数据时降级为柱状图
|
||||
if (!rows.length) {
|
||||
chart.setOption({
|
||||
title: { text: '暂无' + (dim === 'company' ? '企业' : '人员') + '数据', left: 'center', top: 'middle', textStyle: { color: '#b6bfcc', fontSize: 14 } }
|
||||
});
|
||||
} else {
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 60, right: 20, top: 30, bottom: 80 },
|
||||
xAxis: { type: 'category', data: rows.map(function (r) { return r.name; }), axisLabel: { rotate: 45 } },
|
||||
yAxis: { type: 'value' },
|
||||
series: [{ type: 'bar', data: rows.map(function (r) { return r.value; }), itemStyle: { color: '#16a085' }, barMaxWidth: 40 }]
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var values = rows.map(function (r) { return r.value; });
|
||||
var maxVal = Math.max.apply(null, values);
|
||||
|
||||
var option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (p) {
|
||||
// 无数据区域 p.value 为 NaN/undefined,统一显示 0
|
||||
var v = p.value;
|
||||
if (v === undefined || v === null || isNaN(v)) v = 0;
|
||||
return ((p.data && p.data.displayName) || p.name) + ':' + v;
|
||||
}
|
||||
},
|
||||
// 颜色深浅按数据量(数值越大颜色越深),不显示左下角图例
|
||||
visualMap: {
|
||||
show: false,
|
||||
min: 0,
|
||||
max: maxVal,
|
||||
inRange: { color: ['#e8f1ff', '#1e3c72'] }
|
||||
},
|
||||
series: [{
|
||||
type: 'map',
|
||||
map: mapName,
|
||||
roam: true,
|
||||
label: { show: false },
|
||||
emphasis: {
|
||||
label: { show: true, formatter: function (p) { return ((p.data && p.data.displayName) || p.name); } },
|
||||
itemStyle: { areaColor: '#f39c12' }
|
||||
},
|
||||
data: rows
|
||||
}]
|
||||
};
|
||||
chart.setOption(option);
|
||||
window.addEventListener('resize', function () { chart.resize(); });
|
||||
} catch (e) {
|
||||
console && console.error('region chart error', e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============ 行业分布(柱状图,企业/人员) ============ */
|
||||
function renderIndustryChart() {
|
||||
var el = document.getElementById('chart-industry');
|
||||
if (!el || !window.echarts) return;
|
||||
try {
|
||||
var dim = industryState.dim;
|
||||
var rows = ((pageData.industry || {})[dim] || []).map(function (r) {
|
||||
return { name: r.name, value: toNum(r.value) };
|
||||
});
|
||||
var chart = echarts.getInstanceByDom(el) || echarts.init(el);
|
||||
if (!rows.length) {
|
||||
chart.setOption({
|
||||
title: { text: '暂无' + (dim === 'company' ? '企业' : '人员') + '行业数据', left: 'center', top: 'middle', textStyle: { color: '#b6bfcc', fontSize: 14 } }
|
||||
});
|
||||
return;
|
||||
}
|
||||
chart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
grid: { left: 70, right: 20, top: 30, bottom: 50 },
|
||||
xAxis: { type: 'category', data: rows.map(function (r) { return r.name; }), axisLabel: { rotate: 30 } },
|
||||
yAxis: { type: 'value' },
|
||||
series: [{
|
||||
type: 'bar',
|
||||
data: rows.map(function (r) { return r.value; }),
|
||||
itemStyle: { color: '#2a5298', borderRadius: [3, 3, 0, 0] },
|
||||
barMaxWidth: 40
|
||||
}]
|
||||
});
|
||||
window.addEventListener('resize', function () { chart.resize(); });
|
||||
} catch (e) {
|
||||
console && console.error('industry chart error', e);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============ 需求关键词词云(占位) ============ */
|
||||
function renderWordCloud(words) {
|
||||
var el = document.getElementById('chart-wordcloud');
|
||||
if (!el || !window.echarts) return;
|
||||
try {
|
||||
var chart = echarts.getInstanceByDom(el) || echarts.init(el);
|
||||
if (!words.length) {
|
||||
chart.setOption({
|
||||
title: { text: '暂无关键词数据(占位,后续接入分词服务)', left: 'center', top: 'middle', textStyle: { color: '#b6bfcc', fontSize: 14 } }
|
||||
});
|
||||
return;
|
||||
}
|
||||
var data = words.map(function (w) {
|
||||
return {
|
||||
name: w.name,
|
||||
value: w.value,
|
||||
symbolSize: Math.min(60, 14 + w.value * 2),
|
||||
itemStyle: { color: 'hsl(' + (w.name.length * 37 % 360) + ',65%,55%)' }
|
||||
};
|
||||
});
|
||||
chart.setOption({
|
||||
tooltip: { formatter: function (p) { return p.name + ':' + toNum(p.value) + '次'; } },
|
||||
series: [{
|
||||
type: 'scatter',
|
||||
data: data,
|
||||
label: { show: true, formatter: function (p) { return p.name; }, fontSize: 12, color: '#333' },
|
||||
symbol: 'circle'
|
||||
}]
|
||||
});
|
||||
window.addEventListener('resize', function () { chart.resize(); });
|
||||
} catch (e) {
|
||||
console && console.error('wordcloud error', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user