/** * 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 += '
' + '
' + (c.num === undefined ? '-' : c.num) + '
' + '
' + c.label + '
'; }); $('#page-content').html( '
' + html + '
' + '
' + '
区域分布(国内)' + ' ' + ' 企业' + ' 人员' + '
' + '
区域分布(全球)' + ' ' + ' 企业' + ' 人员' + '
' + '
行业分布' + ' ' + ' 企业' + ' 人员' + '
' + '
需求关键词(词云,简化提取,后续接入分词服务)
' + '
' ); } /* ============ 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); } } });