app.controller('dashboardCtrl', ['$scope', '$http', '$timeout', '$interval', 'toaster', 'chartOptions', function ($scope, $http, $timeout, $interval, toaster, chartOptions) { particlesJS.load('particles-js', '/1.0/vendor/libs/particles.json', function () { }); //每4个字自动换行 function autoBreak(str) { var lineNumber = 4; var w = $(window).width() if (w <= 1366) { lineNumber = 3 } else if (w <= 1920) { lineNumber = 4 } else if (w <= 4096) { lineNumber = 8 } var newStr = ''; for (var index = 0; index < str.length; index++) { var item = str[index] newStr = newStr + item if ((index + 1) % lineNumber === 0) { newStr = newStr + '\n' } } return newStr } /*******以下为echarts*********/ var devChartPanelMap; var leftChart1; var leftChart2; var leftChart3; var rightChart1; var rightChart2; var rightChart3; var bottomChart; $timeout(function () { devChartPanelMap = echarts.init(document.getElementById('devChartPanelMap')); leftChart1 = echarts.init(document.getElementById('leftChart1')); leftChart2 = echarts.init(document.getElementById('leftChart2')); leftChart3 = echarts.init(document.getElementById('leftChart3')); rightChart1 = echarts.init(document.getElementById('rightChart1')); rightChart2 = echarts.init(document.getElementById('rightChart2')); rightChart3 = echarts.init(document.getElementById('rightChart3')); bottomChart = echarts.init(document.getElementById('bottomChart')); }, 1); $scope.chartHasLoad = false $scope.loadAllChart = function () { loadChartData(); $scope.chartHasLoad = true } function resizeChart() { clearTimeout(timer); // resize有尺寸改变完后会有一定的延迟,会导致echarts提前渲染,依然是按照原来的尺寸渲染 timer = setTimeout(function () { devChartPanelMap && devChartPanelMap.resize(); leftChart1 && leftChart1.resize(); leftChart2 && leftChart2.resize(); leftChart3.resize(); rightChart1.resize(); rightChart2.resize(); rightChart3.resize(); bottomChart.resize(); }, 300); } $scope.showData = { devBusy: 0, devOnline: 0, devOffline: 0, unregistered: 0 }; $interval(function () { setMapTime(); }, 1000); function setMapTime() { $scope.showData.date = moment().format("YYYY年MM月DD日") $scope.showData.week = moment().format('dddd') $scope.showData.time = moment().format("HH:mm:ss") } setMapTime(); var timer = null; //resize事件绑定 $(window).off("resize.devChart").on("resize.devChart", function () { resizeChart(); }); var colorStopList = { blue: ['#57FFE0', '#3469E2'], red: ['#FF7671', '#A14AFF'], yellow: ['#FFEA4F', '#F89212'], green: ['#61E0B1', '#00B25E'], gray: ['#fff', '#cfcfcf'], purple: ['#D3B5DF', '#8865A5'], } function getColorStop(key) { return { colorStops: [{ offset: 0, color: colorStopList[key][0] // 0% 处的颜色 }, { offset: 1, color: colorStopList[key][1] // 100% 处的颜色 }] } } $scope.optionConfig = { //坐标系的公共风格 getLineOptionsForMap: function (xData, textBreak) { var option = { tooltip: { trigger: 'axis', axisPointer: { lineStyle: { color: '#108EE9', width: .3, } } }, grid: { x: 52, x2: 15, y: 30, y2: textBreak ? 32 : 28,// 自动换行的X坐标需要展示2行,空间要多一点 }, xAxis: [ { type: 'category', axisLabel: { textStyle: { color: "#fff", fontSize: "12px" } }, axisTick: { show: false, }, axisLine: { show: false, }, data: xData.map(function (str) { if (textBreak) { return autoBreak(str) } else { return str } }) } ], yAxis: [ { type: 'value', axisLabel: { textStyle: { color: "#fff", fontSize: "12px" } }, axisTick: { show: false, }, axisLine: { show: false, }, splitLine: { show: false, }, }, ], series: [] }; return option; }, // pie的公共item风格 getPieItemStyle: function () { var opt = { normal: { label: { show: true, position: 'outside', color: '#ddd', formatter: function (params) { return params.name + ':' + params.value; }, rich: { white: { color: '#ddd', align: 'center', padding: [3, 0] } } }, labelLine: { length: 8, length2: 12, show: true, } } }; return opt }, // 设置公告的 pictorialBar风格 setPictorialBarStyle: function (option, yData, hasPercent) { var myColor = this.getColorList(); if (!hasPercent) { myColor.reverse(); } option.tooltip.formatter = function (params) { var param = params[0]; var name = param.name.replace(/[\n]/g, '');//去掉换行符号 var span = ''; if (hasPercent) { return span + name + ":占比" + param.value + "%" } else { return span + name + ":" + param.value } }; option.series.push({ name: '消费次数', type: 'pictorialBar', barCategoryGap: '20%', // 可以在线测试 symbol,http://www.runoob.com/try/try.php?filename=trysvg_path2 // 稍尖:M200 200 c20 -10,80 -40, 85 -180c5 140,65 170,85 180l-170 0Z // 很尖:M200 200 c50 -10,80 -40, 85 -180c5 140,35 170,85 180l-170 0Z symbol: 'path://M200 200 c20 -10,80 -40, 85 -180c5 140,65 170,85 180l-170 0Z', label: { normal: { show: true, position: 'top', formatter: function (param) { if (hasPercent) { return param.value + "%" } else { return param.value } }, textStyle: { fontSize: '12', color: '#fff' } } }, itemStyle: { normal: { color: function (params) { var num = myColor.length; return myColor[params.dataIndex % num] }, } }, data: yData, }); }, // 颜色列表 getColorList: function () { var myColor = ['#f845f1', '#ad46f3', '#5045f6', '#4777f5', '#44aff0', '#45dbf7', '#f6d54a', '#f69846', '#ff4343']; return myColor; }, // 由于早期的消费可能没有数据,需要给源数据,需要补齐 0,否则折现的时间轴和数据对应错误; fixData: function (list) { // 找到所有key var allConsumKey = {}; for (var index in list) { var item = list[index]; var list2 = item.items; for (var index2 in list2) { var item2 = list2[index2]; var name = item2.name; allConsumKey[name] = name; } } //修复该条数据。 这里只是多循环一次,性能问题不大。 for (var index in list) { var item = list[index]; if (!item.items) { item.items = [] } var list2 = item.items; for (var key in allConsumKey) { var hasKeyFlag = false; for (var index2 in list2) { var item2 = list2[index2]; var name = item2.name; // 如果有 key的消费数据,则找下一个key if (key === name) { hasKeyFlag = true; break; } } // 如果list2循环结束后,找不到key,则补全数据; if (!hasKeyFlag) { item.items.push({ name: key, value: 0, }); } } } }, // 大地图 getStaticMapOption: function (chartEntity, dataIn) { var data = dataIn.dataList; var mapName = 'china' var geoCoordMap = {}; /*获取地图数据*/ chartEntity.showLoading(); var mapFeatures = echarts.getMap(mapName).geoJson.features; chartEntity.hideLoading(); mapFeatures.forEach(function (v) { // 地区名称 var name = v.properties.name; // 地区经纬度 geoCoordMap[name] = v.properties.cp; }); var tempSort = data.sort(function (a, b) { return b.value - a.value; }) var tempSortLen = tempSort.length; // 值的范围 var max, min; if (tempSortLen > 0) { // 值的范围 max = tempSort[0].value min = tempSort[tempSortLen - 1].value; } // 地图气泡的大小范围 var maxSize4Pin = 72, minSize4Pin = 30; var convertData = function (data) { var res = []; for (var i = 0; i < data.length; i++) { var geoCoord = geoCoordMap[data[i].name]; if (geoCoord) { res.push({ name: data[i].name, value: geoCoord.concat(data[i].value), }); } } return res; }; var markData = convertData(data); // 底点的最大尺寸 var pointMaxSize = 25; var option = { visualMap: { show: false, min: 0, max: 500, left: 'left', top: 'bottom', text: ['高', '低'], // 文本,默认为数值文本 calculable: true, seriesIndex: [1], inRange: { color: ['#044161', '#2B91B7'] } }, geo: { show: true, map: mapName, label: { normal: { show: false }, emphasis: { show: false, } }, roam: true, itemStyle: { normal: { areaColor: '#031525', borderColor: '#3B5077', }, emphasis: { areaColor: '#2B91B7', } } }, series: [{ name: '散点',// 蓝色的小点 type: 'scatter', coordinateSystem: 'geo', data: markData, symbolSize: function (val) { var size = val[2] / 10 if (size > pointMaxSize) { size = pointMaxSize } return size; }, label: { normal: { formatter: '{b}', position: 'right', show: true }, emphasis: { show: true } }, itemStyle: { normal: { color: '#05C3F9' } } }, { type: 'map', map: mapName, geoIndex: 0, aspectScale: 0.75, //长宽比 showLegendSymbol: false, // 存在legend时显示 label: { normal: { show: true }, emphasis: { show: false, textStyle: { color: '#fff' } } }, roam: true, itemStyle: { normal: { areaColor: '#031525', borderColor: '#3B5077', }, emphasis: { areaColor: '#2B91B7' } }, animation: false, data: data }, { // 小点上的气泡 name: '气泡', type: 'scatter', coordinateSystem: 'geo', symbol: 'pin', //气泡 symbolSize: function (val) { var a = val[2] / (max - min);//当前值和最大值的比例 var size = minSize4Pin + (maxSize4Pin - minSize4Pin) * a;// 实际大小 if (size > maxSize4Pin) { size = maxSize4Pin } return size; }, label: { normal: { show: true, formatter: function (params) { var size = params.value[2] return size }, textStyle: { color: '#fff', fontSize: 9, } } }, itemStyle: { normal: { color: '#108EE9', //标志颜色 } }, zlevel: 6, data: markData, }, { // 排名前5 底部的小点变成黄色 漪涟 name: 'Top 5', type: 'effectScatter', coordinateSystem: 'geo', data: convertData(data.sort(function (a, b) { return b.value - a.value; }).slice(0, 5)), symbolSize: function (val) { var size = val[2] / 10 if (size > pointMaxSize) { size = pointMaxSize } return size; }, showEffectOn: 'render', rippleEffect: { brushType: 'stroke' }, hoverAnimation: true, label: { normal: { formatter: '{b}', position: 'right', show: true } }, itemStyle: { normal: { color: 'yellow', shadowBlur: 10, shadowColor: 'yellow' } }, zlevel: 1 }, ] }; return option }, // 经销商营收TOP getDealerIncomeTopChartOption: function (data) { var xData = []; var yData = []; for (var i = 0; i < data.length; i++) { var item = data[i]; xData.push(item.name); yData.push(item.payIncomeTotal); } var option = this.getLineOptionsForMap(xData, true); this.setPictorialBarStyle(option, yData); return option }, // 全局营收趋势 getIncomeLineOption: function (data) { var payIncomeCount = 0; var lineCoinsCount = 0; var xData = []; var yData = {payIncome: [], lineCoins: []}; for (var i = 0; i < data.length; i++) { var item = data[data.length - i - 1]; xData.push(item.dateStr); yData.payIncome.push(item.payIncome); yData.lineCoins.push(item.lineCoins); payIncomeCount = payIncomeCount + item.payIncome; lineCoinsCount = lineCoinsCount + item.lineCoins; } var option = this.getLineOptionsForMap(xData); option.series = [ { name: '在线收入', stack: '收入', type: 'bar', itemStyle: { color: "rgba(16,142,233,.9)", }, data: yData.payIncome }, { name: '线下投币', stack: '收入', type: 'bar', itemStyle: { color: "rgba(16,142,233,.6)", }, data: yData.lineCoins } ]; return option; }, // 设备类型分布Top getDeviceTypeStatisticsOption: function (data) { var xData = []; var yData = []; for (var i = 0; i < data.length; i++) { var item = data[i]; xData.push(item.name); yData.push(item.value); } var option = this.getLineOptionsForMap(xData, true); this.setPictorialBarStyle(option, yData); return option }, getUserPieOption: function (payload) { }, // 全网用户 getAllUserOption: function (list) { //补齐数据结构 this.fixData(list); var xData = []; var consumeSeriesMap = {}; var length = list.length; for (var index in list) { // 正序时间排列 var item = list[length - 1 - index]; xData.push(item.dateStr); var list2 = item.items; for (var index2 in list2) { var item2 = list2[index2]; var name = item2.name; var value = item2.value; if (!consumeSeriesMap[name]) { consumeSeriesMap[name] = []; } consumeSeriesMap[name].push(value); } } var myColor = this.getColorList(); var option = this.getLineOptionsForMap(xData); var index = 0; for (var key in consumeSeriesMap) { var dataList = consumeSeriesMap[key]; option.series.push({ name: key, type: 'line', itemStyle: { normal: { color: myColor[index * 2],//相邻的颜色太相似了,所以间隔一个,越界不会异常,会使用默认色 } }, data: dataList }); index++; } return option; }, // 设备注册趋势 getDeviceRegOption: function (data) { var xData = []; var yData = {regList: [] }; for (var i = 0; i < data.length; i++) { var item = data[data.length - i - 1]; xData.push(item.dateStr); yData.regList.push(item.reg); } var option = this.getLineOptionsForMap(xData); option.series = [ { name: '注册设备', stack: '设备', type: 'bar', itemStyle: { color: "rgba(16,142,233,.9)", }, data: yData.regList } ]; return option; }, // 用户反馈全网趋势 getFeedbackOption: function (data) { var xData = []; var yData = {refund: [], upper: [], fault: []}; for (var i = 0; i < data.length; i++) { var item = data[data.length - i - 1]; xData.push(item.dateStr); yData.refund.push(item.refund); yData.upper.push(item.upper); yData.fault.push(item.fault); } var option = this.getLineOptionsForMap(xData); option.series = [ { name: '退币', stack: '用户反馈', type: 'bar', itemStyle: { color: colorStopList.yellow[0], }, data: yData.refund }, { name: '上分', stack: '用户反馈', type: 'bar', itemStyle: { color: colorStopList.green[0], }, data: yData.upper }, { name: '报障', stack: '用户反馈', type: 'bar', itemStyle: { color: colorStopList.red[0], }, data: yData.fault } ]; return option; }, // 提现趋势 getWithdrawOption:function (list) { var xData = []; var seriesMap = {}; var length = list.length; for (var index in list) { // 正序时间排列 var item = list[length - 1 - index]; xData.push(item.dateStr); var list2 = item.items; for (var index2 in list2) { var item2 = list2[index2]; var name = item2.name; var value = item2.value; if (!seriesMap[name]) { seriesMap[name] = []; } seriesMap[name].push(value); } } var myColor = this.getColorList(); var option = this.getLineOptionsForMap(xData); var index = 0; for (var key in seriesMap) { var dataList = seriesMap[key]; option.series.push({ name: key, type: 'line', itemStyle: { normal: { color: myColor[index * 2],//相邻的颜色太相似了,所以间隔一个,越界不会异常,会使用默认色 } }, data: dataList }); index++; } return option; } }; $scope.chartActive = { leftChart1: false, leftChart2: false, leftChart3: false, rightChart1: false, rightChart2: false, rightChart3: false, bottomChart: false, }; //按条件加载统计图 因为有的接口太慢,避免等待,直接并发 function loadChartData() { //获取数据:地图设备统计 $http.get('/superadmin/getDevMapChart', {}).then(function (data) { data = data.data var payload = data.payload if (payload) { //地图拓展api地址:https://github.com/ecomfe/echarts/tree/master/extension/bmap var option = $scope.optionConfig.getStaticMapOption(devChartPanelMap, payload); $scope.showData.devBusy = payload.busy; $scope.showData.devOnline = payload.online; $scope.showData.devOffline = payload.offline; $scope.showData.unregistered = payload.unregistered; devChartPanelMap.setOption(option); devChartPanelMap.resize();// 确保地图居中 } }).then(function () { $scope.chartActive.leftChart1 = true; $http.get('/superadmin/getDealerIncomeTotalTop', {}).then(function (data) { data = data.data if (data.payload) { var option = $scope.optionConfig.getDealerIncomeTopChartOption(data.payload.dataList); leftChart1.setOption(option); } }); }).then(function () { $scope.chartActive.leftChart2 = true; $http.get('/superadmin/getIncomeTrend', {}).then(function (data) { data = data.data if (data.payload.dataList) { var option = $scope.optionConfig.getIncomeLineOption(data.payload.dataList); leftChart2.setOption(option); } }); }).then(function () { $scope.chartActive.leftChart3 = true; $http.get('/superadmin/getDeviceTypeStatistics', {}).then(function (data) { data = data.data if (data.payload.dataList) { var option = $scope.optionConfig.getDeviceTypeStatisticsOption(data.payload.dataList); leftChart3.setOption(option); } }); }).then(function () { $scope.chartActive.rightChart1 = true; $http.get('/superadmin/getAllUserTrend', {}).then(function (data) { data = data.data if (data.payload) { var option = $scope.optionConfig.getAllUserOption(data.payload.dataList); rightChart1.setOption(option); } }); }).then(function () { $scope.chartActive.rightChart2 = true; $http.get('/superadmin/getDeviceRegTrend', {}).then(function (data) { data = data.data if (data.payload.dataList) { var option = $scope.optionConfig.getDeviceRegOption(data.payload.dataList); rightChart2.setOption(option); } }); }).then(function () { $scope.chartActive.rightChart3 = true; $http.get('/superadmin/getAllFeedbackTrend', {}).then(function (data) { data = data.data if (data.payload) { var option = $scope.optionConfig.getFeedbackOption(data.payload.dataList); rightChart3.setOption(option); } }); }).then(function () { $scope.chartActive.bottomChart = true; $http.get('/superadmin/getWithdrawTrend', {}).then(function (data) { data = data.data if (data.payload) { var option = $scope.optionConfig.getWithdrawOption(data.payload.dataList); bottomChart.setOption(option); } }); }) } // 全屏事件绑定 $scope.isFullscreen = false; $scope.$on('fullscreenchange', function (evt, data) { $timeout(function () { $scope.isFullscreen = screenfull.isFullscreen; resizeChart() }) }); }]);