diff --git a/lazy-timer-be/app.py b/lazy-timer-be/app.py index 35151ae..41da69a 100644 --- a/lazy-timer-be/app.py +++ b/lazy-timer-be/app.py @@ -2,7 +2,7 @@ import os import sys import json -from datetime import datetime # 处理时间 +from datetime import datetime, timedelta # 处理时间 from flask import Flask, request from flask_cors import CORS, cross_origin from flask_sqlalchemy import SQLAlchemy @@ -21,6 +21,7 @@ db = SQLAlchemy(app) # 数据库模型 class App(db.Model): + __tablename__ = 'app' id = db.Column(db.Integer, primary_key=True) # App id process = db.Column(db.Text) # appName text = db.Column(db.Text) # app窗口名称 @@ -28,17 +29,20 @@ class App(db.Model): class Period(db.Model): + __tablename__ = 'period' timeStart = db.Column(db.DateTime, primary_key=True) # 开始时间 timeEnd = db.Column(db.DateTime) # 结束时间 winId = db.Column(db.Integer) # 活动id class Tag(db.Model): + __tablename__ = 'tag' id = db.Column(db.Integer, primary_key=True) # 标签id text = db.Column(db.Text) # 标签文本 class Win(db.Model): + __tablename__ = 'win' id = db.Column(db.Integer, primary_key=True) # 活动id appId = db.Column(db.Integer) # App id text = db.Column(db.Text, nullable=True) # 活动窗口名称 @@ -52,15 +56,17 @@ class Win(db.Model): def getDayScreenUseTime(): datetimeStr1 = json.loads(request.data) datetimeStr = datetimeStr1['datetimeStr'] - struct_time = datetime.strptime(datetimeStr, "%Y-%m-%d").date() # 获取请求日期, struct_time数据类型为dateTime + struct_time = datetime.strptime(datetimeStr, "%Y-%m-%d").date() # 获取请求日期, struct_time数据类型为dateTime activityTimeList = Period.query.all() - selectedTimeList = [] # 选中的对应今天日期的时间 + selectedTimeList = [] # 选中的对应今天日期的时间 for time in activityTimeList: if time.timeStart.date() == struct_time and time.timeEnd.date() == struct_time: selectedTimeList.append(time) if (len(selectedTimeList) == 0): - sendJson = {'getDayScreenUseTimeH': 0, 'getDayScreenUseTimeM': 0, 'getDayScreenUseTimeS': 0, 'firstScreenTime': '', 'lastScreenTime': '', 'getScreenTimeSpanH': 0, 'getScreenTimeSpanM':0, 'getScreenTimeSpanS': 0 } + sendJson = {'getDayScreenUseTimeH': 0, 'getDayScreenUseTimeM': 0, 'getDayScreenUseTimeS': 0, + 'firstScreenTime': '', 'lastScreenTime': '', 'getScreenTimeSpanH': 0, 'getScreenTimeSpanM': 0, + 'getScreenTimeSpanS': 0} else: timeAmount = 0 for time in selectedTimeList: @@ -69,20 +75,59 @@ def getDayScreenUseTime(): timeAmount = timeAmount + time_diff.total_seconds() timeAmount = int(timeAmount) - getDayScreenUseTimeM, getDayScreenUseTimeS = divmod(timeAmount, 60) # 每日屏幕使用时长, 分时秒 + getDayScreenUseTimeM, getDayScreenUseTimeS = divmod(timeAmount, 60) # 每日屏幕使用时长, 分时秒 getDayScreenUseTimeH, getDayScreenUseTimeM = divmod(getDayScreenUseTimeM, 60) - firstScreenTime = selectedTimeList[0].timeStart.strftime('%H时%M分%S秒') # 第一次屏幕使用时刻, - lastScreenTime = selectedTimeList[-1].timeEnd.strftime('%H时%M分%S秒') # 最后一次屏幕使用时刻 - screenTimeSpan = int((selectedTimeList[-1].timeEnd - selectedTimeList[0].timeStart).total_seconds()) # 持续时间 + firstScreenTime = selectedTimeList[0].timeStart.strftime('%H时%M分%S秒') # 第一次屏幕使用时刻, + lastScreenTime = selectedTimeList[-1].timeEnd.strftime('%H时%M分%S秒') # 最后一次屏幕使用时刻 + screenTimeSpan = int((selectedTimeList[-1].timeEnd - selectedTimeList[0].timeStart).total_seconds()) # 持续时间 getScreenTimeSpanM, getScreenTimeSpanS = divmod(screenTimeSpan, 60) # 每日屏幕使用时长, 分时秒 getScreenTimeSpanH, getScreenTimeSpanM = divmod(getScreenTimeSpanM, 60) - sendJson = {'getDayScreenUseTimeH': getDayScreenUseTimeH, 'getDayScreenUseTimeM': getDayScreenUseTimeM, 'getDayScreenUseTimeS': getDayScreenUseTimeS, 'firstScreenTime': firstScreenTime, 'lastScreenTime': lastScreenTime, 'getScreenTimeSpanH': getScreenTimeSpanH, 'getScreenTimeSpanM': getScreenTimeSpanM, 'getScreenTimeSpanS': getScreenTimeSpanS } + sendJson = {'getDayScreenUseTimeH': getDayScreenUseTimeH, 'getDayScreenUseTimeM': getDayScreenUseTimeM, + 'getDayScreenUseTimeS': getDayScreenUseTimeS, 'firstScreenTime': firstScreenTime, + 'lastScreenTime': lastScreenTime, 'getScreenTimeSpanH': getScreenTimeSpanH, + 'getScreenTimeSpanM': getScreenTimeSpanM, 'getScreenTimeSpanS': getScreenTimeSpanS} return json.dumps(sendJson, indent=4) -@app.route('/getDayScreenUseTime', methods=['POST']) + +@app.route('/getDayScreenUse', methods=['POST']) @cross_origin() -def getDayScreenUseTime(): +def getDayScreenUse(): datetimeStr1 = json.loads(request.data) datetimeStr = datetimeStr1['datetimeStr'] - struct_time = datetime.strptime(datetimeStr, "%Y-%m-%d").date() # 获取请求日期, struct_time数据类型为dateTime - return "1" \ No newline at end of file + struct_time = datetime.strptime(datetimeStr, "%Y-%m-%d").date() # dateTime数据类型 + struct_time_tomorrow = struct_time + timedelta(days=1) + # 连接多张表 + joinTables = Period.query.filter(Period.timeStart > struct_time).filter( + Period.timeStart < struct_time_tomorrow).filter(Period.timeEnd > struct_time).filter( + Period.timeEnd < struct_time_tomorrow).all() + + resultTables = [] + for i in joinTables: + sampleDict = {} + sampleDict['timeStart'] = i.timeStart.strftime('%H:%M:%S') + sampleDict['timeEnd'] = i.timeEnd.strftime('%H:%M:%S') + sampleDict['totolSeconds'] = (i.timeEnd - i.timeStart).seconds + queryWin = Win.query.filter(Win.id == i.winId).first() + sampleDict['activity'] = queryWin.text + sampleDict['appId'] = queryWin.appId + queryApp = App.query.filter(queryWin.appId == App.id).first() + sampleDict['appName'] = queryApp.text + queryTag = Tag.query.filter(queryApp.tagId == Tag.id).first() + sampleDict['tag'] = queryTag.text + resultTables.append(sampleDict) + + returnJson = json.dumps(resultTables) + return returnJson + + +@app.route('/getAppId', methods=['POST']) +@cross_origin() +def getAppId(): + queryAppId = App.query.all() + resultTables = [] + for i in queryAppId: + sampleDict = {} + sampleDict['id'] = i.id + sampleDict['text'] = i.text + resultTables.append(sampleDict) + return json.dumps(resultTables) diff --git a/lazy-timer-fe/package-lock.json b/lazy-timer-fe/package-lock.json index c7a547b..e48e0e1 100644 --- a/lazy-timer-fe/package-lock.json +++ b/lazy-timer-fe/package-lock.json @@ -19,6 +19,7 @@ "@syncfusion/ej2-react-richtexteditor": "^19.4.50", "@syncfusion/ej2-react-schedule": "^19.4.50", "axios": "^0.27.2", + "echarts": "^5.3.3", "echarts-for-react": "^3.0.2", "moment": "^2.29.4", "react": "^17.0.2", @@ -6497,7 +6498,6 @@ "version": "5.3.3", "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.3.3.tgz", "integrity": "sha512-BRw2serInRwO5SIwRviZ6Xgm5Lb7irgz+sLiFMmy/HOaf4SQ+7oYqxKzRHAKp4xHQ05AuHw1xvoQWJjDQq/FGw==", - "peer": true, "dependencies": { "tslib": "2.3.0", "zrender": "5.3.2" @@ -6519,8 +6519,7 @@ "node_modules/echarts/node_modules/tslib": { "version": "2.3.0", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "peer": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, "node_modules/ee-first": { "version": "1.1.1", @@ -16038,7 +16037,6 @@ "version": "5.3.2", "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.3.2.tgz", "integrity": "sha512-8IiYdfwHj2rx0UeIGZGGU4WEVSDEdeVCaIg/fomejg1Xu6OifAL1GVzIPHg2D+MyUkbNgPWji90t0a8IDk+39w==", - "peer": true, "dependencies": { "tslib": "2.3.0" } @@ -16046,8 +16044,7 @@ "node_modules/zrender/node_modules/tslib": { "version": "2.3.0", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "peer": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } }, "dependencies": { @@ -21038,7 +21035,6 @@ "version": "5.3.3", "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.3.3.tgz", "integrity": "sha512-BRw2serInRwO5SIwRviZ6Xgm5Lb7irgz+sLiFMmy/HOaf4SQ+7oYqxKzRHAKp4xHQ05AuHw1xvoQWJjDQq/FGw==", - "peer": true, "requires": { "tslib": "2.3.0", "zrender": "5.3.2" @@ -21047,8 +21043,7 @@ "tslib": { "version": "2.3.0", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "peer": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } }, @@ -28330,7 +28325,6 @@ "version": "5.3.2", "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.3.2.tgz", "integrity": "sha512-8IiYdfwHj2rx0UeIGZGGU4WEVSDEdeVCaIg/fomejg1Xu6OifAL1GVzIPHg2D+MyUkbNgPWji90t0a8IDk+39w==", - "peer": true, "requires": { "tslib": "2.3.0" }, @@ -28338,8 +28332,7 @@ "tslib": { "version": "2.3.0", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "peer": true + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } } } diff --git a/lazy-timer-fe/package.json b/lazy-timer-fe/package.json index 56454cf..8210d07 100644 --- a/lazy-timer-fe/package.json +++ b/lazy-timer-fe/package.json @@ -14,6 +14,7 @@ "@syncfusion/ej2-react-richtexteditor": "^19.4.50", "@syncfusion/ej2-react-schedule": "^19.4.50", "axios": "^0.27.2", + "echarts": "^5.3.3", "echarts-for-react": "^3.0.2", "moment": "^2.29.4", "react": "^17.0.2", diff --git a/lazy-timer-fe/public/index.html b/lazy-timer-fe/public/index.html index 50ad218..df9fde6 100644 --- a/lazy-timer-fe/public/index.html +++ b/lazy-timer-fe/public/index.html @@ -27,6 +27,7 @@ Learn how to configure a non-root public URL by running `npm run build`. --> 懒人记时 + diff --git a/lazy-timer-fe/src/App.js b/lazy-timer-fe/src/App.js index ba72b49..cc59b0f 100644 --- a/lazy-timer-fe/src/App.js +++ b/lazy-timer-fe/src/App.js @@ -5,6 +5,7 @@ import { TooltipComponent } from '@syncfusion/ej2-react-popups'; import { Navbar, Footer, Sidebar, ThemeSettings } from './components'; import { Ecommerce, Orders, Calendar, Employees, Stacked, Pyramid, Customers, Kanban, Line, Area, Bar, Pie, Financial, ColorPicker, ColorMapping, Editor } from './pages'; +import Line2 from './pages/Charts/Line2' import './App.css'; import { useStateContext } from './contexts/ContextProvider'; @@ -70,8 +71,10 @@ const App = () => { {/* pages */} } /> - } /> - } /> + } /> + } /> + } /> + } /> {/* apps */} } /> diff --git a/lazy-timer-fe/src/components/UserProfile.jsx b/lazy-timer-fe/src/components/UserProfile.jsx index 18b9602..a2d996d 100644 --- a/lazy-timer-fe/src/components/UserProfile.jsx +++ b/lazy-timer-fe/src/components/UserProfile.jsx @@ -12,7 +12,7 @@ const UserProfile = () => { return (
-

User Profile

+

用户信息

diff --git a/lazy-timer-fe/src/data/dummy.js b/lazy-timer-fe/src/data/dummy.js index 67cb64b..56f1487 100644 --- a/lazy-timer-fe/src/data/dummy.js +++ b/lazy-timer-fe/src/data/dummy.js @@ -1,12 +1,13 @@ import React from 'react' import { AiOutlineCalendar, AiOutlineShoppingCart, AiOutlineAreaChart, AiOutlineBarChart, AiOutlineStock } from 'react-icons/ai' import { FiShoppingBag, FiEdit, FiPieChart, FiBarChart, FiCreditCard, FiStar, FiShoppingCart } from 'react-icons/fi' -import { BsKanban, BsBarChart, BsCurrencyDollar, BsShield, BsChatLeft, BsFillLaptopFill } from 'react-icons/bs' +import { BsKanban, BsBarChart, BsCurrencyDollar, BsShield, BsChatLeft, BsFillLaptopFill, BsFileSpreadsheet, BsFillMoonFill, BsPhone } from 'react-icons/bs' import { FaSortAmountDown } from 'react-icons/fa' import { BiColorFill } from 'react-icons/bi' import { IoMdContacts } from 'react-icons/io' import { RiContactsLine, RiStockLine } from 'react-icons/ri' import { MdWbSunny, MdBedtime } from 'react-icons/md' +import { GrOverview } from 'react-icons/gr' import { HiOutlineRefresh } from 'react-icons/hi' import { TiTick } from 'react-icons/ti' import { GiLouvrePyramid } from 'react-icons/gi' @@ -23,6 +24,7 @@ import product5 from './product5.jpg' import product6 from './product6.jpg' import product7 from './product7.jpg' import product8 from './product8.jpg' +import { getDayScreenUse } from '../service/getDayScreenUse' export const gridOrderImage = props => (
@@ -87,7 +89,7 @@ const customerGridImage = props => ( const customerAppImage = props => { return (
- App + {/* App */}

{props.AppName}

@@ -400,98 +402,27 @@ export const links = [ { name: '总览', englishName: 'overview', - icon: + icon: }, { name: '表格', - englishName: 'customers', - icon: + englishName: 'sheets', + icon: }, { name: '屏幕使用时间', englishName: 'orders', - icon: + icon: }, { name: '第一次屏幕使用', - englishName: 'employees', - icon: + englishName: 'firstscreen', + icon: }, { name: '最后一次屏幕使用', - englishName: 'customers', - icon: - } - ] - }, - { - title: '标签', - links: [ - { - name: '学习', - englishName: 'calendar', - icon: - }, - { - name: '工作', - englishName: 'kanban', - icon: - }, - { - name: '娱乐', - englishName: 'editor', - icon: - }, - { - name: '生活', - englishName: 'color-picker', - icon: - } - ] - }, - { - title: '单项', - links: [ - { - name: 'line', - englishName: 'line', - icon: - }, - { - name: 'area', - englishName: 'area', - icon: - }, - - { - name: 'bar', - englishName: 'bar', - icon: - }, - { - name: 'pie', - englishName: 'pie', - icon: - }, - { - name: 'financial', - englishName: 'financial', - icon: - }, - { - name: 'color-mapping', - englishName: 'color-mapping', - icon: - }, - { - name: 'pyramid', - englishName: 'pyramid', - icon: - }, - { - name: 'stacked', - englishName: 'stacked', - icon: + englishName: 'lastscreen', + icon: } ] } @@ -772,22 +703,22 @@ export const themeColors = [ export const userProfileData = [ { icon: , - title: 'My Profile', - desc: 'Account Settings', + title: '我的信息', + desc: '账户设置', iconColor: '#03C9D7', iconBg: '#E5FAFB' }, { icon: , - title: 'My Inbox', - desc: 'Messages & Emails', + title: '我的消息', + desc: '接收到的消息或来信', iconColor: 'rgb(0, 194, 146)', iconBg: 'rgb(235, 250, 242)' }, { icon: , - title: 'My Tasks', - desc: 'To-do and Daily Tasks', + title: '我的任务', + desc: 'Todo和每日', iconColor: 'rgb(255, 244, 229)', iconBg: 'rgb(254, 201, 15)' } @@ -838,53 +769,37 @@ export const ordersGrid = [ } ] -export const customersData = [ - { - StartTime: '05:36:45', - EndTime: '05:37:51', - Duration: '00:01:06', - AppName: 'GoogleChrome', - AppImage: require('../images/icons/5l.png'), - Text: 'How to Convert Python Datetime to String', - Tag: '工作' - }, - { - StartTime: '05:37:51', - EndTime: '05:38:57', - Duration: '00:01:06', - AppName: 'PyCharm', - AppImage: require('../images/icons/5l.png'), - Text: '500 Internal Server Error', - Tag: '工作' - }, - { - StartTime: '05:38:57', - EndTime: '05:40:03', - Duration: '00:01:06', - AppName: 'GoogleChrome', - AppImage: require('../images/icons/5l.png'), - Text: 'Python- Fetch a value of SQLalchemy instrumentedattribute', - Tag: '工作' - }, - { - StartTime: '05:40:03', - EndTime: '05:41:28', - Duration: '00:01:25', - AppName: 'GoogleChrome', - AppImage: require('../images/icons/5l.png'), - Text: 'How to Convert Python Datetime to String', - Tag: '工作' - }, - { - StartTime: '05:36:45', - EndTime: '05:37:51', - Duration: '00:01:06', - AppName: 'GoogleChrome', - AppImage: require('../images/icons/79l.png'), - Text: 'How to Convert Python Datetime to String', - Tag: '工作' +const getSecondstoHH = (seconds) => { + let calculatedTime = new Date(null); + calculatedTime.setSeconds( seconds ); + let newTime = calculatedTime.toISOString().substr(11, 8); + return newTime +} + +const getcustomersGriData = async () => { + let DayScreenUse = await getDayScreenUse('2022-09-07') + + DayScreenUse = DayScreenUse.data + + + let customersData = new Array(DayScreenUse.length) + + for(let i = 0; i < customersData.length; i++) { + customersData[i] = { + StartTime: DayScreenUse[i]['timeStart'], + EndTime: DayScreenUse[i]['timeEnd'], + Duration: getSecondstoHH(DayScreenUse[i]['totolSeconds']), + AppName: DayScreenUse[i]['appName'], + Appimage: '', + Text: DayScreenUse[i]['activity'], + Tag: DayScreenUse[i]['tag'] + } } -] + + return customersData +} + +export const customersData = getcustomersGriData() export const employeesData = [ { diff --git a/lazy-timer-fe/src/pages/Charts/Bar.jsx b/lazy-timer-fe/src/pages/Charts/Bar.jsx index d90852a..3d1e2d7 100644 --- a/lazy-timer-fe/src/pages/Charts/Bar.jsx +++ b/lazy-timer-fe/src/pages/Charts/Bar.jsx @@ -210,4 +210,4 @@ const Bar = () => { ) } -export default Bar +export default Bar \ No newline at end of file diff --git a/lazy-timer-fe/src/pages/Charts/Line2.jsx b/lazy-timer-fe/src/pages/Charts/Line2.jsx new file mode 100644 index 0000000..cbcad0e --- /dev/null +++ b/lazy-timer-fe/src/pages/Charts/Line2.jsx @@ -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 += `
+ + ${v.seriesName} + ${Math.floor(v.value / 3600) + '时' + Math.floor((v.value % 3600) / 60) + '分钟' + Math.floor(v.value % 60) + '秒'} + ` + } else { + html = `
+ + ${v.seriesName} + 该天还尚未开始 + ` + } + }) + 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 ( +
+ +
+ +
+
+ ) +} + +export default Line2 diff --git a/lazy-timer-fe/src/pages/Customers.jsx b/lazy-timer-fe/src/pages/Customers.jsx index 67a9a40..cadff1f 100644 --- a/lazy-timer-fe/src/pages/Customers.jsx +++ b/lazy-timer-fe/src/pages/Customers.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { GridComponent, ColumnsDirective, ColumnDirective, Page, Selection, Inject, Edit, Toolbar, Sort, Filter } from '@syncfusion/ej2-react-grids'; import { customersData, customersGrid } from '../data/dummy'; @@ -9,11 +9,22 @@ const Customers = () => { const toolbarOptions = ['Delete']; const editing = { allowDeleting: true, allowEditing: true }; + + let [temp, setTemp] = useState([]) + customersData.then( + (result) => { + setTemp(result); + } + ) + + + + return (
(
@@ -29,6 +30,7 @@ const Ecommerce = () => { var mm = String(today.getMonth() + 1).padStart(2, '0'); var yyyy = today.getFullYear(); const receivedJson = await getDayScreenUseTime(yyyy + '-' + mm + '-' + dd); + setEarningData2([ { icon: , @@ -70,6 +72,8 @@ const Ecommerce = () => { ]) }, []) + + return (
{/* 第一行四个盒子 */} @@ -78,7 +82,7 @@ const Ecommerce = () => {

日期

-

2022年9月5日

+

2022年9月7日

@@ -128,10 +132,11 @@ const Ecommerce = () => {
{/* 实际的盒子 */}
+
-

Revenue Updates

+

屏幕使用记录甘特图

-

+ {/*

@@ -142,12 +147,12 @@ const Ecommerce = () => { Budget -

+

*/}
-
+ {/*

$93,438 @@ -155,17 +160,19 @@ const Ecommerce = () => {

Budget

-
-
+
*/} + {/*

$48,487

Expense

-
+
*/} -
- + {/* 这里弄甘特图 */} +
+ {/* */} +
-
+
- +{/*
-
+
*/}
{/*
@@ -212,7 +219,7 @@ const Ecommerce = () => {
*/}
-
+ {/*

Recent Transactions

@@ -263,9 +270,9 @@ const Ecommerce = () => {
-
+
*/} -
+ {/*

Weekly Stats

@@ -386,7 +393,7 @@ const Ecommerce = () => {
-
+
*/}
); }; diff --git a/lazy-timer-fe/src/pages/Gannt.jsx b/lazy-timer-fe/src/pages/Gannt.jsx new file mode 100644 index 0000000..455a905 --- /dev/null +++ b/lazy-timer-fe/src/pages/Gannt.jsx @@ -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) + + '秒' + + '
' + + '持续时间: ' + + 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 +} + +export default Gannt diff --git a/lazy-timer-fe/src/service/colorGenerator.js b/lazy-timer-fe/src/service/colorGenerator.js new file mode 100644 index 0000000..8d2875a --- /dev/null +++ b/lazy-timer-fe/src/service/colorGenerator.js @@ -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; \ No newline at end of file diff --git a/lazy-timer-fe/src/service/getAppId.js b/lazy-timer-fe/src/service/getAppId.js new file mode 100644 index 0000000..5d3b522 --- /dev/null +++ b/lazy-timer-fe/src/service/getAppId.js @@ -0,0 +1,8 @@ + +import { post } from './index' + +export const getAppId = (appId) => { + return post('/getAppId', { + appId + }) +} \ No newline at end of file diff --git a/lazy-timer-fe/src/service/getDayActivity/index.js b/lazy-timer-fe/src/service/getDayActivity/index.js index 1d51bee..370a51d 100644 --- a/lazy-timer-fe/src/service/getDayActivity/index.js +++ b/lazy-timer-fe/src/service/getDayActivity/index.js @@ -2,6 +2,6 @@ import { post } from '../index' export const getDayScreenUseTime = (datetimeStr) => { return post('/getDayActivity', { - dateTimeStr + datetimeStr }) } \ No newline at end of file diff --git a/lazy-timer-fe/src/service/getDayScreenUse/index.js b/lazy-timer-fe/src/service/getDayScreenUse/index.js new file mode 100644 index 0000000..720d35f --- /dev/null +++ b/lazy-timer-fe/src/service/getDayScreenUse/index.js @@ -0,0 +1,7 @@ +import { post } from '../index' + +export const getDayScreenUse = (datetimeStr) => { + return post('/getDayScreenUse', { + datetimeStr + }) +} \ No newline at end of file diff --git a/lazy-timer-fe/src/service/getSeconds.js b/lazy-timer-fe/src/service/getSeconds.js new file mode 100644 index 0000000..51f0c40 --- /dev/null +++ b/lazy-timer-fe/src/service/getSeconds.js @@ -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 +} \ No newline at end of file