Commit cb3b1aa6 by zhangleyuan

Merge branch 'master' into feature/zhangleyuan/20210304/video-size

parents 25647536 84a5bcb6
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
"private": true, "private": true,
"homepage": "./", "homepage": "./",
"dependencies": { "dependencies": {
"@antv/data-set": "^0.11.8",
"@antv/g2": "^3.5.13",
"@babel/core": "7.9.0", "@babel/core": "7.9.0",
"@babel/plugin-transform-typescript": "^7.11.0", "@babel/plugin-transform-typescript": "^7.11.0",
"@babel/preset-typescript": "^7.10.4", "@babel/preset-typescript": "^7.10.4",
...@@ -29,6 +31,7 @@ ...@@ -29,6 +31,7 @@
"babel-plugin-jsx-control-statements": "^4.1.0", "babel-plugin-jsx-control-statements": "^4.1.0",
"babel-plugin-named-asset-import": "^0.3.6", "babel-plugin-named-asset-import": "^0.3.6",
"babel-preset-react-app": "^9.1.2", "babel-preset-react-app": "^9.1.2",
"bizcharts": "^4.1.7",
"camelcase": "^5.3.1", "camelcase": "^5.3.1",
"case-sensitive-paths-webpack-plugin": "2.3.0", "case-sensitive-paths-webpack-plugin": "2.3.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
......
...@@ -1346,16 +1346,6 @@ input:focus { ...@@ -1346,16 +1346,6 @@ input:focus {
} }
} }
.g2-tooltip {
background: rgba(0, 0, 0, 0.7) !important;
.g2-tooltip-list {
li {
color: #fff !important;
}
}
}
.BMap_cpyCtrl { .BMap_cpyCtrl {
display: none; display: none;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
*/ */
import OSS from 'ali-oss'; import OSS from 'ali-oss';
import { UPLOAD_REGION } from '@/domains/resource-disk/constants'; import { UPLOAD_REGION } from '@/domains/resource-disk/constants';
import { getOssClient } from '@/data-source/basic/basic-apis.ts'; import { getOssClient } from '@/data-source/basic/basic-apis';
interface IMultiPartUpload { interface IMultiPartUpload {
data: object; data: object;
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_qb6r10go4s.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_80qwxi5x2ed.css">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_qb6r10go4s.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_80qwxi5x2ed.css">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
import React from 'react';
import { Select, Tooltip } from 'antd';
import { Chart } from '@antv/g2';
import moment from 'moment'
import Service from "@/common/js/service";
import User from '@/common/js/user';
import './Home.less';
const Option = Select.Option;
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
scheduleType: 'LIVE',
list: [],
dataList: [],
incCustomerNum: 0,
incLiveCourseNum: 0,
incVideoCourseNum: 0,
liveCourseNum: 0,
totalCustomerNum: 0,
videoCourseNum: 0,
timeRange: '7',
studyTimeRange: '7',
}
this._chart = null;
}
componentDidMount() {
this.getPanelInfo();
this.getStudyInfo();
this.getHotCourse();
}
getHotCourse() {
const { timeRange, scheduleType } = this.state;
const data = {
hotNum: 5,
scheduleType,
storeId: User.getStoreId(),
timeRange,
}
Service.Hades('public/courseCloud/hotCourse', data).then((res) => {
if (res.success) {
this.setState({
list: res.result
})
}
})
}
getStudyInfo() {
const { studyTimeRange } = this.state;
Service.Hades('public/hades/studyInfo', { storeId: User.getStoreId(), timeRange: studyTimeRange }).then((res) => {
if (res.success) {
const dataList = res.result.map(item => ({
time: moment(item.dateline).format('MM-DD'),
studyNum: item.studyNum,
studyTime: Math.round(item.studyTime / 6) / 10,
}))
this.createChart(dataList);
}
});
}
getPanelInfo() {
Service.Hades('public/hades/storePanelInfo', { storeId: User.getStoreId() }).then((res) => {
if (res.success) {
this.setState({
incCustomerNum: res.result.incCustomerNum,
incLiveCourseNum: res.result.incLiveCourseNum,
incVideoCourseNum: res.result.incVideoCourseNum,
liveCourseNum: res.result.liveCourseNum,
totalCustomerNum: res.result.totalCustomerNum,
videoCourseNum: res.result.videoCourseNum,
})
}
})
}
showNumber(index) {
switch (index) {
case 0:
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png';
case 1:
return 'https://image.xiaomaiketang.com/xm/Qfib4mnGJT.png';
case 2:
return 'https://image.xiaomaiketang.com/xm/8jKXHyrDaG.png';
default:
return 'https://image.xiaomaiketang.com/xm/D64QhNn74S.png';
}
}
createChart = (data) => {
if (!this._chart) {
this._chart = new Chart({
container: 'chart-id',
forceFit: true,
height: 290,
padding: [48, 64]
})
}
this._chart.clear();
this._chart.source(data, {
studyTime: {
formatter: (val) => {
return val
},
tickCount: 5,
},
time: {
formatter: (val) => {
return `${val}`
},
},
studyNum: {
formatter: (val) => {
return val
},
tickCount: 5,
}
});
this._chart.axis('time', {
label: {
offset: 20,
textStyle: {
fill: '#666666',
fontSize: 14
}
},
line: {
stroke: '#E8E8E8'
},
tickLine: {
stroke: '#E8E8E8'
}
})
this._chart.axis('submitCount', {
label: {
textStyle: {
fill: '#666666',
fontSize: 14
}
}
})
this._chart.axis('studyTime', {
label: {
textStyle: {
fill: '#666666',
fontSize: 14
}
}
})
this._chart.line().position('time*studyNum').color('#5289FA').tooltip('time*studyNum', function( time, studyNum){
return {
name: '学习人数',
value: studyNum + '人'
}
});
this._chart.line().position('time*studyTime').color('#FFB714').tooltip('time*studyTime', function( time, studyTime){
return {
name: '人均学习时长',
value: studyTime + '分钟'
}
});
this._chart.legend(false);
this._chart.tooltip({
containerTpl: '<div class="g2-tooltip" style="background: #fff !important;">'
+ '<div class="g2-tooltip-title" style="margin:10px 0;"></div>'
+ '<ul class="g2-tooltip-list"></ul></div>', // tooltip 容器模板
itemTpl: '<li data-index={index} style="color: #333 !important"><span style="background-color:{color};width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;"></span>{name}<span style="display: inline-block; float: right; margin-left: 30px;">{value}</span></li>', // tooltip 每项记录的默认模板
})
this._chart.render();
}
render() {
const {
list,
incCustomerNum,
incLiveCourseNum,
incVideoCourseNum,
liveCourseNum,
totalCustomerNum,
videoCourseNum,
timeRange,
scheduleType,
studyTimeRange,
} = this.state;
return (
<div className="home-page">
<div className="home-title">数据概况</div>
<div className="data-box">
<div className="data-item">
<div className="header">
<img className="header-icon" src="https://image.xiaomaiketang.com/xm/wAaFtjeRsM.png" />
<span className="header-word">用户总数 (人)</span>
</div>
<div className="data-number">{totalCustomerNum}</div>
<div className="data-footer">
<span className="footer-word">本月新增</span>
{incCustomerNum > 0 &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="footer-number">{incCustomerNum}</span>
</div>
</div>
<div className="data-item course-data">
<div className="header">
<img className="header-icon" src="https://image.xiaomaiketang.com/xm/jPrRhw8EMF.png" />
<span className="header-word">课程总数 (个)</span>
</div>
<div className="data-number">{videoCourseNum + liveCourseNum}</div>
<div className="course-box">
<div className="course-item">
<div className="course-title">直播课</div>
<div className="data">
<span className="course-number">{liveCourseNum}</span>
<span className="course-word">本月新增</span>
{incLiveCourseNum > 0 &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="add-number">{incLiveCourseNum}</span>
</div>
</div>
<div className="course-item">
<div className="course-title">视频课</div>
<div className="data">
<span className="course-number">{videoCourseNum}</span>
<span className="course-word">本月新增</span>
{incVideoCourseNum > 0 &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="add-number">{incVideoCourseNum}</span>
</div>
</div>
<div className="course-item">
<div className="course-title">图文课</div>
<div className="data">
<span className="course-number">0</span>
<span className="course-word">本月新增</span>
{false &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="add-number">0</span>
</div>
</div>
<div className="course-item">
<div className="course-title">线下课</div>
<div className="data">
<span className="course-number">0</span>
<span className="course-word">本月新增</span>
{false &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="add-number">0</span>
</div>
</div>
</div>
</div>
<div className="data-item">
<div className="header">
<img className="header-icon" src="https://image.xiaomaiketang.com/xm/jZf3GNY5tY.png" />
<span className="header-word">培训计划总数 (个)</span>
</div>
<div className="data-number">0</div>
<div className="data-footer">
<span className="footer-word">本月新增</span>
{false &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="footer-number">0</span>
</div>
</div>
<div className="data-item">
<div className="header">
<img className="header-icon" src="https://image.xiaomaiketang.com/xm/3CfrPs23Re.png" />
<span className="header-word">考试总数 (个)</span>
</div>
<div className="data-number">0</div>
<div className="data-footer">
<span className="footer-word">本月新增</span>
{false &&
<span className="icon iconfont">&#xe635;</span>
}
<span className="footer-number">0</span>
</div>
</div>
</div>
<div className="home-title">学习概况</div>
<div className="study-box">
<div className="study-item">
<div className="study-title">课程学习排行榜</div>
<div className="study-header">
<div className="study-tab">
<span
className={`tab${scheduleType === 'LIVE' ? ' selected' : ''}`}
onClick={() => this.setState({ scheduleType: 'LIVE' }, () => this.getHotCourse())}
>直播课</span>
<span
className={`tab${scheduleType === 'VOICE' ? ' selected' : ''}`}
onClick={() => this.setState({ scheduleType: 'VOICE' }, () => this.getHotCourse())}
>视频课</span>
</div>
<div className="study-select">
<span className="select-word">{moment().subtract(timeRange - 1, 'day').format('MM.DD')} ~ {moment().format('MM.DD')}</span>
<Select
style={{ width: 88 }}
value={timeRange}
onChange={(value) => {
this.setState({ timeRange: value }, () => this.getHotCourse());
}}
>
<Option value="7">近7天</Option>
<Option value="15">近15天</Option>
<Option value="30">近30天</Option>
</Select>
</div>
</div>
{_.isEmpty(list) ?
<div className="study-empty">
<img src="https://image.xiaomaiketang.com/xm/52dmait5Bx.png" />
<div>暂无课程上榜</div>
</div>
: list.map((item, index) => (
<div className={`table-item${index % 2 ? '' : ' odd'}`} key={item.id}>
{index < 3 ?
<span className="table-number"><img src={this.showNumber(index)} className="table-image" /></span>
: <span className="table-number">{index + 1}</span>
}
<div className="table-data">
<div className="table-name">
<Tooltip title={item.courseName}>
{item.courseName}
</Tooltip>
</div>
<div className="table-tag">{item.categoryName}</div>
</div>
<span className="table-study">{item.studyNum || 0}人已学习</span>
</div>
))
}
</div>
<div className="study-item">
<div className="study-title">培训计划完成情况
<Tooltip overlayClassName="data-plan-tooltip" title="若某人加入多个培训计划,则需完成所有已加入的培训计划后,才视为已完成培训">
<span className="iconfont icon">&#xe61d;</span>
</Tooltip>
<span className="tip">(本月)</span>
</div>
<div className="circle-box">
<div className="big-circle">
<div className="small-circle">
<div className="tip-box">
<div style={{ color: '#333', fontSize: '20px', marginBottom: 4 }}>0人</div>
<div style={{ color: '#999' }}>新增培训人数</div>
</div>
</div>
</div>
</div>
<div className="circle-tip unfinished">
<div className="spot"></div>
<div className="number">0人</div>
<div className="word">未完成培训</div>
</div>
<div className="circle-tip finished">
<div className="spot"></div>
<div className="number">0人</div>
<div className="word">完成培训</div>
</div>
</div>
</div>
<div className="study-chart">
<div className="study-title">学习人数与时长
<div className="study-select">
<span className="select-word">{moment().subtract(studyTimeRange - 1, 'day').format('MM.DD')} ~ {moment().format('MM.DD')}</span>
<Select
style={{ width: 88 }}
value={studyTimeRange}
onChange={(value) => {
this.setState({ studyTimeRange: value }, () => this.getStudyInfo());
}}
>
<Option value="7">近7天</Option>
<Option value="15">近15天</Option>
<Option value="30">近30天</Option>
</Select>
</div>
</div>
<div className="chart-tip">
<div>学习人数(人)
<Tooltip title="参与任意课程进行学习的人数">
<span className="iconfont icon">&#xe61d;</span>
</Tooltip>
</div>
<div>人均学习时长(分钟)</div>
</div>
<div id="chart-id"></div>
</div>
</div>
)
}
}
export default Home;
\ No newline at end of file
.home-page {
padding: 0 16px 16px;
min-width: 1100px;
.home-title {
height: 44px;
line-height: 44px;
color: #999;
}
@font-face {
font-family: 'number';
src: url('https://image.xiaomaiketang.com/xm/n2sADd2jY6.TTF');
}
.data-box {
display: flex;
justify-content: space-between;
.data-item {
position: relative;
height: 156px;
background: #fff;
width: ~'calc(16.67% - 8px)';
padding: 16px;
&.course-data {
width: ~'calc(50% - 24px)';
}
.header {
display: flex;
.header-icon {
width: 18px;
height: 18px;
margin-right: 4px;
}
.header-word {
display: inline-block;
font-size: 12px;
line-height: 18px;
color: #999;
}
}
.data-number {
font-family: 'number';
font-size: 28px;
color: #333;
margin-top: 24px;
}
.data-footer {
margin-top: 12px;
.footer-word {
font-size: 12px;
color: #999;
margin-right: 8px;
}
.iconfont {
font-size: 12px;
margin-right: 4px;
color: #EC4B35;
}
.footer-number {
font-size: 12px;
color: #999;
}
}
.course-box {
border-radius: 4px;
background: #FAFAFA;
height: 124px;
width: 66%;
position: absolute;
right: 16px;
top: 16px;
padding: 8px 24px 0;
.course-item {
display: inline-block;
width: 50%;
padding: 4px 0 12px;
.course-title {
font-size: 12px;
color: #999;
}
.data {
display: flex;
align-items: flex-end;
.course-number {
font-size: 16px;
font-family: 'number';
margin-right: 16px;
}
.course-word {
font-size: 12px;
color: #999;
margin-right: 8px;
}
.iconfont {
font-size: 12px;
margin-right: 4px;
color: #EC4B35;
}
.add-number {
font-size: 12px;
color: #999;
}
}
}
}
}
}
.study-box {
display: flex;
justify-content: space-between;
.study-item {
position: relative;
height: 414px;
background: #fff;
width: ~'calc(50% - 8px)';
padding: 16px;
.study-title {
font-size: 16px;
color: #333;
font-weight: 500;
line-height: 22px;
margin-bottom: 12px;
.iconfont {
font-size: 14px;
color: #BFBFBF;
margin-left: 4px;
}
.tip {
font-size: 14px;
color: #999999;
margin-left: 8px;
font-weight: 400;
}
}
.study-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
.study-tab {
.tab {
position: relative;
padding-right: 24px;
color: #666;
cursor: pointer;
&.selected {
color: #FDB513;
&::after {
position: absolute;
width: 24px;
height: 2px;
content: '';
background: #FDB513;
border-radius: 1px;
left: 9px;
bottom: -4px;
}
}
}
}
.study-select {
.select-word {
color: #999;
margin-right: 8px;
}
}
}
.table-item {
width: 100%;
height: 60px;
display: flex;
align-items: center;
&.odd {
background: #FAFAFA;
}
.table-image {
width: 24px;
height: 24px;
}
.table-number {
width: 15%;
color: #999;
padding: 0 16px;
text-align: center;
}
.table-data {
width: 55%;
padding: 0 16px;
.table-name {
color: #333;
line-height: 20px;
margin-bottom: 2px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.table-tag {
line-height: 17px;
color: #999;
font-size: 12px;
}
}
.table-study {
width: 30%;
color: #999;
padding: 0 16px;
}
}
.study-empty {
margin-top: 102px;
img {
display: block;
width: 100px;
height: 100px;
margin: 0 auto 8px;
}
div {
text-align: center;
color: #333;
}
}
.circle-box {
width: 70%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.big-circle {
display: flex;
align-items: center;
justify-content: center;
width: 204px;
height: 204px;
border-radius: 102px;
background: #F1F3F6;
margin-top: -20px;
.small-circle {
display: flex;
align-items: center;
justify-content: center;
width: 124px;
height: 124px;
border-radius: 62px;
background: #fff;
.tip-box {
text-align: center;
}
}
}
}
.circle-tip {
position: absolute;
left: 70%;
padding-left: 16px;
&.unfinished {
top: 152px;
.spot {
background: #5289FA;
}
}
&.finished {
top: 232px;
.spot {
background: #FFBB54;
}
}
.spot {
position: absolute;
width: 8px;
height: 8px;
border-radius: 4px;
top: 20px;
left: 0;
}
.number {
color: #333;
font-size: 16px;
margin-bottom: 2px;
}
.word {
color: #999;
}
}
}
}
.study-chart {
width: 100%;
height: 396px;
background: #fff;
padding: 16px;
margin-top: 16px;
.study-title {
font-size: 16px;
color: #333;
font-weight: 500;
line-height: 22px;
margin-bottom: 12px;
display: flex;
justify-content: space-between;
.study-select {
font-weight: 400;
font-size: 14px;
.select-word {
color: #999;
margin-right: 8px;
}
}
}
.chart-tip {
display: flex;
justify-content: space-between;
div {
color: #999;
.iconfont {
color: #bfbfbf;
font-size: 14px;
margin-left: 4px;
}
}
}
}
}
.g2-tooltip {
background-color: #fff !important;
}
.data-plan-tooltip {
max-width: none !important;
.ant-tooltip-inner {
white-space: nowrap;
max-width: none !important;
}
}
\ No newline at end of file
...@@ -26,17 +26,17 @@ function SwitchRoute(props: SwitchProps) { ...@@ -26,17 +26,17 @@ function SwitchRoute(props: SwitchProps) {
switch (userRole) { switch (userRole) {
case "CloudManager": case "CloudManager":
window.RCHistory.replace({ window.RCHistory.replace({
pathname: `/redirect-to-live-course`, pathname: `/home`,
}); });
break; break;
case "StoreManager": case "StoreManager":
window.RCHistory.replace({ window.RCHistory.replace({
pathname: `/redirect-to-live-course`, pathname: `/home`,
}); });
break; break;
case "CloudLecturer": case "CloudLecturer":
window.RCHistory.replace({ window.RCHistory.replace({
pathname: `/redirect-to-resource-disk`, pathname: `/home`,
}); });
break; break;
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* @LastEditTime: 2021-01-18 21:23:08 * @LastEditTime: 2021-01-18 21:23:08
* @Description: 内容线路由配置 * @Description: 内容线路由配置
*/ */
import Home from '@/modules/home/Home';
import EmployeesManagePage from '@/modules/store-manage/EmployeesManagePage'; import EmployeesManagePage from '@/modules/store-manage/EmployeesManagePage';
import personalInfoPage from '@/modules/personalInfo'; import personalInfoPage from '@/modules/personalInfo';
import UserManagePage from '@/modules/store-manage/UserManagePage'; import UserManagePage from '@/modules/store-manage/UserManagePage';
...@@ -21,6 +22,11 @@ import SwitchRoute from '@/modules/root/SwitchRoute'; ...@@ -21,6 +22,11 @@ import SwitchRoute from '@/modules/root/SwitchRoute';
const mainRoutes = [ const mainRoutes = [
{ {
path: '/home',
component: Home,
name: '中心首页'
},
{
path: '/employees-manage', path: '/employees-manage',
component: EmployeesManagePage, component: EmployeesManagePage,
name: '员工管理' name: '员工管理'
......
export const menuList: any = [ export const menuList: any = [
{ {
groupName: "中心首页",
groupCode: "CloudPage",
icon: '&#xe6b7;',
link: '/home'
},
{
groupName: "课程管理", groupName: "课程管理",
groupCode: "CloudCourse", groupCode: "CloudCourse",
icon: '&#xe831;', icon: '&#xe831;',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment