@ -0,0 +1,239 @@ | |||
import React, { useEffect, useState } from 'react' | |||
import ReactECharts from 'echarts-for-react' | |||
import { ChartsHeader } from '../../components' | |||
import { useStateContext } from '../../contexts/ContextProvider' | |||
import { getDayScreenUseTime } from '../../service/getDayScreenUseTime/index' | |||
const Line2 = () => { | |||
const { currentColor, currentMode } = useStateContext() | |||
let [option, setOption] = useState({}) | |||
/* 生成本周日期, 格式为 MM-DD */ | |||
function convertDate(date) { | |||
// 格式化日期 | |||
var yyyy = date.getFullYear().toString() | |||
var mm = (date.getMonth() + 1).toString() | |||
var dd = (date.getDate() + 1).toString() | |||
var mmChars = mm.split('') | |||
var ddChars = dd.split('') | |||
return (mmChars[1] ? mm : '0' + mmChars[0]) + '-' + (ddChars[1] ? dd : '0' + ddChars[0]) | |||
} | |||
let currentWeekDates = new Array(7) | |||
for (let i = 0; i < 7; i++) { | |||
const curr = new Date() | |||
currentWeekDates[i] = convertDate(new Date(curr.setDate(curr.getDate() - curr.getDay() + i))) | |||
} | |||
let currentWeekFirstScreenTime = new Array(7) | |||
let bgColor = '#fff' | |||
let color = ['#0090FF', '#36CE9E', '#FFC005', '#FF515A', '#8B5CFF', '#00CA69'] | |||
let echartData = [ | |||
{ | |||
name: '周一', | |||
value1: 100 | |||
}, | |||
{ | |||
name: '周二', | |||
value1: 138 | |||
}, | |||
{ | |||
name: '周三', | |||
value1: 350 | |||
}, | |||
{ | |||
name: '周四', | |||
value1: 173 | |||
}, | |||
{ | |||
name: '周五', | |||
value1: 180 | |||
}, | |||
{ | |||
name: '周六', | |||
value1: 150 | |||
}, | |||
{ | |||
name: '周日', | |||
value1: 180 | |||
} | |||
] | |||
let xAxisData = echartData.map(v => v.name) | |||
for (let i = 0; i < 7; i++) { | |||
xAxisData[i] = xAxisData[i] + ' ' + currentWeekDates[i] | |||
} | |||
const hexToRgba = (hex, opacity) => { | |||
let rgbaColor = '' | |||
let reg = /^#[\da-f]{6}$/i | |||
if (reg.test(hex)) { | |||
rgbaColor = `rgba(${parseInt('0x' + hex.slice(1, 3))},${parseInt('0x' + hex.slice(3, 5))},${parseInt('0x' + hex.slice(5, 7))},${opacity})` | |||
} | |||
return rgbaColor | |||
} | |||
useEffect(async () => { | |||
for (let i = 0; i < 7; i++) { | |||
let receivedJson = await getDayScreenUseTime('2022-' + currentWeekDates[i]) | |||
let firstScreenTimeH = parseInt(receivedJson.data.lastScreenTime.substring(0, 2)) | |||
let firstScreenTimeM = parseInt(receivedJson.data.lastScreenTime.substring(3, 5)) | |||
let firstScreenTimeS = parseInt(receivedJson.data.lastScreenTime.substring(6, 8)) | |||
if (firstScreenTimeH !== NaN && firstScreenTimeM !== NaN && firstScreenTimeS !== NaN) { | |||
currentWeekFirstScreenTime[i] = 3600 * firstScreenTimeH + 60 * firstScreenTimeM + firstScreenTimeS | |||
} else { | |||
currentWeekFirstScreenTime[i] = NaN | |||
} | |||
} | |||
setOption({ | |||
backgroundColor: bgColor, | |||
color: color, | |||
legend: { | |||
right: 10, | |||
top: 10 | |||
}, | |||
tooltip: { | |||
trigger: 'axis', | |||
formatter: function (params) { | |||
let html = '' | |||
params.forEach(v => { | |||
if (!isNaN(v.value)) { | |||
html += `<div style="color: #666;font-size: 14px;line-height: 24px"> | |||
<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${color[v.componentIndex]};"></span> | |||
${v.seriesName} | |||
<span style="color:${color[v.componentIndex]};font-weight:700;font-size: 18px">${Math.floor(v.value / 3600) + '时' + Math.floor((v.value % 3600) / 60) + '分钟' + Math.floor(v.value % 60) + '秒'}</span> | |||
` | |||
} else { | |||
html = `<div style="color: #666;font-size: 14px;line-height: 24px"> | |||
<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${color[v.componentIndex]};"></span> | |||
${v.seriesName} | |||
<span style="color:${color[v.componentIndex]};font-weight:700;font-size: 18px">该天还尚未开始</span> | |||
` | |||
} | |||
}) | |||
return html | |||
}, | |||
extraCssText: 'background: #fff; border-radius: 0;box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);color: #333;', | |||
axisPointer: { | |||
type: 'shadow', | |||
shadowStyle: { | |||
color: '#ffffff', | |||
shadowColor: 'rgba(225,225,225,1)', | |||
shadowBlur: 5 | |||
} | |||
} | |||
}, | |||
grid: { | |||
top: 100, | |||
containLabel: true | |||
}, | |||
xAxis: [ | |||
{ | |||
type: 'category', | |||
boundaryGap: false, | |||
axisLabel: { | |||
formatter: '{value}', | |||
textStyle: { | |||
color: '#333' | |||
} | |||
}, | |||
axisLine: { | |||
lineStyle: { | |||
color: '#D9D9D9' | |||
} | |||
}, | |||
data: xAxisData.map(function (str) { | |||
return str.replace(' ', '\n') | |||
}) | |||
} | |||
], | |||
yAxis: [ | |||
{ | |||
type: 'value', | |||
// name: '单位:万千瓦时', | |||
axisLabel: { | |||
textStyle: { | |||
color: '#666' | |||
}, | |||
formatter: (value, index, format) => { | |||
return Math.floor(value / 3600) + '时' | |||
} | |||
}, | |||
max: 86400, | |||
nameTextStyle: { | |||
color: '#666', | |||
fontSize: 12, | |||
lineHeight: 40 | |||
}, | |||
splitLine: { | |||
lineStyle: { | |||
type: 'dashed', | |||
color: '#E9E9E9' | |||
} | |||
}, | |||
axisLine: { | |||
show: false | |||
}, | |||
axisTick: { | |||
show: false | |||
} | |||
} | |||
], | |||
series: [ | |||
{ | |||
name: '最后一次屏幕使用时刻', | |||
type: 'line', | |||
smooth: true, | |||
// showSymbol: false,/ | |||
symbolSize: 8, | |||
zlevel: 3, | |||
lineStyle: { | |||
normal: { | |||
color: color[0], | |||
shadowBlur: 3, | |||
shadowColor: hexToRgba(color[0], 0.5), | |||
shadowOffsetY: 8 | |||
} | |||
}, | |||
areaStyle: { | |||
// normal: { | |||
// color: new echarts.graphic.LinearGradient( | |||
// 0, | |||
// 0, | |||
// 0, | |||
// 1, | |||
// [ | |||
// { | |||
// offset: 0, | |||
// color: hexToRgba(color[0], 0.3) | |||
// }, | |||
// { | |||
// offset: 1, | |||
// color: hexToRgba(color[0], 0.1) | |||
// } | |||
// ], | |||
// false | |||
// ), | |||
shadowColor: hexToRgba(color[0], 0.1), | |||
shadowBlur: 10 | |||
}, | |||
data: currentWeekFirstScreenTime | |||
} | |||
] | |||
}) | |||
}, []) | |||
return ( | |||
<div className="m-4 md:m-10 mt-24 p-10 bg-white dark:bg-secondary-dark-bg rounded-3xl"> | |||
<ChartsHeader category="Line" title="最后屏幕使用时刻" /> | |||
<div className="w-full"> | |||
<ReactECharts option={option} style={{ height: '600px' }}></ReactECharts> | |||
</div> | |||
</div> | |||
) | |||
} | |||
export default Line2 |
@ -0,0 +1,199 @@ | |||
import React, { useState, useEffect } from 'react' | |||
import ReactECharts from 'echarts-for-react' | |||
import ColorMaker from '../service/colorGenerator' | |||
import { getSeconds } from '../service/getSeconds' | |||
import { getAppId } from '../service/getAppId' | |||
import { getDayScreenUse } from '../service/getDayScreenUse' | |||
const Gannt = () => { | |||
let [option, setOption] = useState({}) | |||
useEffect(async () => { | |||
let categories = ['使用记录'] | |||
var startTime = +new Date() | |||
function renderItem(params, api) { | |||
var categoryIndex = api.value(0) | |||
var start = api.coord([api.value(1), categoryIndex]) | |||
var end = api.coord([api.value(2), categoryIndex]) | |||
var height = api.size([0, 1])[1] * 0.6 | |||
var rectShape = echarts.graphic.clipRectByRect( | |||
{ | |||
x: start[0], | |||
y: start[1] - height / 2, | |||
width: end[0] - start[0], | |||
height: height | |||
}, | |||
{ | |||
x: params.coordSys.x, | |||
y: params.coordSys.y, | |||
width: params.coordSys.width, | |||
height: params.coordSys.height | |||
} | |||
) | |||
return ( | |||
rectShape && { | |||
type: 'rect', | |||
shape: rectShape, | |||
style: api.style() | |||
} | |||
) | |||
} | |||
let appIdArray = await getAppId('1') | |||
let types = new Array(appIdArray.data.length) | |||
for (let i = 0; i < appIdArray.data.length; i++) { | |||
types[i] = { | |||
appId: i + 1, | |||
name: appIdArray.data[i].text, | |||
color: '#' + ColorMaker.GetColor(i).Color | |||
} | |||
} | |||
for (let i = 0; i < appIdArray.length; i++) { | |||
types.push({ | |||
appId: i + 1, | |||
name: appIdArray[i].text, | |||
color: '#' + ColorMaker.GetColor(i).Color | |||
}) | |||
} | |||
let dateStr = '2022-09-07' | |||
let useRecord = await getDayScreenUse(dateStr) | |||
const date = new Date(dateStr) | |||
const timestampInMs = date.getTime() | |||
// 👇️ timestamp in seconds (Unix timestamp) | |||
const timestampInSeconds = Math.floor(date.getTime()) | |||
let data = new Array(useRecord.data.length) | |||
for (let i = 0; i < useRecord.data.length; i++) { | |||
data[i] = { | |||
name: useRecord.data[i]['activity'], | |||
value: new Array(getSeconds(useRecord.data[i]['timeStart']) * 1000, timestampInSeconds + getSeconds(useRecord.data[i]['timeStart']) * 1000, getSeconds(useRecord.data[i]['timeEnd']) * 1000 + timestampInSeconds, getSeconds(useRecord.data[i]['timeEnd']) * 1000), | |||
itemStyle: { | |||
normal: { | |||
color: types[useRecord.data[i]['appId'] - 1]['color'] | |||
} | |||
} | |||
} | |||
} | |||
setOption({ | |||
tooltip: { | |||
formatter: function (params) { | |||
let seconds = params.value[3] / 1000 | |||
let duration = (params.value[3] - params.value[0]) / 1000 | |||
return ( | |||
params.marker + | |||
params.name + | |||
': ' + | |||
Math.floor(seconds / 3600) + | |||
'时' + | |||
Math.floor((seconds % 3600) / 60) + | |||
'分' + | |||
Math.floor(seconds % 60) + | |||
'秒' + | |||
'<br/>' + | |||
'持续时间: ' + | |||
Math.floor(duration / 3600) + | |||
'时' + | |||
Math.floor((duration % 3600) / 60) + | |||
'分' + | |||
Math.floor(duration % 60) + | |||
'秒' | |||
) | |||
} | |||
}, | |||
dataZoom: [ | |||
{ | |||
type: 'slider', | |||
filterMode: 'weakFilter', | |||
showDataShadow: false, | |||
top: 550, | |||
height: 10, | |||
borderColor: 'transparent', | |||
backgroundColor: '#e2e2e2', | |||
handleIcon: 'M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line | |||
handleSize: 20, | |||
handleStyle: { | |||
shadowBlur: 6, | |||
shadowOffsetX: 1, | |||
shadowOffsetY: 2, | |||
shadowColor: '#aaa' | |||
}, | |||
labelFormatter: '' | |||
}, | |||
{ | |||
type: 'inside', | |||
filterMode: 'weakFilter' | |||
} | |||
], | |||
grid: { | |||
height: 450 | |||
}, | |||
xAxis: { | |||
min: startTime, | |||
scale: true, | |||
axisLabel: { | |||
formatter: function (val) { | |||
let seconds = (val - startTime) / 1000 + 10620 | |||
return Math.floor(seconds / 3600) + '时' + Math.floor(seconds % 3600 / 60) + '分' + Math.floor(seconds % 60) + '秒' | |||
} | |||
} | |||
}, | |||
yAxis: { | |||
data: categories | |||
}, | |||
series: [ | |||
{ | |||
type: 'custom', | |||
renderItem: function (params, api) { | |||
var categoryIndex = api.value(0) | |||
var start = api.coord([api.value(1), categoryIndex]) | |||
var end = api.coord([api.value(2), categoryIndex]) | |||
var height = api.size([0, 1])[1] * 0.6 | |||
var rectShape = echarts.graphic.clipRectByRect( | |||
{ | |||
x: start[0], | |||
y: start[1] - height / 2, | |||
width: end[0] - start[0], | |||
height: height | |||
}, | |||
{ | |||
x: params.coordSys.x, | |||
y: params.coordSys.y, | |||
width: params.coordSys.width, | |||
height: params.coordSys.height | |||
} | |||
) | |||
return ( | |||
rectShape && { | |||
type: 'rect', | |||
shape: rectShape, | |||
style: api.style() | |||
} | |||
) | |||
}, | |||
itemStyle: { | |||
normal: { | |||
opacity: 0.8 | |||
} | |||
}, | |||
encode: { | |||
x: [1, 2], | |||
y: 0 | |||
}, | |||
data: data | |||
} | |||
] | |||
}) | |||
}, []) | |||
return <ReactECharts option={option} style={{ height: '800px', width: '1200px' }}></ReactECharts> | |||
} | |||
export default Gannt |
@ -0,0 +1,120 @@ | |||
function MyColor() { | |||
this.TextColor = 'FFFFFF'; | |||
this.Color = 'FF0000'; | |||
} | |||
var ColorMaker = { | |||
// 最大支持颜色种类数 | |||
TotalColors: 300, | |||
// 获取颜色 seed:颜色种子(任意int) | |||
GetColor: (seed = 0) => { | |||
var ret = new MyColor(); | |||
// 计算对应下标 | |||
var idx = seed % ColorMaker.TotalColors; | |||
// 计算颜色 | |||
var colorVal = ColorMaker._CalColor(idx); | |||
// 转成RGB 16进制字符串 | |||
ret.Color = colorVal.toString(16).padStart(6, '0'); | |||
// 计算互补色 | |||
ret.TextColor = ColorMaker._CalTextColor(ret.Color); | |||
return ret; | |||
}, | |||
_CalColor: (idx = 0) => { | |||
// 默认返回红色 | |||
var ret = 0xFF0000; | |||
// RGB的最大值 | |||
var full = 0xFFFFFF; | |||
// 总共需要支持多少种颜色,若传0则取255 | |||
var total = ColorMaker.TotalColors > 0 ? ColorMaker.TotalColors : 0xFF; | |||
// 将所有颜色平均分成x份 | |||
var perVal = full / total; | |||
if (idx >= 0 && idx <= total) { | |||
ret = perVal * idx; | |||
} | |||
ret = Math.round(ret); | |||
return ret; | |||
}, | |||
// 计算传入颜色的互补色 | |||
_CalTextColor: (input = '') => { | |||
var R = input.substr(0, 2); | |||
var G = input.substr(2, 2); | |||
var B = input.substr(4, 2); | |||
var rVal = parseInt(R, 16); | |||
var gVal = parseInt(G, 16); | |||
var bVal = parseInt(B, 16); | |||
var hsl = rgbToHsl(rVal, gVal, bVal); | |||
hsl.L = (hsl.L + 0.5) % 1.0; | |||
var rgb = hslToRgb(hsl.H, hsl.S, hsl.L); | |||
var ret = (rgb.R << 16) + (rgb.G << 8) + rgb.B; | |||
return ret.toString(16).padStart(6, '0'); | |||
} | |||
}; | |||
/** | |||
* RGB 颜色值转换为 HSL. | |||
* 转换公式参考自 http://en.wikipedia.org/wiki/HSL_color_space. | |||
* r, g, 和 b 需要在 [0, 255] 范围内 | |||
* 返回的 h, s, 和 l 在 [0, 1] 之间 | |||
* | |||
* @param Number r 红色色值 | |||
* @param Number g 绿色色值 | |||
* @param Number b 蓝色色值 | |||
* @return Array HSL各值数组 | |||
*/ | |||
function rgbToHsl(r, g, b) { | |||
r /= 255, g /= 255, b /= 255; | |||
var max = Math.max(r, g, b), min = Math.min(r, g, b); | |||
var h, s, l = (max + min) / 2; | |||
if (max == min) { | |||
h = s = 0; // achromatic | |||
} else { | |||
var d = max - min; | |||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min); | |||
switch (max) { | |||
case r: h = (g - b) / d + (g < b ? 6 : 0); break; | |||
case g: h = (b - r) / d + 2; break; | |||
case b: h = (r - g) / d + 4; break; | |||
} | |||
h /= 6; | |||
} | |||
return { H: h, S: s, L: l }; | |||
} | |||
/** | |||
* HSL颜色值转换为RGB. | |||
* 换算公式改编自 http://en.wikipedia.org/wiki/HSL_color_space. | |||
* h, s, 和 l 设定在 [0, 1] 之间 | |||
* 返回的 r, g, 和 b 在 [0, 255]之间 | |||
* | |||
* @param Number h 色相 | |||
* @param Number s 饱和度 | |||
* @param Number l 亮度 | |||
* @return Array RGB色值数值 | |||
*/ | |||
function hslToRgb(h, s, l) { | |||
var r, g, b; | |||
if (s == 0) { | |||
r = g = b = l; // achromatic | |||
} else { | |||
var hue2rgb = function hue2rgb(p, q, t) { | |||
if (t < 0) t += 1; | |||
if (t > 1) t -= 1; | |||
if (t < 1 / 6) return p + (q - p) * 6 * t; | |||
if (t < 1 / 2) return q; | |||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; | |||
return p; | |||
} | |||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |||
var p = 2 * l - q; | |||
r = hue2rgb(p, q, h + 1 / 3); | |||
g = hue2rgb(p, q, h); | |||
b = hue2rgb(p, q, h - 1 / 3); | |||
} | |||
return { R: Math.round(r * 255), G: Math.round(g * 255), B: Math.round(b * 255) }; | |||
} | |||
export default ColorMaker; |
@ -0,0 +1,8 @@ | |||
import { post } from './index' | |||
export const getAppId = (appId) => { | |||
return post('/getAppId', { | |||
appId | |||
}) | |||
} |
@ -0,0 +1,7 @@ | |||
import { post } from '../index' | |||
export const getDayScreenUse = (datetimeStr) => { | |||
return post('/getDayScreenUse', { | |||
datetimeStr | |||
}) | |||
} |
@ -0,0 +1,7 @@ | |||
export const getSeconds = (str) => { | |||
let hours = parseInt(str.substring(0, 2)) | |||
let minutes = parseInt(str.substring(3, 5)) | |||
let seconds = parseInt(str.substring(6, 8)) | |||
return hours * 3600 + minutes * 60 + seconds | |||
} |