Commit dd07b172 by zhujian

'添加海报‘

parent 0b6f8a2e
declare module 'jquery' declare module 'jquery'
declare module 'cropper' declare module 'cropper'
declare var this: any declare module 'ExamShareModal'
\ No newline at end of file // declare var this: any
\ No newline at end of file
...@@ -36,6 +36,7 @@ function AddExam(props: any) { ...@@ -36,6 +36,7 @@ function AddExam(props: any) {
const [check, setCheck] = useState(false); const [check, setCheck] = useState(false);
const [getData, setGetData] = useState(false); const [getData, setGetData] = useState(false);
const [preview, setPreview] = useState(false); const [preview, setPreview] = useState(false);
const [examTotal, setExamTotal] = useState(0);
const timer = useRef({}); const timer = useRef({});
const request = useRef(false); const request = useRef(false);
const { match } = props; const { match } = props;
...@@ -56,7 +57,7 @@ function AddExam(props: any) { ...@@ -56,7 +57,7 @@ function AddExam(props: any) {
useEffect(() => { useEffect(() => {
setPassScore(parseInt((paperInfo.totalScore || 0) * (passRate || 0) as any / 100 + '')) setPassScore(parseInt((paperInfo.totalScore || 0) * (passRate || 0) as any / 100 + ''))
setExamTotal(paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0)
}, [paperInfo.paperId, passRate]) }, [paperInfo.paperId, passRate])
function disabledDate(current: any) { function disabledDate(current: any) {
...@@ -277,7 +278,7 @@ function AddExam(props: any) { ...@@ -277,7 +278,7 @@ function AddExam(props: any) {
<div className="item">{paperInfo.judgeCnt || 0}</div> <div className="item">{paperInfo.judgeCnt || 0}</div>
<div className="item">{paperInfo.gapFillingCnt || 0}</div> <div className="item">{paperInfo.gapFillingCnt || 0}</div>
<div className="item long">{paperInfo.indefiniteChoiceCnt || 0}</div> <div className="item long">{paperInfo.indefiniteChoiceCnt || 0}</div>
<div className="item">{paperInfo.singleChoiceCnt + paperInfo.multiChoiceCnt + paperInfo.judgeCnt + paperInfo.gapFillingCnt + paperInfo.indefiniteChoiceCnt || 0}</div> <div className="item">{examTotal}</div>
</div> </div>
<div className="body-list"> <div className="body-list">
<div className="item">{paperInfo.singleChoiceScore || 0}</div> <div className="item">{paperInfo.singleChoiceScore || 0}</div>
...@@ -422,7 +423,27 @@ function AddExam(props: any) { ...@@ -422,7 +423,27 @@ function AddExam(props: any) {
<Button type="primary" onClick={handleSave}>保存</Button> <Button type="primary" onClick={handleSave}>保存</Button>
</div> </div>
{ {
preview && <PreviewModal onClose={() => { setPreview(false) }}></PreviewModal> preview && <PreviewModal
info={{
paperId,
startTime: examStartTime,
endTime: examEndTime,
examName,
passRate: passRate / 100,
examStartTime,
examEndTime,
examDesc,
needPhone,
needOptionDisorder,
resultContent,
answerAnalysis,
resultShow,
examDuration,
passScore,
examTotal,
totalScore: paperInfo.totalScore
}}
onClose={() => { setPreview(false) }}></PreviewModal>
} }
</div> </div>
} }
......
import React from 'react';
import { Modal, Button, message } from 'antd';
import html2canvas from 'html2canvas';
import User from "../../../common/js/user";
import QRCode from '../../../libs/qrcode/qrcode';
import { LIVE_SHARE } from '../../../domains/course-domain/constants';
import Service from '../../../common/js/service';
import { copyText } from '../../../domains/basic-domain/utils';
import './ExamShareModal.less';
class ExamShareModal extends React.Component {
constructor(props) {
super(props);
this.state = {
shareUrl: '',
storeName: User.getStoreName()
}
}
componentDidMount() {
// 获取短链接
this.handleConvertShortUrl();
}
handleConvertShortUrl = () => {
const longUrl = `${LIVE_SHARE}test_detail/${this.props.data.examId}?tenantId=${User.getStoreId()}`
console.log(longUrl)
// 发请求
Service.Sales('public/businessShow/convertShortUrls', {
urls: [longUrl]
}).then((res) => {
const { result = [] } = res;
this.setState({
shareUrl: result[0].shortUrl
}, () => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new QRCode({
text: this.state.shareUrl,
size: 98,
})
qrcodeWrapDom.appendChild(qrcodeNode);
});
})
}
// 下载海报
handleDownloadPoster = () => {
const dom = document.querySelector('#poster');
html2canvas(dom, {
useCORS: true,
}).then(canvas => {
const downloadDOM = document.createElement('a');
const { courseName } = this.props.data;
const dataUrl = canvas.toDataURL('image/png');
downloadDOM.href = dataUrl;
downloadDOM.download = `${courseName}.png`;
downloadDOM.click();
});
}
// 复制分享链接
handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText;
copyText(textContent);
message.success('复制成功!');
}
render() {
const { data } = this.props;
const { shareUrl, storeName } = this.state;
return (
<Modal
title="分享考试"
width={680}
visible={true}
footer={null}
maskClosable={false}
className="ExamShareModal"
onCancel={this.props.close}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="left" id="poster">
<div
className="inst-name oneLineText"
>
{storeName}
</div>
<div style={{ color: '#333' }}>邀请你参与考试:</div>
<div className="examName">{data.examName}</div>
<img
src={'https://image.xiaomaiketang.com/xm/HQXwFsyf3e.png'}
className="course-cover"
alt="course-cover"
/>
<div className="qrcode-wrap">
<div className="qrcode-wrap__left">
<div className="text">长按识别二维码进入观看</div>
<img
className="finger"
alt="finger"
src="https://image.xiaomaiketang.com/xm/thpkWDwJsC.png"
/>
</div>
<div className="qrcode-wrap__right" id="qrcodeWrap">
</div>
</div>
</div>
<div className="right">
<div className="share-poster right__item">
<div className="title">② 海报分享</div>
<div className="sub-title">家长可通过微信识别二维码,报名观看直播</div>
<div className="content" onClick={this.handleDownloadPoster}>下载海报</div>
</div>
<div className="share-url right__item" style={{ marginTop: 40 }}>
<div className="title">① 海报分享</div>
<div className="sub-title">家长可通过微信打开链接,报名观看直播</div>
<div className="content">
<div className="share-url" id="shareUrl">{shareUrl}</div>
<Button
type="primary"
onClick={this.handleCopy}
>复制</Button>
</div>
</div>
</div>
</Modal>
)
}
}
export default ExamShareModal;
.ExamShareModal{
.ant-modal-body {
display: flex;
.left {
width: 303px;
padding: 20px;
margin: 0 32px 0 16px;
box-shadow:0px 2px 10px 0px rgba(0,0,0,0.05);
border-radius: 12px;
.course-cover {
width: 263px;
height: 143px;
border-radius: 6px;
margin-top: 8px;
}
.examName{
font-size: 16px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333333;
line-height: 22px;
max-height: 45px;
overflow: hidden;
// text-overflow: -o-ellipsis-lastline;
// overflow: hidden;
// text-overflow: ellipsis;
// display: -webkit-box;
// -webkit-line-clamp: 2;
// line-clamp: 2;
// -webkit-box-orient: vertical;
}
.qrcode-wrap {
padding: 0 16px;
display: flex;
align-items: center;
margin: 24px 0 16px 0;
&__left {
width: 98px;
text-align: center;
margin-right: 22px;
.text {
line-height: 20px;
}
.finger {
width: 40px;
height: 40px;
margin-top: 8px;
}
}
&__right {
width: 110px;
height: 110px;
padding: 6px
}
}
.inst-name {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
.iconfont {
color: #999;
margin-right: 4px;
}
.text {
font-size: 12px;
color: #999;
}
}
}
.right {
.title {
color: #333;
font-weight: 500;
}
.sub-title {
color: #999;
margin-top: 16px;
}
.content {
display: flex;
align-items: center;
margin-top: 8px;
.share-url {
width: 212px;
overflow: hidden;
height: 29px;
line-height: 32px;
border-radius: 4px 0 0 4px;
padding-left: 12px;
white-space: nowrap;
color: #999999;
background: #EFEFEF;
}
.ant-btn {
margin-left: -2px;
}
}
.share-poster {
.content {
color: rgba(82, 137, 250, 1);
cursor: pointer;
}
}
}
}
}
\ No newline at end of file
import React, { useState, useRef, useEffect, useContext } from 'react' import React, { useState, useRef, useEffect, useContext } from 'react'
import {Input, Select, DatePicker, Tooltip, Button, Table, Dropdown, Menu, Modal } from 'antd'; import { Input, Select, DatePicker, Tooltip, Button, Table, Dropdown, Menu, Modal } from 'antd';
import TeacherSelect from '@/modules/common/TeacherSelect'; import TeacherSelect from '@/modules/common/TeacherSelect';
import { Route, withRouter } from 'react-router-dom'; import { Route, withRouter } from 'react-router-dom';
import Service from "@/common/js/service"; import Service from "@/common/js/service";
...@@ -8,6 +8,7 @@ import { PageControl } from "@/components"; ...@@ -8,6 +8,7 @@ import { PageControl } from "@/components";
import AddExam from './AddExam'; import AddExam from './AddExam';
import User from "@/common/js/user"; import User from "@/common/js/user";
import { XMContext } from "@/store/context"; import { XMContext } from "@/store/context";
import ExamShareModal from './ExamShareModal'
import DataAnalysic from './DataAnalysic' import DataAnalysic from './DataAnalysic'
import './index.less' import './index.less'
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
...@@ -40,6 +41,7 @@ function ExaminationManager(props: any) { ...@@ -40,6 +41,7 @@ function ExaminationManager(props: any) {
const [list, setList] = useState([]); const [list, setList] = useState([]);
const [field, setfield] = useState(''); const [field, setfield] = useState('');
const [order, setOrder] = useState(sortStatus.type); const [order, setOrder] = useState(sortStatus.type);
const [modal, setModal] = useState(null);
const [questionCntSort, setQuestionCntSort] = useState(sortState) const [questionCntSort, setQuestionCntSort] = useState(sortState)
const queryRef = useRef({}); const queryRef = useRef({});
...@@ -142,7 +144,7 @@ function ExaminationManager(props: any) { ...@@ -142,7 +144,7 @@ function ExaminationManager(props: any) {
<div <div
key="share" key="share"
className="operate__item" className="operate__item"
onClick={() => { }} onClick={() => { shareModal(record) }}
> >
分享 分享
</div> </div>
...@@ -161,6 +163,16 @@ function ExaminationManager(props: any) { ...@@ -161,6 +163,16 @@ function ExaminationManager(props: any) {
]; ];
function shareModal(record: any) {
const modal = <ExamShareModal
data={record}
close={() => {
setModal(null)
}}
/>
setModal(modal as any)
}
function getOpe(item: any) { function getOpe(item: any) {
return <Menu> return <Menu>
{ {
...@@ -384,7 +396,9 @@ function ExaminationManager(props: any) { ...@@ -384,7 +396,9 @@ function ExaminationManager(props: any) {
<Route path={`${match.url}/analysic/:id`} render={() => { <Route path={`${match.url}/analysic/:id`} render={() => {
return <DataAnalysic />; return <DataAnalysic />;
}} /> }} />
{
modal
}
</div> </div>
} }
......
.ExamPreviewModal{ .ExamPreviewModal{
// height: 880px;
.ant-modal-content .ant-modal-body{
padding: 0 24px !important;
max-height: 80vh !important;
}
.ExamPreview{
display: flex; display: flex;
}
.left{ .left{
width: 460px; width: 460px;
.phone{
background:url(https://image.xiaomaiketang.com/xm/PpKc6zYKCG.png) ; background:url(https://image.xiaomaiketang.com/xm/PpKc6zYKCG.png) ;
height:746px ; height:746px ;
background-size: 100% 100%; background-size: 100% 100%;
padding-top: 1px;
.content{
width: 326px;
height: 520px;
background-color: #fff;
margin: 137px auto;
position: relative;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
.topContent{
width: 100%;
height: 198px;
background: url(https://image.xiaomaiketang.com/xm/bNHBZZfFzb.png);
background-size: 100% 100%;
padding-top: 1px;
position: relative;
.title{
width: 274px;
height: 60px;
font-size: 22px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #FFFFFF;
// line-height: 30px;
text-align: center;
margin: 20px auto 0;
word-break: break-all;
}
.time{
font-size: 11px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
line-height: 16px;
transform: scale(0.9);
margin-top: 12px;
text-align: center;
}
.rule{
width: 298px;
height: 113px;
background: #FFFFFF;
box-shadow: 0px -13px 9px 0px rgba(0, 34, 121, 0.1);
border-radius: 3px 3px 0px 0px;
position: absolute;
left: 14px;
top: 124px;
background: #fff;
display: flex;
.item{
width: 25%;
text-align: center;
.num{
margin-top: 28px;
font-size: 17px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 24px;
}
.text{
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 16px;
transform: scale(0.9);
margin-top: 12px;
.dw{
transform: scale(0.8);
}
}
}
}
}
.fg{
margin-top: 60px;
text-align: center;
img{
width: 164px;
}
}
.examDesc{
width: 298px;
margin: 4px auto 0;
}
}
}
}
.right{
width: 353px;
.title{
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 22px;
text-align: center;
margin-bottom: 16px;
margin-top: 48px;
}
.rule{
background: #F4F6FA;
border-radius: 4px;
padding: 16px;
padding-bottom: 8px;
.item{
margin-bottom: 8px;
.name{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 14px;
width: 90px;
display: inline-block;
margin-right: 4px;
}
.text{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
}
}
}
} }
} }
\ No newline at end of file
import React, { useState, useRef, useEffect, useContext } from 'react' import React, { useState, useRef, useEffect, useContext } from 'react'
import { Input, Select, DatePicker, Tooltip, Button, Table, Dropdown, Menu, Modal } from 'antd'; import moment from 'moment';
import { Modal } from 'antd';
import './PreviewModal.less' import './PreviewModal.less'
function PreviewModal(props: any) { function PreviewModal(props: any) {
console.log(props.info)
const resultContentEnum = {
PASS_AND_SCORE: '显示考试分数和是否及格',
ONLY_SCORE: '仅显示考试分数',
ONLY_PASS: '仅显示是否及格'
}
const answerAnalysisEnum = {
ANALYSE_AND_RIGHT_OR_WRONG: '显示对错与解析',
RIGHT_OR_WRONG: '仅显示对错',
CAN_NOT_CHECK: '都不显示'
}
return <Modal return <Modal
title="预览" title="预览"
...@@ -11,15 +23,74 @@ function PreviewModal(props: any) { ...@@ -11,15 +23,74 @@ function PreviewModal(props: any) {
maskClosable={true} maskClosable={true}
visible={true} visible={true}
footer={null} footer={null}
className='ExamPreviewModal'
closable={true} closable={true}
width={800} width={800}
> >
<div className="ExamPreviewModal"> <div className="ExamPreview">
<div className="left"> <div className="left">
<div className="phone"></div> <div className="phone">
<div className="content">
<div className="topContent">
<div className="title" style={{ fontSize: props.info.examName.length > 24 ? 13 : 22 }}>{props.info.examName || ' '}</div>
{
props.info.examStartTime && <div className="time">{moment(props.info.examStartTime).format("YYYY-MM-DD HH:mm")}~{moment(props.info.examEndTime).format("YYYY-MM-DD HH:mm")}</div>
}
<div className="rule">
<div className="item">
<div className="num">{props.info.totalScore || 0}</div>
<div className="text">总分 <span className="dw">(分)</span></div>
</div>
<div className="item">
<div className="num">{props.info.examTotal || 0}</div>
<div className="text">总题数<span className="dw">(道)</span></div>
</div>
<div className="item">
<div className="num">{props.info.passScore || 0}</div>
<div className="text">及格分<span className="dw">(分)</span></div>
</div>
<div className="item">
<div className="num">{props.info.examDuration || 60}</div>
<div className="text">时长<span className="dw">(分钟)</span></div>
</div>
</div>
</div>
<div className="fg">
<img src="https://image.xiaomaiketang.com/xm/8ASbb6TQ6Q.png" alt="" />
</div>
<div className="examDesc" dangerouslySetInnerHTML={{ __html: props.info.examDesc }}></div>
</div>
</div>
</div>
<div className="right">
<div className="title">本次考试设置</div>
<div className="rule">
<div className="item">
<span className="name"> 身份证验证: </span>
<span className="text">{props.info.needPhone == 'NEED_PHONE_VERIFY' ? '需要验证手机号才能参加考试' : '仅需微信/企业微信授权登录验证'}</span>
</div>
<div className="item" style={{marginBottom:18}}>
<span className="name"> 选择乱序: </span>
<span className="text">{props.info.needOptionDisorder == 'OPTION_RANDOM' ? '选择题的选项随机排序' : '选择题按题目原有顺序展示'}</span>
</div>
<div className="item">
<span className="name"> 考试结果查看: </span>
<span className="text">{props.info.resultShow === 'IMMEDIATELY' ? '交卷后立即显示考试结果' : '到达考试截止日期才显示结果'}</span>
</div>
<div className="item">
<span className="name"> 考试结果内容: </span>
<span className="text">{(resultContentEnum as any)[props.info.resultContent]}</span>
</div>
<div className="item">
<span className="name"> 答案与解析: </span>
<span className="text">{(answerAnalysisEnum as any)[props.info.answerAnalysis]}</span>
</div>
</div>
</div> </div>
<div className="right"></div>
</div> </div>
</Modal> </Modal>
} }
......
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