mh_jy_safe_web/src/components/CustomPages/WB/WB008Operate.js

675 lines
32 KiB
JavaScript
Raw Normal View History

2025-08-25 10:08:30 +08:00
import { Button, Descriptions, Badge, Popconfirm, Row, Col, Form, Input, Select, Table, Modal, Spin, Checkbox, Radio } from 'antd';
import React from 'react';
import { initFilter, extendRule, extendInclude, setDataFieldValue, showApprove, guid, initQueryFilter, ShowDateTime, showFiles } from '../../../utils/common';
import ReactToPrint from 'react-to-print';
import { ExportToExcel } from '@woowalker/feui'
import XLSX from 'xlsx';
import { connect } from 'dva';
import SEstyles from '../SE/SE.css';
import moment from 'moment';
import { message } from 'antd/lib/index';
import styles from '../../../components/CustomPages/HI/StepForm.css';
import config from '../../../config.js';
const Option = Select.Option;
const answer = {
color: 'blue',
}
const marginRight10 = {
marginRight: 10
}
class WB008Operate extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
isOnline: false,
papers: [],
btndisplay: 'none',
currentTime: 0,
lastTime: 0,
readonly: true,
loading: false,
SOURCE_ID: '',
isVodeoEnd: false,
}
this.player = null;
this.videoRef = React.createRef();
this.lastTimeRef = React.createRef(0);
}
componentDidMount() {
this.videoRef?.current?.addEventListener('timeupdate', this.handleTimeUpdate);
if (this.props.data?.id)
this.loadData(this.props.data?.id);
}
componentWillUnmount() {
this.videoRef?.current?.removeEventListener('timeupdate', this.handleTimeUpdate);
if (this.player) {
this.player.destroy();
}
}
handleTimeUpdate = (event) => {
const currentTime = event.target.currentTime;
if (currentTime < this.state.lastTime) {
event.target.currentTime = this.state.lastTime;
} else {
this.setState({
currentTime: currentTime,
lastTime: currentTime,
});
}
}
componentWillReceiveProps(NextProps) {
if (NextProps.data?.id && this.props.data?.id != NextProps.data?.id) {
this.loadData(NextProps.data?.id);
}
}
loadPlayer(vid) {
this.player = window.polyvPlayer({
wrap: '.player',
width: '100%',
height: '1000px',
// height: '100%',
// min-height: '100px',
// minHeight: '200px',
ban_seek_by_limit_time: 'on',
vid: vid,
});
this.player.on('s2j_onPlayOver', () => {
this.setState({
isVodeoEnd: true
})
});
}
// s2j_onPlayOver={() => this.onEnd()}
loadScript(src) {
const headElement = document.head || document.getElementsByTagName('head')[0];
const _importedScript = {};
return new Promise((resolve, reject) => {
if (src in _importedScript) {
resolve();
return;
}
const script = document.createElement('script');
script.type = 'text/javascript';
script.onerror = err => {
headElement.removeChild(script);
reject(new URIError(`The Script ${src} is no accessible.`));
}
script.onload = () => {
_importedScript[src] = true;
resolve();
}
headElement.appendChild(script);
script.src = src;
})
}
BtnClose = () => {
if (typeof this.props.data.onCancel != "undefined" && typeof this.props.data.onCancel == 'function')
this.props.data.onCancel();
}
// onUserAgree(agree) {
// if (agree == 1) {
// this.props.dispatch({
// type: 'app/getDataByPost',
// url: 'WB/WBRegister/UserAgree',
// payload: {
// ID: this.props.data.id,
// TaskID: this.props.data.TaskID,
// ORG_ID: this.props.login.OrgId,
// AUDIT_OPINION: this.state.DEALOPINION,
// },
// onComplete: (ret) => {
// if (ret) {
// message.success('处理完成!');
// this.setState({ isAudit: 'none' })
// this.BtnClose();
// }
// }
// })
// } else {
// this.props.dispatch({
// type: 'app/getDataByPost',
// url: 'WB/WBRegister/UserDisAgree',
// payload: {
// ID: this.props.data.id,
// TaskID: this.props.data.TaskID,
// ORG_ID: this.props.login.OrgId,
// AUDIT_OPINION: this.state.DEALOPINION,
// },
// onComplete: (ret) => {
// if (ret) {
// message.success('处理完成!');
// this.setState({ isAudit: 'none' })
// this.BtnClose();
// }
// }
// })
// }
// }
onTableBtnAgree() {
this.props.dispatch({
type: 'app/getDataByPost',
url: 'WB/WBOutsourceTrainRecord/PersonalAgree',
payload: {
ID: this.props.data.id,
TaskID: this.props.data.TaskID,
IgnoreDataRule: true
},
onComplete: (ret) => {
if (ret) {
message.success('签到成功!');
this.setState({ BtnAgreeDisplay: 'none' })
this.BtnClose();
}
}
})
}
loadData = (id) => {
this.state.loading = true;
var orgId = this.props.login ? this.props.login.OrgId : '';
let json = initFilter(orgId, id);
extendRule(json, 'ID', 1, id);
this.props.dispatch({
type: 'app/getDataByPost',
payload: json,
url: 'WB/WBOutsource/GetSource',
onComplete: (ret) => {
this.state.loading = false;
if (ret) {
var filepath = ''
var isOnline = false
var papers = []
var SOURCE_ID = ''
var readonly = true
var isVodeoEnd = true
ret.Nav_ListUserPaper.forEach(ele => {
if ((SOURCE_ID == '') && (ele.ANSWER == null || ele.ANSWER == 0)) {
SOURCE_ID = ele.SOURCE_ID
readonly = false
isVodeoEnd = false
}
if (SOURCE_ID.length > 0 && ele.SOURCE_ID == SOURCE_ID) {
papers.push(ele)
isOnline = ele.IS_ONLINE
if (isOnline == true && filepath == '') {
if (ele.FILE_PATH.indexOf('http') == -1) {
filepath = config.picServerHost + ele.FILE_PATH //后续看看怎么处理 Nav_Source.Nav_Files[i].Nav_ImgFile.FILE_PATH
} else {
filepath = ele.FILE_PATH
}
}
}
});
if (SOURCE_ID == "") {
//查看模式
papers = ret.Nav_ListUserPaper
}
if (filepath == null || filepath == "") {
isVodeoEnd = true//线下课有上传视频 自动考试
}
// if (ret.Nav_Files != null && ret.Nav_Files.length > 0) {
// filepath = ret.Nav_Files[0].Nav_ImgFile.FILE_PATH
// }
this.setState({
data: ret,
papers: papers,
filepath: filepath,
isOnline: isOnline,
readonly: readonly,
SOURCE_ID: SOURCE_ID,
isVodeoEnd: isVodeoEnd
})
//初始化视频播放器
if (!window.polyvLivePlayer) {
this.loadScript('https://player.polyv.net/resp/vod-player/latest/player.js')
.then(() => {
this.loadPlayer(filepath.split('vid=')[1]);
});
}
}
}
});
}
//播放结束
onEnd = () => {
this.setState({
isVodeoEnd: true
})
}
TimeUpdate = () => {
const currentTime = this.videoRef.current.currentTime;
if (currentTime > this.lastTimeRef.current + 1) {
this.videoRef.current.currentTime = this.lastTimeRef.current
message.error(`禁止快进`);
} else {
this.lastTimeRef.current = currentTime
}
}
onSave = () => {
if (this.state.readonly) {
return;
}
let data = JSON.parse(JSON.stringify(this.state.papers));
var ALLSCORE = 0
var PASSSCORE = 0
for (let i = 0; i < data.length; i++) {
if (data[i].ANSWER == 0) {
message.error(`${i + 1}题尚未选择答题,请完成所有答题后再进行提交`);
return;
}
if (data[i].Nav_Test.TYPE === 2 && ([0, 1, 2, 4, 8].indexOf(data[i].ANSWER) !== -1)) {
message.error(`${i + 1}题为多选题,请选择至少两个选项`);
return;
}
ALLSCORE += data[i].SCORE
if (PASSSCORE == 0) {
PASSSCORE = data[i].Nav_Source.PASSSCORE
}
}
//没过线继续考
if (ALLSCORE < PASSSCORE) {
message.error(`分数不通过,请检查后再提交!`);//(得分${ALLSCORE}通过分数为${PASSSCORE}分)
return false;
} else {
// 判断是静态切换 还是直接提交
let dataS = this.state.data;
let SOURCE_ID = this.state.SOURCE_ID;
if (SOURCE_ID != dataS.Nav_ListUserPaper[dataS.Nav_ListUserPaper.length - 1].SOURCE_ID) {
message.info("请做下一题");
// 切换试题
var filepath = ''
var isOnline = false
var papers = []
var SOURCE_IDNext = ''
var readonly = true
dataS.Nav_ListUserPaper.forEach(ele => {
if ((SOURCE_IDNext == '') && (ele.ANSWER == null || ele.ANSWER == 0)) {
SOURCE_IDNext = ele.SOURCE_ID
readonly = false
}
if (SOURCE_IDNext.length > 0 && ele.SOURCE_ID == SOURCE_IDNext) {
papers.push(ele)
isOnline = ele.IS_ONLINE
if (isOnline == true && filepath == '') {
if (ele.FILE_PATH.indexOf('http') == -1) {
filepath = config.picServerHost + ele.FILE_PATH //后续看看怎么处理 Nav_Source.Nav_Files[i].Nav_ImgFile.FILE_PATH
} else {
filepath = ele.FILE_PATH
this.player.changeVid(filepath.split('vid=')[1])//切换视频
// this.player.changeVid('88083abbf5bcf1356e05d39666be527a_8')
}
}
}
});
this.setState({
data: dataS,
papers: papers,
filepath: filepath,
isOnline: isOnline,
readonly: readonly,
SOURCE_ID: SOURCE_IDNext,
isVodeoEnd: false
})
this.forceUpdate()//刷新加载视频
} else {
//提交答卷
let saveData = () => {
// let data = {
// ID: this.props.data.id,
// // Nav_Config: JSON.parse(JSON.stringify(this.state.config)),
// // Nav_Papers: JSON.parse(JSON.stringify(this.state.papers)),
// Nav_Papers: JSON.parse(JSON.stringify(this.state.data.Nav_ListUserPaper)),
// TaskID: this.props.data.TaskID
// }
this.state.data.TaskID = this.props.data.TaskID
this.props.dispatch({
type: 'app/getDataByPost',
payload: this.state.data,
url: 'WB/WBOutsource/SavePapers',// url: 'WB/WBOutsourceTrainRecord/SavePapers',
onComplete: (ret) => {
if (ret) {
message.success('提交成功');
this.BtnClose();
}
}
});
}
Modal.confirm({
title: '提示',
content: '确定要提交当前问卷么?提交之后不可再次更改',
onOk: () => {
saveData();
},
onCancel() {
},
})
}
}
}
fmtEnum(name, value) {
const enums = this.props.app.enums;
if (!enums || !enums[name]) return '';
return enums[name].enums[value] || '';
}
onTableBtnExport() {
let TableWrap = document.getElementById('tableId' + this.props.data.id);
let Table = TableWrap.getElementsByTagName('table')[0];
const wb = XLSX.utils.table_to_book(Table);
let name = '培训在线答题';
if (this.state.papers && this.state.papers.length > 0) {
let user = this.state.papers[0].Nav_User;
name += `-${user.CODE}-${user.NAME}`;
}
name += '.xlsx';
XLSX.writeFile(wb, name)
}
getJoinDepartment() {
let arr = [];
if (this.state.safe && this.state.safe.Nav_JoinDepartment) {
for (let it of this.state.safe.Nav_JoinDepartment) {
if (it.Nav_Department.NAME == "宁化行洛坑钨矿有限公司") {
arr.push({
id: it.Nav_Department.ID,
name: "公司领导"
})
} else {
arr.push({
id: it.Nav_Department.ID,
name: it.Nav_Department.NAME
})
}
}
}
return arr;
}
doOptionChange = (it, index, mask, evt) => {
//mask 1 2 4 8 16 ... ...
let papers = this.state.papers;
if (evt.target.checked) {
if (it.Nav_Test.TYPE === 2) {
papers[index].ANSWER |= mask;
} else {
papers[index].ANSWER = mask;
}
} else {
papers[index].ANSWER = papers[index].ANSWER & (~mask);
}
if (it.Nav_Test.ANSWER == papers[index].ANSWER) {//答案 与题目答案一致
it.SCORE = it.SCOREVAL
papers[index].SCORE = it.SCOREVAL
} else {
it.SCORE = 0
papers[index].SCORE = 0
}
this.setState({
papers,
})
}
returnModel(level) {
let str = '';
if (level == undefined) {
return str;
}
if (level.indexOf('1') >= 0) {
str += '线上 ';
}
if (level.indexOf('2') >= 0) {
str += '线下 ';
}
return str;
}
getTimeInfo(record) {
var level = record.TRAIN_TYPE
let str = '';
if (level == undefined || level == '') {
return str;
}
if (level.indexOf('1') >= 0 && record.ONLINE_START_TIME != null && record.ONLINE_END_TIME != null) {
str += '线上:' + moment(record.ONLINE_START_TIME).format('yyyy-MM-DD HH:mm') + ' ~ ' + moment(record.ONLINE_END_TIME).format('HH:mm') + ' ';
}
if (level.indexOf('2') >= 0 && record.OFFLINE_START_TIME != null && record.OFFLINE_END_TIME != null) {
str += '线下:' + moment(record.OFFLINE_START_TIME).format('yyyy-MM-DD HH:mm') + ' ~ ' + moment(record.OFFLINE_END_TIME).format('HH:mm');
}
return str;
}
CalcScore = () => {
if (!this.state.readonly) {
return "";
}
let score = 0;
let sscore = 1;
let mscore = 1;
let cscore = 1;
if (this.state.config) {
if (this.state.config.S_TEST_SCORE > 0) {
sscore = this.state.config.S_TEST_SCORE;
}
if (this.state.config.M_TEST_SCORE > 0) {
mscore = this.state.config.M_TEST_SCORE;
}
if (this.state.config.C_TEST_SCORE > 0) {
cscore = this.state.config.C_TEST_SCORE;
}
}
this.state.papers.map((it, idx) => {
if (it.ANSWER !== 0 && it.ANSWER == it.Nav_Test.ANSWER) {
switch (it.Nav_Test.TYPE) {
case 0: {
score += cscore;
break;
}
case 1: {
score += sscore;
break;
}
case 2: {
score += mscore;
break;
}
}
}
})
return score;
}
getType(type, paper) {
let ret = '';
switch (type) {
case 0: ret = `【是非题】(${paper.SCOREVAL}分)`; break;
case 1: ret = `【单选题】(${paper.SCOREVAL}分)`; break;
case 2: ret = `【多选题】(${paper.SCOREVAL}分)`; break;
}
return ret;
}
render() {
//操作页面
//如果是线上试题 先看试题 看完自动跳转答题 答完再下一题
//如果是线下 试题 直接答题
// 答错 继续 答题
//最后一题答完显示提交
const { filepath, isOnline, papers, isVodeoEnd } = this.state;
const tableKey = this.props.data.tableKey;
const enums = this.props.data.enums ? this.props.data.enums : this.props.app.enums;
return <div>
{/* <div style={{ marginTop: '10px' }}>
{
tableKey && (tableKey == '1' || tableKey == '4') ? <Button type={'primary'} style={{ marginLeft: '20px', display: this.state.BtnAgreeDisplay }} onClick={() => this.onTableBtnAgree()} icon="check" >签到</Button> : null
}
</div> */}
<div style={{ textAlign: 'center', marginTop: '30px' }}>
{
papers && papers.length > 0 ? <h1> {papers[0].Nav_Source.NAME + `考试`}</h1> : null
}
</div>
{/* 播放插件 */}
{/* <div key='wrap' className="wrap" >
<div key='player' className="player"></div>
</div> */}
{
(isOnline && filepath && filepath.length > 0) ?
// <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', textAlign: 'center' }}>
// {
// // <video ref={this.videoRef} style={{ marginTop: 5, height: window.innerHeight - 200 }} onEnded={() => this.onEnd()} onTimeUpdate={() => this.TimeUpdate()} src={filepath} controls>
// // <source src={filepath} type="video/mp4" />
// // </video>
// }
// </div> : null
<div key='wrap' className="wrap">
<div key='player' className="player"></div>
</div> : null
}
<div ref={el => (this.componentRef = el)} id={'tableId' + this.props.data.id} style={{ marginTop: '100px' }}>
{/* <h1 style={{ textAlign: 'center' }}>培训在线答题</h1> */}
<Spin spinning={this.state.loading}>
<table style={{ width: '100%', textAlign: 'center', borderTop: '1px solid #333', borderLeft: '1px solid #333' }} className={styles.PrintForm}>
<tbody>
{
isVodeoEnd ? <tr >
<td colSpan={24} rowSpan={1} className={styles.fontBold} ><h2>培训教育在线答题</h2></td>
</tr> : null
}
{
isVodeoEnd && papers.map((it, idx) => {
return (
<tr key={it.ID}>
<td colSpan={24} rowSpan={1}>
<table border={0} className={SEstyles.TestPaperItem}>
<tbody>
<tr>
<td colSpan={24} rowSpan={1} className={SEstyles.TestTiltle}>
<span>{idx + 1}.</span>
<span>{it.Nav_Test.NAME}</span>
<span>{this.getType(it.Nav_Test.TYPE, it)}</span>
</td>
</tr>
<tr style={this.state.readonly && ((it.Nav_Test.ANSWER & 1) != 0) ? answer : null}>
{
it.Nav_Test.TYPE === 0 ?
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 1) != 0) ? styles.answer : null}>
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 1) != 0} onChange={evt => this.doOptionChange(it, idx, 1, evt)}>正确</Radio>
</td>
:
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 1) != 0) ? styles.answer : null}>
{
it.Nav_Test.TYPE === 1 ?
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 1) != 0} onChange={evt => this.doOptionChange(it, idx, 1, evt)} >A.{it.Nav_Test.OPTION_A}</Radio>
:
<Checkbox style={{ marginRight: 10 }} disabled={this.state.readonly} checked={(it.ANSWER & 1) != 0} onChange={evt => this.doOptionChange(it, idx, 1, evt)} >A.{it.Nav_Test.OPTION_A}</Checkbox>
}
</td>
}
</tr>
<tr style={this.state.readonly && ((it.Nav_Test.ANSWER & 2) != 0) ? answer : null}>
{
it.Nav_Test.TYPE === 0 ?
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 2) != 0) ? styles.answer : null}>
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 2) != 0} onChange={evt => this.doOptionChange(it, idx, 2, evt)} >错误</Radio>
</td>
:
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 2) != 0) ? styles.answer : null}>
{
it.Nav_Test.TYPE === 1 ?
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 2) != 0} onChange={evt => this.doOptionChange(it, idx, 2, evt)} >B.{it.Nav_Test.OPTION_B}</Radio>
:
<Checkbox style={marginRight10} disabled={this.state.readonly} checked={(it.ANSWER & 2) != 0} onChange={evt => this.doOptionChange(it, idx, 2, evt)} >B.{it.Nav_Test.OPTION_B}</Checkbox>
}
</td>
}
</tr>
{
it.Nav_Test.TYPE !== 0 &&
<tr style={this.state.readonly && ((it.Nav_Test.ANSWER & 4) != 0) ? answer : null}>
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 4) != 0) ? styles.answer : null}>
{
it.Nav_Test.TYPE === 1 ?
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 4) != 0} onChange={evt => this.doOptionChange(it, idx, 4, evt)} >C.{it.Nav_Test.OPTION_C}</Radio>
:
<Checkbox style={marginRight10} disabled={this.state.readonly} checked={(it.ANSWER & 4) != 0} onChange={evt => this.doOptionChange(it, idx, 4, evt)} >C.{it.Nav_Test.OPTION_C}</Checkbox>
}
</td>
</tr>
}
{
it.Nav_Test.TYPE !== 0 && it.Nav_Test.OPTION_D != "" && it.Nav_Test.OPTION_D != undefined &&
<tr style={this.state.readonly && ((it.Nav_Test.ANSWER & 8) != 0) ? answer : null}>
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 8) != 0) ? styles.answer : null}>
{
it.Nav_Test.TYPE === 1 ?
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 8) != 0} onChange={evt => this.doOptionChange(it, idx, 8, evt)} >D.{it.Nav_Test.OPTION_D}</Radio>
:
<Checkbox style={marginRight10} disabled={this.state.readonly} checked={(it.ANSWER & 8) != 0} onChange={evt => this.doOptionChange(it, idx, 8, evt)} >D.{it.Nav_Test.OPTION_D}</Checkbox>
}
</td>
</tr>
}
{
it.Nav_Test.TYPE !== 0 && it.Nav_Test.OPTION_E != "" && it.Nav_Test.OPTION_E != undefined &&
<tr style={this.state.readonly && ((it.Nav_Test.ANSWER & 16) != 0) ? answer : null}>
<td colSpan={24} rowSpan={1} className={this.state.readonly && ((it.Nav_Test.ANSWER & 16) != 0) ? styles.answer : null}>
{
it.Nav_Test.TYPE === 1 ?
<Radio disabled={this.state.readonly} checked={(it.ANSWER & 16) != 0} onChange={evt => this.doOptionChange(it, idx, 16, evt)} >E.{it.Nav_Test.OPTION_E}</Radio>
:
<Checkbox style={marginRight10} disabled={this.state.readonly} checked={(it.ANSWER & 16) != 0} onChange={evt => this.doOptionChange(it, idx, 16, evt)} >E.{it.Nav_Test.OPTION_E}</Checkbox>
}
</td>
</tr>
}
</tbody>
</table>
</td>
</tr>
)
})
}
{
(isVodeoEnd && !this.state.readonly) ?
<tr>
<td colSpan={24} rowSpan={1} className={styles.fontBold}>
{/* 看看签名 再提交 */}
<Button type="primary" style={{ marginLeft: '8px' }} onClick={() => { this.onSave(); }}>提交问卷</Button>
</td>
</tr>
: null
}
</tbody>
</table>
</Spin>
</div>
</div>
}
}
export default connect(({ login, app }) => ({ login, app }))(WB008Operate)