finish static pages

dev_static
tangjiang 5 years ago
parent 51205c9056
commit 841968d741

@ -7,7 +7,6 @@
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"antd": "^3.23.2",
"antd-table-infinity": "^1.1.3",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
"axios": "^0.18.0",
@ -79,7 +78,6 @@
"react-dev-utils": "^5.0.0",
"react-dom": "^16.9.0",
"react-hot-loader": "^4.0.0",
"react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1",
"react-monaco-editor": "^0.25.1",
"react-player": "^1.11.1",

@ -1,69 +1,80 @@
/*
* @Description: 展示表格数据
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-10 13:46:30
* @Date: 2020-01-14 13:39:12
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-11 17:35:03
* @LastEditTime : 2020-01-14 16:30:05
*/
import 'antd-table-infinity/dist/index.css';
import 'antd-table-infinity/index.css';
import './index.scss';
import React, { useState, useEffect } from 'react';
import { Spin } from 'antd';
// import {SumTable as Table} from 'antd-table-infinity';
import { SumTable as Table } from 'antd-table-infinity';
import { columns, fetchData, sumData } from './mockData';
import React, { useRef, useState, useEffect } from 'react';
import { Table } from 'antd';
import ReactDom from 'react-dom';
const DisplayTableData = ({
// columns = [],
// datas,
// fetchData
}) => {
const [data, setData] = useState([]);
const DisplayTableData = (props) => {
const {columns, datas, fetchData, total} = props;
let tableEl = useRef(null);
const [loading, setLoading] = useState(false);
const handleFetchData = () => {
setLoading(true);
fetchData(data.length).then(newData => {
setLoading(false);
const _data = data.concat(newData);
setData(_data);
})
const renderFooter = (obj = {}) => {
const {course_count, student_count, choice_shixun_num, choice_shixun_frequency, total} = obj;
if (!obj) return ''
else {
return (
<ul className="footer_list">
<li className="footer_item footer-total">总计</li>
<li className="footer_name">{total || '-'}</li>
<li className="footer_item">{course_count || '-'}</li>
<li className="footer_item">{student_count || '-'}</li>
<li className="footer_item">{choice_shixun_num || '-'}</li>
<li className="footer_item">{choice_shixun_frequency || '-'}</li>
</ul>
)
}
}
useEffect(() => {
handleFetchData();
const table = ReactDom.findDOMNode(tableEl);
// console.log(table);
const tableBody = table.querySelector('.ant-table-body');
let _scrollTop = 0;//保存上次滚动距离
let isRun = false;//是否执行查询
tableBody.addEventListener('scroll', () => {
if(tableBody.scrollTop === 0 ){
_scrollTop = 0;
}
// 上一次滚动高度与当前滚动高度不同则是纵向滚动
if (_scrollTop !== tableBody.scrollTop) {
//是否滑动到距离底部40px的位置
const scorll = _scrollTop >= tableBody.scrollHeight-tableBody.clientHeight-40;
//isRun为true时 代表已经执行查询
if(isRun && scorll){
return;
}
//_scrollTop < tableBody.scrollTop 判断是否向下滑动
isRun = _scrollTop < tableBody.scrollTop && scorll;
//保存当前滚动位置
_scrollTop = tableBody.scrollTop;
if (isRun) {
fetchData && fetchData();
}
}
})
}, []);
const loadMoreContent = () => (
<div
style={{
textAlign: 'center',
paddingTop: 40,
paddingBottom: 40,
border: '1px solid #e8e8e8',
}}
>
<Spin tip="Loading..." />
</div>
);
return (
<Table
className='statc_table'
key="key"
loading={loading}
onFetch={handleFetchData}
pageSize={50}
loadingIndicator={loadMoreContent}
className='static_table'
rowKey={record => record.id}
columns={columns}
sumData={sumData}
scroll={{ y: 500 }}
dataSource={data}
// bordered
// debug
dataSource={datas}
pagination={false}
loading={loading}
scroll={{y: 500}}
ref={(ref)=>tableEl=ref}
footer={total ? () => renderFooter(total) : ''}
/>
);
}
export default DisplayTableData;
export default DisplayTableData;

@ -4,102 +4,240 @@
* @Github:
* @Date: 2020-01-10 09:33:45
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-11 17:06:56
* @LastEditTime : 2020-01-14 17:01:32
*/
import './index.scss';
import React, { useState } from 'react';
import React, { useEffect } from 'react';
import StaticNumberAndTxt from './StaticNumberAndTxt';
import DisplayTableData from './DisplayTableData';
import { Table, Tabs } from 'antd';
import { Tabs, Tooltip } from 'antd';
import { connect } from 'react-redux';
import moment from 'moment';
import actions from '../../../redux/actions';
const { TabPane } = Tabs;
const App = () => {
const App = (props) => {
// const datas = [
// {
// key: '1',
// id: 1,
// id2: '',
// id3: '',
// id4: '',
// id5: '',
// id6: ''
// },
// {
// key: '2',
// id: 2,
// id2: '',
// id3: '',
// id4: '',
// id5: '',
// id6: ''
// },
// {
// key: '3',
// id: 3,
// id2: '',
// id3: '',
// id4: '',
// id5: '',
// id6: ''
// },
// {
// key: '4',
// id: 4,
// id2: '',
// id3: '',
// id4: '',
// id5: '',
// id6: ''
// }
// ];
const [datas, setDatas] = useState([]);
const {
subject_info,
other_info,
total,
staticList,
changeParams,
initTotal
} = props;
// const [datas, setDatas] = useState([]);
// const [sortedInfo, setSortedInfo] = useState({});
// console.log(props);
const {pathId} = props.match.params;
const columns = [
{
title: '序号',
id: 'id',
//align: 'center',
width: 100
dataIndex: 'id',
key: 'id',
render: (text, record, i) => i + 1,
width: 100,
align: 'center'
},
{
title: '使用单位',
id: 'id2',
//align: 'center',
flexGrow: 1
// key: 'school_name',
dataIndex: 'school_name',
// width: 300,
className: 'overflow_hidden',
align: 'center'
},
{
title: '使用课堂/个',
id: 'id3',
//align: 'center',
flexGrow: 1,
sortable: true
// title: '使用课堂/个',
title: () => (<Tooltip title="将该课程使用到课堂的数量">使用课堂</Tooltip>),
// key: 'course_count',
width: 150,
dataIndex: 'course_count',
align: 'center',
sorter: (a, b) => a.course_count - b.course_count,
// sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order
// sorter: (a, b) => true,
// sorter: (a, b) => a.age - b.age
},
{
title: '课堂实训/个',
id: 'id4',
//align: 'center',
flexGrow: 1,
sortable: true
title: () => (<Tooltip title="课堂的学生总数(去掉重复)">课堂学生</Tooltip>),
// key: 'student_count',
width:150,
dataIndex: 'student_count',
align: 'center',
sorter: (a, b) => a.student_count - b.student_count,
// sorter: (a, b) => a.age - b.age
},
{
title: '选用实训/个',
id: 'id5',
//align: 'center',
flexGrow: 1,
sortable: true
title: () => (<Tooltip title="选用该课程实训的个数(去重)">选用实训/</Tooltip>),
width: 150,
// key: 'choice_shixun_num',
dataIndex: 'choice_shixun_num',
align: 'center',
sorter: (a, b) => a.choice_shixun_num - b.choice_shixun_num,
// sorter: (a, b) => a.age - b.age
},
{
title: '选用实训/次',
id: 'id6',
//align: 'center',
flexGrow: 1,
sortable: true
title: () => (<Tooltip title="选用该课程实训的次数">选用实训/</Tooltip>),
width: 150,
// key: 'choice_shixun_frequency',
dataIndex: 'choice_shixun_frequency',
align: 'center',
sorter: (a, b) => a.choice_shixun_frequency - b.choice_shixun_frequency,
// sorter: (a, b) => a.bbb - b.bbb
}
];
const sxColumns = [
{
title: '序号',
dataIndex: 'id',
render: (text, record, i) => i + 1,
width: 60,
align: 'center'
}, {
title: '章节',
dataIndex: 'stage',
width: 80,
align: 'center'
},
{
title: '实训名称',
dataIndex: 'shixun_name',
align: 'center',
// ellipsis: true
},
{
title: '关卡数',
dataIndex: 'challenge_count',
width: 100,
align: 'center'
},
{
title: '使用课堂',
dataIndex: 'course_count',
width: 110,
align: 'center',
sorter: (a, b) => a.course_count - b.course_count
},
{
title: '使用单位',
dataIndex: 'school_count',
width: 110,
align: 'center',
sorter: (a, b) => a.school_count - b.school_count
},
{
title: '使用人数',
dataIndex: 'used_count',
width: 110,
align: 'center',
sorter: (a, b) => a.used_count - b.used_count
},
{
title: '通关人数',
dataIndex: 'passed_count',
width: 110,
align: 'center',
sorter: (a, b) => a.passed_count - b.passed_count
},
{
title: '评测次数',
dataIndex: 'evaluate_count',
width: 110,
align: 'center',
sorter: (a, b) => a.evaluate_count - b.evaluate_count
},
{
title: '通关平均时间',
dataIndex: 'passed_ave_time',
width: 140,
align: 'center',
render: (text) => (text && moment(text).format('HH:mm:ss')) || '-',
sorter: (a, b) => a.passed_ave_time - b.passed_ave_time
}
];
const stColumns = [
{
title: '序号',
dataIndex: 'id',
render: (text, record, i) => i + 1,
width: 60,
align: 'center'
},
{
title: '姓名',
dataIndex: 'username',
align: 'center'
},
{
title: '通关实训数',
dataIndex: 'passed_myshixun_count',
align: 'center',
sorter: (a, b) => a.passed_myshixun_count - b.passed_myshixun_count
},
{
title: '完成关卡',
dataIndex: 'passed_games_count',
align: 'center',
sorter: (a, b) => a.passed_games_count - b.passed_games_count
},
{
title: '代码行',
dataIndex: 'code_line_count',
align: 'center',
sorter: (a, b) => a.code_line_count - b.code_line_count
},
{
title: '评测次数',
dataIndex: 'evaluate_count',
align: 'center',
sorter: (a, b) => a.evaluate_count - b.evaluate_count
},
{
title: '所用时间',
dataIndex: 'cost_time',
align: 'center',
render: (text) => (text && moment(text).format('HH:mm:ss')) || '-',
sorter: (a, b) => a.cost_time - b.cost_time
}
];
useEffect(() => {
changeParams({
page: 1
});
pathId && staticList(pathId);
}, []);
const handleFetchData = () => {
pathId && staticList(pathId);
}
const {
study_count,
course_study_count,
initiative_study,
passed_count,
course_used_count,
school_used_count
} = subject_info;
const maps = {
1: 'subject_info', // 实践课程使用情况
2: 'shixun_info', // 实训使用情况
3: 'user_info' // 用户使用情况
};
const handleTabChange = (key) => {
const type = maps[+key];
// console.log(type);
const params = {
page: 1,
type: type
}
// 恢复初始值
changeParams(params);
initTotal();
pathId && staticList(pathId);
}
return (
@ -113,57 +251,60 @@ const App = () => {
</div>
<div className="header-number header-flex">
<StaticNumberAndTxt
count={542678445} // 总数
count={study_count} // 总数
txt={'学习总人数'} // 文字描述
desc={'学习该课程的全部人数(学习总人数=课堂学习人数+自主学习人数)'}
/>
<StaticNumberAndTxt
count={542678445} // 总数
count={course_study_count} // 总数
txt={'课堂学习人数'} // 文字描述
desc={'通过课堂学习该课程的人数'}
/>
<StaticNumberAndTxt
count={542678445} // 总数
count={initiative_study} // 总数
txt={'自主学习人数'} // 文字描述
desc={'通过自主学习该课程的人数'}
/>
<StaticNumberAndTxt
count={542678445} // 总数
count={passed_count} // 总数
txt={'通关总人数'} // 文字描述
desc={'通关该课程所有实训的人数去重。一个人数计算1次'}
/>
<StaticNumberAndTxt
count={542678445} // 总数
count={course_used_count} // 总数
txt={'使用课堂数'} // 文字描述
desc={'使用该课程的课堂数量'}
/>
<StaticNumberAndTxt
count={542678445} // 总数
count={school_used_count} // 总数
txt={'使用单位数'} // 文字描述
desc={'使用该课程的单位数量(包括自主学习者所在单位)'}
/>
</div>
</section>
<section className="static_section_table">
<Tabs defaultActiveKey="1">
<Tabs defaultActiveKey="1" onChange={handleTabChange} style={{ paddingBottom: '15px'}}>
<TabPane tab="课堂使用情况" key="1">
<DisplayTableData
columns={columns}
datas={datas}
datas={other_info}
total={total}
fetchData={handleFetchData}
/>
</TabPane>
<TabPane tab="实际使用情况" key="2">
<DisplayTableData
columns={columns}
datas={datas}
columns={sxColumns}
datas={other_info}
total={total}
fetchData={handleFetchData}
/>
</TabPane>
<TabPane tab="学习情况" key="3">
<DisplayTableData
columns={columns}
datas={datas}
columns={stColumns}
datas={other_info}
total={total}
fetchData={handleFetchData}
/>
</TabPane>
@ -176,4 +317,23 @@ const App = () => {
);
}
export default App;
const mapStateToProps = (state) => {
const { staticReducer: {subject_info, other_info, total} } = state;
return {
subject_info,
other_info,
total
}
};
const mapDispatchToProps = (dispatch) => ({
staticList: (id) => dispatch(actions.staticList(id)),
changeParams: (params) => dispatch(actions.changeParams(params)),
initTotal: () => dispatch(actions.initTotal())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
// export default App;

@ -81,51 +81,72 @@
}
}
}
.statc_table{
// .static_table{
// // .ant-table-header{
// // overflow: hidden !important;
// // margin-bottom: 0px !important;
// // }
// // .ant-table-row-cell-break-word{
// // background: rgba(241,248,255,1) !important;
// // }
// // .overflow_hidden{
// // max-width: 280px;
// // overflow: hidden;
// // text-overflow:ellipsis;
// // white-space: nowrap;
// // }
// }
.static_table{
.ant-table-header{
overflow-x: hidden !important;
margin-bottom: 0px !important;
// border-bottom: 2px solid rgba(241,248,255,1);
// background: green;
overflow: hidden !important;
}
// .ant-table-footer .ant-table-content{
// border-top: 1px solid #e8e8e8;
// box-sizing: border-box;
// }
.overflow_hidden{
max-width: 300px;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
.ant-table-thead{
th{
background: rgba(241,248,255,1);
}
.ant-table-column-title{
color: #303133;
font-weight: bold;
}
}
table th,
table td{
line-height: 1;
padding: 15px 0
.ant-table-tbody tr:nth-child(2n) {
td{
background: rgba(241,248,255,.4);
}
}
table th{
background: rgba(241,248,255,1);
.ant-table-tbody tr td{
color: #303133;
}
}
table tr:nth-child(2n) td{
background: rgba(241,248,255,.4);
}
.ant-table-footer{
background-color: rgba(241,248,255,1);
padding: 16px 0px;
}
.footer_list{
display: flex;
// background: #fff;
box-sizing: border-box;
text-align: center;
// .ant-table-thead>tr>th .ant-table-column-sorters:before{
// background: rgba(241,248,255,1);
// }
.ant-table-thead>tr>th .ant-table-column-sorters:before {
position: absolute;
content: "";
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(241,248,255,1);
-webkit-transition: all .3s;
-o-transition: all .3s;
transition: all .3s;
li{
color: #303133;
}
// border-top: 1px solid green;
.footer_item{
width: 150px;
}
.footer_item:not(:first-child) {
padding-right: 10px;
}
.footer-total{
width: 100px;
}
.footer_name{
flex: 1;
}
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2020-01-11 10:55:33
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-11 17:48:02
* @LastEditTime : 2020-01-14 09:11:36
*/
import { random } from 'lodash';
@ -59,28 +59,28 @@ const columns = [
width: 200,
dataIndex: 'age',
align: 'center',
sorter: (a, b) => a.age - b.age
// sorter: (a, b) => a.age - b.age
},
{
title: '课堂学生/个',
width: 200,
dataIndex: 'address',
align: 'center',
sorter: (a, b) => a.age - b.age
// sorter: (a, b) => a.age - b.age
},
{
title: '选用实训/个',
width: 200,
dataIndex: 'address2',
align: 'center',
sorter: (a, b) => a.age - b.age
// sorter: (a, b) => a.age - b.age
},
{
title: '选用实训/个',
width: 200,
dataIndex: 'bbb',
align: 'center',
sorter: (a, b) => a.bbb - b.bbb
// sorter: (a, b) => a.bbb - b.bbb
}
];

@ -88,7 +88,11 @@ const types = {
CHANGE_COMMENT_PAGINATION_PARAMS: 'CHANGE_COMMENT_PAGINATION_PARAMS', // 改变分页
/** tpi */
SHOW_OR_HIDE_TPI_TEST_CASE: 'SHOW_OR_HIDE_TPI_TEST_CASE', // 显示或隐藏tpi测试集弹框
IS_COLLAPSE_TEST_CASE: 'IS_COLLAPSE_TEST_CASE' // 是否展开测试集
IS_COLLAPSE_TEST_CASE: 'IS_COLLAPSE_TEST_CASE', // 是否展开测试集
/** 统计 */
GET_STATIC_INFO: 'GET_STATIC_INFO',
CHANGE_STATIC_PARAMS: 'CHANGE_STATIC_PARAMS',
CHANGE_STATIC_TOTAL: 'CHANGE_STATIC_TOTAL'
}
export default types;

@ -103,6 +103,12 @@ import {
isCollpaseTsetCase
} from './tpi';
import {
staticList,
changeParams,
initTotal
} from './static';
export default {
toggleTodo,
getOJList,
@ -181,5 +187,9 @@ export default {
changePagination,
// tpi
showOrHideTpiTestCase,
isCollpaseTsetCase
isCollpaseTsetCase,
// 统计
staticList,
changeParams,
initTotal
}

@ -0,0 +1,41 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-14 09:44:02
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-14 17:02:45
*/
import types from "./actionTypes";
import { fetchStaticList } from "../../services/staticService";
export const staticList = (id) => {
return (dispatch, getState) => {
const { params, total_count, other_info } = getState().staticReducer;
if (total_count !== 0 && total_count === other_info.length) return;
fetchStaticList(id, params).then(res => {
// console.log('统计数据=====>>>>>', res);
const {data} = res;
if (data.status === 0) {
dispatch({
type: types.GET_STATIC_INFO,
payload: data.data
});
}
});
}
};
export const changeParams = (params) => {
return {
type: types.CHANGE_STATIC_PARAMS,
payload: params
}
}
export const initTotal = () => {
return {
type: types.CHANGE_STATIC_TOTAL
}
}

@ -16,6 +16,7 @@ import userReducer from './userReducer';
import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer';
import tpiReducer from './tpiReducer';
import staticReducer from './staticReducer';
export default combineReducers({
testReducer,
@ -26,5 +27,6 @@ export default combineReducers({
userReducer,
jupyterReducer,
commentReducer,
tpiReducer
tpiReducer,
staticReducer
});

@ -0,0 +1,69 @@
/*
* @Description: 统计
* @Author: tangjiang
* @Github:
* @Date: 2020-01-14 09:34:49
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-14 15:49:55
*/
import types from "../actions/actionTypes";
// const maps = {
// 1: 'shixun_info', // 实训使用情况
// 2: 'user_info', // 用户使用情况
// 3: 'subject_info' // 实践课程使用情况
// }
const initalState = {
subject_info: {},
other_info: [],
total_count: 0,
total: {},
params: {
// sort_by: '',
// sort_direction: 'desc', // desc || asc
limit: 20, // 一页多少条
page: 1, // 第几页
type: 'subject_info' // 类型: 实训 shixun_info,
}
};
// const getGuid = () =>
// 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
// /* eslint-disable */
// let r = (Math.random() * 16) | 0,
// v = c == 'x' ? r : (r & 0x3) | 0x8;
// return v.toString(16);
// });
const staticReducer = (state = initalState, action) => {
const { payload = {}, type } = action;
const {subject_info, other_info = [], total = {}, total_count} = payload;
switch (type) {
case types.GET_STATIC_INFO:
return {
...state,
subject_info,
other_info: state.other_info.concat(other_info),
total,
total_count,
params: Object.assign({}, state.params, { page: state.params.page + 1 })
}
case types.CHANGE_STATIC_PARAMS: {
return {
...state,
params: Object.assign({}, state.params, payload)
};
}
case types.CHANGE_STATIC_TOTAL: {
return {
...state,
other_info: [],
total: {}
}
}
default:
return state;
}
}
export default staticReducer;

@ -0,0 +1,14 @@
import axios from "axios";
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2020-01-14 09:40:53
* @LastEditors : tangjiang
* @LastEditTime : 2020-01-14 10:47:19
*/
export async function fetchStaticList (id, params) {
const url = `/paths/${id}/statistics_info.json`;
return axios.get(url, { params });
}
Loading…
Cancel
Save