From 841968d7412b02b7935497a5da9cd6b985b80700 Mon Sep 17 00:00:00 2001 From: tangjiang <465264938@qq.com> Date: Tue, 14 Jan 2020 17:04:57 +0800 Subject: [PATCH] finish static pages --- public/react/package.json | 2 - .../modules/paths/statics/DisplayTableData.js | 109 +++--- .../react/src/modules/paths/statics/index.js | 326 +++++++++++++----- .../src/modules/paths/statics/index.scss | 95 +++-- .../src/modules/paths/statics/mockData.js | 10 +- public/react/src/redux/actions/actionTypes.js | 6 +- public/react/src/redux/actions/index.js | 12 +- public/react/src/redux/actions/static.js | 41 +++ public/react/src/redux/reducers/index.js | 4 +- .../react/src/redux/reducers/staticReducer.js | 69 ++++ public/react/src/services/staticService.js | 14 + 11 files changed, 509 insertions(+), 179 deletions(-) create mode 100644 public/react/src/redux/actions/static.js create mode 100644 public/react/src/redux/reducers/staticReducer.js create mode 100644 public/react/src/services/staticService.js diff --git a/public/react/package.json b/public/react/package.json index 771c6257f..bf0b5cce0 100644 --- a/public/react/package.json +++ b/public/react/package.json @@ -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", diff --git a/public/react/src/modules/paths/statics/DisplayTableData.js b/public/react/src/modules/paths/statics/DisplayTableData.js index 15f892535..f59dd719d 100644 --- a/public/react/src/modules/paths/statics/DisplayTableData.js +++ b/public/react/src/modules/paths/statics/DisplayTableData.js @@ -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 ( + + ) + } } + 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 = () => ( -
- -
- ); return ( 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; \ No newline at end of file diff --git a/public/react/src/modules/paths/statics/index.js b/public/react/src/modules/paths/statics/index.js index 1b13500f0..37cc44df3 100644 --- a/public/react/src/modules/paths/statics/index.js +++ b/public/react/src/modules/paths/statics/index.js @@ -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: () => (使用课堂), + // 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: () => (课堂学生), + // 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: () => (选用实训/个), + 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: () => (选用实训/次), + 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 = () => {
- + @@ -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; diff --git a/public/react/src/modules/paths/statics/index.scss b/public/react/src/modules/paths/statics/index.scss index 945fa2740..33c65136a 100644 --- a/public/react/src/modules/paths/statics/index.scss +++ b/public/react/src/modules/paths/statics/index.scss @@ -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; } } } diff --git a/public/react/src/modules/paths/statics/mockData.js b/public/react/src/modules/paths/statics/mockData.js index 54d34a3ff..693bb051c 100644 --- a/public/react/src/modules/paths/statics/mockData.js +++ b/public/react/src/modules/paths/statics/mockData.js @@ -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 } ]; diff --git a/public/react/src/redux/actions/actionTypes.js b/public/react/src/redux/actions/actionTypes.js index efe3ff35c..e3c2f65a9 100644 --- a/public/react/src/redux/actions/actionTypes.js +++ b/public/react/src/redux/actions/actionTypes.js @@ -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; diff --git a/public/react/src/redux/actions/index.js b/public/react/src/redux/actions/index.js index 0376b529d..f7b7a41d9 100644 --- a/public/react/src/redux/actions/index.js +++ b/public/react/src/redux/actions/index.js @@ -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 } \ No newline at end of file diff --git a/public/react/src/redux/actions/static.js b/public/react/src/redux/actions/static.js new file mode 100644 index 000000000..3159d35c2 --- /dev/null +++ b/public/react/src/redux/actions/static.js @@ -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 + } +} diff --git a/public/react/src/redux/reducers/index.js b/public/react/src/redux/reducers/index.js index 7c9601d52..6506cf584 100644 --- a/public/react/src/redux/reducers/index.js +++ b/public/react/src/redux/reducers/index.js @@ -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 }); diff --git a/public/react/src/redux/reducers/staticReducer.js b/public/react/src/redux/reducers/staticReducer.js new file mode 100644 index 000000000..7d2202d03 --- /dev/null +++ b/public/react/src/redux/reducers/staticReducer.js @@ -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; diff --git a/public/react/src/services/staticService.js b/public/react/src/services/staticService.js new file mode 100644 index 000000000..23e4f1b93 --- /dev/null +++ b/public/react/src/services/staticService.js @@ -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 }); +}