Commit 0eedc63e by maolipeng

Merge branch 'master' into feature/maolipeng/20210610/zuhuyouhua

# Conflicts:
#	src/data-source/base/request-apis.ts
#	src/domains/basic-domain/baseService.ts
#	src/modules/course-manage/components/LiveCourseList.jsx
#	src/modules/course-manage/graphics-course/components/GraphicsCourseList.jsx
#	src/modules/root/Login.jsx
#	src/modules/root/Menu.less
#	src/modules/root/Menu.tsx
#	src/modules/teach-tool/paper-manage/OperatePaper.jsx
#	src/modules/teach-tool/paper-manage/OperatePaper.less
parents 466cbc53 7638b279
......@@ -12,7 +12,6 @@ npm-shrinkwrap.json
.DS_Store
.AppleDouble
.LSOverride
yarn.lock
yarn-error.lock
# IntelliJ project files
......@@ -57,7 +56,6 @@ npm-debug.log
*.zip
build/vendor.js.map
package-lock.json
.vscode/*
demo.js
debug.log
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* @Author: yuananting
* @Date: 2021-03-03 15:13:12
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-06-16 09:57:18
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-22 14:31:46
* @Description: 助学工具接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......
/*
* @Author: wufan
* @Date: 2020-12-01 17:21:21
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-04-09 14:28:09
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-22 14:56:34
* @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
import User from "@/common/js/user";
import axios from 'axios';
export function sendLoginAuthCode(params: object) {
return Service.Hades("anon/hades/sendLoginAuthCode", params);
......@@ -51,6 +52,16 @@ export function getWXWorkLoginNoCheck(params: object) {
export function getLesseeVersionMsg() {
return Service.Hades("public/customerHades/getLesseeVersionMsg",{enterpriseId:User.getEnterpriseId()})
}
export function getYoZoSign(params: object) {
return Service.Apollo('public/apollo/getYoZoSign', params);
}
export function saveYoZoFileVersionId(params: object) {
return Service.Apollo('public/apollo/saveYoZoFileVersionId', params);
}
export function yoZoUpload(ossUrl:String,appId:String,uploadSign:String){
return axios.post(`https://dmc.yozocloud.cn/api/file/http?fileUrl=${ossUrl}&appId=${appId}&sign=${uploadSign}`)
}
export const getOssClient = (
data: object,
instId: string,
......@@ -67,3 +78,4 @@ export const getOssClient = (
});
}
/*
* @Author: wufan
* @Date: 2020-12-01 17:20:49
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-04-09 14:28:59
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-22 14:57:01
* @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import { getUserStore, getUserPermission ,logout,getStoreUser,sendBizAuthCode,editUserPhone,checkBizAuthCode,sendNewPhoneAuthCode,sendLoginAuthCode,login,getLastedVersion, getEnterpriseUser,getWXWorkLoginNoCheck,getLesseeVersionMsg} from '@/data-source/base/request-apis';
import { getUserStore, getUserPermission ,logout,getStoreUser,sendBizAuthCode,editUserPhone,checkBizAuthCode,sendNewPhoneAuthCode,sendLoginAuthCode,login,getLastedVersion, getEnterpriseUser,getWXWorkLoginNoCheck,getLesseeVersionMsg,getYoZoSign,saveYoZoFileVersionId,yoZoUpload} from '@/data-source/base/request-apis';
export default class StoreService {
// 获取员工列表
......@@ -59,4 +59,18 @@ export default class StoreService {
static getLesseeVersionMsg() {
return getLesseeVersionMsg();
}
static getYoZoSign(params:any){
return new Promise((resolve) => {
getYoZoSign(params).then(res => {
const { result = [] } = res;
resolve(result)
});
})
}
static saveYoZoFileVersionId(params: any){
return saveYoZoFileVersionId(params);
}
static yoZoUpload(ossUrl:String,appId:String,uploadSign:String){
return yoZoUpload(ossUrl,appId,uploadSign);
}
}
\ No newline at end of file
/*
* @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-06-11 16:44:17
* @LastEditTime: 2021-06-22 16:49:06
* @LastEditors: Please set LastEditors
* @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
......@@ -26,6 +26,10 @@ const PATH_MAP: MapInterface = {
prod: 'https://res.xiaomai0.com/xiaomai-cloud-class-web/h5.html',
}
export const YZ_APPId = "yozoqvpO2Hvz8346";
export const YZ_PREVIEW_URL: string = 'http://eic.yozocloud.cn/api/view/file'
export const OFFICE_PREVIEW_URL: string = 'https://view.officeapps.live.com/op/view.aspx'
// axios headers config
export const TIME_OUT: number = 20000
export const USER_TYPE: string = 'B'
......@@ -37,3 +41,4 @@ export const USER_PREFIX = 'store-live'
// host
export const BASIC_HOST: string = BASIC_HOST_MAP[ENV]
export const PATH: string = PATH_MAP[ENV]
......@@ -2,7 +2,7 @@
* @Author: 吴文洁
* @Date: 2020-04-27 20:35:34
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-06-23 14:14:20
* @LastEditTime: 2021-06-28 16:15:59
* @Description:
*/
......@@ -21,6 +21,7 @@ import '@/core/function';
import '@/core/xmTD';
import User from '@/common/js/user';
import Service from "@/common/js/service";
import BaseService from '@/domains/basic-domain/baseService';
declare var getParameterByName: any;
declare var window: any;
......@@ -71,7 +72,27 @@ if (getParameterByName('code') && isWeiXin()) {
window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId;
mount()
})
} else {
} else if(getParameterByName('from') === 'customer' && getParameterByName('enterpriseId') && getParameterByName('userId')){
User.setCustomerStoreId(getParameterByName('storeId'));
getWXWorkLoginNoCheck(getParameterByName('enterpriseId'),getParameterByName('userId')); //从C端跳转过来的学院自动执行免登录
}else{
mount()
}
function getWXWorkLoginNoCheck(enterpriseId:string,userId:string) {
const params = {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
enterpriseId,
userId,
};
BaseService.getWXWorkLoginNoCheck(params).then((res:any) => {
User.setUserId(res.result.loginInfo.userId)
User.setToken(res.result.loginInfo.xmToken)
User.setEnterpriseId(res.result.enterpriseId)
window.currentStoreUserInfo = {}
window.currentStoreUserInfo.userId = res.result.loginInfo.userId;
window.currentStoreUserInfo.token = res.result.loginInfo.xmToken;
window.currentStoreUserInfo.enterpriseId = res.result.enterpriseId;
User.setIdentifier(res.result.identifier)
mount();
});
}
import React from "react";
import { withRouter } from "react-router-dom";
import { Table, Popover, message, Button, Spin } from "antd";
import dealTimeDuration from "../utils/dealTimeDuration";
import { PageControl } from "@/components";
import CourseService from "@/domains/course-domain/CourseService";
import User from "@/common/js/user";
import "./DataList.less";
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import CourseService from '@/domains/course-domain/CourseService';
import { Button, message, Popover, Spin } from 'antd';
import React from 'react';
import { withRouter } from 'react-router-dom';
import dealTimeDuration from '../utils/dealTimeDuration';
import './DataList.less';
class DataList extends React.Component {
constructor(props) {
super(props);
const courseId = getParameterByName("id"); // 课程ID
const type = getParameterByName("type"); // 来源: 大班直播 large 互动班课 interactive
const courseId = getParameterByName('id'); // 课程ID
const type = getParameterByName('type'); // 来源: 大班直播 large 互动班课 interactive
this.state = {
type,
teacherData: [], // 老师上课数据
......@@ -68,34 +69,30 @@ class DataList extends React.Component {
getVisiterColumns() {
const columns = [
{
title: "序号",
dataIndex: "index",
title: '序号',
dataIndex: 'index',
width: 50,
render: (text, record, index) => {
return <span>{index + 1}</span>;
},
},
{
title: "进入时间",
dataIndex: "entryTime",
title: '进入时间',
dataIndex: 'entryTime',
width: 150,
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{
title: "离开时间",
dataIndex: "leaveTime",
title: '离开时间',
dataIndex: 'leaveTime',
width: 150,
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{
title: "在线时长",
dataIndex: "lookingDuration",
title: '在线时长',
dataIndex: 'lookingDuration',
render: (text, record) => {
return <span>{text ? dealTimeDuration(text) : "-"}</span>;
return <span>{text ? dealTimeDuration(text) : '-'}</span>;
},
},
];
......@@ -106,43 +103,44 @@ class DataList extends React.Component {
getStudentColumns() {
const columns = [
{
title: "学员姓名",
dataIndex: "userName",
title: '学员姓名',
dataIndex: 'userName',
},
{
title: "手机号",
dataIndex: "phone",
title: '手机号',
dataIndex: 'phone',
render: (text, record) => {
const { phone = "", bindingWeChat } = record;
const { phone = '', bindingWeChat } = record;
return <div>{phone}</div>;
},
},
{
title: "观看次数",
dataIndex: "entryNum",
title: '观看次数',
dataIndex: 'entryNum',
render: (text, record) => {
if (text > 0) {
if (
record.visitorInfoVOList &&
record.visitorInfoVOList.length > 0
) {
if (record.visitorInfoVOList && record.visitorInfoVOList.length > 0) {
const table = (
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
columns={this.getVisiterColumns()}
dataSource={record.visitorInfoVOList}
scroll={{ y: 75 }}
size={"small"}
size={'small'}
style={{ width: 450 }}
pagination={false}
/>
);
return (
<Popover content={table} trigger="click">
<span className="handel-btn">{text}</span>
<Popover content={table} trigger='click'>
<span className='handel-btn'>{text}</span>
</Popover>
);
} else {
return <div className="live-table--empty">暂无观看数据</div>;
return <div className='live-table--empty'>暂无观看数据</div>;
}
} else {
return <span>{text}</span>;
......@@ -150,13 +148,13 @@ class DataList extends React.Component {
},
},
{
title: "累计在线时长",
dataIndex: "totalDuration",
title: '累计在线时长',
dataIndex: 'totalDuration',
sorter: (a, b) => a.totalDuration - b.totalDuration,
sortDirections: ["descend", "ascend"],
sortDirections: ['descend', 'ascend'],
render: (text, record) => {
//如无离开时间,就置空
return <span>{text ? dealTimeDuration(text) : "00:00:00"}</span>;
return <span>{text ? dealTimeDuration(text) : '00:00:00'}</span>;
},
},
];
......@@ -169,17 +167,17 @@ class DataList extends React.Component {
CourseService.exportStudentCourseData({
liveCourseId: liveCourseId,
exportLiveType: "VISITOR",
exportLiveType: 'VISITOR',
storeId,
}).then((res) => {
const link = res.result;
this.setState({
link
link,
});
document.getElementById("loadExcel").click();
document.getElementById('loadExcel').click();
if (res.success) {
message.success("导出成功!");
message.success('导出成功!');
}
});
};
......@@ -187,27 +185,28 @@ class DataList extends React.Component {
handleCheckEnterTimes = () => {
const { teacherData } = this.state;
if (teacherData.entryNum > 0) {
if (
teacherData.visitorInfoVOList &&
teacherData.visitorInfoVOList.length > 0
) {
if (teacherData.visitorInfoVOList && teacherData.visitorInfoVOList.length > 0) {
const table = (
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
columns={this.getVisiterColumns()}
dataSource={teacherData.visitorInfoVOList}
scroll={{ y: 75 }}
size={"small"}
size={'small'}
style={{ width: 450 }}
pagination={false}
/>
);
return (
<Popover content={table} trigger="click">
<span className="times-num">{teacherData.entryNum}</span>
<Popover content={table} trigger='click'>
<span className='times-num'>{teacherData.entryNum}</span>
</Popover>
);
} else {
return <div className="live-table--empty">暂无观看数据</div>;
return <div className='live-table--empty'>暂无观看数据</div>;
}
} else {
return <span>{teacherData.entryNum}</span>;
......@@ -216,14 +215,18 @@ class DataList extends React.Component {
showTable = () => {
return (
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
columns={this.getVisiterColumns()}
dataSource={this.state.teacherData.visitorInfoVOList}
scroll={{ y: 80 }}
size={"small"}
size={'small'}
style={{ width: 450 }}
pagination={false}
className="visitor-num-table"
className='visitor-num-table'
/>
);
};
......@@ -232,114 +235,87 @@ class DataList extends React.Component {
if (current == size) {
return;
}
this.setState({ size }, this.fetchUserData)
}
this.setState({ size }, this.fetchUserData);
};
render() {
const {
teacherData,
studentData,
current,
size,
total,
loading,
link
} = this.state;
const { teacherData, studentData, current, size, total, loading, link } = this.state;
return (
<Spin spinning={loading}>
<a
href={link}
target="_blank"
download
id="loadExcel"
style={{ position: "absolute", left: "-10000px" }}
>
<a href={link} target='_blank' download id='loadExcel' style={{ position: 'absolute', left: '-10000px' }}>
111
</a>
{/* 讲师上课数据 */}
<div className="courseData-teacher">
<p className="title">讲师上课数据</p>
<div className="teacher-course-data">
<div className="avatar-name-phone item-block">
<img
src={
teacherData.avatar ||
"https://image.xiaomaiketang.com/xm/rJeQaZxtc7.png"
}
alt=""
className="avatar"
/>
<div className="right">
<div className="name">{teacherData.userName}</div>
<div className="phone">
{teacherData.phone}
</div>
<div className='courseData-teacher'>
<p className='title'>讲师上课数据</p>
<div className='teacher-course-data'>
<div className='avatar-name-phone item-block'>
<img src={teacherData.avatar || 'https://image.xiaomaiketang.com/xm/rJeQaZxtc7.png'} alt='' className='avatar' />
<div className='right'>
<div className='name'>{teacherData.userName}</div>
<div className='phone'>{teacherData.phone}</div>
</div>
</div>
<div className="times item-block">
<div
className={`times-num ${
Number(teacherData.entryNum) > 0 ? "can-click" : ""
}`}
>{
Number(teacherData.entryNum) > 0 ?
<Popover content={this.showTable()} trigger="click">
<span className="times-num">{teacherData.entryNum || 0}</span>
</Popover>:
<span className="times-num">{0}</span>
}
<div className='times item-block'>
<div className={`times-num ${Number(teacherData.entryNum) > 0 ? 'can-click' : ''}`}>
{Number(teacherData.entryNum) > 0 ? (
<Popover content={this.showTable()} trigger='click'>
<span className='times-num'>{teacherData.entryNum || 0}</span>
</Popover>
) : (
<span className='times-num'>{0}</span>
)}
</div>
<div className="text">进入直播间次数</div>
<div className='text'>进入直播间次数</div>
</div>
<div className="online-duration item-block">
<div className="duration">
{teacherData.totalDuration
? dealTimeDuration(teacherData.totalDuration)
: "00:00:00"}
</div>
<div className="text">累计在线时长</div>
<div className='online-duration item-block'>
<div className='duration'>{teacherData.totalDuration ? dealTimeDuration(teacherData.totalDuration) : '00:00:00'}</div>
<div className='text'>累计在线时长</div>
</div>
</div>
</div>
{/* 学员上课数据 */}
<div className="courseData-student">
<p className="title">学员上课数据</p>
<div className="filter-wrap">
<div className="filter">
<div className='courseData-student'>
<p className='title'>学员上课数据</p>
<div className='filter-wrap'>
<div className='filter'>
<Button
style={{ height: 32}}
style={{ height: 32 }}
onClick={_.debounce(
() => {
if (!studentData.length) {
message.warning("暂无数据可导出");
message.warning('暂无数据可导出');
return;
}
this.handleExportV5();
},
500,
true
)}
>
)}>
导出
</Button>
</div>
</div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
bordered
size="small"
size='small'
scroll={{ y: 600 }}
columns={this.getStudentColumns()}
dataSource={studentData}
pagination={false}
style={{ margin: "16px 0" }}
style={{ margin: '16px 0' }}
/>
{ total > 0 &&
{total > 0 && (
<PageControl
size="small"
size='small'
current={current - 1}
pageSize={size}
total={total}
......@@ -348,7 +324,7 @@ class DataList extends React.Component {
this.fetchUserData(page + 1);
}}
/>
}
)}
</div>
</Spin>
);
......
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import CourseService from '@/domains/course-domain/CourseService';
import { Button, message } from 'antd';
import React from 'react';
import { withRouter } from "react-router-dom";
import { Table, Button, Modal, message } from 'antd';
import { withRouter } from 'react-router-dom';
import dealTimeDuration from '../utils/dealTimeDuration';
import { PageControl } from "@/components";
import './DataList.less';
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user';
const liveTypeMap = {
USER: "普通用户",
ANCHOR: "讲师",
ADMIN: "管理员(助教)",
GUEST: "游客"
USER: '普通用户',
ANCHOR: '讲师',
ADMIN: '管理员(助教)',
GUEST: '游客',
};
class PlaybackData extends React.Component {
constructor(props) {
super(props);
const courseId = getParameterByName("id"); // 课程ID
const courseId = getParameterByName('id'); // 课程ID
this.state = {
playbackData: [],
......@@ -24,8 +25,8 @@ class PlaybackData extends React.Component {
size: 10,
total: 0,
liveCourseId: courseId,
storeId: User.getStoreId()
}
storeId: User.getStoreId(),
};
}
componentDidMount() {
......@@ -33,12 +34,12 @@ class PlaybackData extends React.Component {
}
fetchPlaybackList = (page = 1) => {
const { size, liveCourseId } = this.state
const { size, liveCourseId } = this.state;
const params = {
liveCourseId,
current: page,
size
}
size,
};
CourseService.fetchPlaybackList(params).then((res) => {
if (res.result) {
......@@ -47,38 +48,35 @@ class PlaybackData extends React.Component {
playbackData: records,
current,
size,
total
total,
});
}
});
};
getPlaybackColumns() {
const columns = [
{
title: "观看学员",
dataIndex: "userName",
title: '观看学员',
dataIndex: 'userName',
},
{
title: "手机号",
dataIndex: "phone"
title: '手机号',
dataIndex: 'phone',
},
{
title: "观看者类型",
dataIndex: "userRole",
title: '观看者类型',
dataIndex: 'userRole',
render: (text) => <span>{liveTypeMap[text]}</span>,
},
{
title: "开始观看时间",
dataIndex: "entryTime",
render: (text) => (
<span>{text ? formatDate("YYYY-MM-DD H:i", parseInt(text)) : '-'}</span>
),
title: '开始观看时间',
dataIndex: 'entryTime',
render: (text) => <span>{text ? formatDate('YYYY-MM-DD H:i', parseInt(text)) : '-'}</span>,
},
{
title: "观看时长",
dataIndex: "lookingDuration",
title: '观看时长',
dataIndex: 'lookingDuration',
render: (text) => {
return <span>{text ? dealTimeDuration(text) : '-'}</span>;
},
......@@ -92,53 +90,55 @@ class PlaybackData extends React.Component {
CourseService.exportPlayBackCourseData({
liveCourseId,
exportLiveType: "PLAY_BACK",
storeId
exportLiveType: 'PLAY_BACK',
storeId,
}).then((res) => {
const link = res.result;
this.setState({
link
link,
});
document.getElementById("load-play-back-excel").click();
document.getElementById('load-play-back-excel').click();
if(res.success){
message.success("导出成功!")
if (res.success) {
message.success('导出成功!');
}
})
});
}
onShowSizeChange = (current, size) => {
if (current == size) {
return;
}
this.setState({ size }, this.fetchUserData)
}
this.setState({ size }, this.fetchUserData);
};
render() {
const { playbackData, total, current, size, link} = this.state
const { playbackData, total, current, size, link } = this.state;
return (
<div>
<a
href={link}
target="_blank"
download
id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }}
>
<a href={link} target='_blank' download id='load-play-back-excel' style={{ position: 'absolute', left: '-10000px' }}>
111
</a>
<Button onClick={() => {this.handleplaybackExport()}}>导出</Button>
<Table
<Button
onClick={() => {
this.handleplaybackExport();
}}>
导出
</Button>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
bordered
size="small"
size='small'
columns={this.getPlaybackColumns()}
dataSource={playbackData}
pagination={false}
style={{ margin: '16px 0' }}>
</Table>
{ total > 0 &&
style={{ margin: '16px 0' }}></XMTable>
{total > 0 && (
<PageControl
size="small"
size='small'
current={current - 1}
pageSize={size}
total={total}
......@@ -147,9 +147,9 @@ class PlaybackData extends React.Component {
this.fetchPlaybackList(page + 1);
}}
/>
}
)}
</div>
)
);
}
}
......
......@@ -6,48 +6,45 @@
* @Description: 大班直播、互动班课的直播课列表
*/
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import DownloadLiveModal from '@/components/DownloadLiveModal';
import BaseService from '@/domains/basic-domain/baseService';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import React from 'react';
import { Table, Modal, message, Dropdown, Button,Switch,Tooltip} from 'antd';
import { Route, withRouter } from 'react-router-dom';
import { PageControl } from "@/components";
import DownloadLiveModal from '@/components/DownloadLiveModal';
import _ from 'underscore';
import DataList from '../DataList/DataList';
import ManageCoursewareModal from '../modal/ManageCoursewareModal';
import ShareLiveModal from '../modal/ShareLiveModal';
import RelatedPlanModal from '../modal/RelatedPlanModal';
import ShareLiveModal from '../modal/ShareLiveModal';
import './LiveCourseList.less';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { appId, shareUrl, LIVE_SHARE,LIVE_REPLAY_MAP} from '@/domains/course-domain/constants';
import CourseService from "@/domains/course-domain/CourseService";
import BaseService from "@/domains/basic-domain/baseService";
import DataList from '../DataList/DataList';
import User from '@/common/js/user';
import _ from "underscore";
const { confirm } = Modal;
const courseStateShow = {
UN_START: {
code: 1,
title: "待开课",
color: "#FFB129",
title: '待开课',
color: '#FFB129',
},
STARTING: {
code: 2,
title: "上课中",
color: "#238FFF",
title: '上课中',
color: '#238FFF',
},
FINISH: {
code: 3,
title: "已完成",
color: "#3BBDAA",
title: '已完成',
color: '#3BBDAA',
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#999",
title: '未成功开课',
color: '#999',
},
};
class LiveCourseList extends React.Component {
......@@ -55,18 +52,17 @@ class LiveCourseList extends React.Component {
super(props);
this.state = {
columns: [],
openDownloadModal:false,
url:'',
RelatedPlanModalVisible:false,
selectPlanList:{}
}
openDownloadModal: false,
url: '',
RelatedPlanModalVisible: false,
selectPlanList: {},
};
}
componentWillMount(){
componentWillMount() {
this.parseColumns();
}
componentDidMount() {
this.getDownloadVersion()
this.getDownloadVersion();
}
// 显示分享弹窗
handleShowShareModal = (item, needStr = false) => {
......@@ -74,27 +70,27 @@ class LiveCourseList extends React.Component {
const { liveCourseId } = item;
const htmlUrl = `${LIVE_SHARE}live_detail/${liveCourseId}?id=${User.getStoreId()}`;
const longUrl = htmlUrl
console.log('htmlUrl',htmlUrl,longUrl);
const longUrl = htmlUrl;
console.log('htmlUrl', htmlUrl, longUrl);
const shareData = { ...item, longUrl };
const shareLiveModal = (
<ShareLiveModal
needStr={needStr}
data={shareData}
type="liveClass"
title="直播课"
type='liveClass'
title='直播课'
close={() => {
this.setState({
shareLiveModal: null
shareLiveModal: null,
});
localStorage.setItem('largeLiveCourseItem', '');
}}
/>
)
);
this.setState({ shareLiveModal })
}
this.setState({ shareLiveModal });
};
//改变上架状态
changeShelfState = (index,item,checked) => {
......@@ -117,155 +113,179 @@ class LiveCourseList extends React.Component {
}
this.props.changeShelfState(index,_shelfState)
}
})
}
});
};
// 前往上课数据页面
handleLinkToClassData = (item) => {
const { match } = this.props;
window.RCHistory.push({
pathname: `${match.url}/live-course-data?type=large&id=${item.liveCourseId}`
pathname: `${match.url}/live-course-data?type=large&id=${item.liveCourseId}`,
});
}
};
parseColumns = () => {
const menu = (item) => (
<div className="live-course-more-menu">
<div>
上课记录
</div>
<div className='live-course-more-menu'>
<div>上课记录</div>
<div
onClick={() => {
this.handleShowRepeatModal(item);
}}
>
}}>
回放记录
</div>
</div>
);
let columns
let columns;
const userRole = User.getUserRole();
if(userRole !=="CloudLecturer"){
if (userRole !== 'CloudLecturer') {
columns = [
{
title: "直播课",
width: "23%",
key: "course",
dataIndex: "courseName",
title: '直播课',
width: '23%',
key: 'course',
dataIndex: 'courseName',
render: (val, record) => {
let hasCover = false;
return (
<div className="record__item">
{
record.courseMediaVOS.map((item,index)=>{
if( item.contentType === "COVER"){
<div className='record__item'>
{record.courseMediaVOS.map((item, index) => {
if (item.contentType === 'COVER') {
hasCover = true;
return <img className="course-cover" src={item.mediaUrl} />
}
})
}
{ !hasCover &&
<img className="course-cover" src={'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'} />
return <img className='course-cover' src={item.mediaUrl} />;
}
})}
{!hasCover && <img className='course-cover' src={'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'} />}
<div>
{ record.courseName.length > 17?
{record.courseName.length > 17 ? (
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
:
<div className="course-name">{record.courseName}</div>
}
) : (
<div className='course-name'>{record.courseName}</div>
)}
<div>
<span className="course-time">{formatDate("YYYY-MM-DD H:i",parseInt(record.startTime))}~{formatDate("H:i", parseInt(record.endTime))}</span>
<span className="course-status" style={{color:courseStateShow[record.courseState].color,border:`1px solid ${courseStateShow[record.courseState].color}`}}>{courseStateShow[record.courseState].title}</span>
<span className='course-time'>
{formatDate('YYYY-MM-DD H:i', parseInt(record.startTime))}~{formatDate('H:i', parseInt(record.endTime))}
</span>
<span
className='course-status'
style={{ color: courseStateShow[record.courseState].color, border: `1px solid ${courseStateShow[record.courseState].color}` }}>
{courseStateShow[record.courseState].title}
</span>
</div>
<div className="teacher-assistant">
{ record.teacherName.length > 4 ?
<div className='teacher-assistant'>
{record.teacherName && record.teacherName.length > 4 ? (
<Tooltip title={record.teacherName}>
<span className="teacher">讲师:{record.teacherName}</span>
<span className='teacher'>讲师:{record.teacherName}</span>
</Tooltip>
:
<span className="teacher">讲师:{record.teacherName}</span>
}
) : (
<span className='teacher'>讲师:{record.teacherName}</span>
)}
{ record.admins.length >0 &&
{record.admins.length > 0 && (
<>
<span className="split"> | </span>
{ this.handleAdminName(record.admins).length > 4?
<span className='split'> | </span>
{this.handleAdminName(record.admins).length > 4 ? (
<Tooltip title={this.handleAdminName(record.admins)}>
<span className="assistant">助教:
{ record.admins.map((item,index)=>{
return <span>{item.adminName} { (index < record.admins.length-1)&&(<span></span>)} </span>
})
}
<span className='assistant'>
助教:
{record.admins.map((item, index) => {
return (
<span>
{item.adminName} {index < record.admins.length - 1 && <span></span>}{' '}
</span>
);
})}
</span>
</Tooltip>
:
<span className="assistant">助教:
{ record.admins.map((item,index)=>{
return <span>{item.adminName} { (index < record.admins.length-1)&&(<span></span>)} </span>
})
}
) : (
<span className='assistant'>
助教:
{record.admins.map((item, index) => {
return (
<span>
{item.adminName} {index < record.admins.length - 1 && <span></span>}{' '}
</span>
}
);
})}
</span>
)}
</>
}
)}
</div>
</div>
</div>
)
}
);
},
},
{
title: "课程分类",
width: "10%",
key: "couseCatalog",
dataIndex: "couseCatalog",
title: '课程分类',
width: '10%',
key: 'couseCatalog',
dataIndex: 'couseCatalog',
render: (val, item) => {
return (
<div className="categoryName">{item.categoryName}</div>
)
return <div className='categoryName'>{item.categoryName}</div>;
},
},
{
title: "课件管理",
width: "8%",
key: "courseware",
dataIndex: "courseware",
title: '课件管理',
width: '8%',
key: 'courseware',
dataIndex: 'courseware',
render: (val, item) => {
return (
<span className="courseware"
<span
className='courseware'
onClick={() => {
this.setState({
editData: item,
openCoursewareModal: true,
});
}}>{item.courseDocumentCount}</span>
}}>
{item.courseDocumentCount}
</span>
);
},
},
{
title: '上课数据',
width: "9%",
key: "quota",
dataIndex: "quota",
width: '9%',
key: 'quota',
dataIndex: 'quota',
render: (val, item) => {
return (
<span className="iconfont icon quota-icon" onClick={() => {
this.handleLinkToClassData(item)
}}>&#xe7d6;</span>
<span
className='iconfont icon quota-icon'
onClick={() => {
this.handleLinkToClassData(item);
}}>
&#xe7d6;
</span>
);
},
},
{
title: <span>
title: (
<span>
<span>学院展示</span>
<Tooltip title={<div>开启后,学员可在学院内查看到此课程。若课程“未成功开课”,则系统会自动“关闭”学院展示。<br/>关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。</div>}><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:'normal'}}>&#xe61d;</i></Tooltip>
</span>,
width: "9%",
key: "shelfState",
dataIndex: "shelfState",
<Tooltip
title={
<div>
开启后,学员可在学院内查看到此课程。若课程“未成功开课”,则系统会自动“关闭”学院展示。
<br />
关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: 'normal' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: '9%',
key: 'shelfState',
dataIndex: 'shelfState',
render: (val, item, index) => {
return <Switch
checked={item.shelfState === "YES"}
......@@ -275,354 +295,373 @@ class LiveCourseList extends React.Component {
},
{
title: '创建时间',
width: "9%",
key: "created",
dataIndex: "created",
width: '9%',
key: 'created',
dataIndex: 'created',
sorter: true,
render: (val, item) => {
return (
<span>{formatDate('YYYY-MM-DD H:i', val)}</span>
);
// -29000:与后端约定 在初始化学院时,创建时间(标志位-29000)默认展示为'-'
return <span>{val === -29000 ? '-' : formatDate('YYYY-MM-DD H:i', val)}</span>;
},
},
{
title: '关联项',
width: "15%",
key: "planList",
dataIndex: "planList",
width: '15%',
key: 'planList',
dataIndex: 'planList',
render: (val, record) => {
return (
<div className="related-task">
{ record.relatedPlanList ?
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement="top" arrowPointAtCenter>
{ record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
}
<div className='related-task'>
{record.relatedPlanList ? (
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => {
return (
<span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}{' '}
</span>
);
})}
</Tooltip>
:
) : (
<span></span>
}
)}
</div>
)
);
},
},
{
title: "操作",
width: "15%",
key: "operate",
dataIndex: "operate",
title: '操作',
width: '15%',
key: 'operate',
dataIndex: 'operate',
render: (val, item) => {
return (
<div className="operate">
{ ((item.courseState==="UN_START" || item.courseState==="STARTING") && (item.teacherId === User.getUserId() || _.pluck(item.admins, "adminId").includes(User.getUserId()))) &&
<div className='operate'>
{(item.courseState === 'UN_START' || item.courseState === 'STARTING') &&
(item.teacherId === User.getUserId() || _.pluck(item.admins, 'adminId').includes(User.getUserId())) && (
<>
<div
key="enter_live_room1"
className="operate__item"
onClick={() => { this.handleEnterLiveRoom(item) }}
>进入直播间
key='enter_live_room1'
className='operate__item'
onClick={() => {
this.handleEnterLiveRoom(item);
}}>
进入直播间
</div>
<span className="operate__item split" key="view_play_back_split"> | </span>
<span className='operate__item split' key='view_play_back_split'>
{' '}
|{' '}
</span>
</>
}
{ (item.courseState==="FINISH" && item.haveRecord==="YES") &&
)}
{item.courseState === 'FINISH' && item.haveRecord === 'YES' && (
<>
<div
key="view_play_back"
className="operate__item"
onClick={()=>{this.handleViewPlayBack(item)}}
>查看回放</div>
<span className="operate__item split" key="view_play_back_split"> | </span>
key='view_play_back'
className='operate__item'
onClick={() => {
this.handleViewPlayBack(item);
}}>
查看回放
</div>
<span className='operate__item split' key='view_play_back_split'>
{' '}
|{' '}
</span>
</>
}
{ item.courseState!=="EXPIRED" &&
)}
{item.courseState !== 'EXPIRED' && (
<>
<div
key="share"
className="operate__item"
onClick={() => { this.handleShowShareModal(item); }}
>
key='share'
className='operate__item'
onClick={() => {
this.handleShowShareModal(item);
}}>
分享
</div>
</>
}
{ item.courseState!=="EXPIRED" &&
)}
{item.courseState !== 'EXPIRED' && (
<>
<span key="split1" className="operate__item split"> | </span>
<div className="big-live">
<span key='split1' className='operate__item split'>
{' '}
|{' '}
</span>
<div className='big-live'>
<Dropdown overlay={this.renderMoreOperate(item)}>
<span className="more-operate">
<span className="operate-text">更多</span>
<span
className="iconfont icon"
style={{ color: "#2966FF" }}
>
<span className='more-operate'>
<span className='operate-text'>更多</span>
<span className='iconfont icon' style={{ color: '#2966FF' }}>
&#xe824;
</span>
</span>
</Dropdown>
</div>
</>
}
{ item.courseState==="EXPIRED" &&
<div
className="operate__item"
onClick={()=>this.handleDelete(item)}
>删除</div>
}
)}
{item.courseState === 'EXPIRED' && (
<div className='operate__item' onClick={() => this.handleDelete(item)}>
删除
</div>
)
}
}
)}
</div>
);
},
},
];
}else{
} else {
columns = [
{
title: "直播课",
width: "25%",
key: "course",
dataIndex: "courseName",
title: '直播课',
width: '25%',
key: 'course',
dataIndex: 'courseName',
render: (val, record) => {
let hasCover = false;
return (
<div className="record__item">
{
record.courseMediaVOS.map((item,index)=>{
if( item.contentType === "COVER"){
<div className='record__item'>
{record.courseMediaVOS.map((item, index) => {
if (item.contentType === 'COVER') {
hasCover = true;
return <img className="course-cover" src={item.mediaUrl} />
}
})
}
{ !hasCover &&
<img className="course-cover" src={'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'} />
return <img className='course-cover' src={item.mediaUrl} />;
}
})}
{!hasCover && <img className='course-cover' src={'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'} />}
<div>
{ record.courseName.length > 17?
{record.courseName.length > 17 ? (
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
:
<div className="course-name">{record.courseName}</div>
}
) : (
<div className='course-name'>{record.courseName}</div>
)}
<div>
<span className="course-time">{formatDate("YYYY-MM-DD H:i",parseInt(record.startTime))}~{formatDate("H:i", parseInt(record.endTime))}</span>
<span className="course-status" style={{color:courseStateShow[record.courseState].color,border:`1px solid ${courseStateShow[record.courseState].color}`}}>{courseStateShow[record.courseState].title}</span>
<span className='course-time'>
{formatDate('YYYY-MM-DD H:i', parseInt(record.startTime))}~{formatDate('H:i', parseInt(record.endTime))}
</span>
<span
className='course-status'
style={{ color: courseStateShow[record.courseState].color, border: `1px solid ${courseStateShow[record.courseState].color}` }}>
{courseStateShow[record.courseState].title}
</span>
</div>
<div className="teacher-assistant">
{ record.teacherName.length > 4 ?
<div className='teacher-assistant'>
{record.teacherName && record.teacherName.length > 4 ? (
<Tooltip title={record.teacherName}>
<span className="teacher">讲师:{record.teacherName}</span>
<span className='teacher'>讲师:{record.teacherName}</span>
</Tooltip>
:
<span className="teacher">讲师:{record.teacherName}</span>
}
) : (
<span className='teacher'>讲师:{record.teacherName}</span>
)}
{ record.admins.length >0 &&
{record.admins.length > 0 && (
<>
<span className="split"> | </span>
{ this.handleAdminName(record.admins).length > 4?
<span className='split'> | </span>
{this.handleAdminName(record.admins).length > 4 ? (
<Tooltip title={this.handleAdminName(record.admins)}>
<span className="assistant">助教:
{ record.admins.map((item,index)=>{
return <span>{item.adminName} { (index < record.admins.length-1)&&(<span></span>)} </span>
})
}
<span className='assistant'>
助教:
{record.admins.map((item, index) => {
return (
<span>
{item.adminName} {index < record.admins.length - 1 && <span></span>}{' '}
</span>
);
})}
</span>
</Tooltip>
:
<span className="assistant">助教:
{ record.admins.map((item,index)=>{
return <span>{item.adminName} { (index < record.admins.length-1)&&(<span></span>)} </span>
})
}
) : (
<span className='assistant'>
助教:
{record.admins.map((item, index) => {
return (
<span>
{item.adminName} {index < record.admins.length - 1 && <span></span>}{' '}
</span>
}
);
})}
</span>
)}
</>
}
)}
</div>
</div>
</div>
)
}
);
},
},
{
title: "课程分类",
width: "10%",
key: "couseCatalog",
dataIndex: "couseCatalog",
title: '课程分类',
width: '10%',
key: 'couseCatalog',
dataIndex: 'couseCatalog',
render: (val, item) => {
return (
<div className="categoryName">{item.categoryName}</div>
)
return <div className='categoryName'>{item.categoryName}</div>;
},
},
{
title: "课件管理",
width: "8%",
key: "courseware",
dataIndex: "courseware",
title: '课件管理',
width: '8%',
key: 'courseware',
dataIndex: 'courseware',
render: (val, item) => {
return (
<span className="courseware"
<span
className='courseware'
onClick={() => {
this.setState({
editData: item,
openCoursewareModal: true,
});
}}>{item.courseDocumentCount}</span>
}}>
{item.courseDocumentCount}
</span>
);
},
},
{
title: '上课数据',
width: "9%",
key: "quota",
dataIndex: "quota",
width: '9%',
key: 'quota',
dataIndex: 'quota',
render: (val, item) => {
return (
<span className="iconfont icon quota-icon" onClick={() => {
this.handleLinkToClassData(item)
}}>&#xe7d6;</span>
<span
className='iconfont icon quota-icon'
onClick={() => {
this.handleLinkToClassData(item);
}}>
&#xe7d6;
</span>
);
},
},
{
title: '创建时间',
width: "9%",
key: "created",
dataIndex: "created",
width: '9%',
key: 'created',
dataIndex: 'created',
sorter: true,
render: (val, item) => {
return (
<span>{formatDate('YYYY-MM-DD H:i', val)}</span>
);
return <span>{formatDate('YYYY-MM-DD H:i', val)}</span>;
},
},
{
title: '关联项',
width: "15%",
key: "planList",
dataIndex: "planList",
width: '15%',
key: 'planList',
dataIndex: 'planList',
render: (val, record) => {
return (
<div className="related-task">
{ record.relatedPlanList ?
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement="top" arrowPointAtCenter>
{ record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
}
<div className='related-task'>
{record.relatedPlanList ? (
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => {
return (
<span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}{' '}
</span>
);
})}
</Tooltip>
:
) : (
<span></span>
}
)}
</div>
)
);
},
},
];
}
this.setState({ columns })
}
handleAdminName = (adminArray)=>{
let adminStr = "";
adminArray.map((item,index)=>{
if(index < adminArray.length-1){
this.setState({ columns });
};
handleAdminName = (adminArray) => {
let adminStr = '';
adminArray.map((item, index) => {
if (index < adminArray.length - 1) {
adminStr = adminStr + item.adminName + '、';
}else{
adminStr = adminStr + item.adminName
}
})
return adminStr
} else {
adminStr = adminStr + item.adminName;
}
});
return adminStr;
};
handlePlanName = (planArray)=>{
let planStr = "";
planArray.map((item,index)=>{
if(index < planArray.length-1){
handlePlanName = (planArray) => {
let planStr = '';
planArray.map((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、';
}else{
planStr = planStr + item.planName
}
})
return planStr
} else {
planStr = planStr + item.planName;
}
});
return planStr;
};
renderMoreOperate = (item) => {
return (
<div className="live-course-more-menu">
{ (User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") &&
<div
className="operate__item"
onClick={()=>this.handleRelatedModalShow(item)}
>关联培训计划</div>
}
<div
className="operate__item"
onClick={()=>this.toEditCoursePage(item)}
>编辑</div>
{ item.courseState !=="STARTING" &&
<div
className="operate__item"
onClick={()=>this.handleDelete(item)}
>删除</div>
}
<div className='live-course-more-menu'>
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<div className='operate__item' onClick={() => this.handleRelatedModalShow(item)}>
关联培训计划
</div>
)
}
handleDelete = (record)=>{
)}
<div className='operate__item' onClick={() => this.toEditCoursePage(item)}>
编辑
</div>
{item.courseState !== 'STARTING' && (
<div className='operate__item' onClick={() => this.handleDelete(item)}>
删除
</div>
)}
</div>
);
};
handleDelete = (record) => {
return confirm({
title: '你确定要删除直播课?',
content: '删除后,学员将不能观看直播课/回放',
icon: <span className="icon iconfont default-confirm-icon">&#xe839; </span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
width:440,
height:188,
width: 440,
height: 188,
onOk: () => {
this.deleteConfirm(record);
}
})
}
deleteConfirm = (item)=>{
const params={
"liveCourseId": item.liveCourseId,
}
CourseService.delLiveCloudCourse(params).then((res)=>{
if(res.success){
message.success("已删除");
},
});
};
deleteConfirm = (item) => {
const params = {
liveCourseId: item.liveCourseId,
};
CourseService.delLiveCloudCourse(params).then((res) => {
if (res.success) {
message.success('已删除');
this.props.onChange();
}
})
}
toEditCoursePage =(item)=>{
});
};
toEditCoursePage = (item) => {
window.RCHistory.push({
pathname: `/create-live-course?type=edit&id=${item.liveCourseId}`,
})
}
refreshCourseList = ()=>{
});
};
refreshCourseList = () => {
this.props.onChange(this.props.query);
}
};
//进入直播间
handleEnterLiveRoom = (item) => {
if (item.startTime - Date.now() > 1800000) {
Modal.warning({
title: "你来得太早了",
title: '你来得太早了',
okText: '我知道了',
content: "请于开始上课前30分钟来直播上课。",
content: '请于开始上课前30分钟来直播上课。',
icon: (
<span
className="icon iconfont default-confirm-icon"
style={{ color: "#FFBB54 !important" }}
>
<span className='icon iconfont default-confirm-icon' style={{ color: '#FFBB54 !important' }}>
&#xe834;
</span>
),
......@@ -630,127 +669,130 @@ class LiveCourseList extends React.Component {
} else {
CourseService.getLiveCloudCourseDetail({
liveCourseId: item.liveCourseId,
})
.then((res) => {
}).then((res) => {
const url = `xmqx://liveCourseId=${item.liveCourseId}`;
if (res.result.courseState === "FINISH") {
if (res.result.courseState === 'FINISH') {
Modal.warning({
title: "刷新页面",
title: '刷新页面',
icon: <QuestionCircleOutlined />,
content: "课次已结束,请刷新一下",
content: '课次已结束,请刷新一下',
onOk: () => {
this.refreshCourseList();
}
},
});
} else {
this.setState({ url, openDownloadModal: true });
}
});
}
}
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
let _query = this.props.query
let _query = this.props.query;
_query.size = size;
this.props.onChange(_query)
}
this.props.onChange(_query);
};
getDownloadVersion() {
const isMac = /macintosh|mac os x/i.test(navigator.userAgent);
// 判断学员系统
let platform;
if(!isMac){
platform = 1
}else{
platform = 4
if (!isMac) {
platform = 1;
} else {
platform = 4;
}
BaseService
.getLastedVersion({ model: 5, platform})
.then((res) => {
BaseService.getLastedVersion({ model: 5, platform }).then((res) => {
const { result = {} } = res;
this.setState({ downloadUrl: result.releaseUrl });
})
});
}
handleViewPlayBack = (item) => {
let htmlUrl;
if(item.teacherId === User.getUserId()){
if (item.teacherId === User.getUserId()) {
htmlUrl = `${LIVE_SHARE}replay/${item.liveCourseId}?teacherId=${User.getUserId()}&id=${User.getStoreId()}`;
}else if(_.pluck(item.admins, "adminId").includes(User.getUserId())){
} else if (_.pluck(item.admins, 'adminId').includes(User.getUserId())) {
htmlUrl = `${LIVE_SHARE}replay/${item.liveCourseId}?userId=${User.getUserId()}&id=${User.getStoreId()}`;
}else{
} else {
htmlUrl = `${LIVE_SHARE}replay/${item.liveCourseId}?id=${User.getStoreId()}`;
}
window.open(htmlUrl);
}
handleRelatedModalShow = (item)=>{
};
handleRelatedModalShow = (item) => {
const selectPlanList = {};
if(item.relatedPlanList){
item.relatedPlanList.map((item,index)=>{
selectPlanList[item.planId] = {}
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {};
selectPlanList[item.planId].planId = item.planId;
selectPlanList[item.planId].taskBaseVOList = [{taskId:item.taskId}];
return item
})
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }];
return item;
});
}
this.setState({
RelatedPlanModalVisible:true,
selectCourseId:item.liveCourseId,
selectPlanList:selectPlanList
})
}
closeRelatedPlanModalVisible = ()=>{
this.setState({
RelatedPlanModalVisible:false
})
}
onChangeSelectPlanList = (selectPlanList)=>{
RelatedPlanModalVisible: true,
selectCourseId: item.liveCourseId,
selectPlanList: selectPlanList,
});
};
closeRelatedPlanModalVisible = () => {
this.setState({
selectPlanList:selectPlanList
})
}
onConfirmSelectPlanList = ()=>{
RelatedPlanModalVisible: false,
});
};
onChangeSelectPlanList = (selectPlanList) => {
this.setState({
RelatedPlanModalVisible:false
},()=>{this.props.onChange();})
selectPlanList: selectPlanList,
});
};
onConfirmSelectPlanList = () => {
this.setState(
{
RelatedPlanModalVisible: false,
},
() => {
this.props.onChange();
}
);
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
const { query } = this.props;
let _columnKey;
let _order;
// 按创建时间升序排序
if (columnKey === 'created' && order === 'ascend') {_columnKey="CREATED"; _order = 'SORT_ASC'; }
if (columnKey === 'created' && order === 'ascend') {
_columnKey = 'CREATED';
_order = 'SORT_ASC';
}
// 按创建时间降序排序
if (columnKey === 'created' && order === 'descend') { _columnKey="CREATED"; _order = 'SORT_DESC';}
if (columnKey === 'created' && order === 'descend') {
_columnKey = 'CREATED';
_order = 'SORT_DESC';
}
const _query = {
...query,
sortMap:{}
sortMap: {},
};
_query.sortMap[_columnKey]=_order;
_query.sortMap[_columnKey] = _order;
this.props.onChange(_query);
}
};
render() {
const { total, query, courseList, loading} = this.props;
const { total, query, courseList, loading } = this.props;
const { current, size } = query;
const { openDownloadModal,
downloadUrl, url, columns,
openCoursewareModal,
editData,
RelatedPlanModalVisible,
selectCourseId,
selectPlanList
} = this.state;
const { openDownloadModal, downloadUrl, url, columns, openCoursewareModal, editData, RelatedPlanModalVisible, selectCourseId, selectPlanList } = this.state;
const { match } = this.props;
return (
<div className="live-course-list">
<Table
<div className='live-course-list'>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
bordered
size="middle"
size='middle'
pagination={false}
columns={columns}
loading={loading}
......@@ -758,21 +800,21 @@ class LiveCourseList extends React.Component {
onChange={this.handleChangeTable}
rowKey={(row) => row.liveCourseId}
/>
{ total>0 &&
<div className="box-footer">
{total > 0 && (
<div className='box-footer'>
<PageControl
current={current - 1}
pageSize={size}
total={parseInt(total)}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.props.onChange(_query)
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
{ this.state.shareLiveModal }
)}
{this.state.shareLiveModal}
{openCoursewareModal && (
<ManageCoursewareModal
data={editData}
......@@ -789,12 +831,12 @@ class LiveCourseList extends React.Component {
onCancel={() => {
this.setState({
url: '',
openDownloadModal:
false });
openDownloadModal: false,
});
}}
/>
)}
{ RelatedPlanModalVisible &&
{RelatedPlanModalVisible && (
<RelatedPlanModal
onClose={this.closeRelatedPlanModalVisible}
visible={RelatedPlanModalVisible}
......@@ -803,11 +845,11 @@ class LiveCourseList extends React.Component {
onChange={this.onChangeSelectPlanList}
onConfirm={this.onConfirmSelectPlanList}
/>
}
<iframe src={url} style={{ display: "none" }} />
)}
<iframe src={url} style={{ display: 'none' }} />
<Route path={`${match.url}/live-course-data`} component={DataList} />
</div>
)
);
}
}
......
......@@ -6,37 +6,29 @@
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Table, Modal, message , Tooltip,Switch,Dropdown} from 'antd';
import { PageControl } from "@/components";
import { LIVE_SHARE_MAP } from '@/common/constants/academic/cloudClass';
import { appId, shareUrl, LIVE_SHARE } from '@/domains/course-domain/constants';
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import WatchDataModal from '../modal/WatchDataModal'
import CourseService from "@/domains/course-domain/CourseService";
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import React from 'react';
import RelatedPlanModal from '../../modal/RelatedPlanModal';
import User from '@/common/js/user'
import WatchDataModal from '../modal/WatchDataModal';
import './GraphicsCourseList.less';
const ENV = process.env.DEPLOY_ENV || 'dev';
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
class GraphicsCourseList extends React.Component {
constructor(props) {
super(props);
this.state = {
id: '', // 视频课ID
studentIds:[],
RelatedPlanModalVisible:false,
selectPlanList:{}
}
studentIds: [],
RelatedPlanModalVisible: false,
selectPlanList: {},
};
}
componentDidMount() {
......@@ -55,25 +47,25 @@ class GraphicsCourseList extends React.Component {
data={record}
close={() => {
this.setState({
watchDataModal: null
watchDataModal: null,
});
}}
/>
);
this.setState({ watchDataModal });
}
};
handlePlanName = (planArray)=>{
let planStr = "";
planArray.map((item,index)=>{
if(index < planArray.length-1){
handlePlanName = (planArray) => {
let planStr = '';
planArray.map((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、';
}else{
planStr = planStr + item.planName
}
})
return planStr
} else {
planStr = planStr + item.planName;
}
});
return planStr;
};
// 请求表头
parseColumns = () => {
const columns = [
......@@ -81,24 +73,24 @@ class GraphicsCourseList extends React.Component {
title: '图文课',
key: 'scheduleName',
dataIndex: 'scheduleName',
width:321,
width: 321,
fixed: 'left',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
return (
<div className="record__item">
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || defaultCoverUrl} />
{ record.courseName.length > 25?
<img className='course-cover' src={coverUrl || defaultCoverUrl} />
{record.courseName.length > 25 ? (
<Tooltip title={record.courseName}>
<div className="course-name">{record.courseName}</div>
<div className='course-name'>{record.courseName}</div>
</Tooltip>
:
<div className="course-name">{record.courseName}</div>
}
) : (
<div className='course-name'>{record.courseName}</div>
)}
</div>
)
}
);
},
},
{
title: '课程分类',
......@@ -107,11 +99,12 @@ class GraphicsCourseList extends React.Component {
width: 120,
render: (val, record) => {
return (
<div className="record__item">
{record.categoryOneName}{ record.categoryTwoName?`-${record.categoryTwoName}`:''}
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
)
}
);
},
},
{
title: '创建人',
......@@ -121,26 +114,35 @@ class GraphicsCourseList extends React.Component {
render: (val) => {
return (
<div>
{ val &&
{val && (
<Tooltip title={val}>
<div>
{val.length > 4 ? `${val.slice(0,4)}...` : val}
</div>
<div>{val.length > 4 ? `${val.slice(0, 4)}...` : val}</div>
</Tooltip>
}
)}
</div>
)
}
);
},
},
{
title: <span>
title: (
<span>
<span>学院展示</span>
<Tooltip title={<div>开启后,学员可在学院内查看到此课程。<br/>关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。</div>}><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:'normal'}}>&#xe61d;</i></Tooltip>
</span>,
<Tooltip
title={
<div>
开启后,学员可在学院内查看到此课程。
<br />
关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: 'normal' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: 120,
dataIndex: "courseware",
dataIndex: 'courseware',
render: (val, item, index) => {
return (
<Switch
......@@ -151,14 +153,12 @@ class GraphicsCourseList extends React.Component {
},
},
{
title: "观看学员数",
title: '观看学员数',
width: 110,
key: "watchUserCount",
dataIndex: "watchUserCount",
key: 'watchUserCount',
dataIndex: 'watchUserCount',
render: (val, item) => {
return (
<div className="watchUserCount">{val}</div>
)
return <div className='watchUserCount'>{val}</div>;
},
},
{
......@@ -168,8 +168,8 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'created',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: '最近修改时间',
......@@ -178,30 +178,33 @@ class GraphicsCourseList extends React.Component {
dataIndex: 'updated',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: '关联项',
width: 200,
key: "planList",
dataIndex: "planList",
key: 'planList',
dataIndex: 'planList',
render: (val, record) => {
return (
<div className="related-task">
{ record.relatedPlanList ?
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement="top" arrowPointAtCenter>
{ record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
}
<div className='related-task'>
{record.relatedPlanList ? (
<Tooltip title={this.handlePlanName(record.relatedPlanList)} placement='top' arrowPointAtCenter>
{record.relatedPlanList.map((item, index) => {
return (
<span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}{' '}
</span>
);
})}
</Tooltip>
:
) : (
<span></span>
}
)}
</div>
)
}
);
},
},
{
title: '操作',
......@@ -211,90 +214,96 @@ class GraphicsCourseList extends React.Component {
fixed: 'right',
render: (val, record) => {
return (
<div className="operate">
<div className="operate__item" onClick={()=>this.handleShowWatchDataModal(record)}>观看数据</div>
<span className="operate__item split"> | </span>
<div className="operate__item" onClick={() => this.handleShowShareModal(record)}>分享</div>
<span className="operate__item split"> | </span>
<div className='operate'>
<div className='operate__item' onClick={() => this.handleShowWatchDataModal(record)}>
观看数据
</div>
<span className='operate__item split'> | </span>
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
</div>
<span className='operate__item split'> | </span>
<Dropdown overlay={this.renderMoreOperate(record)}>
<span className="more-operate">
<span className="operate-text">更多</span>
<span
className="iconfont icon"
style={{ color: "#2966FF" }}
>
<span className='more-operate'>
<span className='operate-text'>更多</span>
<span className='iconfont icon' style={{ color: '#2966FF' }}>
&#xe824;
</span>
</span>
</Dropdown>
</div>
)
}
}
);
},
},
];
return columns;
}
};
handleRelatedModalShow = (item) => {
const selectPlanList = {};
if(item.relatedPlanList){
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {}
selectPlanList[item.planId] = {};
selectPlanList[item.planId].planId = item.planId;
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }];
return item
})
return item;
});
}
this.setState({
RelatedPlanModalVisible: true,
selectCourseId: item.id,
selectPlanList: selectPlanList,
})
}
});
};
closeRelatedPlanModalVisible = ()=>{
closeRelatedPlanModalVisible = () => {
this.setState({
RelatedPlanModalVisible: false
})
}
RelatedPlanModalVisible: false,
});
};
onChangeSelectPlanList = (selectPlanList) => {
this.setState({
selectPlanList: selectPlanList
})
}
selectPlanList: selectPlanList,
});
};
onConfirmSelectPlanList = () => {
this.setState({
RelatedPlanModalVisible: false
}, () => { this.props.onChange(); });
this.setState(
{
RelatedPlanModalVisible: false,
},
() => {
this.props.onChange();
}
);
};
renderMoreOperate = (item) => {
return (
<div className="live-course-more-menu">
{(User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") &&
<div className='live-course-more-menu'>
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<div
className="operate__item"
key="plan"
className='operate__item'
key='plan'
onClick={() => {
this.handleRelatedModalShow(item);
}}
>关联培训计划</div>
}
}}>
关联培训计划
</div>
)}
<div
className="operate__item"
key="edit"
className='operate__item'
key='edit'
onClick={() => {
RCHistory.push(`/create-graphics-course?type=edit&id=${item.id}`);
}}
>编辑</div>
<div
className="operate__item"
key="delete"
onClick={() => this.handleDeleteGraphicsCourse(item.id)}
>删除</div>
}}>
编辑
</div>
<div className='operate__item' key='delete' onClick={() => this.handleDeleteGraphicsCourse(item.id)}>
删除
</div>
</div>
)
}
......@@ -308,44 +317,40 @@ class GraphicsCourseList extends React.Component {
// }
const params={
courseId: item.id,
shelfState:_shelfState
}
CourseService.changeVideoShelfState(params).then((res)=>{
if(res.success){
if(_shelfState === "YES"){
message.success("已开启展示");
}else{
message.success("已取消展示");
shelfState: _shelfState,
};
CourseService.changeVideoShelfState(params).then((res) => {
if (res.success) {
if (_shelfState === 'YES') {
message.success('已开启展示');
} else {
message.success('已取消展示');
}
this.props.changeShelfState(index,_shelfState)
}
})
}
});
};
// 删除视频课
handleDeleteGraphicsCourse = (scheduleId) => {
Modal.confirm({
title: '你确定要删除此视频课吗?',
content: '删除后,学员将不能进行观看。',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: () => {
const param ={
courseId:scheduleId,
storeId:User.getStoreId()
}
CourseService.delVideoSchedule(
param
).then(() => {
const param = {
courseId: scheduleId,
storeId: User.getStoreId(),
};
CourseService.delVideoSchedule(param).then(() => {
message.success('删除成功');
this.props.onChange();
})
}
});
}
},
});
};
// 显示分享弹窗
handleShowShareModal = (record, needStr = false) => {
......@@ -366,11 +371,11 @@ class GraphicsCourseList extends React.Component {
<ShareLiveModal
needStr={needStr}
data={shareData}
type="graphicsClass"
title="图文课"
type='graphicsClass'
title='图文课'
close={() => {
this.setState({
shareLiveModal: null
shareLiveModal: null,
});
localStorage.setItem('videoCourseItem', '');
}}
......@@ -378,88 +383,105 @@ class GraphicsCourseList extends React.Component {
);
this.setState({ shareLiveModal });
}
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
const { query } = this.props;
let { order: _order } =query;
let { order: _order } = query;
// 按创建时间升序排序
if (columnKey === 'created' && order === 'ascend') { _order = 'CREATED_ASC'; }
if (columnKey === 'created' && order === 'ascend') {
_order = 'CREATED_ASC';
}
// 按创建时间降序排序
if (columnKey === 'created' && order === 'descend') { _order = 'CREATED_DESC'; }
if (columnKey === 'created' && order === 'descend') {
_order = 'CREATED_DESC';
}
// 按更新时间升序排序
if (columnKey === 'updated' && order === 'ascend') { _order = 'UPDATED_ASC'; }
if (columnKey === 'updated' && order === 'ascend') {
_order = 'UPDATED_ASC';
}
// 按更新时间降序排序
if (columnKey === 'updated' && order === 'descend') { _order = 'UPDATED_DESC'; }
if (columnKey === 'updated' && order === 'descend') {
_order = 'UPDATED_DESC';
}
const _query = {
...query,
orderEnum: _order
orderEnum: _order,
};
this.props.onChange(_query);
}
handleRelatedModalShow = (item)=>{
};
handleRelatedModalShow = (item) => {
const selectPlanList = {};
if(item.relatedPlanList){
item.relatedPlanList.map((item,index)=>{
selectPlanList[item.planId] = {}
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {};
selectPlanList[item.planId].planId = item.planId;
selectPlanList[item.planId].taskBaseVOList = [{taskId:item.taskId}];
return item
})
}
this.setState({
RelatedPlanModalVisible:true,
selectCourseId:item.id,
selectPlanList:selectPlanList
})
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }];
return item;
});
}
closeRelatedPlanModalVisible = ()=>{
this.setState({
RelatedPlanModalVisible:false
})
}
onChangeSelectPlanList = (selectPlanList)=>{
RelatedPlanModalVisible: true,
selectCourseId: item.id,
selectPlanList: selectPlanList,
});
};
closeRelatedPlanModalVisible = () => {
this.setState({
selectPlanList:selectPlanList
})
}
onConfirmSelectPlanList = ()=>{
RelatedPlanModalVisible: false,
});
};
onChangeSelectPlanList = (selectPlanList) => {
this.setState({
RelatedPlanModalVisible:false
},()=>{this.props.onChange();})
selectPlanList: selectPlanList,
});
};
onConfirmSelectPlanList = () => {
this.setState(
{
RelatedPlanModalVisible: false,
},
() => {
this.props.onChange();
}
);
};
render() {
const { RelatedPlanModalVisible, selectCourseId, selectPlanList } = this.state;
const { dataSource = [], totalCount, query } = this.props;
const { current, size } = query;
return (
<div className="video-course-list">
<Table
rowKey={record => record.id}
<div className='video-course-list'>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500}}
scroll={{ x: 1500 }}
bordered
className="video-list-table"
className='video-list-table'
/>
<div className="box-footer">
<div className='box-footer'>
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.props.onChange(_query)
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
/>
</div>
{RelatedPlanModalVisible &&
{RelatedPlanModalVisible && (
<RelatedPlanModal
onClose={this.closeRelatedPlanModalVisible}
visible={RelatedPlanModalVisible}
......@@ -468,8 +490,8 @@ class GraphicsCourseList extends React.Component {
onChange={this.onChangeSelectPlanList}
onConfirm={this.onConfirmSelectPlanList}
/>
}
{ RelatedPlanModalVisible &&
)}
{RelatedPlanModalVisible && (
<RelatedPlanModal
onClose={this.closeRelatedPlanModalVisible}
visible={RelatedPlanModalVisible}
......@@ -478,11 +500,11 @@ class GraphicsCourseList extends React.Component {
onChange={this.onChangeSelectPlanList}
onConfirm={this.onConfirmSelectPlanList}
/>
}
{ this.state.shareLiveModal }
{ this.state.watchDataModal }
)}
{this.state.shareLiveModal}
{this.state.watchDataModal}
</div>
)
);
}
}
......
......@@ -6,79 +6,85 @@
* @Description 余额异常弹窗
*/
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import Service from "@/common/js/service";
import User from '@/common/js/user'
import { Modal, Input } from 'antd';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import Service from '@/common/js/service';
import User from '@/common/js/user';
import './WatchDataModal.less';
import dealTimeDuration from "../../utils/dealTimeDuration";
import dealTimeDuration from '../../utils/dealTimeDuration';
const { Search } = Input;
class WatchDataModal extends React.Component {
constructor(props) {
super(props);
this.state = {
visible:true,
dataSource:[],
size:10,
visible: true,
dataSource: [],
size: 10,
query: {
current: 1,
},
totalCount:0
totalCount: 0,
};
}
componentDidMount() {
this.handleFetchDataList();
}
onClose = () =>{
onClose = () => {
this.props.close();
}
};
// 获取观看视频数据列表
handleFetchDataList = () => {
const {query,size,totalCount} = this.state
const { query, size, totalCount } = this.state;
const { id } = this.props.data;
const params ={
const params = {
...query,
size,
courseId:id,
storeId:User.getStoreId()
}
courseId: id,
storeId: User.getStoreId(),
};
Service.Hades('public/hades/mediaCourseWatchInfo', params).then((res) => {
const { result = {} } = res ;
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
totalCount: Number(total),
});
});
}
handleChangNickname = (value)=>{
};
handleChangNickname = (value) => {
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
if (isPhone) {
query.phone = value;
query.nickName = null;
}else{
} else {
query.nickName = value;
query.phone = null;
}
query.current = 1;
this.setState({
query
})
}
query,
});
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
this.setState({
size
},()=>{this.handleFetchDataList()})
this.setState(
{
size,
},
() => {
this.handleFetchDataList();
}
);
};
// 请求表头
parseColumns = () => {
......@@ -86,91 +92,109 @@ class WatchDataModal extends React.Component {
{
title: '观看学员',
key: 'name',
dataIndex: 'name'
dataIndex: 'name',
},
{
title: '手机号',
key: 'phone',
dataIndex: 'phone'
dataIndex: 'phone',
},
{
title: '观看者类型',
key: 'userRole',
dataIndex: 'userRole'
dataIndex: 'userRole',
},
{
title: '首次观看时间',
key: 'firstWatch',
dataIndex: 'firstWatch',
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: '观看总时长',
key: 'watchDuration',
dataIndex: 'watchDuration',
render: (val) => {
return <span>{val ? dealTimeDuration(val) : "00:00:00" }</span>
}
return <span>{val ? dealTimeDuration(val) : '00:00:00'}</span>;
},
},
{
title: '学习进度',
key: 'progress',
dataIndex: 'progress',
render: (val) => {
return <span>{val === 100 ? '已完成' : `${val || 0}%`}</span>
}
}
return <span>{val === 100 ? '已完成' : `${val || 0}%`}</span>;
},
},
];
return columns;
}
};
render() {
const { visible,size,dataSource,totalCount,query} = this.state;
const { visible, size, dataSource, totalCount, query } = this.state;
return (
<Modal
title="图文课观看数据"
title='图文课观看数据'
visible={visible}
footer={null}
onCancel={this.onClose}
maskClosable={false}
className="watch-data-modal"
className='watch-data-modal'
closable={true}
width={800}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索学员姓名/手机号" style={{ width: 200 }} onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='search-container'>
<Search
placeholder='搜索学员姓名/手机号'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangNickname(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList();
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div>
<Table
rowKey={record => record.id}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
/>
{dataSource.length >0 &&
<div className="box-footer">
{dataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
size="small"
size='small'
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList();
}
);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
)}
</div>
</Modal>
)
);
}
}
......
import college from '@/common/lottie/college';
import { XMTable } from '@/components';
import { Modal } from 'antd';
import React from 'react';
import { Modal, Table } from "antd";
import ChargeArgeement from "./ChargeArgeement";
import "./AccountChargeModal.less";
class AccountChargeRecords extends React.Component{
import './AccountChargeModal.less';
import ChargeArgeement from './ChargeArgeement';
class AccountChargeRecords extends React.Component {
constructor(props) {
super(props);
this.state = {
......@@ -15,9 +17,7 @@ class AccountChargeRecords extends React.Component{
getList = () => {
const { instId } = window.currentUserInstInfo;
axios
.Business("public/liveAssets/rechargeProtocol", { instId })
.then((res) => {
axios.Business('public/liveAssets/rechargeProtocol', { instId }).then((res) => {
const list = res.result;
this.setState({
list,
......@@ -42,29 +42,28 @@ class AccountChargeRecords extends React.Component{
render() {
const columns = [
{
title: "签订人",
dataIndex: "operatorName",
width: 140
title: '签订人',
dataIndex: 'operatorName',
width: 140,
},
{ title: "关联订单ID", dataIndex: "orderId" },
{ title: '关联订单ID', dataIndex: 'orderId' },
{
title: "签订时间",
dataIndex: "createTime",
title: '签订时间',
dataIndex: 'createTime',
render: (text, record) => {
return <span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>;
return <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>;
},
},
{
title: "签订协议",
dataIndex: "operate",
title: '签订协议',
dataIndex: 'operate',
render: (text, record) => {
return (
<div
style={{ cursor: "pointer", color: "#FC9C6B" }}
style={{ cursor: 'pointer', color: '#FC9C6B' }}
onClick={() => {
this.handleProtcol(record.protocolId);
}}
>
}}>
《服务协议》
</div>
);
......@@ -74,29 +73,31 @@ class AccountChargeRecords extends React.Component{
const { list } = this.state;
return (
<Modal
title="服务协议签订记录"
title='服务协议签订记录'
visible={true}
width={680}
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
onCancel={() => {
this.props.close();
}}
>
}}>
<div>
<div
style={{
fontSize: "14px",
color: "#666666",
lineHeight: "20px",
fontSize: '14px',
color: '#666666',
lineHeight: '20px',
marginBottom: 16,
}}
>
}}>
以下是本校区自助充值时签订协议的记录
</div>
<Table
size="middle"
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='middle'
columns={columns}
dataSource={list}
pagination={false}
......
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { Modal, Tooltip } from 'antd';
import React from 'react';
import { Modal, Table, Tooltip } from "antd";
import { ShowTips, PageControl } from "@/components";
import "./AccountChargeModal.less";
import './AccountChargeModal.less';
class ChargingDetailModal extends React.Component {
constructor(props) {
......@@ -25,9 +26,7 @@ class ChargingDetailModal extends React.Component {
handleToPage = (page = 1) => {
const params = _.clone(this.state.query);
params.current = page;
axios
.Apollo("public/businessLive/queryStudentVisitData", params)
.then((res) => {
axios.Apollo('public/businessLive/queryStudentVisitData', params).then((res) => {
if (res.result) {
const { records = [], total } = res.result;
this.setState({
......@@ -40,7 +39,7 @@ class ChargingDetailModal extends React.Component {
};
getTeacherData = () => {
window.axios
.Apollo("public/businessLive/queryTeacherVisitData", {
.Apollo('public/businessLive/queryTeacherVisitData', {
liveCourseId: this.props.liveCourseId,
})
.then((res) => {
......@@ -57,27 +56,27 @@ class ChargingDetailModal extends React.Component {
let hours = Math.floor(time / 3600);
let mins = Math.floor(diff / 60);
let seconds = Math.floor(time % 60);
hours = hours < 10 ? "0" + hours : hours;
mins = mins < 10 ? "0" + mins : mins;
seconds = seconds < 10 ? "0" + seconds : seconds;
return hours + ":" + mins + ":" + seconds;
hours = hours < 10 ? '0' + hours : hours;
mins = mins < 10 ? '0' + mins : mins;
seconds = seconds < 10 ? '0' + seconds : seconds;
return hours + ':' + mins + ':' + seconds;
};
getColumns = (type) => {
const columns = [
{
title: type == "student" ? "学生姓名" : "老师姓名",
dataIndex: "userName",
title: type == 'student' ? '学生姓名' : '老师姓名',
dataIndex: 'userName',
},
{
title: "手机号",
dataIndex: "phone",
title: '手机号',
dataIndex: 'phone',
render: (text, record) => {
return <p>{text}</p>;
},
},
{
title: "累计在线时长",
dataIndex: "totalDuration",
title: '累计在线时长',
dataIndex: 'totalDuration',
render: (text, record) => {
return <span>{text ? this.dealTimeDuration(text) : '-'}</span>;
},
......@@ -86,14 +85,14 @@ class ChargingDetailModal extends React.Component {
title: (
<span>
是否计费&nbsp;
<Tooltip title="仅对累计在线时长≥10分钟的老师或学员计费">
<span className="icon iconfont">&#xe6f2;</span>
<Tooltip title='仅对累计在线时长≥10分钟的老师或学员计费'>
<span className='icon iconfont'>&#xe6f2;</span>
</Tooltip>
</span>
),
dataIndex: "type",
dataIndex: 'type',
render: (text, record) => {
return <span>{record.totalDuration > 600 ? "计费" : "不计费"}</span>; //大于十分钟的计费
return <span>{record.totalDuration > 600 ? '计费' : '不计费'}</span>; //大于十分钟的计费
},
},
];
......@@ -103,38 +102,45 @@ class ChargingDetailModal extends React.Component {
const { list, query, totalCount, teacherList } = this.state;
return (
<Modal
title="计费人数详情"
title='计费人数详情'
visible={true}
width={680}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="charging-detail-modal"
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='charging-detail-modal'
footer={null}
onCancel={() => {
this.props.close();
}}
>
}}>
<div>
<div style={{ marginBottom: 16 }}>
<div className="detail-title">老师详情</div>
<Table
size="middle"
columns={this.getColumns("teacher")}
<div className='detail-title'>老师详情</div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='middle'
columns={this.getColumns('teacher')}
dataSource={teacherList}
pagination={false}
bordered
/>
</div>
<div className="detail-title">学生详情</div>
<Table
size="middle"
columns={this.getColumns("student")}
<div className='detail-title'>学生详情</div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='middle'
columns={this.getColumns('student')}
dataSource={list}
pagination={false}
bordered
/>
<PageControl
size="small"
size='small'
current={query.current - 1}
pageSize={query.size}
total={totalCount}
......
......@@ -6,16 +6,14 @@
* @LastEditTime: 2021-02-01 14:00:19
*/
import React, { useState, useEffect } from "react";
import { Modal, Table, Input, Button, message, Checkbox, Tooltip } from "antd";
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import Bus from '@/core/bus';
import { PageControl } from "@/components";
import hasExportPermission from '../utils/hasExportPermission';
import { Button, Checkbox, Input, message, Modal, Tooltip } from 'antd';
import React from 'react';
import dealTimeDuration from '../utils/dealTimeDuration';
import "./ClassRecordModal.less";
import hasExportPermission from '../utils/hasExportPermission';
import './ClassRecordModal.less';
const { Search } = Input;
......@@ -28,7 +26,7 @@ class ClassRecordModal extends React.Component {
query: {
current: 1,
size: 10,
nameOrPhone: "",
nameOrPhone: '',
liveSignState: '',
durationSort: null,
liveCourseId: props.liveItem.liveCourseId,
......@@ -42,13 +40,11 @@ class ClassRecordModal extends React.Component {
fetchClassList = (page = 1) => {
const params = _.clone(this.state.query);
if(!params.liveSignState) {
if (!params.liveSignState) {
delete params.liveSignState;
}
params.current = page;
window.axios
.Apollo("public/businessLive/queryStudentVisitData", params)
.then((res) => {
window.axios.Apollo('public/businessLive/queryStudentVisitData', params).then((res) => {
if (res.result) {
const { records = [], total } = res.result;
this.setState({
......@@ -61,9 +57,7 @@ class ClassRecordModal extends React.Component {
};
fetchTeacherData = () => {
const { liveCourseId } = this.props.liveItem;
window.axios
.Apollo("public/businessLive/queryTeacherVisitData", { liveCourseId })
.then((res) => {
window.axios.Apollo('public/businessLive/queryTeacherVisitData', { liveCourseId }).then((res) => {
if (res.result) {
const teacherData = [res.result];
this.setState({
......@@ -78,70 +72,72 @@ class ClassRecordModal extends React.Component {
const source = this.props.type;
const columns = [
{
title: type == "student" ? "学生姓名" : "老师姓名",
dataIndex: "userName",
title: type == 'student' ? '学生姓名' : '老师姓名',
dataIndex: 'userName',
},
{
title: "手机号",
dataIndex: "phone",
title: '手机号',
dataIndex: 'phone',
render: (text, record) => {
return (
<p>
{!(
(!window.NewVersion && !window.currentUserInstInfo.teacherId) ||
(window.NewVersion && Permission.hasEduStudentPhone())
) && type == "student"
? (text || "").replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3")
{!((!window.NewVersion && !window.currentUserInstInfo.teacherId) || (window.NewVersion && Permission.hasEduStudentPhone())) && type == 'student'
? (text || '').replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
: text}
</p>
);
},
},
{
title: type == "student" ? "观看直播次数" : "进入直播间次数",
dataIndex: "entryNum",
align:'right'
title: type == 'student' ? '观看直播次数' : '进入直播间次数',
dataIndex: 'entryNum',
align: 'right',
},
{
title: "累计上课时长",
dataIndex: type == "student" ? "watchDuration" : "totalDuration",
sorter:
type == "student"
? (a, b) => a.watchDuration - b.watchDuration
: null,
sortDirections: ["descend", "ascend"],
title: '累计上课时长',
dataIndex: type == 'student' ? 'watchDuration' : 'totalDuration',
sorter: type == 'student' ? (a, b) => a.watchDuration - b.watchDuration : null,
sortDirections: ['descend', 'ascend'],
render: (text, record) => {
//如无离开时间,就置空
return (
<span>
{text ? dealTimeDuration(text) : '00:00:00'}
</span>
);
return <span>{text ? dealTimeDuration(text) : '00:00:00'}</span>;
},
},
];
if(type == "student") {
if (type == 'student') {
columns.push({
title: <span>到课状态<Tooltip title={<div>学员累计上课时长达到<span className="bulge">{consumeClassTime}</span>分钟,即视为学员“到课”</div>}><span className="iconfont">&#xe6f2;</span></Tooltip></span>,
title: (
<span>
到课状态
<Tooltip
title={
<div>
学员累计上课时长达到<span className='bulge'>{consumeClassTime}</span>分钟,即视为学员“到课”
</div>
}>
<span className='iconfont'>&#xe6f2;</span>
</Tooltip>
</span>
),
width: 100,
dataIndex: "signState",
dataIndex: 'signState',
render: (text) => {
if(text) {
return <span>{text === 'ABSENT' ? '未到' : '到课'}</span>
if (text) {
return <span>{text === 'ABSENT' ? '未到' : '到课'}</span>;
} else {
return <span>-</span>
}
return <span>-</span>;
}
})
if(source) {
},
});
if (source) {
columns.push({
title: "获得奖杯数",
dataIndex: "trophyNum",
align:'right',
title: '获得奖杯数',
dataIndex: 'trophyNum',
align: 'right',
render: (text) => {
return <span>{text ? text : 0}</span>
}
})
return <span>{text ? text : 0}</span>;
},
});
}
}
return columns;
......@@ -150,11 +146,11 @@ class ClassRecordModal extends React.Component {
handleTableChange = (pagination, filters, sorter) => {
const query = this.state.query;
if (!_.isEmpty(sorter)) {
if (sorter.columnKey === "totalDuration") {
if (sorter.order === "ascend") {
query.durationSort = "SORT_ASC";
} else if (sorter.order === "descend") {
query.durationSort = "SORT_DESC";
if (sorter.columnKey === 'totalDuration') {
if (sorter.order === 'ascend') {
query.durationSort = 'SORT_ASC';
} else if (sorter.order === 'descend') {
query.durationSort = 'SORT_DESC';
}
this.setState({ query }, this.fetchClassList);
}
......@@ -165,20 +161,22 @@ class ClassRecordModal extends React.Component {
handleExportV5 = () => {
const { liveItem, type } = this.props;
const { liveCourseId } = liveItem;
const url = !type ? 'public/businessLive/exportLargeClassLiveAsync' : 'public/businessLive/exportClassInteractionLiveSync'
const url = !type ? 'public/businessLive/exportLargeClassLiveAsync' : 'public/businessLive/exportClassInteractionLiveSync';
window.axios.Apollo(url, {
window.axios
.Apollo(url, {
liveCourseId,
exportLiveType: 'VISITOR'
}).then((res) => {
exportLiveType: 'VISITOR',
})
.then((res) => {
Bus.trigger('get_download_count');
Modal.success({
title: '导出任务提交成功',
content: '请前往右上角的“任务中心”进行下载',
okText: '我知道了',
});
})
}
});
};
// 4.0导出
handleExport = () => {
......@@ -186,43 +184,41 @@ class ClassRecordModal extends React.Component {
const { liveCourseId } = liveItem;
const url = !type ? 'api-b/b/lesson/exportLargeClassLiveAsync' : 'api-b/b/lesson/exportClassInteractionLiveSync';
window.axios.post(url, {
window.axios
.post(url, {
liveCourseId,
exportLiveType: 1
}).then((res) => {
exportLiveType: 1,
})
.then((res) => {
Bus.trigger('get_download_count');
Modal.success({
title: '导出任务提交成功',
content: '请前往右上角的“导出中心”进行下载',
okText: '我知道了',
});
})
}
});
};
render() {
const { type } = this.props;
const { query, total, teacherData, classList } = this.state;
const expandedColumns = [
{
title: "进入时间",
dataIndex: "entryTime",
key: "entryTime",
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
title: '进入时间',
dataIndex: 'entryTime',
key: 'entryTime',
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{
title: "离开时间",
dataIndex: "leaveTime",
key: "leaveTime",
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
title: '离开时间',
dataIndex: 'leaveTime',
key: 'leaveTime',
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{
title: "上课时长",
dataIndex: "lookingDuration",
key: "lookingDuration",
title: '上课时长',
dataIndex: 'lookingDuration',
key: 'lookingDuration',
render: (text, record) => {
return <span>{text ? dealTimeDuration(text) : '-'}</span>;
},
......@@ -230,64 +226,73 @@ class ClassRecordModal extends React.Component {
];
return (
<Modal
title="上课记录"
title='上课记录'
visible={true}
footer={null}
width={680}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="class-record-modal"
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='class-record-modal'
onCancel={() => {
this.props.close();
}}
>
}}>
<div>
<p className="class-record-title" style={{ marginBottom: 18 }}>
<p className='class-record-title' style={{ marginBottom: 18 }}>
老师上课数据
</p>
<Table
size="small"
columns={this.getColumns("teacher")}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='small'
columns={this.getColumns('teacher')}
dataSource={teacherData}
pagination={false}
className="table-no-scrollbar"
className='table-no-scrollbar'
expandedRowRender={(record) => {
if (
record.visitorInfoVOList &&
record.visitorInfoVOList.length > 0
) {
if (record.visitorInfoVOList && record.visitorInfoVOList.length > 0) {
return (
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
columns={expandedColumns}
dataSource={record.visitorInfoVOList}
size={"small"}
className="no-scrollbar expanded-table"
size={'small'}
className='no-scrollbar expanded-table'
pagination={false}
></Table>
/>
);
} else {
return <div className="live-table--empty">暂无上课数据</div>;
return <div className='live-table--empty'>暂无上课数据</div>;
}
}}
></Table>
<div className="student-wrapper">
<section className="class-record-title">学员上课数据</section>
/>
<div className='student-wrapper'>
<section className='class-record-title'>学员上课数据</section>
<section>
<Checkbox
style={{lineHeight: '33px'}}
style={{ lineHeight: '33px' }}
onChange={(e) => {
const param = _.clone(this.state.query);
param.current = 1;
param.liveSignState = e.target.checked ? 'SIGN' : '';
this.setState({
query: param
}, () => {
this.setState(
{
query: param,
},
() => {
this.fetchClassList();
})
}}>只看“到课”学员</Checkbox>
}
);
}}>
只看“到课”学员
</Checkbox>
<Search
className="student-wrapper__search"
placeholder="搜索学员姓名/手机号"
className='student-wrapper__search'
placeholder='搜索学员姓名/手机号'
style={{ width: 200, marginBottom: 0 }}
onSearch={(value) => {
const param = _.clone(this.state.query);
......@@ -303,9 +308,10 @@ class ClassRecordModal extends React.Component {
);
}}
/>
{
hasExportPermission(type) &&
<Button onClick={_.debounce(() => {
{hasExportPermission(type) && (
<Button
onClick={_.debounce(
() => {
if (!classList.length) {
message.warning('暂无数据可导出');
return;
......@@ -315,40 +321,49 @@ class ClassRecordModal extends React.Component {
} else {
this.handleExport();
}
}, 500, true)}>导出</Button>
}
},
500,
true
)}>
导出
</Button>
)}
</section>
</div>
<div>
<Table
size="small"
columns={this.getColumns("student")}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='small'
columns={this.getColumns('student')}
dataSource={classList}
pagination={false}
className="table-no-scrollbar"
className='table-no-scrollbar'
onChange={this.handleTableChange}
expandedRowRender={(record) => {
if (
record.visitorInfoVOList &&
record.visitorInfoVOList.length > 0
) {
if (record.visitorInfoVOList && record.visitorInfoVOList.length > 0) {
return (
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
columns={expandedColumns}
dataSource={record.visitorInfoVOList}
size={"small"}
className="no-scrollbar expanded-table"
size={'small'}
className='no-scrollbar expanded-table'
pagination={false}
></Table>
/>
);
} else {
return <div className="live-table--empty">暂无上课数据</div>;
return <div className='live-table--empty'>暂无上课数据</div>;
}
}}
></Table>
/>
<PageControl
size="small"
size='small'
current={query.current - 1}
pageSize={query.size}
total={total}
......
......@@ -5,61 +5,49 @@
* @Last Modified time: 2020-07-22 16:25:30
* 查看学员名单
*/
import React from "react";
import {
Modal,
Button,
Input,
Table,
Popconfirm,
message,
Tooltip,
} from "antd";
import PropTypes from "prop-types";
import { PageControl } from "@/components";
import SelectStudent from "./select-student/index";
import "./LiveStudentListModal.less";
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { Button, Input, message, Modal, Popconfirm, Tooltip } from 'antd';
import PropTypes from 'prop-types';
import React from 'react';
import './LiveStudentListModal.less';
import SelectStudent from './select-student/index';
const { Search } = Input;
const isTeacher = !!window.currentUserInstInfo.teacherId;
const liveTypeMap = {
LIVE: "直播",
PLAYBACK: "回放",
LIVE: '直播',
PLAYBACK: '回放',
};
const expandedColumns = [
{
title: "类型",
dataIndex: "liveType",
key: "liveType",
title: '类型',
dataIndex: 'liveType',
key: 'liveType',
render: (text) => <span>{liveTypeMap[text]}</span>,
},
{
title: "进入时间",
dataIndex: "entryTime",
key: "entryTime",
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
title: '进入时间',
dataIndex: 'entryTime',
key: 'entryTime',
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{
title: "离开时间",
dataIndex: "leaveTime",
key: "leaveTime",
render: (text) => (
<span>{formatDate("YYYY-MM-DD H:i", parseInt(text))}</span>
),
title: '离开时间',
dataIndex: 'leaveTime',
key: 'leaveTime',
render: (text) => <span>{formatDate('YYYY-MM-DD H:i', parseInt(text))}</span>,
},
{ title: "观看时长", dataIndex: "lookingTime", key: "lookingTime" },
{ title: '观看时长', dataIndex: 'lookingTime', key: 'lookingTime' },
];
const STATUS_ENUM = {
'NORMAL': '在读',
'POTENTIAL': '潜在',
'HISTORY': '历史',
'ABANDON': '废弃',
NORMAL: '在读',
POTENTIAL: '潜在',
HISTORY: '历史',
ABANDON: '废弃',
};
class LiveStudentListModal extends React.Component {
......@@ -86,9 +74,7 @@ class LiveStudentListModal extends React.Component {
fetchStudentList = (current = 1) => {
const query = _.clone(this.state.query);
query.current = current;
window.axios
.Apollo("public/businessLive/getStudentList", query)
.then((res) => {
window.axios.Apollo('public/businessLive/getStudentList', query).then((res) => {
const { records = [], total } = res.result;
this.setState({
......@@ -100,15 +86,16 @@ class LiveStudentListModal extends React.Component {
};
hanldSelect = () => {
const { query: { liveCourseId } } = this.state;
axios.Apollo('public/businessLive/getCourseDetail', { liveCourseId})
.then((res) => {
const {
query: { liveCourseId },
} = this.state;
axios.Apollo('public/businessLive/getCourseDetail', { liveCourseId }).then((res) => {
const { result = {} } = res;
const { consumeStudentIds, studentIds } = result;
const studentList = [];
const excludeStudentIds = studentIds;
const excludeConsumeStudentIds = _.pluck(consumeStudentIds, 'studentId');
this.setState({ excludeStudentIds, excludeConsumeStudentIds});
this.setState({ excludeStudentIds, excludeConsumeStudentIds });
_.each(studentIds, (item) => {
studentList.push({ studentId: item });
......@@ -120,42 +107,39 @@ class LiveStudentListModal extends React.Component {
studentList={studentList}
excludeStudentIds={excludeStudentIds}
after={true}
close={() => { this.setState({ studentModal: null }); }}
close={() => {
this.setState({ studentModal: null });
}}
onSelect={(studentIds) => {
this.handleSelectStudent(studentIds)
this.handleSelectStudent(studentIds);
}}
/>
)
);
this.setState({ studentModal });
})
});
};
handleSelectStudent = (studentIds) => {
const {
liveType,
liveCourseId,
podium,
quota,
} = this.props.liveItem;
const { liveType, liveCourseId, podium, quota } = this.props.liveItem;
if (liveType !== "SMALL_CLASS_INTERACTION" && (studentIds.length) > 1000) {
if (liveType !== 'SMALL_CLASS_INTERACTION' && studentIds.length > 1000) {
message.info(`最多选择1000人`);
return;
} else if (liveType == "SMALL_CLASS_INTERACTION" && (studentIds.length) > podium) {
} else if (liveType == 'SMALL_CLASS_INTERACTION' && studentIds.length > podium) {
message.info(`最多选择${podium}人`);
return;
} else {
const param = {
liveCourseId: liveCourseId,
studentIds: studentIds
studentIds: studentIds,
};
axios.Apollo("public/businessLive/addCourseStu", param).then(res => {
axios.Apollo('public/businessLive/addCourseStu', param).then((res) => {
if (res.success) {
this.setState({
studentModal: null
studentModal: null,
});
message.success("学员变更成功");
message.success('学员变更成功');
this.fetchStudentList();
this.props.refresh();
}
......@@ -170,10 +154,8 @@ class LiveStudentListModal extends React.Component {
liveCourseId,
studentId,
};
window.axios
.Apollo("public/businessLive/moveCourseStu", param)
.then((res) => {
message.success("移除学员成功");
window.axios.Apollo('public/businessLive/moveCourseStu', param).then((res) => {
message.success('移除学员成功');
this.fetchStudentList(1);
this.props.refresh();
});
......@@ -182,47 +164,41 @@ class LiveStudentListModal extends React.Component {
parseColumns = () => {
const { type, liveItem } = this.props;
const columns = [
{ title: "姓名", dataIndex: "studentName", key: "studentName" },
{ title: '姓名', dataIndex: 'studentName', key: 'studentName' },
{
title: "手机号",
dataIndex: "phone",
title: '手机号',
dataIndex: 'phone',
width: 150,
key: "phone",
key: 'phone',
render: (text, record) => {
return (
<p>
{!(
(!window.NewVersion && !window.currentUserInstInfo.teacherId) ||
(window.NewVersion && Permission.hasEduStudentPhone())
)
? (text || "").replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3")
{!((!window.NewVersion && !window.currentUserInstInfo.teacherId) || (window.NewVersion && Permission.hasEduStudentPhone()))
? (text || '').replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
: text}
<Tooltip
title={`${record.wechatStatus ? "已绑定微信" : "未绑定微信"}`}
>
<Tooltip title={`${record.wechatStatus ? '已绑定微信' : '未绑定微信'}`}>
<span
className="icon iconfont"
className='icon iconfont'
style={
record.wechatStatus
? {
color: "#00D20D",
fontSize: "16px",
color: '#00D20D',
fontSize: '16px',
marginLeft: 6,
}
: {
color: "#BFBFBF",
fontSize: "16px",
color: '#BFBFBF',
fontSize: '16px',
marginLeft: 6,
}
}
>
}>
&#xe68d;
</span>
</Tooltip>
</p>
);
},
}
},
];
// 非互动班课类型增加学员类型
......@@ -233,100 +209,97 @@ class LiveStudentListModal extends React.Component {
dataIndex: 'statusEnum',
render: (val) => {
return STATUS_ENUM[val];
}
},
});
}
// 如果是非视频课, 显示操作的条件是课程未开始,且不是T端
// 如果是视频课,那么只要满足不是T端就可以了
if ((liveItem.courseState === "UN_START" || type === 'videoCourse') && !isTeacher) {
if ((liveItem.courseState === 'UN_START' || type === 'videoCourse') && !isTeacher) {
// 未开始
columns.push({
title: "操作",
dataIndex: "operate",
key: "operate",
align:'right',
title: '操作',
dataIndex: 'operate',
key: 'operate',
align: 'right',
render: (text, record) => {
return (
<Popconfirm
title="你确定要移出这个学员吗?"
title='你确定要移出这个学员吗?'
onConfirm={() => {
// 如果是非视频课,且直播间类型是自研, 且晚于开课前30分钟, 不允许移出
if (
liveItem.channel == "XIAOMAI" &&
liveItem.startTime - Date.now() < 1800000
) {
if (liveItem.channel == 'XIAOMAI' && liveItem.startTime - Date.now() < 1800000) {
Modal.warning({
title: "不可移出",
icon: (
<span className="icon iconfont default-confirm-icon">
&#xe6f4;
</span>
),
content: "晚于开课前30分钟,不能移出学员",
title: '不可移出',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
content: '晚于开课前30分钟,不能移出学员',
});
} else {
this.removeStudent(record.studentId);
}
}}
>
<span className="live-operate">移出</span>
}}>
<span className='live-operate'>移出</span>
</Popconfirm>
);
},
});
}
return columns;
}
};
render() {
const { studentList, query, total } = this.state;
const { current, size } = query;
return (
<Modal
title="查看学员名单"
title='查看学员名单'
visible={true}
width={680}
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="live-student-list-modal"
onCancel={this.props.close}
>
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='live-student-list-modal'
onCancel={this.props.close}>
{/* 任意状态都可以添加学员 */}
<div className="live-student-list-modal__operate">
{
!isTeacher &&
<Button type="primary" onClick={this.hanldSelect}>
<div className='live-student-list-modal__operate'>
{!isTeacher && (
<Button type='primary' onClick={this.hanldSelect}>
添加上课学员
</Button>
}
)}
<Search
placeholder="搜索学员姓名/手机号"
placeholder='搜索学员姓名/手机号'
style={{ width: 200 }}
onSearch={(value) => {
this.setState({
this.setState(
{
query: {
...this.state.query,
nameOrPhone: value
}
}, () => {
nameOrPhone: value,
},
},
() => {
this.fetchStudentList(1);
});
}
);
}}
className="search"
className='search'
/>
</div>
<Table
size="small"
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='small'
columns={this.parseColumns()}
dataSource={studentList}
pagination={false}
scroll={{ y: 400 }}
className="live-student-table table-no-scrollbar"
className='live-student-table table-no-scrollbar'
/>
<PageControl
size="small"
size='small'
current={current - 1}
pageSize={size}
total={Number(total)}
......
......@@ -4,26 +4,28 @@
* @Last Modified by: chenshu
* @Last Modified time: 2021-06-08 18:18:46
*/
import React from 'react';
import { Modal, Button, Table, message, Tooltip, Spin, Popconfirm } from 'antd';
import { QuestionCircleOutlined,LoadingOutlined} from "@ant-design/icons";
import _ from 'underscore';
import moment from 'moment';
import Lottie from "lottie-web";
import PreviewFileModal from '@/bu-components/PreviewFileModal';
import { FileTypeIcon, FileVerifyMap } from '@/common/constants/academic/lessonEnum';
import { DEFAULT_SIZE_UNIT, suffixType } from '@/common/constants/academic/liveEnum';
import Service from '@/common/js/service';
import User from '@/common/js/user';
import { suffixType, DEFAULT_SIZE_UNIT } from '@/common/constants/academic/liveEnum';
import { FileVerifyMap, FileTypeIcon } from '@/common/constants/academic/lessonEnum';
import ScanFileModal from '@/modules/prepare-lesson/modal/ScanFileModal'
import college from '@/common/lottie/college';
import { XMTable } from '@/components';
import BaseService from '@/domains/basic-domain/baseService';
import { YZ_APPId, YZ_PREVIEW_URL } from '@/domains/basic-domain/constants';
import ScanFileModal from '@/modules/prepare-lesson/modal/ScanFileModal';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Service from '@/common/js/service';
import { LoadingOutlined } from '@ant-design/icons';
import { Button, message, Modal, Popconfirm, Spin, Tooltip } from 'antd';
import Lottie from 'lottie-web';
import moment from 'moment';
import React from 'react';
import _ from 'underscore';
import './ManageCoursewareModal.less';
class ManageCoursewareModal extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
list: [],
uploadObject: {},
......@@ -33,35 +35,36 @@ class ManageCoursewareModal extends React.Component {
scanFileModal: false,
isLessonPermission: false,
diskList: [], // 机构可见的磁盘目录
selectedFileList: []
}
selectedFileList: [],
showPreviewModal: false, //是否显示loading
previewing: false, //是否正在预览
previewStatus: 'UPLOAD', //预览文件的生成状态
};
}
componentDidMount() {
this.getCoursewareList();
var animation = Lottie.loadAnimation({
path: "https://image.xiaomaiketang.com/xm/SDBkP7mbJX.json",
name: "test",
renderer: "svg",
path: 'https://image.xiaomaiketang.com/xm/SDBkP7mbJX.json',
name: 'test',
renderer: 'svg',
loop: true,
autoplay: true,
container: document.getElementById("lottie-box")
container: document.getElementById('lottie-box'),
});
}
// 获取课件列表
getCoursewareList(id) {
const { liveCourseId } = this.props.data;
const params={
const params = {
liveCourseId,
storeId:User.getStoreId()
}
storeId: User.getStoreId(),
};
Service.Hades('/anon/courseWare/getCourseWareDocList', params).then((res) => {
let newList = [];
const { list } = this.state;
const data = _.find(res.result, item => item.docId == id);
const data = _.find(res.result, (item) => item.docId == id);
if (id && !_.isEmpty(data)) {
newList = list.map((item) => {
if (item.id == id) {
......@@ -80,7 +83,7 @@ class ManageCoursewareModal extends React.Component {
} else {
return item;
}
})
});
} else {
newList = res.result.map((item) => {
item.id = item.docId;
......@@ -98,10 +101,9 @@ class ManageCoursewareModal extends React.Component {
});
}
this.setState({ list: newList });
})
});
}
// 上传文件
addFile() {
// 判断是否早于开课前30分钟
......@@ -109,17 +111,14 @@ class ManageCoursewareModal extends React.Component {
const currentTime = new Date().getTime();
if (currentTime >= startTime - 30 * 60 * 1000) {
Modal.info({
title: "不能再上传课件了",
title: '不能再上传课件了',
icon: (
<span
className="icon iconfont default-confirm-icon"
style={{ color: "#FFBB54 !important" }}
>
<span className='icon iconfont default-confirm-icon' style={{ color: '#FFBB54 !important' }}>
&#xe834;
</span>
),
content: "请在开课前30分钟前上传课件,开课后可在客户端中进行上传。",
okText: '我知道了'
content: '请在开课前30分钟前上传课件,开课后可在客户端中进行上传。',
okText: '我知道了',
});
return;
}
......@@ -132,13 +131,12 @@ class ManageCoursewareModal extends React.Component {
this.setState({
selectedFileList: list,
showSelectFileModal: true, // 选择文件弹窗
})
});
}
handleAddFile = (addFolderIds) => {
this.setState({
showSelectFileModal: false
showSelectFileModal: false,
});
const { liveCourseId } = this.props.data;
......@@ -151,222 +149,248 @@ class ManageCoursewareModal extends React.Component {
Service.Hades('public/courseWare/uploadCourseWareFile', params).then((res) => {
this.getCoursewareList();
});
}
};
// 删除文件
deleteFile(item) {
const { list } = this.state;
Service.Hades('public/courseWare/delCourseDoc', { docId: item.id }).then(() => {
item.docId && message.success('删除成功')
})
item.docId && message.success('删除成功');
});
const _list = _.reject(list, (data) => data.id == item.id);
this.setState({ list: _list });
}
// 预览文件
handleScanFile(item) {
handleScanFile = async (item) => {
if (!item.srcDocUrl) return null;
const suffix = _.last(item.fileName.split('.')).toLowerCase();
const type = suffixType[suffix]
const type = suffixType[suffix];
const fileType = FileVerifyMap[type].type;
const that = this;
switch (fileType) {
case "PDF":
window.open(item.srcDocUrl, "_blank");
break;
case "Excel":
case "EXCEL":
case "PPT":
case "PPTX":
case "word":
case "WORD":
case "DOCX":
case "DOC":
let size = parseFloat(item.fileSize.replace(/M$|KB$/g, ''));
if (item.fileSize.includes('KB')) {
size = 0;
}
if (((fileType == 'word' || fileType == 'PPT') && size > 10) || ((fileType == 'Excel') && size > 5)) {
Modal.confirm({
title: '抱歉,不能在线预览',
content: '由于文件较大,不支持在线预览,请下载后再查看',
icon: <QuestionCircleOutlined />,
okText:"下载",
onOk:() => {
const a = document.createElement('a');
a.href = item.srcDocUrl;
a.click();
case 'PDF':
case 'Excel':
case 'EXCEL':
case 'PPT':
case 'PPTX':
case 'word':
case 'WORD':
case 'DOCX':
case 'DOC':
if (!item.fileVersionId) {
this.setState(
{
previewing: true,
showPreviewModal: true,
previewStatus: 'UPLOAD',
},
async () => {
const uploadParams = {
fileUrl: item.srcDocUrl,
instId: User.getStoreId(),
yoZoTypeEnum: 'UPLOAD',
};
const uploadSign = await BaseService.getYoZoSign(uploadParams);
BaseService.yoZoUpload(item.srcDocUrl, YZ_APPId, uploadSign).then(async function (response) {
const saveParams = {
fileVersionId: response.data.data.fileVersionId,
folderId: item.folderId,
instId: User.getStoreId(),
};
BaseService.saveYoZoFileVersionId(saveParams);
const { previewing } = that.state;
if (previewing) {
const previewParams = {
fileVersionId: response.data.data.fileVersionId,
instId: User.getStoreId(),
yoZoTypeEnum: 'VIEW',
htmlTitle: item.fileName,
};
const previewSign = await BaseService.getYoZoSign(previewParams);
const url = `${YZ_PREVIEW_URL}?fileVersionId=${response.data.data.fileVersionId}&appId=${YZ_APPId}&sign=${previewSign}&htmlTitle=${item.fileName}`;
that.setState({
previewStatus: 'UPLOAD_SUCCESS',
url,
});
}
});
}
);
} else {
const scanUrl = "https://view.officeapps.live.com/op/view.aspx?src=" + encodeURIComponent(item.srcDocUrl);
window.open(scanUrl, "_blank");
const previewParams = {
fileVersionId: item.fileVersionId,
instId: User.getStoreId(),
yoZoTypeEnum: 'VIEW',
htmlTitle: item.fileName,
};
const previewSign = await BaseService.getYoZoSign(previewParams);
const url = `${YZ_PREVIEW_URL}?fileVersionId=${item.fileVersionId}&appId=${YZ_APPId}&sign=${previewSign}&htmlTitle=${item.fileName}`;
const a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('href', url);
a.setAttribute('target', '_blank');
a.click();
document.body.removeChild(a);
}
break;
case "JPG":
case "PNG":
case "MP4":
case "MP3":
this.setState({ scanFileModal: true, editData: { fileType, ossAddress: item.srcDocUrl } })
case 'JPG':
case 'PNG':
case 'MP4':
case 'MP3':
this.setState({ scanFileModal: true, editData: { fileType, ossAddress: item.srcDocUrl } });
break;
default:
break;
}
};
cancelPreview = () => {
this.setState({
previewing: false,
showPreviewModal: false,
previewStatus: 'UPLOAD',
});
};
render() {
const columns = [
{
title: "名称",
width: "25%",
dataIndex: "name",
title: '名称',
width: '25%',
dataIndex: 'name',
render: (_value, item) => {
const suffix = _.last(item.fileName.split('.')).toLowerCase();
const fileType = suffixType[suffix]
const fileType = suffixType[suffix];
const antIcon = <LoadingOutlined/>;
const antIcon = <LoadingOutlined />;
const type = FileVerifyMap[fileType].type;
return <div className="courseware-name" onClick={() => this.handleScanFile(item)}>
return (
<div className='courseware-name' onClick={() => this.handleScanFile(item)}>
<Choose>
<When condition={(type === 'JPG' || type === 'PNG') && item.progress}>
<Spin indicator={antIcon} />
</When>
<Otherwise>
<img
src={FileTypeIcon[FileVerifyMap[fileType].type] || (item.docUrls[0] || {}).conversionFileUrl}
alt=""
className="item-img"
/>
<img src={FileTypeIcon[FileVerifyMap[fileType].type] || (item.docUrls[0] || {}).conversionFileUrl} alt='' className='item-img' />
</Otherwise>
</Choose>
<Tooltip title={item.fileName}><span className="name">{item.fileName}</span></Tooltip>
<Tooltip title={item.fileName}>
<span className='name'>{item.fileName}</span>
</Tooltip>
</div>
);
},
},
{
title: "创建人",
width: "12%",
dataIndex: "adminName",
title: '创建人',
width: '12%',
dataIndex: 'adminName',
render: (_value, item) => {
return <span>{item.operatorName}</span>
return <span>{item.operatorName}</span>;
},
},
{
title: "上传时间",
width: "20%",
dataIndex: "created",
title: '上传时间',
width: '20%',
dataIndex: 'created',
render: (_value, item) => {
return item.failState ? '-' : <span>{moment(item.created).format('YYYY-MM-DD HH:mm')}</span>
return item.failState ? '-' : <span>{moment(item.created).format('YYYY-MM-DD HH:mm')}</span>;
},
},
{
title: "大小",
width: "12%",
dataIndex: "size",
title: '大小',
width: '12%',
dataIndex: 'size',
render: (_value, item) => {
return <span>{item.fileSize}</span>
return <span>{item.fileSize}</span>;
},
},
{
title: "操作",
width: "16%",
dataIndex: "control",
title: '操作',
width: '16%',
dataIndex: 'control',
render: (_value, item) => {
const { failObject } = this.state;
const uploadFail = failObject[item.id];
// 上课前45分钟/上课中/已结束的情况下都不可操作
if (this.props.data.startTime < Date.now() + 1800000 || item.progress || uploadFail) {
return <span>-</span>
return <span>-</span>;
}
return (
<Popconfirm
title="你确定要删除这个课件吗?"
onConfirm={() => this.deleteFile(item)}
onCancel={() => { }}
>
<span style={{
<Popconfirm title='你确定要删除这个课件吗?' onConfirm={() => this.deleteFile(item)} onCancel={() => {}}>
<span
style={{
color: '#2966FF',
cursor: 'pointer'
}}>删除</span>
cursor: 'pointer',
}}>
删除
</span>
</Popconfirm>
)
);
},
},
]
const {
list, scanFileModal, editData, cancelObject,
showSelectFileModal, selectedFileList,
diskList
} = this.state;
];
const { list, scanFileModal, editData, cancelObject, showSelectFileModal, selectedFileList, diskList, showPreviewModal, previewStatus, url } = this.state;
const _list = _.reject(list, (item) => cancelObject[item.id]);
return (
<Modal
visible={true}
title="课件管理"
title='课件管理'
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="manage-courseware-modal"
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
className='manage-courseware-modal'
width={_.isEmpty(_list) ? 680 : 800}
onCancel={() => {
this.props.onCancel()
}}
>
{_.isEmpty(_list) ?
<div className="empty-body">
<div id="lottie-box" className="empty-image">
</div>
<Button
className="empty-button"
type="primary"
onClick={() => this.addFile()}
>上传课件</Button>
<p className="empty-tip">提前上传直播需要的课件和素材,直播将会变得更便捷!</p>
this.props.onCancel();
}}>
{_.isEmpty(_list) ? (
<div className='empty-body'>
<div id='lottie-box' className='empty-image'></div>
<Button className='empty-button' type='primary' onClick={() => this.addFile()}>
上传课件
</Button>
<p className='empty-tip'>提前上传直播需要的课件和素材,直播将会变得更便捷!</p>
</div>
: <div className="manage-body">
<div id="lottie-box" className="empty-image" style={{display:'none'}}></div>
<div className="header">
<Button
className="header-button"
type="primary"
onClick={() => this.addFile()}
>上传课件</Button>
) : (
<div className='manage-body'>
<div id='lottie-box' className='empty-image' style={{ display: 'none' }}></div>
<div className='header'>
<Button className='header-button' type='primary' onClick={() => this.addFile()}>
上传课件
</Button>
</div>
<Table
size="small"
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='small'
pagination={false}
rowKey={record => record.id}
rowKey={(record) => record.id}
dataSource={_list}
columns={columns}
bordered
/>
</div>
}
{
scanFileModal &&
<ScanFileModal
item={editData}
fileType={editData.fileType}
close={() => this.setState({ scanFileModal: false })}
/>
}
)}
{scanFileModal && <ScanFileModal item={editData} fileType={editData.fileType} close={() => this.setState({ scanFileModal: false })} />}
<SelectPrepareFileModal
multiple={true}
scene="liveCourse"
operateType="select"
scene='liveCourse'
operateType='select'
isOpen={showSelectFileModal}
diskList={diskList}
selectedFileList={selectedFileList}
onClose={() => {
this.setState({ showSelectFileModal: false })
this.setState({ showSelectFileModal: false });
}}
onSelect={this.handleAddFile}
/>
{showPreviewModal && <PreviewFileModal onCancel={() => this.cancelPreview()} previewStatus={previewStatus} url={url} />}
</Modal>
)
);
}
}
......
......@@ -6,21 +6,19 @@
* @LastEditTime: 2021-02-01 14:00:36
*/
import React, { useState, useEffect } from "react";
import { Modal, Table, Button, message } from "antd";
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import Bus from '@/core/bus';
import { PageControl } from "@/components";
import hasExportPermission from '../utils/hasExportPermission';
import { Button, message, Modal } from 'antd';
import React from 'react';
import dealTimeDuration from '../utils/dealTimeDuration';
import "./ClassRecordModal.less";
import hasExportPermission from '../utils/hasExportPermission';
import './ClassRecordModal.less';
const liveTypeMap = {
USER: "学生",
ANCHOR: "老师",
ADMIN: "助教",
USER: '学生',
ANCHOR: '老师',
ADMIN: '助教',
};
class PlayBackRecordModal extends React.Component {
......@@ -46,9 +44,7 @@ class PlayBackRecordModal extends React.Component {
fetchPlayBackList = (page = 1) => {
const params = _.clone(this.state.query);
params.current = page;
window.axios
.Apollo("public/businessLive/queryUserReplayRecordPage", params)
.then((res) => {
window.axios.Apollo('public/businessLive/queryUserReplayRecordPage', params).then((res) => {
const { records = [], total } = res.result;
this.setState({
query: params,
......@@ -61,7 +57,7 @@ class PlayBackRecordModal extends React.Component {
fetchAllStatistics = () => {
const { liveCourseId } = this.props.liveItem;
window.axios
.Apollo("public/businessLive/queryReplayStatistics", {
.Apollo('public/businessLive/queryReplayStatistics', {
liveCourseId,
})
.then((res) => {
......@@ -80,7 +76,7 @@ class PlayBackRecordModal extends React.Component {
const hours = Math.floor(time / 3600);
const mins = Math.floor(diff / 60);
const seconds = Math.floor(time % 60);
return hours + "小时" + mins + "分";
return hours + '小时' + mins + '分';
};
// 导出
......@@ -88,10 +84,12 @@ class PlayBackRecordModal extends React.Component {
const { liveItem, type } = this.props;
const { liveCourseId } = liveItem;
const url = !type ? 'api-b/b/lesson/exportLargeClassLiveAsync' : 'api-b/b/lesson/exportClassInteractionLiveSync';
window.axios.post(url, {
window.axios
.post(url, {
liveCourseId,
exportLiveType: 0
}).then((res) => {
exportLiveType: 0,
})
.then((res) => {
Bus.trigger('get_download_count');
Modal.success({
title: '导出任务提交成功',
......@@ -99,16 +97,18 @@ class PlayBackRecordModal extends React.Component {
okText: '我知道了',
});
});
}
};
handleExportV5 = () => {
const { liveItem, type } = this.props;
const { liveCourseId } = liveItem;
const url = !type ? 'public/businessLive/exportLargeClassLiveAsync' : 'public/businessLive/exportClassInteractionLiveSync';
window.axios.Apollo(url, {
window.axios
.Apollo(url, {
liveCourseId,
exportLiveType: 'PLAY_BACK'
}).then((res) => {
exportLiveType: 'PLAY_BACK',
})
.then((res) => {
Bus.trigger('get_download_count');
Modal.success({
title: '导出任务提交成功',
......@@ -116,78 +116,67 @@ class PlayBackRecordModal extends React.Component {
okText: '我知道了',
});
});
}
};
render() {
const columns = [
{
title: "观看者姓名",
dataIndex: "userName",
title: '观看者姓名',
dataIndex: 'userName',
},
{
title: "观看者手机号",
dataIndex: "phone",
title: '观看者手机号',
dataIndex: 'phone',
render: (text, record) => {
return (
<p>
{!(
(!window.NewVersion && !window.currentUserInstInfo.teacherId) ||
(window.NewVersion && Permission.hasEduStudentPhone())
)
? (text || "").replace(/(\d{3})(\d{4})(\d{4})/, "$1****$3")
{!((!window.NewVersion && !window.currentUserInstInfo.teacherId) || (window.NewVersion && Permission.hasEduStudentPhone()))
? (text || '').replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
: text}
</p>
);
},
},
{
title: "观看者类型",
dataIndex: "liveRole",
key: "liveRole",
title: '观看者类型',
dataIndex: 'liveRole',
key: 'liveRole',
render: (text) => <span>{liveTypeMap[text]}</span>,
},
{
title: "开始观看时间",
dataIndex: "entryTime",
key: "entryTime",
render: (text) => (
<span>{text ? formatDate("YYYY-MM-DD H:i", parseInt(text)) : '-'}</span>
),
title: '开始观看时间',
dataIndex: 'entryTime',
key: 'entryTime',
render: (text) => <span>{text ? formatDate('YYYY-MM-DD H:i', parseInt(text)) : '-'}</span>,
},
{
title: "观看时长",
dataIndex: "lookingDuration",
key: "lookingDuration",
title: '观看时长',
dataIndex: 'lookingDuration',
key: 'lookingDuration',
render: (text) => {
return <span>{text ? dealTimeDuration(text) : '-'}</span>;
},
},
];
const {
query,
total,
playBackList,
totalWatchNum,
recordDuration,
} = this.state;
const { query, total, playBackList, totalWatchNum, recordDuration } = this.state;
const { type } = this.props;
return (
<Modal
title="回放记录"
className="play-back-modal"
title='回放记录'
className='play-back-modal'
width={680}
visible={true}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}
footer={null}
onCancel={() => {
this.props.close();
}}
>
{
hasExportPermission(type) &&
<Button onClick={_.debounce(() => {
}}>
{hasExportPermission(type) && (
<Button
onClick={_.debounce(
() => {
if (!playBackList.length) {
message.warning('暂无数据可导出');
return;
......@@ -197,17 +186,26 @@ class PlayBackRecordModal extends React.Component {
} else {
this.handleExport();
}
}, 500, true)}>导出</Button>
}
<Table
size="small"
},
500,
true
)}>
导出
</Button>
)}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
size='small'
columns={columns}
dataSource={playBackList}
pagination={false}
className="table-no-scrollbar"
className='table-no-scrollbar'
/>
<PageControl
size="small"
size='small'
current={query.current - 1}
pageSize={query.size}
total={total}
......
import React from 'react'
import { Table, Modal, Input } from 'antd'
import { PageControl } from '@/components'
import CourseService from '@/domains/course-domain/CourseService'
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
import './RelatedPlanModal.less'
import _ from 'underscore'
const { Search } = Input
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import CourseService from '@/domains/course-domain/CourseService';
import PlanService from '@/domains/plan-domain/planService';
import { Input, Modal } from 'antd';
import React from 'react';
import _ from 'underscore';
import './RelatedPlanModal.less';
const { Search } = Input;
class RelatedPlanModal extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
dataSource: [],
size: 10,
query: {
current: 1
current: 1,
},
totalCount: 0,
selectPlanList: {}
}
selectPlanList: {},
};
}
componentDidMount() {
this.handleFetchDataList()
this.handleFetchDataList();
}
// 获取培训计划列表
handleFetchDataList = () => {
const { query, size } = this.state
const { query, size } = this.state;
const params = {
...query,
size,
storeId: User.getStoreId()
}
storeId: User.getStoreId(),
};
PlanService.getStorePlanAll(params).then((res) => {
const { result = {} } = res
const { records = [], total = 0 } = result
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
})
})
}
totalCount: Number(total),
});
});
};
handleChangePlanName = (value) => {
const { query } = this.state
query.planName = value
query.current = 1
const { query } = this.state;
query.planName = value;
query.current = 1;
this.setState({
query
})
}
query,
});
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
this.setState(
{
size
size,
},
() => {
this.handleFetchDataList()
}
)
this.handleFetchDataList();
}
);
};
// 请求表头
parsePlanColumns = () => {
......@@ -72,12 +73,12 @@ class RelatedPlanModal extends React.Component {
key: 'planName',
dataIndex: 'planName',
render: (val, record) => {
return <span>{val}</span>
}
}
]
return columns
}
return <span>{val}</span>;
},
},
];
return columns;
};
parseTaskColumns = (parentIndex) => {
const columns = [
......@@ -86,76 +87,76 @@ class RelatedPlanModal extends React.Component {
key: 'taskName',
dataIndex: 'taskName',
render: (val, record) => {
return <span>{val}</span>
}
}
]
return columns
}
return <span>{val}</span>;
},
},
];
return columns;
};
selectPlanList = (record, selected, planId) => {
const { selectPlanList } = this.props
let _selectPlanList = { ...selectPlanList }
const { selectPlanList } = this.props;
let _selectPlanList = { ...selectPlanList };
if (selected) {
if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {}
_selectPlanList[planId] = {};
}
_selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId
_selectPlanList[planId].taskBaseVOList.push(record)
_selectPlanList[planId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId;
_selectPlanList[planId].taskBaseVOList.push(record);
} else {
if (!_selectPlanList[planId]) {
_selectPlanList[planId] = {}
_selectPlanList[planId] = {};
}
_selectPlanList[planId].taskBaseVOList = []
_selectPlanList[planId].planId = planId
_selectPlanList[planId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId;
}
this.props.onChange(_selectPlanList)
this.props.onChange(_selectPlanList);
// this.setState({selectPlanList:_selectPlanList});
}
};
handleSelectPlanListData(selectPlanList) {
let _selectPlanList = []
let _selectPlanList = [];
for (let key in selectPlanList) {
let item = {}
let item = {};
if (selectPlanList[key].taskBaseVOList) {
item.planId = selectPlanList[key].planId
item.planId = selectPlanList[key].planId;
if (selectPlanList[key].taskBaseVOList[0]) {
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId;
}
}
if (item.taskId) {
_selectPlanList.push(item)
_selectPlanList.push(item);
}
}
return _selectPlanList
return _selectPlanList;
}
confirmRelatedPlan = () => {
const { selectPlanList } = this.props
const { selectPlanList } = this.props;
const params = {
courseId: this.props.selectCourseId,
relatedPlanList: this.handleSelectPlanListData(selectPlanList),
storeId: User.getStoreId()
}
storeId: User.getStoreId(),
};
CourseService.relatedCourseToPlan(params).then((res) => {
this.props.onConfirm()
})
}
this.props.onConfirm();
});
};
getSelectLength = (selectList) => {
let num = 0
let num = 0;
for (let key in selectList) {
if (selectList[key].taskBaseVOList.length > 0) {
num = num + 1
}
num = num + 1;
}
return num
}
return num;
};
clearSelect = () => {
const _selectPlanList = {}
this.props.onChange(_selectPlanList)
}
const _selectPlanList = {};
this.props.onChange(_selectPlanList);
};
render() {
const { size, dataSource, totalCount, query } = this.state
const { visible, selectPlanList } = this.props
const { size, dataSource, totalCount, query } = this.state;
const { visible, selectPlanList } = this.props;
return (
<Modal
title='关联培训计划'
......@@ -172,10 +173,10 @@ class RelatedPlanModal extends React.Component {
placeholder='搜索培训计划名称'
style={{ width: 207 }}
onChange={(e) => {
this.handleChangePlanName(e.target.value)
this.handleChangePlanName(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList()
this.handleFetchDataList();
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
......@@ -194,7 +195,11 @@ class RelatedPlanModal extends React.Component {
</span>
</div>
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.planId}
className='plan-table'
dataSource={dataSource}
......@@ -202,20 +207,24 @@ class RelatedPlanModal extends React.Component {
pagination={false}
expandedRowRender={(_record, index) => {
if (!_record.taskBaseVOList) {
return
return;
}
if (_record.taskBaseVOList.length !== 0) {
const selectPlan = selectPlanList[_record.planId]
let taskBaseVOList = []
const selectPlan = selectPlanList[_record.planId];
let taskBaseVOList = [];
if (selectPlan) {
taskBaseVOList = selectPlan.taskBaseVOList
taskBaseVOList = selectPlan.taskBaseVOList;
}
console.log('taskBaseVOList', taskBaseVOList)
console.log('taskBaseVOList', taskBaseVOList);
return (
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.taskId}
pagination={false}
dataSource={_record.taskBaseVOList}
......@@ -225,20 +234,20 @@ class RelatedPlanModal extends React.Component {
type: 'checkbox',
selectedRowKeys: _.pluck(taskBaseVOList, 'taskId'),
onSelect: (record, selected) => {
this.selectPlanList(record, selected, _record.planId)
this.selectPlanList(record, selected, _record.planId);
},
onSelectAll: (selected, _selectedRows, changeRows) => {}
onSelectAll: (selected, _selectedRows, changeRows) => {},
}}
/>
</div>
)
);
}
}}
rowClassName={(record, index) => {
if (index % 2 === 0) {
return 'odd-row'
return 'odd-row';
} else {
return 'even-row'
return 'even-row';
}
}}
/>
......@@ -250,15 +259,15 @@ class RelatedPlanModal extends React.Component {
total={totalCount}
size='small'
toPage={(page) => {
const _query = { ...query, current: page + 1 }
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query
query: _query,
},
() => {
this.handleFetchDataList()
this.handleFetchDataList();
}
)
);
}}
onShowSizeChange={this.onShowSizeChange}
/>
......@@ -266,8 +275,8 @@ class RelatedPlanModal extends React.Component {
)}
</div>
</Modal>
)
);
}
}
export default RelatedPlanModal
export default RelatedPlanModal;
......@@ -6,67 +6,67 @@
* @Description: 大班直播分享弹窗
*/
import React from 'react'
import { Modal, Button, message } from 'antd'
import domtoimage from 'dom-to-image'
import React from 'react';
import { Modal, Button, message } from 'antd';
import domtoimage from 'dom-to-image';
import qrcode from '@/libs/qrcode/qrcode.js'
import User from '@/common/js/user'
import $ from 'jquery'
import _ from 'underscore'
import CourseService from '@/domains/course-domain/CourseService'
import qrcode from '@/libs/qrcode/qrcode.js';
import User from '@/common/js/user';
import $ from 'jquery';
import _ from 'underscore';
import CourseService from '@/domains/course-domain/CourseService';
import './ShareLiveModal.less'
import './ShareLiveModal.less';
class ShareLiveModal extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
shareUrl: 'https://xiaomai5.com/liveShare?courseId=12',
}
};
}
componentDidMount() {
// 获取短链接
this.handleConvertShortUrl()
this.handleConvertShortUrl();
}
handleConvertShortUrl = () => {
const { longUrl } = this.props.data
const { longUrl } = this.props.data;
// 发请求
CourseService.getQrcode({
urls: [longUrl],
}).then((res) => {
const { result = [] } = res
const { result = [] } = res;
this.setState(
{
shareUrl: result[0].shortUrl,
},
() => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap')
const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new qrcode({
text: this.state.shareUrl,
size: 98,
})
qrcodeWrapDom.appendChild(qrcodeNode)
});
qrcodeWrapDom.appendChild(qrcodeNode);
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload')
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload');
const qrcodeNodeDownLoad = new qrcode({
text: this.state.shareUrl,
size: 196,
})
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad)
}
)
})
});
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad);
}
);
});
};
componentWillUnmount() {
// 页面销毁之前清空定时器
clearTimeout(this.timer)
clearTimeout(this.timer);
}
// 下载海报
......@@ -79,47 +79,48 @@ class ShareLiveModal extends React.Component {
() => {
this.setState({ time: new Date().valueOf() }, () => {
setTimeout(() => {
let node = document.getElementById('poster-dowload')
let node = document.getElementById('poster-dowload');
domtoimage.toPng(node).then((imgData) => {
console.log(imgData)
const download = document.createElement('a')
const { courseName } = this.props.data
$(download).attr('href', imgData).attr('download', `${courseName}.png`).get(0).click()
})
}, 1000)
})
}
)
console.log(imgData);
const download = document.createElement('a');
const { courseName } = this.props.data;
$(download).attr('href', imgData).attr('download', `${courseName}.png`).get(0).click();
});
}, 1000);
});
}
);
};
// 复制分享链接
handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText
const textContent = document.getElementById('shareUrl').innerText;
window.copyText(textContent)
message.success('复制成功!')
}
window.copyText(textContent);
message.success('复制成功!');
};
render() {
const { courseDivision, data, type, title } = this.props
const { courseName, scheduleVideoUrl, courseMediaVOS, coverUrl } = data
const { shareUrl, showImg, time } = this.state
const { courseDivision, data, type, title } = this.props;
const { courseName, scheduleVideoUrl, courseMediaVOS, coverUrl } = data;
const { shareUrl, showImg, time } = this.state;
// 判断是否是默认图, 默认图不需要在URL后面增加字符串
let coverImgSrc = '';
switch (type) {
case 'liveClass': // 直播课
if (courseMediaVOS && courseMediaVOS.length > 0) {
data.courseMediaVOS.map((item, index) => {
if (item.contentType === 'COVER') {
coverImgSrc = item.mediaUrl
}
})
const coverItem = courseMediaVOS.filter((item) => item.contentType === 'COVER');
coverImgSrc = coverItem.length === 0 ? 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png' : coverItem[0].mediaUrl;
} else {
coverImgSrc = 'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png';
}
break;
case 'videoClass': // 视频课
coverImgSrc = coverUrl || (courseDivision === 'internal' ? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring` : 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
coverImgSrc =
coverUrl ||
(courseDivision === 'internal'
? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast&anystring=anystring`
: 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png');
break;
case 'graphicsClass': // 图文课
coverImgSrc = coverUrl || 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png';
......@@ -214,8 +215,8 @@ class ShareLiveModal extends React.Component {
</div>
</div>
</Modal>
)
);
}
}
export default ShareLiveModal
export default ShareLiveModal;
......@@ -7,40 +7,42 @@
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { Dropdown, Menu, Tooltip } from 'antd';
import React from 'react';
import { Table, Tooltip, Menu, Dropdown } from 'antd';
import _ from 'underscore';
import Bus from '@/core/bus';
import { PageControl } from '@/components';
let resourceData = {};
_.map(window.RESOURCE, item => {
resourceData[item.code] = item.name
_.map(window.RESOURCE, (item) => {
resourceData[item.code] = item.name;
});
const isNewVersion = window.NewVersion;
class StudentList extends React.Component {
constructor(props) {
super(props);
this.state = {
showDegiest: false,
selectedRows: [],
prevSelectRows: props.savedSelectedRows || [],
savedSelectedRows: props.savedSelectedRows || []
}
savedSelectedRows: props.savedSelectedRows || [],
};
}
parseColumns = () => {
const { query: { status }, type, studentType } = this.props;
const {
query: { status },
type,
studentType,
} = this.props;
// 是否是在读学员
const isNormal = (status === 'NORMAL' || status === 1);
const isNormal = status === 'NORMAL' || status === 1;
// 是否是潜在学员
const isPotential = (status === 'POTENTIAL' || status === 2);
const isPotential = status === 'POTENTIAL' || status === 2;
// 是否是历史学员
const isHistory = (status === 'HISTORY' || status === 4);
const isHistory = status === 'HISTORY' || status === 4;
const columns = [
{
......@@ -50,7 +52,7 @@ class StudentList extends React.Component {
render: (text, record) => {
const { studentBasicVO = {}, name } = record;
return studentBasicVO.name || name;
}
},
},
{
title: '手机号',
......@@ -58,50 +60,54 @@ class StudentList extends React.Component {
render: (text, record) => {
const { type } = this.props;
const { studentBasicVO = {}, phone, weChatStatus, wechatStatus } = record;
const { NewVersion, currentUserInstInfo: { teacherId } } = window;
const {
NewVersion,
currentUserInstInfo: { teacherId },
} = window;
return (
<div className="record__item">
{
!((!NewVersion && !teacherId) || (NewVersion && Permission.hasEduStudentPhone())) ?
(studentBasicVO.phone || phone).replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3') :
studentBasicVO.phone || phone
}
{
type !== 'videoCourse' &&
<div className='record__item'>
{!((!NewVersion && !teacherId) || (NewVersion && Permission.hasEduStudentPhone()))
? (studentBasicVO.phone || phone).replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
: studentBasicVO.phone || phone}
{type !== 'videoCourse' && (
// 此处为了兼容4.0 和 5.0的扣课 不扣课学员的绑定微信
<Tooltip
title={`${(studentBasicVO.weChatStatus || weChatStatus || wechatStatus) ? '已绑定微信' : '未绑定微信'}`}
>
<Tooltip title={`${studentBasicVO.weChatStatus || weChatStatus || wechatStatus ? '已绑定微信' : '未绑定微信'}`}>
<span
className="iconfont icon"
style={(studentBasicVO.weChatStatus || weChatStatus || wechatStatus) ? {
className='iconfont icon'
style={
studentBasicVO.weChatStatus || weChatStatus || wechatStatus
? {
color: '#00D20D',
fontSize: '16px',
marginLeft: 6
} : {
marginLeft: 6,
}
: {
fontSize: '16px',
color:'#BFBFBF',
marginLeft: 6
}}
>&#xe68d;</span>
</Tooltip>
color: '#BFBFBF',
marginLeft: 6,
}
}>
&#xe68d;
</span>
</Tooltip>
)}
</div>
)
}
}
);
},
},
];
// 扣课时学员显示消耗课程和剩余课时
if (studentType === 'DEDUCTION') {
const { consumeStudentList } = this.props;
columns.push({
columns.push(
{
title: (
<div className="consumption-course">
<span className="text">消耗课程</span>
<Tooltip title="学员在这上课所消耗的课程">
<span className="icon iconfont">&#xe6f2;</span>
<div className='consumption-course'>
<span className='text'>消耗课程</span>
<Tooltip title='学员在这上课所消耗的课程'>
<span className='icon iconfont'>&#xe6f2;</span>
</Tooltip>
</div>
),
......@@ -112,26 +118,26 @@ class StudentList extends React.Component {
const { digestHourVOS, studentId } = record;
if (!digestHourVOS || !digestHourVOS.length) {
return <span className="digest-hour--empty">无可消耗课程</span>
return <span className='digest-hour--empty'>无可消耗课程</span>;
} else if (digestHourVOS.length === 1) {
return <span className="course-name">{digestHourVOS[0].courseName}</span>
return <span className='course-name'>{digestHourVOS[0].courseName}</span>;
} else {
const { showDegiest, currentCourse } = this.state;
// 默认显示第一个课程包的名称
let _currentCourse = digestHourVOS[0];
// 判断学员是否被勾选了
const hasSelect = _.find(consumeStudentList, item => {
const hasSelect = _.find(consumeStudentList, (item) => {
return item.studentId === studentId;
});
// 如果被勾选了,再判断选中的课时包是哪个
if (hasSelect) {
const hasSelectCourse = _.find(digestHourVOS, item => {
return item.courseId === hasSelect.classHourId
const hasSelectCourse = _.find(digestHourVOS, (item) => {
return item.courseId === hasSelect.classHourId;
});
if (hasSelectCourse) {
_currentCourse = hasSelectCourse
_currentCourse = hasSelectCourse;
}
}
// 选择课时包之后, 根据学员ID判断选择的是哪个学员的课时包
......@@ -140,32 +146,27 @@ class StudentList extends React.Component {
}
if (this.isDisabledRow(record)) {
return <span className="course-name">{_currentCourse.courseName}</span>
return <span className='course-name'>{_currentCourse.courseName}</span>;
} else {
return (
<Dropdown
overlay={this.renderCourseMenu(digestHourVOS)}
placement="bottomCenter"
arrow
>
<Dropdown overlay={this.renderCourseMenu(digestHourVOS)} placement='bottomCenter' arrow>
<div
className="digest-hour"
className='digest-hour'
onMouseEnter={() => {
this.setState({ showDegiest: true });
}}
>
<span className="course-name">{_currentCourse.courseName}</span>
{
digestHourVOS.length > 1 && !this.isDisabledRow(record) &&
}}>
<span className='course-name'>{_currentCourse.courseName}</span>
{digestHourVOS.length > 1 && !this.isDisabledRow(record) && (
<span className={`icon iconfont ${showDegiest ? 'show' : 'hidden'}`}>&#xe6fa;</span>
}
)}
</div>
</Dropdown>
)
);
}
}
}
}, {
},
},
{
title: '剩余课时',
key: 'leftLessons',
dataIndex: 'leftLessons',
......@@ -174,23 +175,23 @@ class StudentList extends React.Component {
const { digestHourVOS, studentId } = record;
if (!digestHourVOS || !digestHourVOS.length) {
return '-'
return '-';
} else if (digestHourVOS.length === 1) {
return digestHourVOS[0].leftLessons;
} else {
const { currentCourse } = this.state;
let _currentCourse = digestHourVOS[0];
// 判断学员是否被选中了
const hasSelect = _.find(consumeStudentList, item => {
const hasSelect = _.find(consumeStudentList, (item) => {
return item.studentId === studentId;
});
// 如果学员被勾选了,再判断该学员选择的课程包是哪个
if (hasSelect) {
const hasSelectCourse = _.find(digestHourVOS, item => {
return item.courseId === hasSelect.classHourId
const hasSelectCourse = _.find(digestHourVOS, (item) => {
return item.courseId === hasSelect.classHourId;
});
if (hasSelectCourse) {
_currentCourse = hasSelectCourse
_currentCourse = hasSelectCourse;
}
}
// 选择课时包之后, 根据学员ID判断选择的是哪个学员的课时包
......@@ -200,8 +201,9 @@ class StudentList extends React.Component {
return _currentCourse.leftLessons;
}
},
}
})
);
}
// 在读学员显示年级
if (type === 'videoCourse' && isNormal) {
......@@ -211,78 +213,87 @@ class StudentList extends React.Component {
render: (text, record) => {
const { studentBasicVO = {}, gradeName } = record;
return studentBasicVO.gradeName || gradeName;
}
})
},
});
}
// 潜在学员显示跟进人和学员来源
if (isPotential) {
columns.push({
columns.push(
{
title: '跟进人',
dataIndex: 'saleName',
render: (val, record) => {
const { studentSaleVO = {}, saleName } = record;
return studentSaleVO.saleName || saleName;
}
}, {
},
},
{
title: '学员来源',
dataIndex: 'resourceType',
render: (val, record) => {
const { studentSaleVO = {}, resourceType } = record;
return resourceData[studentSaleVO.resourceType || resourceType];
},
}
});
);
}
// 历史学员显示结业时间和报读课程
if (isHistory) {
columns.push({
columns.push(
{
title: '结业时间',
key: 'graduationTime',
dataIndex: 'graduationTime',
width: 200,
render: (val, record) => {
const { studentHistoryVO = {}, graduationTime } = record;
return formatDate('YYYY-MM-DD', (studentHistoryVO.graduationTime || graduationTime))
}
}, {
return formatDate('YYYY-MM-DD', studentHistoryVO.graduationTime || graduationTime);
},
},
{
title: '报读课程',
key: 'lostCourseName',
dataIndex: 'lostCourseName',
width: 150,
render: (val, record) => {
const { studentHistoryVO = {}, lostCourseName } = record;
return studentHistoryVO.lostCourseName || lostCourseName
return studentHistoryVO.lostCourseName || lostCourseName;
},
}
});
);
}
return columns;
}
};
// 课程下拉选项
renderCourseMenu = (courseList) => {
return (
<Menu>
{
_.map(courseList, (item) => {
{_.map(courseList, (item) => {
return (
<Menu.Item onClick={(e) => { this.handleSelectCourse(e, item)} }>
{ item.courseName }
<Menu.Item
onClick={(e) => {
this.handleSelectCourse(e, item);
}}>
{item.courseName}
</Menu.Item>
)
})
}
);
})}
</Menu>
)
}
);
};
// 选择当前课程
handleSelectCourse = (e, currentCourse) => {
e.domEvent.stopPropagation();
this.setState({
this.setState(
{
currentCourse,
showDegiest: false,
}, () => {
},
() => {
const { consumeStudentList = [], studentIds, excludeIds } = this.props;
const { prevSelectRows } = this.state;
const { studentId, courseId, leftLessons, name, phone } = currentCourse;
......@@ -290,12 +301,12 @@ class StudentList extends React.Component {
// 选完课时包之后,自动勾选当前学员
const _studentIds = [...studentIds, studentId];
const hasExist = _.find(consumeStudentList, item => {
return item.studentId === studentId
const hasExist = _.find(consumeStudentList, (item) => {
return item.studentId === studentId;
});
if (hasExist) {
_consumeStudentList = _.map(consumeStudentList, item => {
_consumeStudentList = _.map(consumeStudentList, (item) => {
if (item.studentId === studentId) {
item.classHourId = courseId;
}
......@@ -307,13 +318,14 @@ class StudentList extends React.Component {
phone,
studentId,
classHourId: courseId,
consumeHourNum: leftLessons
})
consumeHourNum: leftLessons,
});
}
this.props.onSelect(_studentIds , _consumeStudentList, prevSelectRows);
});
this.props.onSelect(_studentIds, _consumeStudentList, prevSelectRows);
}
);
};
handleSelect = (selectedRowKeys, selectedRows) => {
const { studentType, allstudentList, consumeStudentList } = this.props;
......@@ -325,14 +337,14 @@ class StudentList extends React.Component {
let { currentCourse, prevSelectRows } = this.state;
prevSelectRows = _.filter(prevSelectRows, (item) => {
return allStudentIds.indexOf(item.studentId) === -1
return allStudentIds.indexOf(item.studentId) === -1;
});
const savedSelectedRows = [...prevSelectRows, ...selectedRows];
allSelectRows = savedSelectedRows.map((item) => {
const { studentId, digestHourVOS = [], name, phone } = item;
// 如果该学员已经被选择了
const hasSelectItem = _.find(consumeStudentList, _item => {
const hasSelectItem = _.find(consumeStudentList, (_item) => {
return _item.studentId === item.studentId;
});
if (hasSelectItem) {
......@@ -343,103 +355,95 @@ class StudentList extends React.Component {
phone,
studentId,
classHourId: digestHourVOS[0].courseId,
consumeHourNum: digestHourVOS[0].leftLessons
}
consumeHourNum: digestHourVOS[0].leftLessons,
};
});
this.setState({
this.setState(
{
selectedRows,
savedSelectedRows
}, () => {
savedSelectedRows,
},
() => {
this.props.onSelect(studentIds, allSelectRows, savedSelectedRows);
});
}
);
} else {
this.props.onSelect(studentIds);
}
}
};
isDisabledRow = (record) => {
// 扣课时的情况下, 无消耗课程的禁用
const {
after,
excludeIds,
studentType,
studentList = [],
consumeStudentList = []
} = this.props;
const { after, excludeIds, studentType, studentList = [], consumeStudentList = [] } = this.props;
const { digestHourVOS } = record;
const hasDigestHours = digestHourVOS && digestHourVOS.length;
// 已经入库的学员不可再选择
const hasSelect = _.find(excludeIds, item => {
return item == record.studentId
const hasSelect = _.find(excludeIds, (item) => {
return item == record.studentId;
});
let disabled = false;
// 扣课时学员课时为0的情况下禁止选择
if (studentType === 'DEDUCTION') {
// 判断是否已经在不扣课时里
if (_.find(studentList, item => item.studentId === record.studentId)) {
if (_.find(studentList, (item) => item.studentId === record.studentId)) {
disabled = true;
} else if (!hasDigestHours || (after && !!hasSelect)) {
disabled = true;
}
} else {
if (_.find(consumeStudentList, item => item.studentId === record.studentId)) {
if (_.find(consumeStudentList, (item) => item.studentId === record.studentId)) {
disabled = true;
} else if (after && !!hasSelect) {
disabled = true
disabled = true;
}
}
return disabled
}
return disabled;
};
render() {
const {
after,
query,
totalCount,
studentIds,
allstudentList,
} = this.props;
const { after, query, totalCount, studentIds, allstudentList } = this.props;
const { prevSelectRows, selectedRows } = this.state;
const { current, size, pageNo, pageSize, } = query;
const { current, size, pageNo, pageSize } = query;
const rowSelection = {
selectedRowKeys: studentIds,
onChange: this.handleSelect,
getCheckboxProps: (record) => {
return {
disabled: this.isDisabledRow(record)
}
}
disabled: this.isDisabledRow(record),
};
},
};
return (
<div className="student-list">
<Table
<div className='student-list'>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
bordered
size={'small'}
rowKey={item => item.studentId}
rowKey={(item) => item.studentId}
dataSource={allstudentList}
columns={this.parseColumns()}
rowSelection={rowSelection}
onRow={record => ({
onClick: e => {
e.currentTarget
.getElementsByClassName("ant-checkbox-wrapper")[0]
.click();
}
onRow={(record) => ({
onClick: (e) => {
e.currentTarget.getElementsByClassName('ant-checkbox-wrapper')[0].click();
},
})}
scroll={{ y: 350 }}
pagination={false}
/>
{
isNewVersion ?
{isNewVersion ? (
<PageControl
size="small"
size='small'
current={current - 1}
pageSize={size}
total={totalCount}
......@@ -451,9 +455,10 @@ class StudentList extends React.Component {
onShowSizeChange={(current, size) => {
this.props.onChange('size', size);
}}
/> :
/>
) : (
<PageControl
size="small"
size='small'
current={pageNo}
pageSize={pageSize}
total={totalCount}
......@@ -465,9 +470,9 @@ class StudentList extends React.Component {
this.props.onChange('pageNo', pageSize);
}}
/>
}
)}
</div>
)
);
}
}
......
import React from 'react';
import { Tooltip, Input, Radio, Table, Checkbox } from 'antd';
import Service from '@/common/js/service';
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import Breadcrumbs from '@/components/Breadcrumbs';
import { Checkbox, Input, Tooltip } from 'antd';
import moment from 'moment';
import React from 'react';
import _ from 'underscore';
import Breadcrumbs from "@/components/Breadcrumbs";
import PageControl from '@/components/PageControl';
import Service from "@/common/js/service";
import User from '@/common/js/user';
import './OfflineCourseData.less';
const { Search } = Input;
......@@ -34,7 +35,7 @@ export default class OfflineCourseData extends React.Component {
joinInNum: 0,
joinNum: 0,
joinOutNum: 0,
}
};
}
componentDidMount() {
......@@ -46,10 +47,11 @@ export default class OfflineCourseData extends React.Component {
Service.Hades('public/hades/getOfflineCourseJoinBase', { courseId }).then((res) => {
if (res.success) {
const { result } = res;
const group = _.groupBy(result.calendarTime, item => moment(item).format('YYYY-MM'));
const group = _.groupBy(result.calendarTime, (item) => moment(item).format('YYYY-MM'));
const calendarTime = _.map(group, (value, key) => ({ key, value }));
const currentDate = calendarTime[currentIndex].value[0];
this.setState({
this.setState(
{
courseName: result.courseName,
fullJoin: result.fullJoin || 0,
totalJoin: result.totalJoin || 0,
......@@ -60,10 +62,12 @@ export default class OfflineCourseData extends React.Component {
whetherSetSignOut: result.whetherSetSignOut,
whetherSetSignInOut: result.whetherSetSignInOut,
whetherSetApply: result.whetherSetApply,
}, () => this.getDateDetail());
}
})
},
() => this.getDateDetail()
);
}
});
};
getDateDetail = (current = 1) => {
const { query, currentDate, courseId } = this.state;
......@@ -75,9 +79,9 @@ export default class OfflineCourseData extends React.Component {
joinInNum: res.result.joinInNum || 0,
joinNum: res.result.joinNum || 0,
joinOutNum: res.result.joinOutNum || 0,
})
});
}
})
});
}
query.current = current;
query.date = currentDate;
......@@ -85,8 +89,8 @@ export default class OfflineCourseData extends React.Component {
if (res.success) {
this.setState({ data: res.result.records, total: res.result.total });
}
})
}
});
};
getColumns = () => {
const { whetherSetSignIn, whetherSetSignOut, whetherSetApply } = this.state;
......@@ -96,50 +100,49 @@ export default class OfflineCourseData extends React.Component {
key: 'name',
dataIndex: 'name',
render: (val, record) => {
return (
<div>{record.name}</div>
)
}
return <div>{record.name}</div>;
},
},
{
title: "手机号",
key: "phone",
dataIndex: "phone",
title: '手机号',
key: 'phone',
dataIndex: 'phone',
render: (val, item) => {
return (
<div>{item.phone || '-'}</div>
)
return <div>{item.phone || '-'}</div>;
},
},
];
whetherSetApply === 'YES' && columns.push({
whetherSetApply === 'YES' &&
columns.push({
title: '报名时间',
key: 'joinTime',
dataIndex: 'joinTime',
render: (val, item) => {
return <div>{formatDate('YYYY-MM-DD H:i', item.joinTime)}</div>
}
return <div>{formatDate('YYYY-MM-DD H:i', item.joinTime)}</div>;
},
});
whetherSetSignIn === 'YES' && columns.push({
whetherSetSignIn === 'YES' &&
columns.push({
title: '签到时间',
key: 'joinInTime',
dataIndex: 'joinInTime',
sorter: true,
render: (val) => {
return val ? formatDate('YYYY-MM-DD H:i', val) : '-';
}
})
whetherSetSignOut === 'YES' && columns.push({
},
});
whetherSetSignOut === 'YES' &&
columns.push({
title: '签退时间',
key: 'joinOutTime',
dataIndex: 'joinOutTime',
sorter: true,
render: (val) => {
return val ? formatDate('YYYY-MM-DD H:i', val) : '-';
}
})
},
});
return columns;
}
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
......@@ -161,8 +164,8 @@ export default class OfflineCourseData extends React.Component {
};
this.setState({ query: _query }, () => {
this.getDateDetail(_query.current);
})
}
});
};
render() {
const {
......@@ -189,26 +192,32 @@ export default class OfflineCourseData extends React.Component {
const calendarLength = calendarTime.length;
const dateList = (calendarTime[currentIndex] || {}).value || [];
return (
<div className="page offline-course-data">
<div className='page offline-course-data'>
<Breadcrumbs
navList="参与数据"
navList='参与数据'
goBack={() => {
window.RCHistory.goBack();
}}
/>
<div className="box">
<div className="offline-name">课程名称:{courseName}</div>
{whetherSetApply === 'YES' && <div className="offline-application">报名总人数:{totalJoin}</div>}
{whetherSetSignInOut === 'YES' && <div className="offline-application">
完成考勤总人数<Tooltip title="根据上课日期,在规定时间内完成签到和签退的学员数"><span className="icon iconfont">&#xe7c4;</span></Tooltip>{fullJoin}
</div>}
<div className='box'>
<div className='offline-name'>课程名称:{courseName}</div>
{whetherSetApply === 'YES' && <div className='offline-application'>报名总人数:{totalJoin}</div>}
{whetherSetSignInOut === 'YES' && (
<div className='offline-application'>
完成考勤总人数
<Tooltip title='根据上课日期,在规定时间内完成签到和签退的学员数'>
<span className='icon iconfont'>&#xe7c4;</span>
</Tooltip>
{fullJoin}
</div>
)}
</div>
<div className="box data-box">
<div className="left-box">
<div className="left-title">上课日期</div>
<div className="left-calendar">
<div className='box data-box'>
<div className='left-box'>
<div className='left-title'>上课日期</div>
<div className='left-calendar'>
<div
className="icon-box"
className='icon-box'
onClick={() => {
const index = currentIndex - 1;
if (index >= 0 && index < calendarLength) {
......@@ -216,13 +225,12 @@ export default class OfflineCourseData extends React.Component {
this.getDateDetail();
});
}
}}
>
<span className="icon iconfont">&#xe79c;</span>
}}>
<span className='icon iconfont'>&#xe79c;</span>
</div>
<div className="calendar-text">{(calendarTime[currentIndex] || {}).key}</div>
<div className='calendar-text'>{(calendarTime[currentIndex] || {}).key}</div>
<div
className="icon-box"
className='icon-box'
onClick={() => {
const index = currentIndex + 1;
if (index >= 0 && index < calendarLength) {
......@@ -230,60 +238,69 @@ export default class OfflineCourseData extends React.Component {
this.getDateDetail();
});
}
}}
>
<span className="icon iconfont">&#xe79b;</span>
}}>
<span className='icon iconfont'>&#xe79b;</span>
</div>
</div>
<div className="date-list">
{dateList.map(item => (
<div className='date-list'>
{dateList.map((item) => (
<div
className={`date-item${item === currentDate ? ' selected' : ''}`}
key={item}
onClick={() => {
this.setState({ currentDate: item }, () => {
this.getDateDetail();
})
}}
>{window.formatDate('MM月DD日(WW)', item)}</div>
});
}}>
{window.formatDate('MM月DD日(WW)', item)}
</div>
))}
</div>
</div>
<div className="right-box">
<div className="selected-date">{moment(currentDate).format('MM-DD')}</div>
<div className="detail-data">
{offlineCourseType !== 'ALL_DAY_OFFLINE' && whetherSetApply === 'YES' && <span className="icon iconfont">&#xe89f;</span>}
{offlineCourseType !== 'ALL_DAY_OFFLINE' && whetherSetApply === 'YES' && <span className="data-text">报名人数:{joinNum}</span>}
{whetherSetSignInOut === 'YES' && <span className="icon iconfont">&#xe89e;</span>}
{whetherSetSignInOut === 'YES' && <span className="data-text">完成考勤数<Tooltip title="当日在规定时间内完成签到和签退的学员数"><span className="icon iconfont">&#xe7c4;</span></Tooltip>{fullJoinNum}</span>}
{whetherSetSignIn === 'YES' && <span className="icon iconfont">&#xe8a0;</span>}
{whetherSetSignIn === 'YES' && <span className="data-text">签到人数:{joinInNum}</span>}
{whetherSetSignOut === 'YES' && <span className="icon iconfont">&#xe89d;</span>}
{whetherSetSignOut === 'YES' && <span className="data-text">签退人数:{joinOutNum}</span>}
<div className='right-box'>
<div className='selected-date'>{moment(currentDate).format('MM-DD')}</div>
<div className='detail-data'>
{offlineCourseType !== 'ALL_DAY_OFFLINE' && whetherSetApply === 'YES' && <span className='icon iconfont'>&#xe89f;</span>}
{offlineCourseType !== 'ALL_DAY_OFFLINE' && whetherSetApply === 'YES' && <span className='data-text'>报名人数:{joinNum}</span>}
{whetherSetSignInOut === 'YES' && <span className='icon iconfont'>&#xe89e;</span>}
{whetherSetSignInOut === 'YES' && (
<span className='data-text'>
完成考勤数
<Tooltip title='当日在规定时间内完成签到和签退的学员数'>
<span className='icon iconfont'>&#xe7c4;</span>
</Tooltip>
{fullJoinNum}
</span>
)}
{whetherSetSignIn === 'YES' && <span className='icon iconfont'>&#xe8a0;</span>}
{whetherSetSignIn === 'YES' && <span className='data-text'>签到人数:{joinInNum}</span>}
{whetherSetSignOut === 'YES' && <span className='icon iconfont'>&#xe89d;</span>}
{whetherSetSignOut === 'YES' && <span className='data-text'>签退人数:{joinOutNum}</span>}
</div>
<div className="detail-filter">
<div className='detail-filter'>
<Search
className="search-input"
placeholder="搜索学员姓名/手机号"
className='search-input'
placeholder='搜索学员姓名/手机号'
style={{ width: 200, marginRight: 24 }}
enterButton={<span className="icon iconfont">&#xe832;</span>}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
onSearch={(value) => {
const _query = { ...query };
if (value) {
const isPhone = (value || "").match(/^\d+$/);
const name = isPhone ? "storeCustomerPhone" : "storeCustomerName";
_query.storeCustomerName = "";
_query.storeCustomerPhone = "";
const isPhone = (value || '').match(/^\d+$/);
const name = isPhone ? 'storeCustomerPhone' : 'storeCustomerName';
_query.storeCustomerName = '';
_query.storeCustomerPhone = '';
_query[name] = value;
} else {
_query.storeCustomerName = "";
_query.storeCustomerPhone = "";
_query.storeCustomerName = '';
_query.storeCustomerPhone = '';
}
this.setState({ query: _query }, () => this.getDateDetail());
}}
/>
{whetherSetSignInOut === 'YES' && <div className="filter-box">
<span className="label">签到情况:</span>
{whetherSetSignInOut === 'YES' && (
<div className='filter-box'>
<span className='label'>签到情况:</span>
<Checkbox
checked={query.joinInState === 'YES'}
onChange={(e) => {
......@@ -291,10 +308,11 @@ export default class OfflineCourseData extends React.Component {
this.setState({ query: { ...query, joinInState: 'YES' } }, () => this.getDateDetail());
} else {
delete query.joinInState;
this.setState({ query }, () => this.getDateDetail())
this.setState({ query }, () => this.getDateDetail());
}
}}
>已签到</Checkbox>
}}>
已签到
</Checkbox>
<Checkbox
checked={query.joinInState === 'NO'}
onChange={(e) => {
......@@ -302,13 +320,16 @@ export default class OfflineCourseData extends React.Component {
this.setState({ query: { ...query, joinInState: 'NO' } }, () => this.getDateDetail());
} else {
delete query.joinInState;
this.setState({ query }, () => this.getDateDetail())
this.setState({ query }, () => this.getDateDetail());
}
}}
>未签到</Checkbox>
</div>}
{whetherSetSignOut === 'YES' && <div className="filter-box">
<span className="label">签退情况:</span>
}}>
未签到
</Checkbox>
</div>
)}
{whetherSetSignOut === 'YES' && (
<div className='filter-box'>
<span className='label'>签退情况:</span>
<Checkbox
checked={query.joinOutState === 'YES'}
onChange={(e) => {
......@@ -316,10 +337,11 @@ export default class OfflineCourseData extends React.Component {
this.setState({ query: { ...query, joinOutState: 'YES' } }, () => this.getDateDetail());
} else {
delete query.joinOutState;
this.setState({ query }, () => this.getDateDetail())
this.setState({ query }, () => this.getDateDetail());
}
}}
>已签退</Checkbox>
}}>
已签退
</Checkbox>
<Checkbox
checked={query.joinOutState === 'NO'}
onChange={(e) => {
......@@ -327,15 +349,21 @@ export default class OfflineCourseData extends React.Component {
this.setState({ query: { ...query, joinOutState: 'NO' } }, () => this.getDateDetail());
} else {
delete query.joinOutState;
this.setState({ query }, () => this.getDateDetail())
this.setState({ query }, () => this.getDateDetail());
}
}}
>未签退</Checkbox>
</div>}
}}>
未签退
</Checkbox>
</div>
<Table
)}
</div>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
bordered
size="middle"
size='middle'
pagination={false}
columns={this.getColumns()}
loading={loading}
......@@ -354,6 +382,6 @@ export default class OfflineCourseData extends React.Component {
</div>
</div>
</div>
)
);
}
}
......@@ -6,35 +6,32 @@
* @Description: 视频课-列表模块
* @Copyright: 杭州杰竞科技有限公司 版权所有
*/
import Service from '@/common/js/service';
import User from '@/common/js/user';
import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components';
import { appId, LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCourseData';
import { Dropdown, message, Modal, Switch, Tooltip } from 'antd';
import moment from 'moment';
import React from 'react';
import { Table, Modal, message , Tooltip,Switch,Dropdown} from 'antd';
import { Route, withRouter } from 'react-router-dom';
import moment from 'moment';
import $ from 'jquery';
import { PageControl } from "@/components";
import { appId, shareUrl, LIVE_SHARE } from '@/domains/course-domain/constants';
import OfflineCourseData from '@/modules/course-manage/offline-course/OfflineCourseData';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import Service from "@/common/js/service";
import CourseService from "@/domains/course-domain/CourseService";
import RelatedPlanModal from '../../modal/RelatedPlanModal';
import ENUM from '../../../knowledge-base/ENUM.js';
import PreviewOfflineModal from '../modal/PreviewOfflineModal';
import ENUM from "../../../knowledge-base/ENUM.js";
import User from '@/common/js/user'
import QRCodeModal from '../modal/QRCodeModal';
import './OfflineCourseList.less';
const ENV = process.env.DEPLOY_ENV || 'dev';
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/pxbWKsYA87.png';
class OfflineCourseList extends React.Component {
constructor(props) {
super(props);
this.state = {
id: '', // 视频课ID
studentIds: [],
}
};
}
componentDidMount() {
......@@ -45,17 +42,17 @@ class OfflineCourseList extends React.Component {
}
}
handlePlanName = (planArray)=>{
let planStr = "";
planArray.map((item,index)=>{
if(index < planArray.length-1){
handlePlanName = (planArray) => {
let planStr = '';
planArray.map((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + '、';
}else{
planStr = planStr + item.planName
}
})
return planStr
} else {
planStr = planStr + item.planName;
}
});
return planStr;
};
// 请求表头
parseColumns = () => {
const columns = [
......@@ -63,35 +60,39 @@ class OfflineCourseList extends React.Component {
title: '线下课',
key: 'scheduleName',
dataIndex: 'scheduleName',
width:321,
width: 321,
fixed: 'left',
render: (val, record) => {
const { courseMediaVOS, courseName, offlinePlace, calendarTime, startTime, endTime } = record;
const coverUrl = (_.find(courseMediaVOS, data => data.contentType === 'COVER') || {}).mediaUrl;
const coverUrl = (_.find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl;
let isContinue = calendarTime.length > 1;
_.reduce(calendarTime, (a, b) => {
isContinue = isContinue && (b - a === 86400000);
isContinue = isContinue && b - a === 86400000;
return b;
})
});
const lastTime = _.last(calendarTime);
const time = `${!isContinue ? calendarTime.map(item => moment(item).format('MM-DD')).join('、') : `${moment(calendarTime[0]).format('MM-DD')}~${moment(lastTime).format('MM-DD')}`} ${moment(startTime).format('HH:mm')} ~ ${moment(endTime).format('HH:mm')}`;
const time = `${
!isContinue
? calendarTime.map((item) => moment(item).format('MM-DD')).join('、')
: `${moment(calendarTime[0]).format('MM-DD')}~${moment(lastTime).format('MM-DD')}`
} ${moment(startTime).format('HH:mm')} ~ ${moment(endTime).format('HH:mm')}`;
return (
<div className="record__item">
<img className="course-cover" src={coverUrl || defaultCoverUrl} />
<div className='record__item'>
<img className='course-cover' src={coverUrl || defaultCoverUrl} />
<div style={{ width: 175 }}>
<Tooltip title={courseName}>
<div className="course-name">{courseName}</div>
<div className='course-name'>{courseName}</div>
</Tooltip>
<Tooltip title={`地点:${offlinePlace}`}>
<div className="course-text">地点:{offlinePlace}</div>
<div className='course-text'>地点:{offlinePlace}</div>
</Tooltip>
<Tooltip title={time}>
<div className="course-text">{time}</div>
<div className='course-text'>{time}</div>
</Tooltip>
</div>
</div>
)
}
);
},
},
{
title: '课程状态',
......@@ -100,12 +101,12 @@ class OfflineCourseList extends React.Component {
width: 120,
render: (val, record) => {
return (
<div className="course-state">
<div className='course-state'>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: ENUM.offlineStateShow[val].color, marginRight: 8 }}></div>
{ENUM.offlineStateShow[val].title}
</div>
)
}
);
},
},
{
title: '课程分类',
......@@ -113,39 +114,40 @@ class OfflineCourseList extends React.Component {
dataIndex: 'categoryName',
width: 120,
render: (val, record) => {
return (
<div className="record__item">
{record.categorySonName}
</div>
)
}
return <div className='record__item'>{record.categorySonName}</div>;
},
},
{
title: <span>
title: (
<span>
<span>学院展示</span>
<Tooltip title={<div>开启后,学员可在学院内查看到此课程。若课程“取消”,则系统会自动“关闭”学院展示。<br/>关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。</div>}><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:'normal'}}>&#xe61d;</i></Tooltip>
</span>,
<Tooltip
title={
<div>
开启后,学员可在学院内查看到此课程。若课程“取消”,则系统会自动“关闭”学院展示。
<br />
关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: 'normal' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: 120,
dataIndex: "courseware",
dataIndex: 'courseware',
render: (val, item, index) => {
return (
<Switch
disabled={item.courseState === 'EXPIRED'}
checked={item.shelfState === "YES"}
onChange={() => this.changeShelfState(item)}
/>
)
return <Switch disabled={item.courseState === 'EXPIRED'} checked={item.shelfState === 'YES'} onChange={() => this.changeShelfState(item)} />;
},
},
{
title: "讲师",
title: '讲师',
width: 200,
key: "teacher",
dataIndex: "teacher",
key: 'teacher',
dataIndex: 'teacher',
render: (val, item) => {
return (
<div>{item.teacherName}</div>
)
return <div>{item.teacherName}</div>;
},
},
{
......@@ -155,9 +157,10 @@ class OfflineCourseList extends React.Component {
dataIndex: 'apply',
sorter: true,
render: (val, item) => {
return <div>
return (
<div>
{item.startTimeApply ? `${formatDate('MM-DD H:i', item.startTimeApply)} ~ ${formatDate('MM-DD H:i', item.endTimeApply)}` : '-'}
{item.whetherApplyFull === 'YES' &&
{item.whetherApplyFull === 'YES' && (
<span
style={{
marginLeft: 4,
......@@ -167,10 +170,13 @@ class OfflineCourseList extends React.Component {
fontSize: '12px',
height: '18px',
lineHeight: '18px',
}}>已报满</span>
}
}}>
已报满
</span>
)}
</div>
}
);
},
},
{
title: '创建时间',
......@@ -179,8 +185,8 @@ class OfflineCourseList extends React.Component {
dataIndex: 'created',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: '操作',
......@@ -190,29 +196,32 @@ class OfflineCourseList extends React.Component {
fixed: 'right',
render: (val, record) => {
return (
<div className="operate">
<div className="operate__item" onClick={() => window.RCHistory.push(`/offline-course/data?id=${record.courseId}`)}>参与数据</div>
{record.courseState !== 'EXPIRED' && <span className="operate__item split"> | </span>}
{record.courseState !== 'EXPIRED' && <div className="operate__item" onClick={() => this.handleShowShareModal(record)}>分享</div>}
<span className="operate__item split"> | </span>
<div className='operate'>
<div className='operate__item' onClick={() => window.RCHistory.push(`/offline-course/data?id=${record.courseId}`)}>
参与数据
</div>
{record.courseState !== 'EXPIRED' && <span className='operate__item split'> | </span>}
{record.courseState !== 'EXPIRED' && (
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
</div>
)}
<span className='operate__item split'> | </span>
<Dropdown overlay={this.renderMoreOperate(record)}>
<span className="more-operate">
<span className="operate-text">更多</span>
<span
className="iconfont icon"
style={{ color: "#2966FF" }}
>
<span className='more-operate'>
<span className='operate-text'>更多</span>
<span className='iconfont icon' style={{ color: '#2966FF' }}>
&#xe824;
</span>
</span>
</Dropdown>
</div>
)
}
}
);
},
},
];
return columns;
}
};
// 显示预览弹窗
handleShowPreviewModal = (courseId) => {
......@@ -221,123 +230,132 @@ class OfflineCourseList extends React.Component {
courseId={courseId}
close={() => {
this.setState({
previewOfflineModal: null
})
previewOfflineModal: null,
});
}}
/>
);
this.setState({ previewOfflineModal });
}
};
renderMoreOperate = (item) => {
return (
<div className="live-course-more-menu">
{item.courseState !== 'EXPIRED' && (item.whetherSetSignIn === 'YES' || item.whetherSetSignOut === 'YES') && <div
className="operate__item"
key="qrcode"
<div className='live-course-more-menu'>
{item.courseState !== 'EXPIRED' && (item.whetherSetSignIn === 'YES' || item.whetherSetSignOut === 'YES') && (
<div
className='operate__item'
key='qrcode'
onClick={() => {
this.setState({ openQRCodeModal: true, qrcodeData: item })
}}
>考勤二维码</div>}
this.setState({ openQRCodeModal: true, qrcodeData: item });
}}>
考勤二维码
</div>
)}
<div
className="operate__item"
key="preview"
className='operate__item'
key='preview'
onClick={() => {
this.handleShowPreviewModal(item.courseId);
}}
>预览</div>
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (item.courseState === 'UN_START' || item.courseState === 'STARTING') && <div
className="operate__item"
key="cancel"
}}>
预览
</div>
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') &&
(item.courseState === 'UN_START' || item.courseState === 'STARTING') && (
<div
className='operate__item'
key='cancel'
onClick={() => {
this.handleDeleteOfflineCourse(item.courseId);
}}
>取消课程</div>}
{item.courseState === 'UN_START' && window.ctx.xmState.storeUserPermissionList.includes('EditOfflineClass') && <div
className="operate__item"
key="edit"
}}>
取消课程
</div>
)}
{item.courseState === 'UN_START' && window.ctx.xmState.storeUserPermissionList.includes('EditOfflineClass') && (
<div
className='operate__item'
key='edit'
onClick={() => {
RCHistory.push(`/create-offline-course?type=edit&id=${item.courseId}`);
}}
>编辑</div>}
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && <div
className="operate__item"
key="delete"
onClick={() => this.handleDeleteOfflineCourse(item.courseId, true)}
>删除</div>}
}}>
编辑
</div>
)
}
)}
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<div className='operate__item' key='delete' onClick={() => this.handleDeleteOfflineCourse(item.courseId, true)}>
删除
</div>
)}
</div>
);
};
//改变上架状态
changeShelfState = (item) => {
if (!window.ctx.xmState.storeUserPermissionList.includes('EditOfflineClass')) {
message.warning('无【编辑线下课】权限,请联系管理员')
message.warning('无【编辑线下课】权限,请联系管理员');
return;
}
let _shelfState = item.shelfState
if(_shelfState==='NO'){
_shelfState = "YES";
item.shelfState = "YES"
}else{
_shelfState = "NO"
item.shelfState = "NO"
let _shelfState = item.shelfState;
if (_shelfState === 'NO') {
_shelfState = 'YES';
item.shelfState = 'YES';
} else {
_shelfState = 'NO';
item.shelfState = 'NO';
}
const params={
const params = {
courseId: item.courseId,
shelfState:_shelfState
}
CourseService.changeVideoShelfState(params).then((res)=>{
if(res.success){
if(_shelfState === "YES"){
message.success("已开启展示");
}else{
message.success("已取消展示");
shelfState: _shelfState,
};
CourseService.changeVideoShelfState(params).then((res) => {
if (res.success) {
if (_shelfState === 'YES') {
message.success('已开启展示');
} else {
message.success('已取消展示');
}
this.props.onChange();
}
})
}
});
};
// 删除视频课
handleDeleteOfflineCourse = (courseId, isDelete) => {
if (isDelete) {
Modal.confirm({
title: '删除课程',
content: '确定删除该课程吗?删除后学员数据和课程数据将无法恢复。',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: () => {
const param ={
const param = {
courseId: courseId,
}
};
Service.Hades('public/hades/delOfflineCourse', param).then(() => {
message.success('删除成功');
this.props.onChange();
})
}
});
},
});
} else {
Modal.confirm({
title: '取消课程',
content: '确定要取消该课程吗?取消后课程将失效,仅保留已有数据。',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: '确定',
cancelText: '取消',
onOk: () => {
const param ={
const param = {
courseId: courseId,
}
};
Service.Hades('public/hades/cancelOfflineCourse', param).then(() => {
message.success('取消成功');
this.props.onChange();
})
}
});
},
});
}
}
};
// 显示分享弹窗
handleShowShareModal = (record, needStr = false) => {
......@@ -347,7 +365,7 @@ class OfflineCourseList extends React.Component {
const htmlUrl = `${LIVE_SHARE}offline_detail/${courseId}?id=${User.getStoreId()}`;
const longUrl = htmlUrl;
const { courseName, courseMediaVOS } = record;
const coverUrl = (_.find(courseMediaVOS, data => data.contentType === 'COVER') || {}).mediaUrl;
const coverUrl = (_.find(courseMediaVOS, (data) => data.contentType === 'COVER') || {}).mediaUrl;
const shareData = {
longUrl,
coverUrl,
......@@ -358,11 +376,11 @@ class OfflineCourseList extends React.Component {
<ShareLiveModal
needStr={needStr}
data={shareData}
type="offlineClass"
title="线下课"
type='offlineClass'
title='线下课'
close={() => {
this.setState({
shareLiveModal: null
shareLiveModal: null,
});
localStorage.setItem('videoCourseItem', '');
}}
......@@ -370,7 +388,7 @@ class OfflineCourseList extends React.Component {
);
this.setState({ shareLiveModal });
}
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
......@@ -378,44 +396,60 @@ class OfflineCourseList extends React.Component {
let _columnKey;
let _order;
// 按创建时间升序排序
if (columnKey === 'apply' && order === 'ascend') {_columnKey="START_APPLY_DESC"; _order = 'SORT_ASC'; }
if (columnKey === 'created' && order === 'ascend') {_columnKey="CREATED"; _order = 'SORT_ASC'; }
if (columnKey === 'apply' && order === 'ascend') {
_columnKey = 'START_APPLY_DESC';
_order = 'SORT_ASC';
}
if (columnKey === 'created' && order === 'ascend') {
_columnKey = 'CREATED';
_order = 'SORT_ASC';
}
// 按创建时间降序排序
if (columnKey === 'apply' && order === 'descend') { _columnKey="START_APPLY_DESC"; _order = 'SORT_DESC';}
if (columnKey === 'created' && order === 'descend') { _columnKey="CREATED"; _order = 'SORT_DESC';}
if (columnKey === 'apply' && order === 'descend') {
_columnKey = 'START_APPLY_DESC';
_order = 'SORT_DESC';
}
if (columnKey === 'created' && order === 'descend') {
_columnKey = 'CREATED';
_order = 'SORT_DESC';
}
const _query = {
...query,
sortMap:{}
sortMap: {},
};
_query.sortMap[_columnKey]=_order;
_query.sortMap[_columnKey] = _order;
this.props.onChange(_query);
}
};
render() {
const { openQRCodeModal, qrcodeData } = this.state;
const { dataSource = [], totalCount, query, match } = this.props;
const { current, size } = query;
return (
<div className="offline-course-list">
<Table
rowKey={record => record.courseId}
<div className='offline-course-list'>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.courseId}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500}}
scroll={{ x: 1500 }}
bordered
className="offline-list-table"
className='offline-list-table'
/>
<div className="box-footer">
<div className='box-footer'>
<PageControl
current={current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.props.onChange(_query)
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
/>
</div>
......@@ -425,12 +459,12 @@ class OfflineCourseList extends React.Component {
visible={openQRCodeModal}
data={qrcodeData}
onCancel={() => {
this.setState({ openQRCodeModal: false })
this.setState({ openQRCodeModal: false });
}}
/>
<Route path={`${match.url}/data`} component={OfflineCourseData} />
</div>
)
);
}
}
......
import React from "react"
import { Table, Modal, message, Tooltip, Switch, Dropdown } from "antd"
import _ from "underscore"
import { PageControl } from "@/components"
import { LIVE_SHARE } from "@/domains/course-domain/constants"
import ShareLiveModal from "@/modules/course-manage/modal/ShareLiveModal"
import WatchDataModal from "../modal/WatchDataModal"
import CourseService from "@/domains/course-domain/CourseService"
import RelatedPlanModal from "../../modal/RelatedPlanModal"
import User from "@/common/js/user"
import "./VideoCourseList.less"
import { Dropdown, message, Modal, Switch, Table, Tooltip } from 'antd';
import User from '@/common/js/user';
import { PageControl } from '@/components';
import { LIVE_SHARE } from '@/domains/course-domain/constants';
import CourseService from '@/domains/course-domain/CourseService';
import ShareLiveModal from '@/modules/course-manage/modal/ShareLiveModal';
import React from 'react';
import RelatedPlanModal from '../../modal/RelatedPlanModal';
import WatchDataModal from '../modal/WatchDataModal';
import { XMTable } from '@/components';
import college from '@/common/lottie/college';
import './VideoCourseList.less';
class VideoCourseList extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
id: "", // 视频课ID
id: '', // 视频课ID
studentIds: [],
RelatedPlanModalVisible: false,
selectPlanList: {},
ShelfLoading: false
ShelfLoading: false,
// dataSource: [],
}
};
}
componentDidMount() {
const videoCourseItem = localStorage.getItem("videoCourseItem")
const videoCourseItem = localStorage.getItem('videoCourseItem');
if (videoCourseItem) {
const _videoCourseItem = JSON.parse(videoCourseItem)
this.handleShowShareModal(_videoCourseItem, true)
const _videoCourseItem = JSON.parse(videoCourseItem);
this.handleShowShareModal(_videoCourseItem, true);
}
}
......@@ -41,27 +40,27 @@ class VideoCourseList extends React.Component {
data={record}
close={() => {
this.setState({
watchDataModal: null
})
watchDataModal: null,
});
}}
/>
)
this.setState({ watchDataModal })
}
);
this.setState({ watchDataModal });
};
// 请求表头
parseColumns = () => {
const { type } = this.props
const { ShelfLoading } = this.state
const { type } = this.props;
const { ShelfLoading } = this.state;
const columns = [
{
title: "视频课",
key: "scheduleName",
dataIndex: "scheduleName",
title: '视频课',
key: 'scheduleName',
dataIndex: 'scheduleName',
width: 321,
fixed: "left",
fixed: 'left',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record
const { coverUrl, scheduleVideoUrl } = record;
return (
<div className='record__item'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
......@@ -69,7 +68,7 @@ class VideoCourseList extends React.Component {
className='course-cover'
src={
coverUrl ||
(type === "internal" ? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast` : "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png")
(type === 'internal' ? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast` : 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
}
alt=''
/>
......@@ -84,45 +83,45 @@ class VideoCourseList extends React.Component {
</Otherwise>
</Choose>
</div>
)
}
);
},
},
{
title: (
<span>
<span>课程分类</span>
<If condition={type !== "internal"}>
<If condition={type !== 'internal'}>
<Tooltip title={<div>外部课程的分类由系统提供,不影响企业课程分类。</div>}>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px", fontWeight: "normal" }}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: 'normal' }}>
&#xe61d;
</i>
</Tooltip>
</If>
</span>
),
key: "categoryName",
dataIndex: "categoryName",
key: 'categoryName',
dataIndex: 'categoryName',
width: 200,
render: (val, record) => {
return (
<Choose>
<When condition={type === "internal"}>
<When condition={type === 'internal'}>
<div className='record__item'>
{record.categoryOneName}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ""}
{record.categoryTwoName ? `-${record.categoryTwoName}` : ''}
</div>
</When>
<Otherwise>
<div className='record__item'>{record.categorySonName}</div>
</Otherwise>
</Choose>
)
}
);
},
},
{
title: "创建人",
key: "createName",
dataIndex: "createName",
title: '创建人',
key: 'createName',
dataIndex: 'createName',
width: 100,
render: (val) => {
return (
......@@ -133,8 +132,8 @@ class VideoCourseList extends React.Component {
</Tooltip>
)}
</div>
)
}
);
},
},
{
title: (
......@@ -148,62 +147,62 @@ class VideoCourseList extends React.Component {
关闭后,学院内不再展示此课程,但学员仍可通过分享的海报/链接查看此课程。
</div>
}>
<i className='icon iconfont' style={{ marginLeft: "5px", cursor: "pointer", color: "#bfbfbf", fontSize: "14px" }}>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
width: 120,
key: "shelfState",
dataIndex: "shelfState",
key: 'shelfState',
dataIndex: 'shelfState',
render: (val, item, index) => {
return (
<Switch
loading={ShelfLoading}
checked={item.shelfState === "YES"}
checked={item.shelfState === 'YES'}
defaultChecked={item.shelfState}
onClick={(checked) => {
this.changeShelfState(checked, item, index)
this.changeShelfState(checked, item, index);
}}
/>
)
}
);
},
},
{
title: "观看学员数",
title: '观看学员数',
width: 110,
key: "watchUserCount",
dataIndex: "watchUserCount",
key: 'watchUserCount',
dataIndex: 'watchUserCount',
render: (val, item) => {
return <div className='watchUserCount'>{val || 0}</div>
}
return <div className='watchUserCount'>{val || 0}</div>;
},
},
{
title: "创建时间",
title: '创建时间',
width: 181,
key: "created",
dataIndex: "created",
key: 'created',
dataIndex: 'created',
sorter: true,
render: (val) => {
return formatDate("YYYY-MM-DD H:i", val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: "更新时间",
title: '更新时间',
width: 181,
key: "updated",
dataIndex: "updated",
key: 'updated',
dataIndex: 'updated',
sorter: true,
render: (val) => {
return formatDate("YYYY-MM-DD H:i", val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: "关联项",
title: '关联项',
width: 200,
key: "planList",
dataIndex: "planList",
key: 'planList',
dataIndex: 'planList',
render: (val, record) => {
return (
<div className='related-task'>
......@@ -215,7 +214,7 @@ class VideoCourseList extends React.Component {
<span>
{item.planName} {index < record.relatedPlanList.length - 1 && <span></span>}
</span>
)
);
})}
</Tooltip>
</When>
......@@ -224,15 +223,15 @@ class VideoCourseList extends React.Component {
</Otherwise>
</Choose>
</div>
)
}
);
},
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
title: '操作',
key: 'operate',
dataIndex: 'operate',
width: 210,
fixed: "right",
fixed: 'right',
render: (val, record) => {
return (
<div className='operate'>
......@@ -240,7 +239,7 @@ class VideoCourseList extends React.Component {
观看数据
</div>
<If condition={type === "internal"}>
<If condition={type === 'internal'}>
<span className='operate__item split'> | </span>
<div className='operate__item' onClick={() => this.handleShowShareModal(record)}>
分享
......@@ -250,40 +249,40 @@ class VideoCourseList extends React.Component {
<Dropdown overlay={this.renderMoreOperate(record)}>
<span className='more-operate'>
<span className='operate-text'>更多</span>
<span className='iconfont icon' style={{ color: "#2966FF" }}>
<span className='iconfont icon' style={{ color: '#2966FF' }}>
&#xe824;
</span>
</span>
</Dropdown>
</div>
)
}
}
]
);
},
},
];
type !== "internal" && columns.splice(2, 1)
return columns
}
type !== 'internal' && columns.splice(2, 1);
return columns;
};
renderMoreOperate = (item) => {
const { type } = this.props
const { type } = this.props;
return (
<div className='live-course-more-menu'>
<If condition={type !== "internal"}>
<If condition={type !== 'internal'}>
<div className='operate__item' onClick={() => this.handleShowShareModal(item)}>
分享
</div>
</If>
{(User.getUserRole() === "CloudManager" || User.getUserRole() === "StoreManager") && (
{(User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') && (
<div className='operate__item' onClick={() => this.handleRelatedModalShow(item)}>
关联培训计划
</div>
)}
<If condition={type === "internal"}>
<If condition={type === 'internal'}>
<div
className='operate__item'
onClick={() => {
window.RCHistory.push(`/create-video-course?type=edit&id=${item.id}`)
window.RCHistory.push(`/create-video-course?type=edit&id=${item.id}`);
}}>
编辑
</div>
......@@ -292,77 +291,77 @@ class VideoCourseList extends React.Component {
</div>
</If>
</div>
)
}
);
};
handlePlanName = (planArray) => {
let planStr = ""
let planStr = '';
planArray.map((item, index) => {
if (index < planArray.length - 1) {
planStr = planStr + item.planName + "、"
planStr = planStr + item.planName + '、';
} else {
planStr = planStr + item.planName
}
})
return planStr
planStr = planStr + item.planName;
}
});
return planStr;
};
//改变上架状态
changeShelfState = (checked, item, index) => {
let _shelfState = checked ? "YES" : "NO"
let _shelfState = checked ? 'YES' : 'NO';
if (checked) {
_shelfState = "YES"
_shelfState = 'YES';
} else {
_shelfState = "NO"
_shelfState = 'NO';
}
const params = {
courseId: item.id,
shelfState: _shelfState
}
shelfState: _shelfState,
};
CourseService.changeVideoShelfState(params).then(() => {
if (_shelfState === "YES") {
message.success("已开启展示")
if (_shelfState === 'YES') {
message.success('已开启展示');
} else {
message.success("已取消展示")
}
this.props.changeShelfState(index, _shelfState)
})
message.success('已取消展示');
}
this.props.changeShelfState(index, _shelfState);
});
};
// 删除视频课
handleDeleteVideoCourse = (scheduleId) => {
Modal.confirm({
title: "你确定要删除此视频课吗?",
content: "删除后,学员将不能进行观看。",
title: '你确定要删除此视频课吗?',
content: '删除后,学员将不能进行观看。',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
okText: "确定",
okType: "danger",
cancelText: "取消",
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk: () => {
const param = {
courseId: scheduleId,
storeId: User.getStoreId()
}
storeId: User.getStoreId(),
};
CourseService.delVideoSchedule(param).then(() => {
message.success("删除成功")
this.props.onChange()
})
}
})
}
message.success('删除成功');
this.props.onChange();
});
},
});
};
// 显示分享弹窗
handleShowShareModal = (record, needStr = false) => {
const { type } = this.props
const { id, scheduleVideoUrl } = record
const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`
const longUrl = htmlUrl
const { coverUrl, courseName } = record
const { type } = this.props;
const { id, scheduleVideoUrl } = record;
const htmlUrl = `${LIVE_SHARE}video_detail/${id}?id=${User.getStoreId()}`;
const longUrl = htmlUrl;
const { coverUrl, courseName } = record;
const shareData = {
longUrl,
coverUrl,
scheduleVideoUrl,
courseName
}
courseName,
};
const shareLiveModal = (
<ShareLiveModal
......@@ -373,95 +372,109 @@ class VideoCourseList extends React.Component {
title='视频课'
close={() => {
this.setState({
shareLiveModal: null
})
localStorage.setItem("videoCourseItem", "")
shareLiveModal: null,
});
localStorage.setItem('videoCourseItem', '');
}}
/>
)
);
this.setState({ shareLiveModal })
}
this.setState({ shareLiveModal });
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter
const { query } = this.props
let { order: _order } = query
const { columnKey, order } = sorter;
const { query } = this.props;
let { order: _order } = query;
// 按创建时间升序排序
if (columnKey === "created" && order === "ascend") {
_order = "CREATED_ASC"
if (columnKey === 'created' && order === 'ascend') {
_order = 'CREATED_ASC';
}
// 按创建时间降序排序
if (columnKey === "created" && order === "descend") {
_order = "CREATED_DESC"
if (columnKey === 'created' && order === 'descend') {
_order = 'CREATED_DESC';
}
// 按更新时间升序排序
if (columnKey === "updated" && order === "ascend") {
_order = "UPDATED_ASC"
if (columnKey === 'updated' && order === 'ascend') {
_order = 'UPDATED_ASC';
}
// 按更新时间降序排序
if (columnKey === "updated" && order === "descend") {
_order = "UPDATED_DESC"
if (columnKey === 'updated' && order === 'descend') {
_order = 'UPDATED_DESC';
}
const _query = {
...query,
orderEnum: _order
}
this.props.onChange(_query)
}
orderEnum: _order,
};
this.props.onChange(_query);
};
handleRelatedModalShow = (item) => {
const selectPlanList = {}
const selectPlanList = {};
if (item.relatedPlanList) {
item.relatedPlanList.map((item, index) => {
selectPlanList[item.planId] = {}
selectPlanList[item.planId].planId = item.planId
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }]
return item
})
selectPlanList[item.planId] = {};
selectPlanList[item.planId].planId = item.planId;
selectPlanList[item.planId].taskBaseVOList = [{ taskId: item.taskId }];
return item;
});
}
this.setState({
RelatedPlanModalVisible: true,
selectCourseId: item.id,
selectPlanList: selectPlanList
})
}
selectPlanList: selectPlanList,
});
};
closeRelatedPlanModalVisible = () => {
this.setState({
RelatedPlanModalVisible: false
})
}
RelatedPlanModalVisible: false,
});
};
onChangeSelectPlanList = (selectPlanList) => {
this.setState({
selectPlanList: selectPlanList
})
}
selectPlanList: selectPlanList,
});
};
onConfirmSelectPlanList = () => {
this.setState(
{
RelatedPlanModalVisible: false
RelatedPlanModalVisible: false,
},
() => {
this.props.onChange()
}
)
this.props.onChange();
}
);
};
render() {
const { dataSource = [], totalCount, query, type } = this.props
const { current, size } = query
const { RelatedPlanModalVisible, selectPlanList, selectCourseId } = this.state
const { dataSource = [], totalCount, query, type } = this.props;
const { current, size } = query;
const { RelatedPlanModalVisible, selectPlanList, selectCourseId } = this.state;
return (
<div className={`video-course-list ${type !== "internal" ? "video-course-list-mt" : ""}`}>
<Table
<div className={`video-course-list ${type !== 'internal' ? 'video-course-list-mt' : ''}`}>
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500 }}
bordered
scroll={{ x: 1500 }}
className='video-list-table'
/>
{/* <Table
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
onChange={this.handleChangeTable}
pagination={false}
scroll={{ x: 1500 }}
bordered
className='video-list-table'
/> */}
<div className='box-footer'>
<PageControl
......@@ -469,8 +482,8 @@ class VideoCourseList extends React.Component {
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 }
this.props.onChange(_query)
const _query = { ...query, current: page + 1 };
this.props.onChange(_query);
}}
/>
</div>
......@@ -487,8 +500,8 @@ class VideoCourseList extends React.Component {
{this.state.shareLiveModal}
{this.state.watchDataModal}
</div>
)
);
}
}
export default VideoCourseList
export default VideoCourseList;
......@@ -5,80 +5,86 @@
* @Last Modified time: 2020-05-25 16:50:47
* @Description 余额异常弹窗
*/
import User from '@/common/js/user';
import { PageControl, XMTable } from '@/components';
import CourseService from '@/domains/course-domain/CourseService';
import { Input, Modal } from 'antd';
import college from '@/common/lottie/college';
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user'
import dealTimeDuration from '../../utils/dealTimeDuration';
import './WatchDataModal.less';
import dealTimeDuration from "../../utils/dealTimeDuration";
const { Search } = Input;
class WatchDataModal extends React.Component {
constructor(props) {
super(props);
this.state = {
visible:true,
dataSource:[],
size:10,
visible: true,
dataSource: [],
size: 10,
query: {
current: 1,
},
totalCount:0
totalCount: 0,
};
}
componentDidMount() {
this.handleFetchDataList();
}
onClose = () =>{
onClose = () => {
this.props.close();
}
};
// 获取观看视频数据列表
handleFetchDataList = () => {
const {query,size,totalCount} = this.state
const { query, size, totalCount } = this.state;
const { id } = this.props.data;
const params ={
const params = {
...query,
size,
courseId:id,
storeId:User.getStoreId()
}
courseId: id,
storeId: User.getStoreId(),
};
CourseService.videoWatchInfo(params).then((res) => {
const { result = {} } = res ;
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
totalCount: Number(total),
});
});
}
handleChangNickname = (value)=>{
};
handleChangNickname = (value) => {
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
if (isPhone) {
query.phone = value;
query.nickName = null;
}else{
} else {
query.nickName = value;
query.phone = null;
}
query.current = 1;
this.setState({
query
})
}
query,
});
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
this.setState({
size
},()=>{this.handleFetchDataList()})
this.setState(
{
size,
},
() => {
this.handleFetchDataList();
}
);
};
// 请求表头
parseColumns = () => {
......@@ -86,83 +92,101 @@ class WatchDataModal extends React.Component {
{
title: '观看学员',
key: 'name',
dataIndex: 'name'
dataIndex: 'name',
},
{
title: '手机号',
key: 'phone',
dataIndex: 'phone'
dataIndex: 'phone',
},
{
title: '观看者类型',
key: 'userRole',
dataIndex: 'userRole'
dataIndex: 'userRole',
},
{
title: '首次观看时间',
key: 'firstWatch',
dataIndex: 'firstWatch',
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
return formatDate('YYYY-MM-DD H:i', val);
},
},
{
title: '观看时长',
key: 'watchDuration',
dataIndex: 'watchDuration',
render: (val) => {
return <span>{val ? dealTimeDuration(val) : "00:00:00" }</span>
}
}
return <span>{val ? dealTimeDuration(val) : '00:00:00'}</span>;
},
},
];
return columns;
}
};
render() {
const { visible,size,dataSource,totalCount,query} = this.state;
const { visible, size, dataSource, totalCount, query } = this.state;
return (
<Modal
title="视频课观看数据"
title='视频课观看数据'
visible={visible}
footer={null}
onCancel={this.onClose}
maskClosable={false}
className="watch-data-modal"
className='watch-data-modal'
closable={true}
width={800}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索学员姓名/手机号" style={{ width: 200 }} onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='search-container'>
<Search
placeholder='搜索学员姓名/手机号'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangNickname(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList();
}}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div>
<Table
rowKey={record => record.id}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
/>
{dataSource.length >0 &&
<div className="box-footer">
{dataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
size="small"
size='small'
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList();
}
);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
)}
</div>
</Modal>
)
);
}
}
......
import React from 'react';
import { withRouter } from "react-router-dom";
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import PlanService from '@/domains/plan-domain/planService'
import { withRouter } from 'react-router-dom';
import { Input } from 'antd';
import { PageControl, XMTable } from '@/components';
import college from '@/common/lottie/college';
import PlanService from '@/domains/plan-domain/planService';
import User from '@/common/js/user';
import Bus from '@/core/bus';
import './EmployeeShareData.less';
......@@ -10,103 +11,129 @@ import './EmployeeShareData.less';
const { Search } = Input;
const UserRole = {
Store_Manager: {
text: "学院管理员"
text: '学院管理员',
},
Cloud_Manager: {
text:"管理员"
text: '管理员',
},
Cloud_Operator: {
text:'运营师'
text: '运营师',
},
Cloud_Lecture: {
text:"讲师"
text: '讲师',
},
};
class EmployeeShareData extends React.Component {
constructor(props) {
super(props);
const id = getParameterByName("id");
const id = getParameterByName('id');
this.state = {
id,
dataSource:[],
size:10,
dataSource: [],
size: 10,
query: {
current: 1,
},
totalCount:0,
}
totalCount: 0,
};
}
componentDidMount(){
componentDidMount() {
this.handleFetchDataList();
}
handleFetchDataList = ()=>{
const { query ,size,id} = this.state;
const params ={
handleFetchDataList = () => {
const { query, size, id } = this.state;
const params = {
...query,
size,
planId:id,
storeId:User.getStoreId(),
}
planId: id,
storeId: User.getStoreId(),
};
PlanService.getPlanUserRecordPage(params).then((res) => {
const { result = {} } = res ;
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
totalCount: Number(total),
});
});
}
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
this.setState({
size
},()=>{this.handleFetchDataList()})
this.setState(
{
size,
},
() => {
this.handleFetchDataList();
}
handleChangeTable = (pagination, filters, sorter)=> {
);
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
const { query } = this.state;
let _columnKey;
let _order;
if (columnKey === 'learnNum' && order === 'ascend') { _columnKey="LEARN_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnNum' && order === 'descend') { _columnKey="LEARN_NUM"; _order = 'SORT_DESC'; }
if (columnKey === 'learnNum' && order === 'ascend') {
_columnKey = 'LEARN_NUM';
_order = 'SORT_ASC';
}
if (columnKey === 'learnNum' && order === 'descend') {
_columnKey = 'LEARN_NUM';
_order = 'SORT_DESC';
}
if (columnKey === 'learnFinishNum' && order === 'ascend') { _columnKey="FINISH_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnFinishNum' && order === 'descend') { _columnKey="FINISH_NUM"; _order = 'SORT_DESC'; }
if (columnKey === 'learnFinishNum' && order === 'ascend') {
_columnKey = 'FINISH_NUM';
_order = 'SORT_ASC';
}
if (columnKey === 'learnFinishNum' && order === 'descend') {
_columnKey = 'FINISH_NUM';
_order = 'SORT_DESC';
}
if (columnKey === 'learnNoFinishNum' && order === 'ascend') { _columnKey="NOT_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnNoFinishNum' && order === 'descend') { _columnKey="NOT_NUM"; _order = 'SORT_DESC'; }
if (columnKey === 'learnNoFinishNum' && order === 'ascend') {
_columnKey = 'NOT_NUM';
_order = 'SORT_ASC';
}
if (columnKey === 'learnNoFinishNum' && order === 'descend') {
_columnKey = 'NOT_NUM';
_order = 'SORT_DESC';
}
const _query = {
...query,
sortMap:{}
sortMap: {},
};
_query.sortMap[_columnKey]=_order;
this.setState({
query:_query
},()=>this.handleFetchDataList())
}
handleChangNickname = (value)=>{
_query.sortMap[_columnKey] = _order;
this.setState(
{
query: _query,
},
() => this.handleFetchDataList()
);
};
handleChangNickname = (value) => {
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
if (isPhone) {
query.userPhone = value;
query.userName = null;
}else{
} else {
query.userName = value;
query.userPhone = null;
}
query.current = 1;
this.setState({
query
})
}
watchDataView = (record)=>{
Bus.trigger('watchDataView',record);
}
query,
});
};
watchDataView = (record) => {
Bus.trigger('watchDataView', record);
};
// 请求表头
parselumns = () => {
const columns = [
......@@ -115,24 +142,16 @@ class EmployeeShareData extends React.Component {
key: 'storeUserName',
dataIndex: 'storeUserName',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
return <div>{val}</div>;
},
},
{
title: '角色',
key: 'roleEnum',
dataIndex: 'roleEnum',
render: (val, record) => {
return (
<div>
{UserRole[record.roleEnum].text}
</div>
)
}
return <div>{UserRole[record.roleEnum].text}</div>;
},
},
//产品暂时性隐藏
// {
......@@ -151,28 +170,20 @@ class EmployeeShareData extends React.Component {
title: '最近分享成功时间',
key: 'recentlyForwardTime',
dataIndex: 'recentlyForwardTime',
width:240,
width: 240,
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>;
},
},
{
title: '学习人数',
key: 'learnNum',
dataIndex: 'learnNum',
width:110,
sorter:true,
width: 110,
sorter: true,
render: (val, record) => {
return (
<div className="learn-num">
{val}
</div>
)
}
return <div className='learn-num'>{val}</div>;
},
},
// {
// title: '已学完',
......@@ -208,24 +219,39 @@ class EmployeeShareData extends React.Component {
dataIndex: 'operate',
render: (val, record) => {
return (
<span className="operate-item" onClick={()=>this.watchDataView(record)}>数据详情</span>
)
}
}
<span className='operate-item' onClick={() => this.watchDataView(record)}>
数据详情
</span>
);
},
},
];
return columns;
}
};
render() {
const { dataSource,query,size,totalCount} = this.state;
const { dataSource, query, size, totalCount } = this.state;
return (
<div className="employee-share-data">
<div className="search-container">
<Search placeholder="搜索员工姓名或手机号" onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} style={{ width: 200 }} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
<div className='employee-share-data'>
<div className='search-container'>
<Search
placeholder='搜索员工姓名或手机号'
onChange={(e) => {
this.handleChangNickname(e.target.value);
}}
onSearch={() => {
this.handleFetchDataList();
}}
style={{ width: 200 }}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div>
<Table
rowKey={record => record.id}
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={dataSource}
columns={this.parselumns()}
pagination={false}
......@@ -233,25 +259,30 @@ class EmployeeShareData extends React.Component {
showSorterTooltip={false}
bordered
/>
{dataSource.length >0 &&
<div className="box-footer">
{dataSource.length > 0 && (
<div className='box-footer'>
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList();
}
);
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
)}
</div>
</div>
)
);
}
}
......
import React from 'react'
import { withRouter } from 'react-router-dom'
import { Table, Modal, message, Tooltip } from 'antd'
import { PageControl } from '@/components'
import UserLearningDataFilter from './UserLearningDataFilter'
import PlanService from '@/domains/plan-domain/planService'
import UserLearnDetailModal from '../modal/UserLearnDetailModal'
import UnbundEmployeeModal from '../modal/UnbundEmployeeModal'
import User from '@/common/js/user'
import './UserLearningData.less'
const { confirm } = Modal
import React from 'react';
import { withRouter } from 'react-router-dom';
import { Modal, message, Tooltip } from 'antd';
import { PageControl, XMTable } from '@/components';
import college from '@/common/lottie/college';
import UserLearningDataFilter from './UserLearningDataFilter';
import PlanService from '@/domains/plan-domain/planService';
import UserLearnDetailModal from '../modal/UserLearnDetailModal';
import UnbundEmployeeModal from '../modal/UnbundEmployeeModal';
import User from '@/common/js/user';
import './UserLearningData.less';
const { confirm } = Modal;
const LearnState = {
UN_PLAY: {
text: '未开始',
......@@ -19,12 +20,12 @@ const LearnState = {
FINISH: {
text: '已完成',
},
}
};
class UserLearningData extends React.Component {
constructor(props) {
super(props)
const id = getParameterByName('id')
super(props);
const id = getParameterByName('id');
this.state = {
id,
dataSource: [],
......@@ -35,13 +36,13 @@ class UserLearningData extends React.Component {
totalCount: 0,
userLearnDetailModalSHow: false,
unbundEmployeeModalVisible: false,
}
};
}
componentDidMount() {
this.handleFetchDataList()
this.handleFetchDataList();
}
handleFetchDataList = (_query) => {
const { query, size, id } = this.state
const { query, size, id } = this.state;
const params = {
...query,
..._query,
......@@ -49,88 +50,88 @@ class UserLearningData extends React.Component {
planId: id,
storeId: User.getStoreId(),
storeUserId: User.getStoreUserId(),
}
this.setState({ query: params })
};
this.setState({ query: params });
PlanService.getPlanCustomerRecordPage(params).then((res) => {
const { result = {} } = res
const { records = [], total = 0 } = result
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total),
})
})
}
});
});
};
onShowSizeChange = (current, size) => {
if (current == size) {
return
return;
}
this.setState(
{
size,
},
() => {
this.handleFetchDataList()
}
)
this.handleFetchDataList();
}
);
};
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter
const { query } = this.state
let _columnKey
let _order
const { columnKey, order } = sorter;
const { query } = this.state;
let _columnKey;
let _order;
if (columnKey === 'latelyLearnTime' && order === 'ascend') {
_columnKey = 'LATE_LEARN_TIME'
_order = 'SORT_ASC'
_columnKey = 'LATE_LEARN_TIME';
_order = 'SORT_ASC';
}
if (columnKey === 'latelyLearnTime' && order === 'descend') {
_columnKey = 'LATE_LEARN_TIME'
_order = 'SORT_DESC'
_columnKey = 'LATE_LEARN_TIME';
_order = 'SORT_DESC';
}
if (columnKey === 'startLearnTime' && order === 'ascend') {
_columnKey = 'START_LEARN_TIME'
_order = 'SORT_ASC'
_columnKey = 'START_LEARN_TIME';
_order = 'SORT_ASC';
}
if (columnKey === 'startLearnTime' && order === 'descend') {
_columnKey = 'START_LEARN_TIME'
_order = 'SORT_DESC'
_columnKey = 'START_LEARN_TIME';
_order = 'SORT_DESC';
}
if (columnKey === 'learnNum' && order === 'ascend') {
_columnKey = 'LEARN_NUM'
_order = 'SORT_ASC'
_columnKey = 'LEARN_NUM';
_order = 'SORT_ASC';
}
if (columnKey === 'learnNum' && order === 'descend') {
_columnKey = 'LEARN_NUM'
_order = 'SORT_DESC'
_columnKey = 'LEARN_NUM';
_order = 'SORT_DESC';
}
const _query = {
...query,
sortMap: {},
}
_query.sortMap[_columnKey] = _order
};
_query.sortMap[_columnKey] = _order;
this.setState(
{
query: _query,
},
() => this.handleFetchDataList()
)
}
);
};
watchDetail = (record) => {
this.setState({
userLearnDetailModalSHow: true,
storeCustomerId: record.storeCustomerId,
planId: record.planId,
})
}
});
};
closeUserLearnDetailModal = () => {
this.setState({
userLearnDetailModalSHow: false,
})
}
});
};
UnbundEmployee = (record) => {
if (User.getUserRole() === 'CloudOperator') {
return confirm({
......@@ -141,40 +142,40 @@ class UserLearningData extends React.Component {
okType: 'danger',
cancelText: '取消',
onOk: () => {
this.handleConfirmUnbundEmployee(record.storeCustomerId)
this.handleConfirmUnbundEmployee(record.storeCustomerId);
},
})
});
} else {
this.setState({
unbundEmployeeModalVisible: true,
storeCustomerId: record.storeCustomerId,
})
}
});
}
};
handleConfirmUnbundEmployee = (storeCustomerId) => {
let removeUserIds = []
removeUserIds.push(storeCustomerId)
let removeUserIds = [];
removeUserIds.push(storeCustomerId);
const params = {
planId: getParameterByName('id'),
removeUserIds,
storeCustomerId: storeCustomerId,
storeId: User.getStoreId(),
storeUserId: User.getStoreUserId(),
}
};
PlanService.removePlanCustomer(params).then((res) => {
this.handleFetchDataList()
message.success('解绑成功')
})
}
this.handleFetchDataList();
message.success('解绑成功');
});
};
handleCloseUnbundEmployeeModal = () => {
this.setState({
unbundEmployeeModalVisible: false,
})
}
});
};
// 请求表头
parselumns = () => {
let columns
let columns;
if (User.getUserRole() === 'CloudManager' || User.getUserRole() === 'StoreManager') {
columns = [
{
......@@ -182,7 +183,7 @@ class UserLearningData extends React.Component {
key: 'storeCustomerName',
dataIndex: 'storeCustomerName',
render: (val, record) => {
return <div>{val}</div>
return <div>{val}</div>;
},
},
{
......@@ -190,14 +191,20 @@ class UserLearningData extends React.Component {
key: 'learnState',
dataIndex: 'learnState',
render: (val, record) => {
return <div>{LearnState[val].text}</div>
return <div>{LearnState[val].text}</div>;
},
},
{
title: <span>
title: (
<span>
<span>负责人</span>
<Tooltip title="培训计划的分享者/跟进人"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:"400"}}>&#xe61d;</i></Tooltip>
</span>,
<Tooltip title='培训计划的分享者/跟进人'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: '400' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'userNameList',
dataIndex: 'userNameList',
render: (val, record) => {
......@@ -208,10 +215,10 @@ class UserLearningData extends React.Component {
<span>
{item} {index < record.userNameList.length - 1 && <span></span>}{' '}
</span>
)
);
})}
</div>
)
);
},
},
{
......@@ -221,7 +228,7 @@ class UserLearningData extends React.Component {
sorter: true,
width: 240,
render: (val, record) => {
return `${formatDate('YYYY-MM-DD H:i', parseInt(record.latelyLearnTime))}`
return `${formatDate('YYYY-MM-DD H:i', parseInt(record.latelyLearnTime))}`;
},
},
{
......@@ -231,14 +238,20 @@ class UserLearningData extends React.Component {
width: 240,
sorter: true,
render: (val, record) => {
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>;
},
},
{
title: <span>
title: (
<span>
<span>学习进度</span>
<Tooltip title="学员培训计划中达到“已完成”状态的课程数/总课程数"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px', fontWeight:"400"}}>&#xe61d;</i></Tooltip>
</span>,
<Tooltip title='学员培训计划中达到“已完成”状态的课程数/总课程数'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: '400' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'learnNum',
dataIndex: 'learnNum',
width: 130,
......@@ -250,7 +263,7 @@ class UserLearningData extends React.Component {
<span>/</span>
<span>{record.courseNum}</span>
</div>
)
);
},
},
{
......@@ -267,15 +280,15 @@ class UserLearningData extends React.Component {
<span
className='operate-item'
onClick={() => {
this.UnbundEmployee(record)
this.UnbundEmployee(record);
}}>
解绑
</span>
</div>
)
);
},
},
]
];
} else {
columns = [
{
......@@ -283,7 +296,7 @@ class UserLearningData extends React.Component {
key: 'storeCustomerName',
dataIndex: 'storeCustomerName',
render: (val, record) => {
return <div>{val}</div>
return <div>{val}</div>;
},
},
{
......@@ -291,7 +304,7 @@ class UserLearningData extends React.Component {
key: 'learnState',
dataIndex: 'learnState',
render: (val, record) => {
return <div>{LearnState[val].text}</div>
return <div>{LearnState[val].text}</div>;
},
},
{
......@@ -300,7 +313,7 @@ class UserLearningData extends React.Component {
dataIndex: 'latelyLearnTime',
sorter: true,
render: (val, record) => {
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>;
},
},
{
......@@ -309,7 +322,7 @@ class UserLearningData extends React.Component {
dataIndex: 'startLearnTime',
sorter: true,
render: (val, record) => {
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>
return <div>{formatDate('YYYY-MM-DD H:i', val)}</div>;
},
},
{
......@@ -333,7 +346,7 @@ class UserLearningData extends React.Component {
<span>/</span>
<span>{record.courseNum}</span>
</div>
)
);
},
},
{
......@@ -350,28 +363,32 @@ class UserLearningData extends React.Component {
<span
className='operate-item'
onClick={() => {
this.UnbundEmployee(record)
this.UnbundEmployee(record);
}}>
解绑
</span>
</div>
)
);
},
},
]
];
}
return columns
}
return columns;
};
render() {
const { dataSource, query, size, totalCount, userLearnDetailModalSHow, storeCustomerId, planId, unbundEmployeeModalVisible } = this.state
const { dataSource, query, size, totalCount, userLearnDetailModalSHow, storeCustomerId, planId, unbundEmployeeModalVisible } = this.state;
return (
<div className='user-learning-data'>
<div className='search-container'>
<UserLearningDataFilter onChange={this.handleFetchDataList} />
</div>
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.storeCustomerId}
dataSource={dataSource}
columns={this.parselumns()}
......@@ -388,15 +405,15 @@ class UserLearningData extends React.Component {
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = { ...query, current: page + 1 }
const _query = { ...query, current: page + 1 };
this.setState(
{
query: _query,
},
() => {
this.handleFetchDataList()
this.handleFetchDataList();
}
)
);
}}
onShowSizeChange={this.onShowSizeChange}
/>
......@@ -412,14 +429,14 @@ class UserLearningData extends React.Component {
onClose={this.handleCloseUnbundEmployeeModal}
storeCustomerId={storeCustomerId}
onConfirm={() => {
this.handleFetchDataList()
this.handleCloseUnbundEmployeeModal()
this.handleFetchDataList();
this.handleCloseUnbundEmployeeModal();
}}
/>
)}
</div>
)
);
}
}
export default withRouter(UserLearningData)
export default withRouter(UserLearningData);
import React from "react"
import _ from "underscore"
import { Table, Radio, Tabs, Modal, Input, message, Button, Tooltip } from "antd"
import React from 'react';
import _ from 'underscore';
import { Table, Radio, Tabs, Modal, Input, message, Button, Tooltip } from 'antd';
import { PageControl } from "@/components"
import { PageControl, XMTable } from '@/components';
import college from '@/common/lottie/college';
import CourseService from "@/domains/course-domain/CourseService"
import User from "@/common/js/user"
import Service from "@/common/js/service"
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration"
import CourseService from '@/domains/course-domain/CourseService';
import User from '@/common/js/user';
import Service from '@/common/js/service';
import dealTimeDuration from '../../course-manage/utils/dealTimeDuration';
import "./relatedCourseModal.less"
import './relatedCourseModal.less';
const { Search } = Input
const { TabPane } = Tabs
const { Search } = Input;
const { TabPane } = Tabs;
const courseStateShow = {
UN_START: {
code: 1,
title: "待开课",
color: "#FFB129",
title: '待开课',
color: '#FFB129',
},
STARTING: {
code: 2,
title: "上课中",
color: "#238FFF"
title: '上课中',
color: '#238FFF',
},
FINISH: {
code: 3,
title: "已完成",
color: "#3BBDAA"
title: '已完成',
color: '#3BBDAA',
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#999"
}
}
title: '未成功开课',
color: '#999',
},
};
class SelectOperatorModal extends React.Component {
constructor(props) {
super(props)
super(props);
this.state = {
liveDataSource: [],
liveSize: 10,
liveQuery: {
current: 1
current: 1,
},
liveTotalCount: 0,
selectLive: [], //弹窗内已选择的直播课程
currentCourseListData: [],
currentLiveCourseListData: [], //页面中已关联的直播课程
videoCourseDivision: "internal",
videoCourseDivision: 'internal',
videoDataSource: {
external: [],
internal: []
internal: [],
},
videoSize: {
external: 10,
internal: 10
internal: 10,
},
videoSearchName: {
external: "",
internal: ""
external: '',
internal: '',
},
videoSearchDefalt: "",
videoSearchDefalt: '',
videoQuery: {
external: {
current: 1
current: 1,
},
internal: {
current: 1
}
current: 1,
},
},
videoTotalCount: {
external: 0,
internal: 0
internal: 0,
},
selectVideo: {
external: [],
internal: []
internal: [],
}, //弹窗内已选择的视频课程
currentVideoCourseListData: {
external: [],
internal: []
internal: [],
}, //页面中已关联的视频课程
pictureDataSource: [],
pictureSize: 10,
pictureQuery: {
current: 1
current: 1,
},
pictureTotalCount: 0,
selectPicture: [], //弹窗内已选择的视频课程
currentPictureCourseListData: [], //页面中已关联的视频课程
activeKey: "video",
currentTaskCourseData: this.props.data[this.props.selectedTaskIndex].courseList || []
}
activeKey: 'video',
currentTaskCourseData: this.props.data[this.props.selectedTaskIndex].courseList || [],
};
}
componentDidMount() {
this.handleFetchLiveDataList()
this.handleFetchVideoDataList()
this.handleFetchPictureDataList()
this.handleFetchLiveDataList();
this.handleFetchVideoDataList();
this.handleFetchPictureDataList();
}
// 获取直播课列表
handleFetchLiveDataList = () => {
const { liveQuery, liveSize } = this.state
const _data = [...this.props.data]
let currentLiveCourseListData = []
const { liveQuery, liveSize } = this.state;
const _data = [...this.props.data];
let currentLiveCourseListData = [];
_data.map((item) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === "LIVE") {
currentLiveCourseListData.push(childItem.courseId)
if (childItem.courseType === 'LIVE') {
currentLiveCourseListData.push(childItem.courseId);
}
return childItem
})
return item
})
return childItem;
});
return item;
});
const params = {
...liveQuery,
size: liveSize,
excludeCourseIdList: currentLiveCourseListData
}
excludeCourseIdList: currentLiveCourseListData,
};
CourseService.getLiveCloudCourseBasePage(params).then((res) => {
const { result = {} } = res
const { records = [], total = 0 } = result
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
liveDataSource: records,
liveTotalCount: Number(total),
currentLiveCourseListData
})
})
}
currentLiveCourseListData,
});
});
};
// 获取视频课列表
handleFetchVideoDataList = () => {
const { videoQuery, videoSize, videoDataSource, videoTotalCount, videoCourseDivision } = this.state
const { videoQuery, videoSize, videoDataSource, videoTotalCount, videoCourseDivision } = this.state;
const _data = [...this.props.data]
let currentVideoCourseListData = []
const _data = [...this.props.data];
let currentVideoCourseListData = [];
_data.map((item, index) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === "VOICE") {
currentVideoCourseListData.push(childItem.courseId)
if (childItem.courseType === 'VOICE') {
currentVideoCourseListData.push(childItem.courseId);
}
return childItem
})
return item
})
return childItem;
});
return item;
});
const params = {
...videoQuery[videoCourseDivision],
size: videoSize[videoCourseDivision],
courseDivision: videoCourseDivision === "internal" ? "INTERNAL" : "EXTERNAL",
excludeCourseIdList: currentVideoCourseListData
}
courseDivision: videoCourseDivision === 'internal' ? 'INTERNAL' : 'EXTERNAL',
excludeCourseIdList: currentVideoCourseListData,
};
CourseService.videoScheduleBasePage(params).then((res) => {
const { result = {} } = res
console.log("result", result)
const { result = {} } = res;
console.log('result', result);
const { records = [], total = 0 } = result
const { records = [], total = 0 } = result;
this.setState({
videoDataSource: {
...videoDataSource,
[videoCourseDivision]: records
[videoCourseDivision]: records,
},
videoTotalCount: {
...videoTotalCount,
[videoCourseDivision]: Number(total)
[videoCourseDivision]: Number(total),
},
currentVideoCourseListData
})
})
}
currentVideoCourseListData,
});
});
};
// 获取图文课列表
handleFetchPictureDataList = () => {
const { pictureQuery, pictureSize } = this.state
const _data = [...this.props.data]
let currentPictureCourseListData = []
const { pictureQuery, pictureSize } = this.state;
const _data = [...this.props.data];
let currentPictureCourseListData = [];
_data.map((item, index) => {
item.courseList.map((childItem, childIndex) => {
if (childItem.courseType === "PICTURE") {
currentPictureCourseListData.push(childItem.courseId)
if (childItem.courseType === 'PICTURE') {
currentPictureCourseListData.push(childItem.courseId);
}
return childItem
})
return item
})
return childItem;
});
return item;
});
const params = {
...pictureQuery,
size: pictureSize,
courseType: "PICTURE",
courseType: 'PICTURE',
storeId: User.getStoreId(),
excludeCourseIdList: currentPictureCourseListData
}
excludeCourseIdList: currentPictureCourseListData,
};
Service.Hades("public/hades/mediaCoursePage", params).then((res) => {
const { result = {} } = res
const { records = [], total = 0 } = result
Service.Hades('public/hades/mediaCoursePage', params).then((res) => {
const { result = {} } = res;
const { records = [], total = 0 } = result;
this.setState({
pictureDataSource: records,
pictureTotalCount: Number(total),
currentPictureCourseListData
})
})
}
currentPictureCourseListData,
});
});
};
handleChangVideoCourseName = (value) => {
const { videoQuery, videoCourseDivision, videoSearchName } = this.state
videoQuery[videoCourseDivision].courseName = value
videoQuery[videoCourseDivision].current = 1
const { videoQuery, videoCourseDivision, videoSearchName } = this.state;
videoQuery[videoCourseDivision].courseName = value;
videoQuery[videoCourseDivision].current = 1;
this.setState({
...videoQuery,
videoSearchDefalt: value,
videoSearchName: {
...videoSearchName,
[videoCourseDivision]: value
}
})
}
[videoCourseDivision]: value,
},
});
};
handleChangLiveCourseName = (value) => {
const { liveQuery } = this.state
liveQuery.courseName = value
liveQuery.current = 1
const { liveQuery } = this.state;
liveQuery.courseName = value;
liveQuery.current = 1;
this.setState({
liveQuery
})
}
liveQuery,
});
};
handleChangPictureCourseName = (value) => {
const { pictureQuery } = this.state
pictureQuery.courseName = value
pictureQuery.current = 1
const { pictureQuery } = this.state;
pictureQuery.courseName = value;
pictureQuery.current = 1;
this.setState({
pictureQuery
})
}
pictureQuery,
});
};
onShowLiveSizeChange = (current, size) => {
if (current === size) {
return
return;
}
this.setState(
{
liveSize: size
liveSize: size,
},
() => {
this.handleFetchLiveDataList()
}
)
this.handleFetchLiveDataList();
}
);
};
onShowVideoSizeChange = (current, size) => {
if (current === size) {
return
return;
}
this.setState(
{
videoSize: size
videoSize: size,
},
() => {
this.handleFetchLiveDataList()
}
)
this.handleFetchLiveDataList();
}
);
};
onShowPictureSizeChange = (current, size) => {
if (current === size) {
return
return;
}
this.setState(
{
pictureSize: size
pictureSize: size,
},
() => {
this.handleFetchPictureDataList()
}
)
this.handleFetchPictureDataList();
}
);
};
// 请求表头
parseLiveColumns = () => {
const columns = [
{
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:"400"
}}>&#xe61d;</i></Tooltip></span>,
title: (
<span>
<span>课程信息</span>
<Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: '400' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'course',
dataIndex: 'course',
width:'40%',
width: '40%',
render: (val, record) => {
let hasCover = false
let hasCover = false;
return (
<div className='course-info'>
{record.courseMediaVOS.map((item) => {
if (item.contentType === "COVER") {
hasCover = true
return <img className='course-cover' src={item.mediaUrl} alt='' />
if (item.contentType === 'COVER') {
hasCover = true;
return <img className='course-cover' src={item.mediaUrl} alt='' />;
}
return null
return null;
})}
<If condition={!hasCover}>
<img className='course-cover' src={"https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png"} alt='' />
<img className='course-cover' src={'https://image.xiaomaiketang.com/xm/Yip2YtFDwH.png'} alt='' />
</If>
<div>
......@@ -316,35 +325,35 @@ class SelectOperatorModal extends React.Component {
</span>
</div>
</div>
)
}
);
},
},
{
title: "上课时间",
key: "courseTime",
dataIndex: "courseTime",
width: "40%",
title: '上课时间',
key: 'courseTime',
dataIndex: 'courseTime',
width: '40%',
render: (val, record) => {
return (
<div>
<div>{formatDate("YYYY-MM-DD", record.startTime)}</div>
<div>{formatDate('YYYY-MM-DD', record.startTime)}</div>
<div>
{formatDate("H:i", record.startTime)}~{formatDate("H:i", record.endTime)}
{formatDate('H:i', record.startTime)}~{formatDate('H:i', record.endTime)}
</div>
</div>
)
}
);
},
},
{
title: "学院展示",
key: "shelfState",
dataIndex: "shelfState",
width: "20%",
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width: '20%',
render: (val, record) => {
return (
<span>
<Choose>
<When condition={record.shelfState === "YES"}>
<When condition={record.shelfState === 'YES'}>
<span>开启</span>
</When>
<Otherwise>
......@@ -352,24 +361,33 @@ class SelectOperatorModal extends React.Component {
</Otherwise>
</Choose>
</span>
)
}
}
]
return columns
}
);
},
},
];
return columns;
};
// 请求表头
parseVideoColumns = () => {
const { videoCourseDivision } = this.state
const { videoCourseDivision } = this.state;
const columns = [
{
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:"400"}}>&#xe61d;</i></Tooltip></span>,
title: (
<span>
<span>课程信息</span>
<Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: '400' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'course',
dataIndex: 'course',
width:'60%',
width: '60%',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record
const { coverUrl, scheduleVideoUrl } = record;
return (
<div className='course-info'>
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
......@@ -377,242 +395,251 @@ class SelectOperatorModal extends React.Component {
className='course-cover'
src={
coverUrl ||
(videoCourseDivision === "internal"
(videoCourseDivision === 'internal'
? `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`
: "https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png")
: 'https://image.xiaomaiketang.com/xm/mt3ZQRxGKB.png')
}
alt=''
/>
<div className='course-name'>{record.courseName}</div>
</div>
)
}
);
},
},
{
title: "课程时长",
key: "courseTime",
dataIndex: "courseTime",
width: "20%",
title: '课程时长',
key: 'courseTime',
dataIndex: 'courseTime',
width: '20%',
render: (val, record) => {
return <span className='course-status'>{dealTimeDuration(record.videoDuration)}</span>
}
return <span className='course-status'>{dealTimeDuration(record.videoDuration)}</span>;
},
},
{
title: "学院展示",
key: "shelfState",
dataIndex: "shelfState",
width: "20%",
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width: '20%',
render: (val, record) => {
return (
<Choose>
<When condition={record.shelfState === "YES"}>
<When condition={record.shelfState === 'YES'}>
<span>开启</span>
</When>
<Otherwise>
<span>关闭</span>
</Otherwise>
</Choose>
)
}
}
]
return columns
}
);
},
},
];
return columns;
};
// 请求表头
parsePictureColumns = () => {
const columns = [
{
title: <span><span>课程信息</span><Tooltip title="仅显示未关联课程,已关联课程不支持重复选择"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px',fontWeight:"400"}}>&#xe61d;</i></Tooltip></span>,
title: (
<span>
<span>课程信息</span>
<Tooltip title='仅显示未关联课程,已关联课程不支持重复选择'>
<i className='icon iconfont' style={{ marginLeft: '5px', cursor: 'pointer', color: '#bfbfbf', fontSize: '14px', fontWeight: '400' }}>
&#xe61d;
</i>
</Tooltip>
</span>
),
key: 'course',
dataIndex: 'course',
width:'55%',
width: '55%',
render: (val, record) => {
const { coverUrl } = record
const { coverUrl } = record;
return (
<div className='course-info'>
<img className='course-cover' src={coverUrl || "https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png"} alt='' />
<img className='course-cover' src={coverUrl || 'https://image.xiaomaiketang.com/xm/wFnpZtp2yB.png'} alt='' />
<div className='course-name'>{record.courseName}</div>
</div>
)
}
);
},
},
{
title: "更新时间",
key: "updated",
dataIndex: "updated",
width: "25%",
title: '更新时间',
key: 'updated',
dataIndex: 'updated',
width: '25%',
render: (val, record) => {
return <span className='course-status'>{formatDate("YYYY-MM-DD", record.updated)}</span>
}
return <span className='course-status'>{formatDate('YYYY-MM-DD', record.updated)}</span>;
},
},
{
title: "学院展示",
key: "shelfState",
dataIndex: "shelfState",
width: "20%",
title: '学院展示',
key: 'shelfState',
dataIndex: 'shelfState',
width: '20%',
render: (val, record) => {
return <span>{record.shelfState === "YES" ? "开启" : "关闭"}</span>
}
}
]
return columns
}
return <span>{record.shelfState === 'YES' ? '开启' : '关闭'}</span>;
},
},
];
return columns;
};
selectLiveList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state;
let _list = [];
if (selected || !_.find(selectLive, (item) => item.liveCourseId === record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId)
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId);
} else {
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId)
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId);
}
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
return
}
this.setState({ selectLive: _list })
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
this.setState({ selectLive: _list });
};
selectVideoList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state
const { selectVideo, currentTaskCourseData, selectLive, selectPicture, videoCourseDivision } = this.state;
let { [videoCourseDivision]: selectList } = selectVideo
let { [videoCourseDivision]: selectList } = selectVideo;
let _list = []
let _list = [];
if (selected || !_.find(selectList, (item) => item.id === record.id)) {
_list = _.uniq(selectList.concat([record]), false, (item) => item.id)
_list = _.uniq(selectList.concat([record]), false, (item) => item.id);
} else {
_list = _.reject(selectList, (item) => item.id === record.id)
_list = _.reject(selectList, (item) => item.id === record.id);
}
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
return
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
}
[videoCourseDivision]: _list,
},
});
};
selectPictureList = (record, selected) => {
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state
let _list = []
const { selectVideo, currentTaskCourseData, selectLive, selectPicture } = this.state;
let _list = [];
if (selected || !_.find(selectPicture, (item) => item.id === record.id)) {
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id)
_list = _.uniq(selectPicture.concat([record]), false, (item) => item.id);
} else {
_list = _.reject(selectPicture, (item) => item.id === record.id)
_list = _.reject(selectPicture, (item) => item.id === record.id);
}
if (_list.length + currentTaskCourseData.length + selectLive.length + selectVideo.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
return
}
this.setState({ selectPicture: _list })
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
this.setState({ selectPicture: _list });
};
clearSelectCourse = () => {
this.setState({
selectLive: [],
selectVideo: {
internal: [],
external: []
external: [],
},
selectPicture: []
})
}
selectPicture: [],
});
};
handleSelectVideo = (selectVideo) => {
return selectVideo.map((item) => {
let _item = {}
_item.courseId = item.id
_item.courseType = "VOICE"
_item.courseName = item.courseName
let _item = {};
_item.courseId = item.id;
_item.courseType = 'VOICE';
_item.courseName = item.courseName;
return _item
})
}
return _item;
});
};
handleSelectLive = (selectLive) => {
return selectLive.map((item, index) => {
let _item = {}
_item.courseId = item.liveCourseId
_item.courseType = "LIVE"
_item.courseName = item.courseName
_item.courseState = item.courseState
return _item
})
}
let _item = {};
_item.courseId = item.liveCourseId;
_item.courseType = 'LIVE';
_item.courseName = item.courseName;
_item.courseState = item.courseState;
return _item;
});
};
videoCourseDivisionChange = (e) => {
const { videoSearchName } = this.state
const { videoSearchName } = this.state;
this.setState(
{
videoCourseDivision: e.target.value,
videoSearchDefalt: videoSearchName[e.target.value]
videoSearchDefalt: videoSearchName[e.target.value],
},
() => {
this.handleFetchVideoDataList()
}
)
this.handleFetchVideoDataList();
}
);
};
handleSelectPicture = (selectPicture) => {
return selectPicture.map((item, index) => {
let _item = {}
_item.courseId = item.id
_item.courseType = "PICTURE"
_item.courseName = item.courseName
return _item
})
}
let _item = {};
_item.courseId = item.id;
_item.courseType = 'PICTURE';
_item.courseName = item.courseName;
return _item;
});
};
renderFooter = () => {
const { activeKey } = this.state
let href = ""
const { activeKey } = this.state;
let href = '';
switch (activeKey) {
case "live":
case 'live':
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + "#/create-live-course?type=add"}
href={window.location.origin + window.location.pathname + '#/create-live-course?type=add'}
onClick={this.props.onClose}>
没有找到需要的直播课?<span>去创建</span>
</a>
)
break
case "video":
);
break;
case 'video':
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + "#/create-video-course?type=add"}
href={window.location.origin + window.location.pathname + '#/create-video-course?type=add'}
onClick={this.props.onClose}>
没有找到需要的视频课?<span>去创建</span>
</a>
)
break
case "picture":
);
break;
case 'picture':
href = (
<a
target='_blank'
rel='noopener noreferrer'
className='link-create-course'
href={window.location.origin + window.location.pathname + "#/create-graphics-course?type=add"}
href={window.location.origin + window.location.pathname + '#/create-graphics-course?type=add'}
onClick={this.props.onClose}>
没有找到需要的图文课?<span>去创建</span>
</a>
)
break
);
break;
default:
break
}
return href
break;
}
return href;
};
render() {
const { visible } = this.props
const { visible } = this.props;
const {
liveDataSource,
liveSize,
......@@ -632,8 +659,8 @@ class SelectOperatorModal extends React.Component {
pictureQuery,
pictureTotalCount,
videoCourseDivision
} = this.state
videoCourseDivision,
} = this.state;
return (
<Modal
title='关联课程'
......@@ -649,7 +676,7 @@ class SelectOperatorModal extends React.Component {
this.renderFooter(),
<Button
onClick={() => {
this.props.onClose()
this.props.onClose();
}}>
取消
</Button>,
......@@ -660,18 +687,18 @@ class SelectOperatorModal extends React.Component {
...this.handleSelectVideo(selectVideo.internal),
...this.handleSelectVideo(selectVideo.external),
...this.handleSelectLive(selectLive),
...this.handleSelectPicture(selectPicture)
...this.handleSelectPicture(selectPicture),
])
}>
确定
</Button>
</Button>,
]}>
<div>
<Tabs
type='line'
defaultActiveKey='live'
onChange={(activeKey) => {
this.setState({ activeKey: activeKey })
this.setState({ activeKey: activeKey });
}}>
<TabPane tab='直播课' key='live'>
<div className='search-container'>
......@@ -680,10 +707,10 @@ class SelectOperatorModal extends React.Component {
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangLiveCourseName(e.target.value)
this.handleChangLiveCourseName(e.target.value);
}}
onSearch={() => {
this.handleFetchLiveDataList()
this.handleFetchLiveDataList();
}}
/>
</div>
......@@ -707,32 +734,36 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.liveCourseId}
dataSource={liveDataSource}
columns={this.parseLiveColumns()}
pagination={false}
bordered
rowSelection={{
type: "checkbox",
selectedRowKeys: _.pluck(selectLive, "liveCourseId"),
type: 'checkbox',
selectedRowKeys: _.pluck(selectLive, 'liveCourseId'),
onSelect: (record, selected) => {
this.selectLiveList(record, selected)
this.selectLiveList(record, selected);
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []
let _list = [];
if (selected) {
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId)
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId);
} else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId))
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId));
}
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length - 20
_list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectLive: _list })
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectPicture.length - 20;
_list.splice(_list.length - extraLength, extraLength);
}
this.setState({ selectLive: _list });
},
}}
/>
{liveDataSource.length > 0 && (
......@@ -743,15 +774,15 @@ class SelectOperatorModal extends React.Component {
size='small'
total={liveTotalCount}
toPage={(page) => {
const _query = { ...liveQuery, current: page + 1 }
const _query = { ...liveQuery, current: page + 1 };
this.setState(
{
liveQuery: _query
liveQuery: _query,
},
() => {
this.handleFetchLiveDataList()
this.handleFetchLiveDataList();
}
)
);
}}
onShowSizeChange={this.onShowLiveSizeChange}
/>
......@@ -772,10 +803,10 @@ class SelectOperatorModal extends React.Component {
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangVideoCourseName(e.target.value)
this.handleChangVideoCourseName(e.target.value);
}}
onSearch={() => {
this.handleFetchVideoDataList()
this.handleFetchVideoDataList();
}}
/>
</div>
......@@ -798,37 +829,41 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={videoDataSource[videoCourseDivision]}
columns={this.parseVideoColumns()}
pagination={false}
bordered
rowSelection={{
type: "checkbox",
selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], "id"),
type: 'checkbox',
selectedRowKeys: _.pluck(selectVideo[videoCourseDivision], 'id'),
onSelect: (record, selected) => {
this.selectVideoList(record, selected)
this.selectVideoList(record, selected);
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []
let _list = [];
if (selected) {
_list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id)
_list = _.uniq(selectVideo[videoCourseDivision].concat(changeRows), false, (item) => item.id);
} else {
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id))
_list = _.reject(selectVideo[videoCourseDivision], (item) => _.find(changeRows, (data) => data.id === item.id));
}
if (_list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = _list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length - 20
_list.splice(_list.length - extraLength, extraLength)
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectLive.length + selectPicture.length - 20;
_list.splice(_list.length - extraLength, extraLength);
}
this.setState({
selectVideo: {
...selectVideo,
[videoCourseDivision]: _list
}
})
}
[videoCourseDivision]: _list,
},
});
},
}}
/>
{videoDataSource[videoCourseDivision].length > 0 && (
......@@ -839,19 +874,19 @@ class SelectOperatorModal extends React.Component {
size='small'
total={videoTotalCount[videoCourseDivision]}
toPage={(page) => {
const _query = { ...videoQuery[videoCourseDivision], current: page + 1 }
const _query = { ...videoQuery[videoCourseDivision], current: page + 1 };
this.setState(
{
videoQuery: {
...videoQuery,
[videoCourseDivision]: _query
}
[videoCourseDivision]: _query,
},
},
() => {
this.handleFetchVideoDataList()
this.handleFetchVideoDataList();
}
)
);
}}
onShowSizeChange={this.onShowVideoSizeChange}
/>
......@@ -866,10 +901,10 @@ class SelectOperatorModal extends React.Component {
placeholder='搜索课程名称'
style={{ width: 200 }}
onChange={(e) => {
this.handleChangPictureCourseName(e.target.value)
this.handleChangPictureCourseName(e.target.value);
}}
onSearch={() => {
this.handleFetchPictureDataList()
this.handleFetchPictureDataList();
}}
/>
</div>
......@@ -893,32 +928,36 @@ class SelectOperatorModal extends React.Component {
</div>
</div>
<div>
<Table
<XMTable
renderEmpty={{
image: college,
description: '暂无数据',
}}
rowKey={(record) => record.id}
dataSource={pictureDataSource}
columns={this.parsePictureColumns()}
pagination={false}
bordered
rowSelection={{
type: "checkbox",
selectedRowKeys: _.pluck(selectPicture, "id"),
type: 'checkbox',
selectedRowKeys: _.pluck(selectPicture, 'id'),
onSelect: (record, selected) => {
this.selectPictureList(record, selected)
this.selectPictureList(record, selected);
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = []
let _list = [];
if (selected) {
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id)
_list = _.uniq(selectPicture.concat(changeRows), false, (item) => item.id);
} else {
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id))
_list = _.reject(selectPicture, (item) => _.find(changeRows, (data) => data.id === item.id));
}
if (_list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length > 20) {
message.warning("无法继续选择,一个任务最多关联20个课程")
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length - 20
_list.splice(_list.length - extraLength, extraLength)
}
this.setState({ selectPicture: _list })
message.warning('无法继续选择,一个任务最多关联20个课程');
const extraLength = _list.length + currentTaskCourseData.length + selectVideo.length + selectLive.length - 20;
_list.splice(_list.length - extraLength, extraLength);
}
this.setState({ selectPicture: _list });
},
}}
/>
{pictureDataSource.length > 0 && (
......@@ -929,15 +968,15 @@ class SelectOperatorModal extends React.Component {
size='small'
total={pictureTotalCount}
toPage={(page) => {
const _query = { ...pictureQuery, current: page + 1 }
const _query = { ...pictureQuery, current: page + 1 };
this.setState(
{
pictureQuery: _query
pictureQuery: _query,
},
() => {
this.handleFetchPictureDataList()
this.handleFetchPictureDataList();
}
)
);
}}
onShowSizeChange={this.onShowPictureSizeChange}
/>
......@@ -948,8 +987,8 @@ class SelectOperatorModal extends React.Component {
</Tabs>
</div>
</Modal>
)
);
}
}
export default SelectOperatorModal
export default SelectOperatorModal;
/*
* @Author: 吴文洁
* @Date: 2020-06-09 10:47:51
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-23 09:33:09
* @Description: 文件夹列表
*/
import React from 'react';
import { Table, Menu, Dropdown, Upload, Modal, message, Tooltip, Icon } from 'antd';
import { PageControl } from '@/components';
import DefaultIcon from '@/modules/common/DefaultIcon';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { FileTypeIcon, SupportFileType, DEFAULT_SIZE_UNIT } from "@/common/constants/academic/lessonEnum";
import ScanFileModal from '../modal/ScanFileModal';
import CreateFolderModal from '../modal/CreateFolderModal';
import UploadProgressModal from '../modal/UploadProgressModal';
import SelectPrepareFileModal from '../modal/SelectPrepareFileModal';
const DEL_FOLDER_URL_MAP = {
'MYSELF': 'public/apollo/delFolder',
'COMMON': 'public/apollo/delCommonFolder'
}
let count = 0;
class FolderList extends React.Component {
constructor(props) {
super(props);
this.state = {
localFileList: [], // 本地文件列表(待上传)
currentFolder: {}, // 当前文件/文件夹(操作列表中的文件/文件夹的时候需要用到)
uploadFolderPath: {}, // 上传文件的目录,防止中途切换文件夹
renameModalData: {}, // 重命名弹窗
scanFileModal: null, // 预览文件弹窗
showUploadModal: false, // 上传进度弹窗,
nonCompliantFileList: []
}
}
componentWillReceiveProps(nextProps) {
const { folderPathList } = nextProps
const currentFolder = folderPathList[folderPathList.length - 1];
this.setState({
currentFolder
})
}
//预览文件
handleSelect = (folder) => {
// 只有文件才有预览功能
if (folder.folderType === 'FOLDER') {
this.handleSelectFolder(folder);
} else {
this.handleScanFile(folder);
}
}
// 埋点
handleDataDot = (folderFormat) => {
switch (folderFormat) {
case 'PDF':
window.WEBTRACING('resource_disk_file_preview_pdf', '资料云盘_点击预览_pdf');
break;
case 'WORD':
case 'DOCX':
case 'DOC':
window.WEBTRACING('resource_disk_file_preview_word', '资料云盘_点击预览_word');
break;
case 'EXCEL':
window.WEBTRACING('resource_disk_file_preview_excel', '资料云盘_点击预览_excel');
break;
case 'PPT':
case 'PPTX':
window.WEBTRACING('resource_disk_file_preview_ppt', '资料云盘_点击预览_ppt');
break;
default:
break;
}
}
// 预览文件
handleScanFile = (folder) => {
const { folderFormat, folderSize, ossUrl } = folder;
switch (folderFormat) {
case 'PDF':
window.open(ossUrl, "_blank");
break;
case "WORD":
case "DOCX":
case "DOC":
case "EXCEL":
case "PPT":
case "PPTX":
case "PDF":
if ((folderFormat === 'PPT' || folderFormat === 'PPTX' ||
folderFormat === 'DOCX' || folderFormat === 'WORD' ||
folderFormat === 'DOC') && folderSize > 10 * DEFAULT_SIZE_UNIT) {
Modal.confirm({
title: '抱歉,不能在线预览',
content: '由于文件较大,不支持在线预览,请下载后再查看',
icon: <QuestionCircleOutlined />,
okText:"下载",
onOk:() => {
const a = document.createElement('a');
a.href = ossUrl;
a.click();
}
});
break;
}
if (folderFormat === 'EXCEL' && folderSize > 5 * DEFAULT_SIZE_UNIT) {
Modal.confirm({
title: '抱歉,不能在线预览',
content: '由于文件较大,不支持在线预览,请下载后再查看',
icon: <QuestionCircleOutlined />,
okText:"下载",
onOk:() => {
const a = document.createElement('a');
a.href = ossUrl;
a.click();
}
});
break;
}
const prefixUrl = "https://view.officeapps.live.com/op/view.aspx?src=";
const scanUrl = `${prefixUrl}${encodeURIComponent(ossUrl)}`
window.open(scanUrl, "_blank");
break;
default:
const scanFileModal = <ScanFileModal
fileType={folderFormat}
item={folder}
close={() => {
this.setState({ scanFileModal: null })
}}
/>
this.setState({ scanFileModal });
break;
}
// 预览文件埋点
this.handleDataDot(folderFormat);
}
// 选择文件夹
handleSelectFolder = (folder) => {
const { folderPathList, showResultPage, currentRootDisk } = this.props;
// 判断是否是员工文件的根目录
const employeeDisk = currentRootDisk.disk === 'EMPLOYEE' && folderPathList.length === 1;
if (showResultPage) {
folderPathList.pop();
}
folderPathList.push({
id: folder.id,
folderName: folder.folderName
});
this.props.onChangeFolderPath(folderPathList);
this.props.onRefresh({
parentId: folder.id,
folderIdType: employeeDisk ? 'USER' : 'FOLDER'
});
}
// 修改文件路径
handleChangeFolderPath = (folder) => {
const { instId } = window.currentUserInstInfo;
const { id } = folder;
const { currentRootDisk } = this.props;
const params = {
id,
instId: instId || LS.get('instId'),
disk: currentRootDisk.disk,
}
axios.Apollo('public/apollo/folderPath', params).then((res) => {
const { result = [] } = res;
this.props.onChangeFolderPath(result, false);
})
}
parseColumns = () => {
const { currentRootDisk, showResultPage, folderPathList } = this.props;
const hasManagementAuthority = currentRootDisk.uploadPower;
// 判断是否是员工文件的根目录
const employeeDisk = currentRootDisk.disk === 'EMPLOYEE' && folderPathList.length === 1;
const columns = [
{
title: '名称',
key: 'folderName',
dataIndex: 'folderName',
width: '28%',
sorter: (employeeDisk || !hasManagementAuthority) ? false : true,
render: (value, record) => {
const { folderType, folderFormat } = record;
const isFolder = folderType === 'FOLDER';
let imgSrc = !isFolder ?
FileTypeIcon[folderFormat] :
'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1594871430788.png';
if (employeeDisk) {
imgSrc = 'https://xiaomai-image.oss-cn-hangzhou.aliyuncs.com/1594871440736.png'
}
return (
<div
className="file-name"
onClick={() => this.handleSelect(record)}
>
{
<img
alt="img-src"
className="file-name__icon"
src={imgSrc}
/>
}
<span
className={`file-name__text ${!isFolder ? 'highlight' : ''}`}
>
{value}
</span>
</div>
)
}
},
{
title: '更新时间',
key: 'updated',
dataIndex: 'updated',
sorter: (employeeDisk || !hasManagementAuthority) ? false : true,
render: (value) => {
return <span>{formatDate('YYYY-MM-DD H:i', value)}</span>
}
},
{
title: '大小',
key: 'folderSize',
dataIndex: 'folderSize',
width: '10%',
sorter: (employeeDisk || !hasManagementAuthority) ? false : true,
render: (value, record) => {
const { folderType } = record;
const _fileSize = Number(value);
let _size = `${(_fileSize / DEFAULT_SIZE_UNIT).toFixed(1)}M`;
if (_fileSize < 0.1 * DEFAULT_SIZE_UNIT) {
_size = `${(_fileSize / 1024).toFixed(1)}kb`;
}
return (
<span>{folderType === 'FILE' ? _size : '-'}</span>
)
}
},
{
title: '操作',
key: 'operate',
render: (value, record) => {
if (!(currentRootDisk.disk === 'EMPLOYEE' && (folderPathList.length === 1 || record.folderType === 'FOLDER')) ||
hasManagementAuthority) {
return (
<Dropdown overlay={this.renderMenu(record)} trigger={['hover']}>
<span className="icon iconfont">&#xe756;</span>
</Dropdown>
)
}
return <span>-</span>
}
}
]
// 公共文件需要显示创建者
if (currentRootDisk.disk === 'COMMON') {
columns.splice(1, 0, {
title: '创建者',
key: 'createName',
dataIndex: 'createName'
});
}
// 搜索结果需要显示所在目录
if (showResultPage) {
columns.push({
title: '所在目录',
key: 'parentName',
dataIndex: 'parentName',
render: (value, record) => {
return <span
className="file-path"
onClick={() => this.handleChangeFolderPath(record)}
>
{value || currentRootDisk.folderName}
</span>
}
})
}
return columns;
}
// 删除文件
handleDeleteFolder = (folder) => {
const { currentRootDisk: { disk } } = this.props;
const { instId } = window.currentUserInstInfo;
// 判断此文件是否有关联的课次
axios.Apollo('public/apollo/judgeRelation', {
folderIds: [folder.id],
instId: instId || LS.get('instId')
}).then((res) => {
// 如果有关联的文件,二次弹窗确认
const hasRelative = !!res.result;
Modal.confirm({
title: '确认删除所选的文件吗?',
content: hasRelative ? '此文件已关联了课次,删除后,学员将不能查看到此文件。' : '删除后,数据将无法恢复。',
icon:<span className="icon iconfont default-confirm-icon">&#xe839; </span>,
okType: 'danger',
onOk: () => {
const { currentFolder } = this.state;
axios.Apollo(DEL_FOLDER_URL_MAP[disk], {
ids: [folder.id],
instId: instId || LS.get('instId')
}).then(() => {
message.success('删除成功');
this.props.onRefresh({ parentId: currentFolder.id || null });
})
}
});
})
}
// 重命名
handleRename = (folder) => {
this.setState({
renameModalData: {
visible: true,
id: folder.id,
folderName: folder.folderName,
}
});
}
// 重命名完成或者取消重命名之后隐藏重命名弹窗
handleRenameDone = (folderName) => {
const { renameModalData, currentFolder } = this.state;
// 名称未修改不发送请求
if (folderName === renameModalData.folderName) {
this.setState({ renameModalData: {} });
return;
}
// 判断是否有同名文件
this.handleGetSameNameFiles(folderName).then((res) => {
if (res) {
message.warning('此目录下已存在同名文件');
return;
}
axios.Apollo('public/apollo/renameFolder', {
id: renameModalData.id,
name: folderName
}).then(() => {
message.success('重命名成功');
this.setState({ renameModalData: {} });
this.props.onRefresh({ parentId: currentFolder.id || null });
})
});
}
// 获取同名文件
handleGetSameNameFiles = async (folderName) => {
const { currentRootDisk, folderPathList } = this.props;
const currentFolder = folderPathList[folderPathList.length - 1];
const { instId } = window.currentUserInstInfo;
const params = {
name: folderName,
disk: currentRootDisk.disk,
parentId: currentFolder.id,
folderType: 'FOLDER',
instId: instId || LS.get('instId')
}
const res = await axios.Apollo('public/apollo/sameNameFile', params);
const { result } = res;
return !!result || result && Object.keys(result).length;
}
// 显示移动文件弹窗
handleShowSelectFileModal = (file) => {
this.setState({
currentFile: file,
showSelectFileModal: true
});
}
getBlob = (url) => {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.responseType = 'blob'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
}
}
xhr.send()
})
}
saveAs = (blob, filename) => {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename)
} else {
const link = document.createElement('a')
const body = document.querySelector('body')
// 创建对象url
link.href = window.URL.createObjectURL(blob)
link.download = filename
body.appendChild(link)
link.click()
body.removeChild(link)
// 通过调用 URL.createObjectURL() 创建的 URL 对象
window.URL.revokeObjectURL(link.href)
}
}
// 下载文件
handleDownload = (folder) => {
this.getBlob(folder.ossUrl).then((blob) => {
this.saveAs(blob, folder.folderName)
})
}
handleChooseFile = async () => {
// 判断是否欠费,旗舰版用户不需要校验余额
const { instId } = window.currentUserInstInfo;
const ultimateRes = await axios.Business('public/inst/checkInstProduct', {
instId: instId || LS.get("instId"),
productCodeList: ['ULTIMATESELL', 'PIP_TO_ULTIMATE', 'HIGH_TO_ULTIMATE']
});
const { balance } = this.props;
if (balance <= 0 && !ultimateRes.result) {
this.handleShowNoticeModal();
return;
}
const dom = document.querySelector('#detailFileInput');
dom.click();
}
// 准备上传
handleUpload = (event) => {
const fileList = event.target.files;
// 判断文件的大小是否超出了限制
const nonCompliantFileList = [];
const _fileList = [...fileList];
_fileList.map((file, index) => {
let { size, type, name } = file;
if (!type) {
type = getFileTypeByName(name);
}
if (type.indexOf('image') > -1 && size > 50 * DEFAULT_SIZE_UNIT) {
nonCompliantFileList.push(file);
_fileList.splice(index, 1);
}
if (type.indexOf('audio') > -1 && size > 50 * DEFAULT_SIZE_UNIT) {
nonCompliantFileList.push(file);
_fileList.splice(index, 1);
}
if (type.indexOf('video') > -1 && size > 2000 * DEFAULT_SIZE_UNIT) {
nonCompliantFileList.push(file);
_fileList.splice(index, 1);
}
if (localFileType.indexOf(type) > -1 && size > 100 * DEFAULT_SIZE_UNIT) {
nonCompliantFileList.push(file);
_fileList.splice(index, 1);
}
file.key = count++;
});
// 不符合规则的文件列表
if (nonCompliantFileList.length > 0) {
this.setState({
nonCompliantFileList,
showNonCompliantFileModal: true,
})
} else {
this.handleShowUploadModal(_fileList);
}
// 清空文件,防止第二次无法上传同一个文件
const dom = document.querySelector('#detailFileInput');
dom.value = '';
}
// 显示上传进度弹窗
handleShowUploadModal = (fileList) => {
// 保存当前路径
const { folderPathList } = this.props;
if (fileList.length) {
this.setState({
showUploadModal: true,
localFileList: fileList,
uploadFolderPath: folderPathList[folderPathList.length - 1]
});
}
}
// 上传成功
handleUploadDone = (file, resourceId) => {
this.props.onCreate(file, resourceId);
}
// 取消上传
handleHiddenUploadModal = () => {
this.setState({
showUploadModal: false,
localFileList: []
});
}
// 余额欠费提示弹窗
handleShowNoticeModal = () => {
const { balance } = this.props;
Modal.info({
title: '无法继续操作',
content: '直播服务已升级,请联系运营老师。',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>
})
}
// 排序
handleChangeTable = (pagination, filters, sorter) => {
const { columnKey, order } = sorter;
let sort = null;
if (columnKey === 'folderName' && order === 'ascend') { sort = 'NAME_ASC'; }
if (columnKey === 'folderName' && order === 'descend') { sort = 'NAME_DESC'; }
if (columnKey === 'folderSize' && order === 'ascend') { sort = 'SIZE_ASC'; }
if (columnKey === 'folderSize' && order === 'descend') { sort = 'SIZE_DESC'; }
if (columnKey === 'updated' && order === 'ascend') { sort = 'UPDATE_ASC'; }
if (columnKey === 'updated' && order === 'descend') { sort = 'UPDATE_DESC'; }
const { currentFolder } = this.state;
this.props.onRefresh({
sort,
parentId: currentFolder.id || null
});
}
renderMenu = (record) => {
const { currentRootDisk } = this.props;
// 是否有编辑权限
const hasManagementAuthority = currentRootDisk.uploadPower;
const { folderType } = record;
const menu = (
<Menu>
{
folderType === 'FILE' &&
<Menu.Item key="download">
<span onClick={() => { this.handleDownload(record) }}>下载</span>
</Menu.Item>
}
{
hasManagementAuthority &&
[
<Menu.Item key="move">
<span onClick={() => this.handleShowSelectFileModal(record)}>移动</span>
</Menu.Item>,
<Menu.Item key="rename">
<span onClick={() => this.handleRename(record)}>重命名</span>
</Menu.Item>,
<Menu.Item key="delete">
<span onClick={() => this.handleDeleteFolder(record)}>删除</span>
</Menu.Item>
]
}
</Menu>
);
return menu;
}
render() {
const {
currentFolder, showScanFileModal, currentFile, renameModalData,
showSelectFileModal, showUploadModal, localFileList, currentFolderName
} = this.state;
const {
selectedFileIds, folderList, showResultPage, folderPathList,
currentRootDisk, balance, query, totalCount
} = this.props;
// 是否有编辑权限
const hasManagementAuthority = currentRootDisk.uploadPower;
// 是否已经欠费
const hasOwnedFee = balance <= 0;
return (
<div className="file-list">
{
!_.isEmpty(folderList) ?
[<Table
key="table"
rowKey={(record) => record.id}
columns={this.parseColumns()}
dataSource={folderList}
rowSelection={
hasManagementAuthority ? {
selectedRowKeys: selectedFileIds,
onChange: this.props.onChangeRow
} : null
}
pagination={false}
onChange={this.handleChangeTable}
/>,
<div className="box-footer" key="pagination">
<PageControl
current={query.current - 1}
pageSize={query.size}
total={totalCount}
showSizeChanger={true}
toPage={(page) => this.props.onChangePage('current', page + 1)}
onShowSizeChange={(current, size) => this.props.onChangePage('size', size)}
/>
</div>] :
<DefaultIcon
type='student'
title={
!showResultPage ?
<div className="desc">
<input
multiple
type="file"
style={{ display: 'none' }}
id="detailFileInput"
accept=".ppt,.pptx,.doc,.docx,.pdf,.jpg,.jpeg,.png,.mp3,.mp4,.xlsx,.xls"
onChange={(e) => this.handleUpload(e)}
/>
{
hasManagementAuthority ?
<div>你还没有上传文件,点击
<Tooltip title="支持文件类型:ppt、word、excel、pdf、jpg、mp3、mp4">
<span
className="upload-btn"
onClick={this.handleChooseFile}
>上传文件</span>
</Tooltip>
按钮
</div> :
<span>这个文件夹是空的</span>
}
</div> :
<div className="desc">搜索无结果</div>
}
/>
}
<CreateFolderModal
title="重命名"
folderName={renameModalData.folderName}
isOpen={renameModalData.visible}
onClose={() => { this.setState({ renameModalData: {} })}}
onOk={this.handleRenameDone}
/>
<UploadProgressModal
isOpen={showUploadModal}
currentFolder={currentFolder}
fileList={localFileList}
onUpload={this.handleUploadDone}
onCancel={this.handleHiddenUploadModal}
/>
<SelectPrepareFileModal
multiple={true}
isOpen={showSelectFileModal}
currentRootDisk={currentRootDisk}
onClose={() => {
this.setState({ showSelectFileModal: false });
}}
onMove={(targetFolder) => {
this.setState({ showSelectFileModal: false });
this.props.onMove(targetFolder, [currentFile.id]);
}}
/>
{ this.state.scanFileModal }
{ this.state.chargeModal }
</div>
)
}
}
export default FolderList;
\ No newline at end of file
......@@ -13,7 +13,6 @@ import { Spin, message } from 'antd';
import User from '@/common/js/user';
import OperateArea from './OperateArea';
import FolderList from './FolderList';
import { DISK_MAP, suffixMap } from "@/common/constants/academic/lessonEnum";
......@@ -301,27 +300,6 @@ class FolderManage extends React.Component {
onChangeFolderPath={this.handleChangeFolderPath}
onRefresh={this.handleFetchFolderList}
/>
{/* 文件夹列表 */}
<FolderList
query={query}
totalCount={totalCount}
balance={balance}
showResultPage={showResultPage}
currentRootDisk={currentRootDisk}
hasManagementAuthority={hasManagementAuthority}
folderList={folderList}
folderPathList={folderPathList}
selectedFileIds={selectedFileIds}
onChangeRow={this.handleChangeRow}
onChangeFolderPath={this.handleChangeFolderPath}
onMove={this.handleMove}
onUpload={this.handleUploadDone}
onChangePage={this.handleChangePage}
onRefresh={this.handleFetchFolderList}
/>
</div>
</Spin>
......
......@@ -21,9 +21,9 @@ import UploadProgressModal from '@/bu-components/UploadProgressModal';
import SelectPrepareFileModal from '@/bu-components/SelectPrepareFileModal';
import CopyFileModal from '@/bu-components/CopyFileModal';
import ManagingMembersModal from '@/bu-components/ManagingMembersModal';
import PreviewFileModal from '../modal/PreviewFileModal'
import PreviewFileModal from '@/bu-components/PreviewFileModal';
import {YZ_APPId,YZ_PREVIEW_URL,OFFICE_PREVIEW_URL} from '@/domains/basic-domain/constants';
import BaseService from "@/domains/basic-domain/baseService";
import ScanFileModal from '../modal/ScanFileModal';
import CreateFolderModal from '../modal/CreateFolderModal';
import User from '@/common/js/user';
......@@ -121,40 +121,6 @@ class FolderList extends React.Component {
break;
}
}
getYoZoSign = (data,type,folderName)=>{
return new Promise((resolve) => {
let uploadParams;
if(type==="UPLOAD"){
uploadParams ={
fileUrl:data,
instId:window.currentUserInstInfo.instId,
yoZoTypeEnum:'UPLOAD'
}
}else{
uploadParams ={
fileVersionId:data,
instId:window.currentUserInstInfo.instId,
yoZoTypeEnum:'VIEW',
htmlTitle:folderName
}
}
Service.Apollo('public/apollo/getYoZoSign', uploadParams).then(res => {
const { result = [] } = res;
resolve(result)
});
})
}
saveYoZoFileVersionId = (fileVersionId,folderId)=>{
const params ={
fileVersionId,
folderId,
instId: window.currentUserInstInfo.instId,
}
Service.Apollo('public/apollo/saveYoZoFileVersionId', params).then(res => {
});
}
// 预览文件
handleScanFile = async (folder) => {
const { folderFormat, folderSize, ossUrl,rights,fileVersionId,id,folderName} = folder;
......@@ -177,24 +143,46 @@ class FolderList extends React.Component {
showPreviewModal:true,
previewStatus:'UPLOAD'
},async ()=>{
const uploadSign = await that.getYoZoSign(ossUrl,"UPLOAD");
axios.post(`https://dmc.yozocloud.cn/api/file/http?fileUrl=${ossUrl}&appId=${appId}&sign=${uploadSign}`)
.then(async function (response){
that.saveYoZoFileVersionId(response.data.data.fileVersionId,id);
const uploadParams ={
fileUrl:ossUrl,
instId:User.getStoreId(),
yoZoTypeEnum:'UPLOAD'
}
const uploadSign = await BaseService.getYoZoSign(uploadParams);
BaseService.yoZoUpload(ossUrl,YZ_APPId,uploadSign).then(async function (response){
const saveParams ={
fileVersionId:response.data.data.fileVersionId,
folderId:id,
instId:User.getStoreId(),
}
BaseService.saveYoZoFileVersionId(saveParams);
const { previewing } = that.state;
if(previewing){
const previewSign = await that.getYoZoSign(response.data.data.fileVersionId,"VIEW",folderName);
const url = `https://eic.yozocloud.cn/api/view/file?fileVersionId=${response.data.data.fileVersionId}&appId=${appId}&sign=${previewSign}&htmlTitle=${folderName}`
const previewParams ={
fileVersionId:response.data.data.fileVersionId,
instId:User.getStoreId(),
yoZoTypeEnum:'VIEW',
htmlTitle:folderName
}
const previewSign = await BaseService.getYoZoSign(previewParams);
const url = `${YZ_PREVIEW_URL}?fileVersionId=${response.data.data.fileVersionId}&appId=${YZ_APPId}&sign=${previewSign}&htmlTitle=${folderName}`
that.setState({
previewStatus:'UPLOAD_SUCCESS',
url
})
}
})
})
}else{
const previewSign = await that.getYoZoSign(fileVersionId,"VIEW",folderName);
const url = `http://eic.yozocloud.cn/api/view/file?fileVersionId=${fileVersionId}&appId=${appId}&sign=${previewSign}&htmlTitle=${folderName}`
const previewParams ={
fileVersionId,
instId:User.getStoreId(),
yoZoTypeEnum:'VIEW',
htmlTitle:folderName
}
const previewSign = await BaseService.getYoZoSign(previewParams);
const url = `${YZ_PREVIEW_URL}?fileVersionId=${fileVersionId}&appId=${YZ_APPId}&sign=${previewSign}&htmlTitle=${folderName}`
const a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('href', url);
......@@ -234,7 +222,6 @@ class FolderList extends React.Component {
Modal.confirm({
title: '抱歉,不能在线预览',
content: '由于文件较大,不支持在线预览,请下载后再查看',
// icon: <Icon type="question-circle" theme="filled" style={{ color: '#FF8534' }}></Icon>,
okText: "下载",
onOk: () => {
const a = document.createElement('a');
......@@ -244,21 +231,7 @@ class FolderList extends React.Component {
});
break;
}
// if (folderFormat === 'EXCEL') {
// Modal.confirm({
// title: '抱歉,不能在线预览',
// content: ' 该文件类型不支持在线预览,请下载后再查看',
// // icon: <Icon type="question-circle" theme="filled" style={{ color: '#FF8534' }}></Icon>,
// okText: "下载",
// onOk: () => {
// const a = document.createElement('a');
// a.href = ossUrl;
// a.click();
// }
// });
// break;
// }
const prefixUrl = "https://view.officeapps.live.com/op/view.aspx?src=";
const prefixUrl = `${OFFICE_PREVIEW_URL}?src=`;
const scanUrl = `${prefixUrl}${encodeURIComponent(ossUrl)}`
window.open(scanUrl, "_blank");
break;
......
......@@ -32,28 +32,11 @@ function Login(props) {
*/
useEffect(() => {
const enterpriseId = getParameterByName('enterpriseId');
const userId = getParameterByName('userId');
const from = getParameterByName('from');
const storeId = getParameterByName('storeId');
if (storeId) {
User.setCustomerStoreId(storeId);
}
if (from === 'customer' && enterpriseId && userId) {
if (!user.getToken() || enterpriseId !== user.getEnterpriseId() || userId !== User.getUserId()) {
getWXWorkLoginNoCheck(enterpriseId, userId);
} else {
window.RCHistory.push({
pathname: `/switch-route`,
});
}
} else {
User.removeUserId();
User.removeToken();
User.removeEnterpriseId();
}
storage.set("expiration_tip_login",true)
}, [])
}, []);
function getWXWorkLoginNoCheck(enterpriseId, userId) {
const params = {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
......
@import '../../core/variables.less';
@top-height: 0px;
@menu-bakg: #FFF;
@active-color: #2966FF;
@menu-bakg: #fff;
@active-color: #2966ff;
.left-container {
position: absolute;
z-index: 10;
......@@ -24,12 +24,12 @@
margin: 15px 0 15px 8px;
}
}
.menu-type-icon{
.menu-type-icon {
margin: 8px 14px 0px 4px;
cursor: pointer;
.icon{
font-size:14px;
color:#5E606A;
.icon {
font-size: 14px;
color: #5e606a;
}
}
}
......@@ -37,7 +37,10 @@
.ant-menu {
padding-left: 0 !important;
color: #333;
background: #FFF !important;
background: #fff !important;
.ant-menu-title-content {
margin-left: 0 !important;
}
}
.left {
-webkit-user-select: none;
......@@ -60,65 +63,76 @@
display: none;
}
.icon {
margin-right: 20px
margin-right: 20px;
}
.icon-img-box {
// display: flex;
display: inline-block;
width: 40px;
height: 40px;
.icon-img {
margin-left: 12px;
}
margin-right: 0 !important;
}
.icon-img{
width:18px;
height:18px;
margin-right:6px;
.icon-img-title {
margin-left: 0 !important;
}
.icon-img {
width: 18px;
height: 18px;
}
.listType {
width: 5px;
height: 5px;
background: #9A9DA7;
background: #9a9da7;
border-radius: 50%;
top: 18px;
left: 38px;
position: absolute;
}
.ant-menu-item{
padding-left: 13px !important;
.ant-menu-item {
padding-left: 0 !important;
padding-right: 0px;
margin: 6px 8px;
width: calc(100% - 15px);
&:hover{
background: #F3F6FA;
&:hover {
background: #f3f6fa;
border-radius: 2px;
color:#333;
color: #333;
}
}
.ant-menu-item-selected{
background-color:@active-color;
color:#FFF;
border-radius:2px;
&:hover{
color:#FFF;
.ant-menu-item-selected {
background-color: @active-color;
color: #fff;
border-radius: 2px;
&:hover {
color: #fff;
}
}
.ant-menu-submenu{
.ant-menu-submenu-title{
margin:6px 8px;
padding-left:13px !important;
.ant-menu-submenu {
.ant-menu-submenu-title {
margin: 6px 8px;
padding-left: 0 !important;
}
.ant-menu-item{
padding-left:46px !important;
.ant-menu-item {
padding-left: 46px !important;
}
}
.ant-menu-submenu-selected{
color:@active-color;
.ant-menu-submenu-selected {
color: @active-color;
.ant-menu-item-selected{
color:#FFF;
.ant-menu-item-selected {
color: #fff;
.listType {
background: @active-color;
}
}
}
.ant-menu-submenu-arrow{
right:22px;
color:#5E606A;
.ant-menu-submenu-arrow {
right: 22px;
color: #5e606a;
}
}
.version-info {
......@@ -240,12 +254,12 @@
&.left-container-vertical {
width: 56px;
.menu-type-icon{
margin:4px 0 0px 22px;
.menu-type-icon {
margin: 4px 0 0px 22px;
}
.left {
.ant-menu-submenu-arrow{
display:none !important;
.ant-menu-submenu-arrow {
display: none !important;
}
}
}
......@@ -261,15 +275,17 @@
}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background:@active-color !important;
background: @active-color !important;
}
.ant-menu.ant-menu-dark, .ant-menu-dark .ant-menu-sub, .ant-menu.ant-menu-dark .ant-menu-sub{
.ant-menu.ant-menu-dark,
.ant-menu-dark .ant-menu-sub,
.ant-menu.ant-menu-dark .ant-menu-sub {
background: @menu-bakg !important;
}
.ant-menu-submenu-popup{
left:67px !important;
.ant-menu-submenu-popup {
left: 67px !important;
}
.ant-menu-submenu-popup>.ant-menu {
.ant-menu-submenu-popup > .ant-menu {
background: @menu-bakg !important;
color: #333;
width: 132px;
......@@ -277,18 +293,18 @@
li {
width: calc(100% - 16px);
padding-left: 20px;
margin:12px 8px !important;
&:hover{
background: #F3F6FA;
margin: 12px 8px !important;
&:hover {
background: #f3f6fa;
border-radius: 2px;
color:#333 !important;
color: #333 !important;
}
}
.ant-menu-item-selected {
background: @active-color;
color: #fff;
&:hover{
&:hover {
color: #fff !important;
}
}
......
......@@ -113,12 +113,13 @@ function VersionPanel(props: any) {
function Aside(props: any) {
const { menuType, handleMenuType } = props
const { menuType, handleMenuType } = props;
const ctx: any = useContext(XMContext);
const [selectKey, setSelectKey] = useState();
const [selectKeyParent, setSelectKeyParent] = useState();
const [openKeys, setOpenKeys] = useState(['']);
const [topLogoUrl, setTopLogoUrl] = useState("")
const [collapsed, setCollapsed] = useState(false)
const [topLogoUrl, setTopLogoUrl] = useState('');
const [collapsed, setCollapsed] = useState(false);
const rootSubmenuKeys = _.pluck(menuList, 'groupCode');
useEffect(() => {
......@@ -126,44 +127,46 @@ function Aside(props: any) {
menuList.map((item: any, index: any) => {
if (link.indexOf(item.link) !== -1) {
setSelectKey(item.groupCode);
setOpenKeys([])
setSelectKeyParent(item.groupCode);
setOpenKeys([]);
} else if (item.children) {
item.children.map((_item: any, _index: any) => {
if (link.indexOf(_item.link) !== -1) {
setSelectKeyParent(item.groupCode);
setSelectKey(_item.groupCode + index + _index);
}
})
});
}
return item;
})
}, [props.location.pathname])
});
}, [props.location.pathname]);
useEffect(() => {
getTopLeftLogo()
}, [])
getTopLeftLogo();
}, []);
function getTopLeftLogo() {
if (User.getToken()) {
StoreService.getStoreDetail({ storeId: User.getStoreId() })
.then(res => {
StoreService.getStoreDetail({ storeId: User.getStoreId() }).then((res) => {
if (_.isEmpty(res.result.logo)) {
setTopLogoUrl("https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png")
setTopLogoUrl('https://image.xiaomaiketang.com/xm/fe4NCjr7XF.png');
} else {
setTopLogoUrl(res.result.logo)
setTopLogoUrl(res.result.logo);
}
})
});
}
}
function toggleMenu(item: any) {
window.RCHistory.push(item.link)
function toggleMenu(item: any, selectKey: any) {
console.log('item', item, selectKey);
setSelectKeyParent(selectKey);
window.RCHistory.push(item.link);
if (!menuType) {
setOpenKeys([])
setOpenKeys([]);
}
}
function onOpenChange(key: Key[]) {
if (typeof key === "string") {
if (typeof key === 'string') {
if (openKeys.includes(key)) {
setOpenKeys([]);
} else {
......@@ -174,9 +177,9 @@ function Aside(props: any) {
function onOpenChangeForHover(key: Key[]) {
if (menuType) {
return
return;
}
if (typeof key === "string") {
if (typeof key === 'string') {
if (openKeys.includes(key)) {
setOpenKeys([]);
} else {
......@@ -187,94 +190,95 @@ function Aside(props: any) {
function handleMenu() {
handleMenuType();
setCollapsed(!collapsed)
setCollapsed(!collapsed);
}
const openKeysConstrol = useMemo(() => {
return {
[menuType ? 'openKeys' : 'defaultOpenKeys']: openKeys
[menuType ? 'openKeys' : 'defaultOpenKeys']: openKeys,
};
}, [menuType, openKeys])
}, [menuType, openKeys]);
return (
<div
id="left-container"
className={
menuType
? "left-container"
: "left-container left-container-vertical"
}
>
<div className="top-ctrl">
<div className="topLogo">
{menuType && <img src={topLogoUrl} alt="" className="img1"></img>}
</div>
<div className="menu-type-icon" onClick={handleMenu}>
{menuType ? (
<span
className="icon iconfont"
>
&#xe83d;
</span>
) : (
<span
className="icon iconfont"
>
&#xe615;
</span>
)}
<div id='left-container' className={menuType ? 'left-container' : 'left-container left-container-vertical'}>
<div className='top-ctrl'>
<div className='topLogo'>{menuType && <img src={topLogoUrl} alt='' className='img1'></img>}</div>
<div className='menu-type-icon' onClick={handleMenu}>
{menuType ? <span className='icon iconfont'>&#xe83d;</span> : <span className='icon iconfont'>&#xe615;</span>}
</div>
</div>
<div className="left">
<div className="nav">
<div className='left'>
<div className='nav'>
<Menu
{...openKeysConstrol}
style={menuType ? { minHeight: "100%", background: '#0E1935' } : { minHeight: "100%", background: '#0E1935', width: "56px" }}
style={menuType ? { minHeight: '100%', background: '#0E1935' } : { minHeight: '100%', background: '#0E1935', width: '56px' }}
selectedKeys={selectKey}
onOpenChange={onOpenChange}
inlineCollapsed={collapsed}
mode={menuType ? "inline" : "vertical"}
>
{
menuList.map((item: any, index: any) => {
mode={menuType ? 'inline' : 'vertical'}>
{menuList.map((item: any, index: any) => {
if (ctx.xmState.storeUserPermissionList.indexOf(item.groupCode) === -1) {
return null;
}
if (item.children) {
return <SubMenu
return (
<SubMenu
key={item.groupCode}
style={{ marginTop: 0 }}
icon={<img src={item.img} className="icon-img"></img>}
title={menuType ? <span>{item.groupName}</span> : ""}
onTitleClick={() => onOpenChange(item.groupCode)}
>
{
item.children.map((_item: any, _index: any) => {
icon={
!menuType && item.groupCode === selectKeyParent ? (
<div className='icon-img-box' style={{ backgroundColor: '#2966FF', width: '40px', height: '40px' }}>
<img src={item.selectImg} className='icon-img' />
</div>
) : (
<div className='icon-img-box' style={{ backgroundColor: '#fff', width: '40px', height: '40px', display: 'inline-block' }}>
<img src={item.img} className='icon-img' />
</div>
)
}
title={menuType ? <span className='icon-img-title'>{item.groupName}</span> : ''}
onTitleClick={() => onOpenChange(item.groupCode)}>
{item.children.map((_item: any, _index: any) => {
if (ctx.xmState.storeUserPermissionList.indexOf(_item.groupCode) === -1) {
return null;
}
return <Menu.Item onClick={() => { toggleMenu(_item) }} style={{ marginTop: 0 }} key={_item.groupCode + index + _index} >
<span className="name">{_item.groupName}</span>
return (
<Menu.Item
onClick={() => {
toggleMenu(_item, item.groupCode);
}}
style={{ marginTop: 0 }}
key={_item.groupCode + index + _index}>
<span className='name'>{_item.groupName}</span>
</Menu.Item>
})
}
);
})}
</SubMenu>
);
} else {
return <Menu.Item
onClick={() => { toggleMenu(item) }}
return (
<Menu.Item
onClick={() => {
toggleMenu(item, item.groupCode);
}}
key={item.groupCode}
icon={<img src={selectKey === item.groupCode ? item.selectImg : item.img} className="icon-img"></img>}
>
{menuType ? item.groupName : ""}
icon={
!menuType && item.groupCode === selectKeyParent ? (
<div className='icon-img-box' style={{ backgroundColor: '#2966FF' }}>
<img src={item.selectImg} className='icon-img' />
</div>
) : (
<div className='icon-img-box'>
<img src={selectKey === item.groupCode ? item.selectImg : item.img} className='icon-img' />
</div>
)
}>
{menuType ? <span className='icon-img-title'>{item.groupName}</span> : ''}
</Menu.Item>
);
}
})
}
})}
</Menu>
</div>
{
......
import React, { useState, useRef, useEffect, useContext } from 'react'
import Service from "@/common/js/service";
import { PageControl } from "@/components";
import React, { useState, useRef, useEffect, useContext } from 'react';
import Service from '@/common/js/service';
import { PageControl } from '@/components';
import { Input, Select, Tooltip, Button, Table } from 'antd';
import User from "@/common/js/user";
import User from '@/common/js/user';
import moment from 'moment';
import './userData.less'
import './userData.less';
const { Search } = Input;
const { Option } = Select;
interface sortType {
type: "ascend" | "descend" | null | undefined
type: 'ascend' | 'descend' | null | undefined;
}
function ExamData(props: any) {
const sortStatus: sortType = {
type: undefined
}
type: undefined,
};
const examDataInit: any = {};
const queryInit: any = { current: 1, size: 10, order: 'SORT_ASC' };
......@@ -28,165 +27,152 @@ function ExamData(props: any) {
const [allData, setAllData] = useState(0);
const [order, setOrder] = useState(sortStatus.type);
const questionTypeList = {
SINGLE_CHOICE: "单选题",
MULTI_CHOICE: "多选题",
JUDGE: "判断题",
GAP_FILLING: "填空题",
INDEFINITE_CHOICE: "不定项选择题",
SINGLE_CHOICE: '单选题',
MULTI_CHOICE: '多选题',
JUDGE: '判断题',
GAP_FILLING: '填空题',
INDEFINITE_CHOICE: '不定项选择题',
};
const userTypeEnum = {
WORK_WE_CHAT: '企业微信',
WE_CHAT: '微信'
}
WE_CHAT: '微信',
};
const userExamStateEnum = {
EXAM: '进行中',
LACK_EXAM: '缺考',
FINISH_EXAM: '已考试'
}
FINISH_EXAM: '已考试',
};
const orderEnum = {
currentAccuracy: {
ascend: 'ACCURACY_ASC',
descend: 'ACCURACY_DESC'
descend: 'ACCURACY_DESC',
},
}
};
const queryRef = useRef({});
useEffect(() => {
queryExamUserData();
}, [])
}, []);
useEffect(() => {
queryRef.current = query;
queryExamUserDataList();
}, [query])
}, [query]);
function queryExamUserData() {
Service.Hades('public/hades/queryExamQuestionData', {
examId: props.examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
source: 0,
}).then((res) => {
setUserData(res.result)
})
setUserData(res.result);
});
}
function queryExamUserDataList() {
Service.Hades('public/hades/queryExamQuestionDataList', {
...query,
examId: props.examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
source: 0,
}).then((res) => {
setList(res.result.records);
setTotal(parseInt(res.result.total))
setTotal(parseInt(res.result.total));
if (!allData) {
setAllData(parseInt(res.result.total))
setAllData(parseInt(res.result.total));
}
})
});
}
const columns = [
{
title: "序号",
dataIndex: "sort",
title: '序号',
dataIndex: 'sort',
width: 60,
render: (text: any) => <span>{text + 1}</span>,
render: (text: any, record: any, index: any) => <span>{index + 1}</span>,
},
{
title: "题目",
dataIndex: "questionStem",
title: '题目',
dataIndex: 'questionStem',
ellipsis: true,
width: 350,
render: (val: any) => {
var handleVal = val;
handleVal = handleVal.replace(/<(?!img|input).*?>/g, "");
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, "_、");
handleVal = handleVal.replace(/\&nbsp\;/gi, " ");
handleVal = handleVal.replace(/<(?!img|input).*?>/g, '');
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, '_、');
handleVal = handleVal.replace(/\&nbsp\;/gi, ' ');
return (
<Tooltip
overlayClassName="aid-tool-list"
title={
<div style={{ maxWidth: 700, width: "auto" }}>{handleVal}</div>
}
placement="topLeft"
overlayStyle={{ maxWidth: 700 }}
>
overlayClassName='aid-tool-list'
title={<div style={{ maxWidth: 700, width: 'auto' }}>{handleVal}</div>}
placement='topLeft'
overlayStyle={{ maxWidth: 700 }}>
{handleVal}
</Tooltip>
);
},
},
{
title: "题型",
dataIndex: "questionType",
title: '题型',
dataIndex: 'questionType',
render: (text: any) => <span>{(questionTypeList as any)[text]}</span>,
filters: Object.keys(questionTypeList).map((key) => {
return {
text: (questionTypeList as any)[key],
value: key
}
value: key,
};
}),
},
{
title: "本次正确率",
dataIndex: "currentAccuracy",
title: '本次正确率',
dataIndex: 'currentAccuracy',
sorter: true,
sortOrder: field === "currentAccuracy" ? order : sortStatus.type,
render: (text: any) => <span>{parseInt(text * 100 as any)}%</span>,
sortOrder: field === 'currentAccuracy' ? order : sortStatus.type,
render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>,
},
{
title: <div>历史正确率 <Tooltip
overlayClassName="tool-list"
title='包含本次考试正确率'
placement="top"
overlayStyle={{ maxWidth: 700 }}
> <span style={{ color: 'rgba(191, 191, 191, 1)' }} className="icon iconfont">&#xe61d;</span>
title: (
<div>
历史正确率{' '}
<Tooltip overlayClassName='tool-list' title='包含本次考试正确率' placement='top' overlayStyle={{ maxWidth: 700 }}>
{' '}
<span style={{ color: 'rgba(191, 191, 191, 1)' }} className='icon iconfont'>
&#xe61d;
</span>
</Tooltip>
</div>,
dataIndex: "totalAccuracy",
render: (text: any) => <span>{parseInt(text * 100 as any)}%</span>,
</div>
),
dataIndex: 'totalAccuracy',
render: (text: any) => <span>{parseInt((text * 100) as any)}%</span>,
},
];
function onChange(pagination: any, filters: any, sorter: any, extra: any) {
console.log(filters, sorter);
setfield(sorter.field);
setOrder(sorter.order)
console.log(sorter.field, sorter.order, (orderEnum as any)[sorter.field])
setOrder(sorter.order);
console.log(sorter.field, sorter.order, (orderEnum as any)[sorter.field]);
let _query: any = { ...queryRef.current };
console.log(filters.questionType)
console.log(filters.questionType);
if (filters.questionType) {
console.log(233232)
console.log(233232);
_query.questionType = filters.questionType;
_query.current = 1;
} else {
delete _query.questionType
delete _query.questionType;
}
_query.order = (orderEnum as any)[sorter.field][sorter.order]
setQuery(_query)
_query.order = (orderEnum as any)[sorter.field][sorter.order];
setQuery(_query);
}
function download() {
Service.Hades('public/hades/exportExamData', {
// ...query,
......@@ -194,103 +180,102 @@ function ExamData(props: any) {
exportDataType: 'EXAM_QUESTION_DATA',
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
source: 0,
}).then((res) => {
const dom = (document as any).getElementById("load-play-back-excel")
const dom = (document as any).getElementById('load-play-back-excel');
dom.setAttribute('href', res.result);
dom.click();
})
});
}
return <div className="rr">
<a
download
id="load-play-back-excel"
style={{ position: "absolute", left: "-10000px" }}
></a>
<div className="dataPanal">
{
!!examData.singleChoiceCnt && <div className="item">
<div className="num">{Math.round((examData.singleChoiceAccuracy || 0) * 100)}%</div>
<div className="percent">正确率</div>
<div className="subTitle"><div className="type"><span className="icon iconfont">&#xe7fa;</span>单选题 <span>(共{examData.singleChoiceCnt}题)</span></div></div>
return (
<div className='rr'>
<a download id='load-play-back-excel' style={{ position: 'absolute', left: '-10000px' }}></a>
<div className='dataPanal'>
{!!examData.singleChoiceCnt && (
<div className='item'>
<div className='num'>{Math.round((examData.singleChoiceAccuracy || 0) * 100)}%</div>
<div className='percent'>正确率</div>
<div className='subTitle'>
<div className='type'>
<span className='icon iconfont'>&#xe7fa;</span>单选题 <span>(共{examData.singleChoiceCnt}题)</span>
</div>
}
{
!!examData.multiChoiceCnt && <div className="item">
<div className="num">{Math.round((examData.multiChoiceAccuracy || 0) * 100)}%</div>
<div className="percent">正确率</div>
<div className="subTitle"><div className="type"><span className="icon iconfont">&#xe7fb;</span>多选题<span>(共{examData.multiChoiceCnt}题)</span></div></div>
</div>
}
{
!!examData.judgeCnt && <div className="item">
<div className="num">{Math.round((examData.judgeAccuracy || 0) * 100)}%</div>
<div className="percent">正确率</div>
<div className="subTitle"><div className="type"><span className="icon iconfont">&#xe7fc;</span>判断题<span>(共{examData.judgeCnt}题)</span></div></div>
</div>
}
{
!!examData.gapFillingCnt && <div className="item">
<div className="num">{Math.round((examData.gapFillingAccuracy || 0) * 100)}%</div>
<div className="percent">正确率</div>
<div className="subTitle"><div className="type"><span className="icon iconfont">&#xe7fd;</span>填空题<span>(共{examData.gapFillingCnt}题)</span></div></div>
)}
{!!examData.multiChoiceCnt && (
<div className='item'>
<div className='num'>{Math.round((examData.multiChoiceAccuracy || 0) * 100)}%</div>
<div className='percent'>正确率</div>
<div className='subTitle'>
<div className='type'>
<span className='icon iconfont'>&#xe7fb;</span>多选题<span>(共{examData.multiChoiceCnt}题)</span>
</div>
}
{
!!examData.indefiniteChoiceCnt && <div className="item">
<div className="num">{Math.round((examData.indefiniteChoiceAccuracy || 0) * 100)}%</div>
<div className="percent">正确率</div>
<div className="subTitle"><div className="type"><span className="icon iconfont">&#xe7fe;</span>不定项选择题 <span>(共{examData.indefiniteChoiceCnt}题)</span></div></div>
</div>
}
</div>
{
!!allData && <Button style={{ marginBottom: 12, marginTop: 12 }} onClick={download} >导出</Button>
}
<div className="content">
<Table
bordered
size="small"
columns={columns}
dataSource={list}
onChange={onChange}
pagination={false}
>
</Table>
{total > 0 &&
)}
{!!examData.judgeCnt && (
<div className='item'>
<div className='num'>{Math.round((examData.judgeAccuracy || 0) * 100)}%</div>
<div className='percent'>正确率</div>
<div className='subTitle'>
<div className='type'>
<span className='icon iconfont'>&#xe7fc;</span>判断题<span>(共{examData.judgeCnt}题)</span>
</div>
</div>
</div>
)}
{!!examData.gapFillingCnt && (
<div className='item'>
<div className='num'>{Math.round((examData.gapFillingAccuracy || 0) * 100)}%</div>
<div className='percent'>正确率</div>
<div className='subTitle'>
<div className='type'>
<span className='icon iconfont'>&#xe7fd;</span>填空题<span>(共{examData.gapFillingCnt}题)</span>
</div>
</div>
</div>
)}
{!!examData.indefiniteChoiceCnt && (
<div className='item'>
<div className='num'>{Math.round((examData.indefiniteChoiceAccuracy || 0) * 100)}%</div>
<div className='percent'>正确率</div>
<div className='subTitle'>
<div className='type'>
<span className='icon iconfont'>&#xe7fe;</span>不定项选择题 <span>(共{examData.indefiniteChoiceCnt}题)</span>
</div>
</div>
</div>
)}
</div>
{!!allData && (
<Button style={{ marginBottom: 12, marginTop: 12 }} onClick={download}>
导出
</Button>
)}
<div className='content'>
<Table bordered size='small' columns={columns} dataSource={list} onChange={onChange} pagination={false}></Table>
{total > 0 && (
<PageControl
size="small"
size='small'
current={query.current - 1}
pageSize={query.size}
total={total}
toPage={(page: any) => {
console.log(page)
console.log(page);
let _query: any = { ...queryRef.current };
_query.current = page + 1;
setQuery(_query)
setQuery(_query);
}}
/>
}
)}
</div>
</div>
);
}
export default ExamData;
/*
* @Author: yuananting
* @Date: 2021-03-27 16:15:13
* @LastEditors: yuananting
* @LastEditTime: 2021-06-10 19:57:55
* @LastEditors: fusanqiasng
* @LastEditTime: 2021-07-01 16:30:38
* @Description: 助学工具-新建/复制/编辑试卷
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { Component } from "react";
import {
Form,
Button,
Input,
Table,
InputNumber,
ConfigProvider,
Empty,
Tooltip,
message,
Modal,
Spin,
Space,
Radio,
} from "antd";
import { PlusOutlined } from "@ant-design/icons";
import { XMTable } from "@/components";
import ShowTips from "@/components/ShowTips";
import Breadcrumbs from "@/components/Breadcrumbs";
import "./OperatePaper.less";
import SelectQuestionModal from "./modal/SelectQuestionModal";
import PaperPreviewModal from "./modal/PreviewPaperModal";
import User from "@/common/js/user";
import AidToolService from "@/domains/aid-tool-domain/AidToolService";
import Bus from "@/core/bus";
import _ from "underscore";
import { Route, withRouter } from "react-router-dom";
import React, { Component } from 'react';
import { Form, Button, Input, Table, InputNumber, ConfigProvider, Empty, Tooltip, message, Modal, Spin, Space, Radio } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { XMTable } from '@/components';
import ShowTips from '@/components/ShowTips';
import Breadcrumbs from '@/components/Breadcrumbs';
import './OperatePaper.less';
import SelectQuestionModal from './modal/SelectQuestionModal';
import PaperPreviewModal from './modal/PreviewPaperModal';
import User from '@/common/js/user';
import AidToolService from '@/domains/aid-tool-domain/AidToolService';
import Bus from '@/core/bus';
import _ from 'underscore';
import { Route, withRouter } from 'react-router-dom';
import * as paperEmpty from '../../lottie/paperEmpty/data.json';
import AddExam from "@/modules/teach-tool/examination-manager/AddExam";
import AddExam from '@/modules/teach-tool/examination-manager/AddExam';
const questionTypeEnum = {
SINGLE_CHOICE: "单选题",
MULTI_CHOICE: "多选题",
JUDGE: "判断题",
GAP_FILLING: "填空题",
INDEFINITE_CHOICE: "不定项选择题",
SINGLE_CHOICE: '单选题',
MULTI_CHOICE: '多选题',
JUDGE: '判断题',
GAP_FILLING: '填空题',
INDEFINITE_CHOICE: '不定项选择题',
};
class OperatePaper extends Component {
formRef = React.createRef();
......@@ -50,7 +36,7 @@ class OperatePaper extends Component {
super(props);
this.state = {
formData: {
categoryId: getParameterByName("categoryId"), // 分类ID
categoryId: getParameterByName('categoryId'), // 分类ID
singleChoiceCnt: 0, // 单选题数量
multiChoiceCnt: 0, // 多选题数量
judgeCnt: 0, // 判断题数量
......@@ -75,34 +61,28 @@ class OperatePaper extends Component {
paperPreviewModal: null,
quickSortModalVisible: false, // 快捷排序弹窗显隐
selectQuestionList: [],
currentOperate: "",
currentNav: "",
currentOperate: '',
currentNav: '',
currentCategoryPapers: [],
loading: false,
check: false,
sorterMethod: "addOrder",
sorterBy: [
"SINGLE_CHOICE",
"MULTI_CHOICE",
"JUDGE",
"GAP_FILLING",
"INDEFINITE_CHOICE",
],
sorterMethod: 'addOrder',
sorterBy: ['SINGLE_CHOICE', 'MULTI_CHOICE', 'JUDGE', 'GAP_FILLING', 'INDEFINITE_CHOICE'],
sorterTypeList: [
{
typeKey: "SINGLE_CHOICE",
typeKey: 'SINGLE_CHOICE',
},
{
typeKey: "MULTI_CHOICE",
typeKey: 'MULTI_CHOICE',
},
{
typeKey: "JUDGE",
typeKey: 'JUDGE',
},
{
typeKey: "GAP_FILLING",
typeKey: 'GAP_FILLING',
},
{
typeKey: "INDEFINITE_CHOICE",
typeKey: 'INDEFINITE_CHOICE',
},
],
};
......@@ -110,30 +90,26 @@ class OperatePaper extends Component {
componentDidMount() {
this.queryCurrentCategoryPapers();
switch (getParameterByName("type")) {
case "new":
this.setState({ currentOperate: "new", currentNav: "新建试卷" });
switch (getParameterByName('type')) {
case 'new':
this.setState({ currentOperate: 'new', currentNav: '新建试卷' });
break;
case "edit":
this.setState({ currentOperate: "edit", currentNav: "编辑试卷" }, () =>
this.queryPaperDetail()
);
case 'edit':
this.setState({ currentOperate: 'edit', currentNav: '编辑试卷' }, () => this.queryPaperDetail('edit'));
break;
case "copy":
this.setState({ currentOperate: "copy", currentNav: "复制试卷" }, () =>
this.queryPaperDetail()
);
case 'copy':
this.setState({ currentOperate: 'copy', currentNav: '复制试卷' }, () => this.queryPaperDetail('copy'));
break;
}
}
// 获取当前分类下的所有试卷
queryCurrentCategoryPapers = () => {
const categoryId = getParameterByName("categoryId");
const categoryId = getParameterByName('categoryId');
let params = {
current: 1,
size: 9999,
categoryId: categoryId === "null" ? null : categoryId,
categoryId: categoryId === 'null' ? null : categoryId,
paperName: null, // 试卷名称
source: 0,
tenantId: User.getStoreId(),
......@@ -146,14 +122,15 @@ class OperatePaper extends Component {
};
// 编辑/复制试卷时获取相应试卷详情
queryPaperDetail = async () => {
queryPaperDetail = async (flag) => {
this.setState({ loading: true });
const { currentOperate } = this.state;
let query = {
paperId: getParameterByName("paperId"),
paperId: getParameterByName('paperId'),
source: 0,
userId: User.getStoreUserId(),
tenantId: User.getStoreId(),
flag: flag === 'copy',
};
const res = await AidToolService.queryPaperDetail(query);
const { result } = res;
......@@ -163,9 +140,8 @@ class OperatePaper extends Component {
selectQuestionList: questionList,
formData: {
...result,
paperId: getParameterByName("paperId"),
paperName:
currentOperate === "copy" ? paperName + "(复制)" : paperName,
paperId: getParameterByName('paperId'),
paperName: currentOperate === 'copy' ? paperName + '(复制)' : paperName,
},
loading: false,
},
......@@ -188,26 +164,11 @@ class OperatePaper extends Component {
const _selectQuestionList = [...list];
// 各类型题目汇总
const singleQuestion = _.filter(
_selectQuestionList,
(item) => item.questionType === "SINGLE_CHOICE"
);
const multiQuestion = _.filter(
_selectQuestionList,
(item) => item.questionType === "MULTI_CHOICE"
);
const judgeQuestion = _.filter(
_selectQuestionList,
(item) => item.questionType === "JUDGE"
);
const gapQuestion = _.filter(
_selectQuestionList,
(item) => item.questionType === "GAP_FILLING"
);
const indefiniteQuestion = _.filter(
_selectQuestionList,
(item) => item.questionType === "INDEFINITE_CHOICE"
);
const singleQuestion = _.filter(_selectQuestionList, (item) => item.questionType === 'SINGLE_CHOICE');
const multiQuestion = _.filter(_selectQuestionList, (item) => item.questionType === 'MULTI_CHOICE');
const judgeQuestion = _.filter(_selectQuestionList, (item) => item.questionType === 'JUDGE');
const gapQuestion = _.filter(_selectQuestionList, (item) => item.questionType === 'GAP_FILLING');
const indefiniteQuestion = _.filter(_selectQuestionList, (item) => item.questionType === 'INDEFINITE_CHOICE');
// 各类型题目总分值
const singleChoiceScore = singleQuestion.reduce((prev, cur) => {
......@@ -277,9 +238,7 @@ class OperatePaper extends Component {
const selectQuestionList = [...this.state.selectQuestionList];
const item = selectQuestionList.splice(index + moveLength, 1);
selectQuestionList.splice(index, 0, item[0]);
this.setState({ selectQuestionList }, () =>
this.setFormData(this.state.selectQuestionList)
);
this.setState({ selectQuestionList }, () => this.setFormData(this.state.selectQuestionList));
};
// 移除已选题目
......@@ -287,9 +246,7 @@ class OperatePaper extends Component {
const { selectQuestionList } = this.state;
this.setState(
{
selectQuestionList: [...selectQuestionList].filter(
(item) => item.questionId !== delQuestionId
),
selectQuestionList: [...selectQuestionList].filter((item) => item.questionId !== delQuestionId),
},
() => this.setFormData(this.state.selectQuestionList)
);
......@@ -303,15 +260,12 @@ class OperatePaper extends Component {
if (result != null) {
return result;
}
if (["new", "copy"].includes(currentOperate)) {
if (['new', 'copy'].includes(currentOperate)) {
if (item.paperName === paperName) {
result = item;
}
} else if (currentOperate === "edit") {
if (
item.paperName === paperName &&
item.paperId !== getParameterByName("paperId")
) {
} else if (currentOperate === 'edit') {
if (item.paperName === paperName && item.paperId !== getParameterByName('paperId')) {
result = item;
}
}
......@@ -321,15 +275,15 @@ class OperatePaper extends Component {
validatePaperName = (paperName) => {
if (this.state.check && !paperName) {
return "请输入试卷名称";
return '请输入试卷名称';
}
if (this.checkExist(paperName)) {
return "该试卷名称已存在";
return '该试卷名称已存在';
}
if (paperName && paperName.length > 40) {
return "试卷名称最多40字";
return '试卷名称最多40字';
}
};
......@@ -337,19 +291,14 @@ class OperatePaper extends Component {
savePaper = (saveType) => {
this.setState({ check: true });
const { selectQuestionList, formData, currentOperate } = this.state;
const categoryId = getParameterByName("categoryId");
const categoryId = getParameterByName('categoryId');
const { match } = this.props;
let questionList = [];
if (
!formData.passRate ||
!formData.paperName ||
this.checkExist(formData.paperName) ||
(formData.paperName && formData.paperName.length > 40)
) {
if (!formData.passRate || !formData.paperName || this.checkExist(formData.paperName) || (formData.paperName && formData.paperName.length > 40)) {
return;
}
if (selectQuestionList.length === 0) {
return message.warning("请选择题目");
return message.warning('请选择题目');
}
selectQuestionList.forEach((item, index) => {
......@@ -370,29 +319,23 @@ class OperatePaper extends Component {
},
},
() => {
if (["new", "copy"].includes(currentOperate)) {
if (['new', 'copy'].includes(currentOperate)) {
AidToolService.createPaper(this.state.formData)
.then((res) => {
if (res.success) {
if (saveType === "saveToAddExam") {
this.setState({formData:{ ...formData, paperId: res.result}},() => {
if (saveType === 'saveToAddExam') {
this.setState({ formData: { ...formData, paperId: res.result } }, () => {
window.RCHistory.push({
pathname: `${match.url}/exam-operate-page`,
});
})
});
} else {
message.success(
currentOperate === "new" ? "新建成功" : "复制成功"
);
message.success(currentOperate === 'new' ? '新建成功' : '复制成功');
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
Bus.trigger(
"queryPaperPageList",
categoryId,
selectQuestionList.length
);
Bus.trigger("queryCategoryTree", "remain");
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
Bus.trigger('queryCategoryTree', 'remain');
}
}
})
......@@ -400,34 +343,26 @@ class OperatePaper extends Component {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
Bus.trigger(
"queryPaperPageList",
categoryId,
selectQuestionList.length
);
Bus.trigger("queryCategoryTree", "remain");
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
Bus.trigger('queryCategoryTree', 'remain');
});
} else if (currentOperate === "edit") {
} else if (currentOperate === 'edit') {
AidToolService.editPaper({
...this.state.formData,
paperId: getParameterByName("paperId"),
paperId: getParameterByName('paperId'),
})
.then((res) => {
if (res.success) {
if (saveType === "saveToAddExam") {
if (saveType === 'saveToAddExam') {
window.RCHistory.push({
pathname: `${match.url}/exam-operate-page`,
});
} else {
message.success("编辑成功");
message.success('编辑成功');
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
Bus.trigger(
"queryPaperPageList",
categoryId,
selectQuestionList.length
);
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
}
}
})
......@@ -435,11 +370,7 @@ class OperatePaper extends Component {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${categoryId}`,
});
Bus.trigger(
"queryPaperPageList",
categoryId,
selectQuestionList.length
);
Bus.trigger('queryPaperPageList', categoryId, selectQuestionList.length);
});
}
}
......@@ -451,7 +382,7 @@ class OperatePaper extends Component {
const { selectQuestionList, formData } = this.state;
const m = (
<PaperPreviewModal
previewPage="paper-operate"
previewPage='paper-operate'
paperInfo={{ ...formData, questionList: selectQuestionList }}
close={() => {
this.setState({
......@@ -466,21 +397,17 @@ class OperatePaper extends Component {
// 取消/返回
handleGoBack = () => {
Modal.confirm({
title: "确定要返回吗?",
content: "返回后,本次编辑的内容将不被保存",
okText: "确认返回",
cancelText: "留在本页",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe6f4;</span>
),
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
window.RCHistory.push({
pathname: `/paper-manage-index?categoryId=${getParameterByName(
"categoryId"
)}`,
pathname: `/paper-manage-index?categoryId=${getParameterByName('categoryId')}`,
});
Bus.trigger("queryCategoryTree", "remain");
Bus.trigger("queryPaperPageList", getParameterByName("categoryId"), 0);
Bus.trigger('queryCategoryTree', 'remain');
Bus.trigger('queryPaperPageList', getParameterByName('categoryId'), 0);
},
});
};
......@@ -490,39 +417,39 @@ class OperatePaper extends Component {
const { selectQuestionList } = this.state;
const columns = [
{
title: "序号",
dataIndex: "index",
key: "index",
width: "10%",
title: '序号',
dataIndex: 'index',
key: 'index',
width: '10%',
render: (val, record, index) => {
return <span>{index + 1}</span>;
},
},
{
title: "题型",
dataIndex: "questionType",
key: "questionType",
width: "12%",
title: '题型',
dataIndex: 'questionType',
key: 'questionType',
width: '12%',
filters: [
{
text: "单选题",
value: "SINGLE_CHOICE",
text: '单选题',
value: 'SINGLE_CHOICE',
},
{
text: "多选题",
value: "MULTI_CHOICE",
text: '多选题',
value: 'MULTI_CHOICE',
},
{
text: "判断题",
value: "JUDGE",
text: '判断题',
value: 'JUDGE',
},
{
text: "填空题",
value: "GAP_FILLING",
text: '填空题',
value: 'GAP_FILLING',
},
{
text: "不定项选择题",
value: "INDEFINITE_CHOICE",
text: '不定项选择题',
value: 'INDEFINITE_CHOICE',
},
],
filterMultiple: true,
......@@ -530,26 +457,23 @@ class OperatePaper extends Component {
render: (val) => questionTypeEnum[val],
},
{
title: "题目",
dataIndex: "questionStem",
key: "questionStem",
title: '题目',
dataIndex: 'questionStem',
key: 'questionStem',
ellipsis: {
showTitle: false,
},
render: (val) => {
var handleVal = val;
handleVal = handleVal.replace(/<(?!img|input).*?>/g, "");
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, "_、");
handleVal = handleVal.replace(/\&nbsp\;/gi, " ");
handleVal = handleVal.replace(/<(?!img|input).*?>/g, '');
handleVal = handleVal.replace(/<\s?input[^>]*>/gi, '_、');
handleVal = handleVal.replace(/\&nbsp\;/gi, ' ');
return (
<Tooltip
overlayClassName="aid-tool-list"
title={
<div style={{ maxWidth: 700, width: "auto" }}>{handleVal}</div>
}
placement="topLeft"
overlayStyle={{ maxWidth: 700 }}
>
overlayClassName='aid-tool-list'
title={<div style={{ maxWidth: 700, width: 'auto' }}>{handleVal}</div>}
placement='topLeft'
overlayStyle={{ maxWidth: 700 }}>
{handleVal}
</Tooltip>
);
......@@ -558,20 +482,17 @@ class OperatePaper extends Component {
{
title: (
<span>
分值{" "}
<Tooltip title="多选题和填空题的漏选/半对得分不能高于题目本身分值">
<span
className="icon iconfont"
style={{ color: "#BFBFBF", fontSize: 14, fontWeight: "400" }}
>
分值{' '}
<Tooltip title='多选题和填空题的漏选/半对得分不能高于题目本身分值'>
<span className='icon iconfont' style={{ color: '#BFBFBF', fontSize: 14, fontWeight: '400' }}>
&#xe7c4;
</span>
</Tooltip>
</span>
),
dataIndex: "score",
key: "score",
width: "12%",
dataIndex: 'score',
key: 'score',
width: '12%',
render: (val, record, index) => {
return (
<InputNumber
......@@ -582,11 +503,7 @@ class OperatePaper extends Component {
const _selectQuestionList = [...selectQuestionList];
this.setState(
{
selectQuestionList: _selectQuestionList.map((item) =>
item.questionId === record.questionId
? { ...item, score: value }
: item
),
selectQuestionList: _selectQuestionList.map((item) => (item.questionId === record.questionId ? { ...item, score: value } : item)),
},
() => this.setFormData(this.state.selectQuestionList)
);
......@@ -596,20 +513,14 @@ class OperatePaper extends Component {
},
},
{
dataIndex: "portionScore",
key: "portionScore",
width: "18%",
dataIndex: 'portionScore',
key: 'portionScore',
width: '18%',
render: (val, record, index) => {
return (
["MULTI_CHOICE", "GAP_FILLING", "INDEFINITE_CHOICE"].includes(
record.questionType
) && (
['MULTI_CHOICE', 'GAP_FILLING', 'INDEFINITE_CHOICE'].includes(record.questionType) && (
<div>
{record.questionType === "GAP_FILLING" ? (
<span>半对得</span>
) : (
<span>漏选得</span>
)}{" "}
{record.questionType === 'GAP_FILLING' ? <span>半对得</span> : <span>漏选得</span>}{' '}
<InputNumber
min={0}
max={record.score - 1}
......@@ -619,9 +530,7 @@ class OperatePaper extends Component {
this.setState(
{
selectQuestionList: _selectQuestionList.map((item) =>
item.questionId === record.questionId
? { ...item, portionScore: value }
: item
item.questionId === record.questionId ? { ...item, portionScore: value } : item
),
},
() => this.setFormData(this.state.selectQuestionList)
......@@ -634,43 +543,34 @@ class OperatePaper extends Component {
},
},
{
title: "操作",
dataIndex: "operate",
key: "operate",
width: "16%",
title: '操作',
dataIndex: 'operate',
key: 'operate',
width: '16%',
render: (val, record, index) => {
return (
<div className="record-operate">
<div className='record-operate'>
<div
className={
index > 0 ? "record-operate__item" : "record-operate__ban"
}
className={index > 0 ? 'record-operate__item' : 'record-operate__ban'}
onClick={() => {
this.handleMoveItem(index, -1);
}}
>
}}>
上移
</div>
<span className="record-operate__item split"> | </span>
<span className='record-operate__item split'> | </span>
<div
className={
index < selectQuestionList.length - 1
? "record-operate__item"
: "record-operate__ban"
}
className={index < selectQuestionList.length - 1 ? 'record-operate__item' : 'record-operate__ban'}
onClick={() => {
this.handleMoveItem(index, 1);
}}
>
}}>
下移
</div>
<span className="record-operate__item split"> | </span>
<span className='record-operate__item split'> | </span>
<div
className="record-operate__item"
className='record-operate__item'
onClick={() => {
this.handleDelItem(record.questionId);
}}
>
}}>
移除
</div>
</div>
......@@ -686,7 +586,7 @@ class OperatePaper extends Component {
const sorterTypeList = [...this.state.sorterTypeList];
const item = sorterTypeList.splice(index + moveLength, 1);
sorterTypeList.splice(index, 0, item[0]);
const sorterBy = _.pluck(sorterTypeList, "typeKey");
const sorterBy = _.pluck(sorterTypeList, 'typeKey');
this.setState({ sorterTypeList, sorterBy });
};
......@@ -694,13 +594,9 @@ class OperatePaper extends Component {
quickSorter = (list, sorterMethod, sorterBy) => {
this.setState({
selectQuestionList:
sorterMethod === "addOrder"
sorterMethod === 'addOrder'
? list.sort((a, b) => a.sorterIndex - b.sorterIndex)
: list.sort(
(a, b) =>
sorterBy.indexOf(a.questionTypeEnum || a.questionType) -
sorterBy.indexOf(b.questionTypeEnum || b.questionType)
),
: list.sort((a, b) => sorterBy.indexOf(a.questionTypeEnum || a.questionType) - sorterBy.indexOf(b.questionTypeEnum || b.questionType)),
});
};
......@@ -739,41 +635,39 @@ class OperatePaper extends Component {
const selectQuestionList = [...this.state.selectQuestionList];
const questionTypeEnum = {
SINGLE_CHOICE: "【单选题】",
MULTI_CHOICE: "【多选题】",
JUDGE: "【判断题】",
GAP_FILLING: "【填空题】",
INDEFINITE_CHOICE: "【不定项选择题】",
SINGLE_CHOICE: '【单选题】',
MULTI_CHOICE: '【多选题】',
JUDGE: '【判断题】',
GAP_FILLING: '【填空题】',
INDEFINITE_CHOICE: '【不定项选择题】',
};
const typeColumns = [
{
title: "题型",
dataIndex: "typeKey",
key: "typeKey",
render: (text, record, index) => <span style={{color: '#333333'}}>{questionTypeEnum[text]}</span>,
title: '题型',
dataIndex: 'typeKey',
key: 'typeKey',
render: (text, record, index) => <span style={{ color: '#333333' }}>{questionTypeEnum[text]}</span>,
},
{
title: "操作",
key: "action",
title: '操作',
key: 'action',
align: 'right',
render: (text, record, index) => (
<Space size="middle">
<Space size='middle'>
<span
style={{color: index > 0 ? '#2966FF' : '#CCCCCC', cursor: 'pointer'}}
style={{ color: index > 0 ? '#2966FF' : '#CCCCCC', cursor: 'pointer' }}
onClick={() => {
index > 0 && this.handleMoveTypeSorter(index, -1);
}}
>
}}>
上移
</span>
<span style={{color: '#BFBFBF'}}> | </span>
<span style={{ color: '#BFBFBF' }}> | </span>
<span
style={{color: index < 4 ? '#2966FF' : '#CCCCCC', cursor: 'pointer'}}
style={{ color: index < 4 ? '#2966FF' : '#CCCCCC', cursor: 'pointer' }}
onClick={() => {
index < 4 && this.handleMoveTypeSorter(index, 1);
}}
>
}}>
下移
</span>
</Space>
......@@ -782,32 +676,26 @@ class OperatePaper extends Component {
];
return (
<div>
<div className="page operate-paper-page">
<Breadcrumbs
navList={currentNav}
goBack={() => this.handleGoBack()}
/>
<div className='page operate-paper-page'>
<Breadcrumbs navList={currentNav} goBack={() => this.handleGoBack()} />
<Spin spinning={loading}>
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利" />
<div className='box'>
<div className='show-tips'>
<ShowTips message='请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企学院保有依据国家规定及平台规则进行处理的权利' />
</div>
<Form ref={this.formRef} style={{ marginTop: 24 }}>
<Form.Item
name="paperName"
label="试卷名称:"
name='paperName'
label='试卷名称:'
required
validateStatus={
this.validatePaperName(paperName) ? "error" : ""
}
help={this.validatePaperName(paperName)}
>
validateStatus={this.validatePaperName(paperName) ? 'error' : ''}
help={this.validatePaperName(paperName)}>
<Input
value={paperName}
autoComplete="off"
autoComplete='off'
maxLength={40}
style={{ width: 300 }}
placeholder="请输入试卷名称(40字以内)"
placeholder='请输入试卷名称(40字以内)'
onChange={(e) => {
this.setState({
formData: {
......@@ -820,12 +708,11 @@ class OperatePaper extends Component {
</Form.Item>
<Form.Item
name="passRate"
label="及格线:"
name='passRate'
label='及格线:'
required
validateStatus={check && !passRate ? "error" : ""}
help={check && !passRate && "请输入及格线"}
>
validateStatus={check && !passRate ? 'error' : ''}
help={check && !passRate && '请输入及格线'}>
<div>
<InputNumber
min={1}
......@@ -842,9 +729,9 @@ class OperatePaper extends Component {
() => this.setFormData(selectQuestionList)
);
}}
/>{" "}
/>{' '}
%
<span className="score-info">
<span className='score-info'>
总分({Number(totalScore) || 0})*及格线(
{Number(passRate) || 0}
%)=及格分数(
......@@ -853,42 +740,28 @@ class OperatePaper extends Component {
</div>
</Form.Item>
<Space size={8}>
<Button
className="choose-btn"
type="primary"
icon={<PlusOutlined />}
onClick={this.chooseQuestion}
>
<Button className='choose-btn' type='primary' icon={<PlusOutlined />} onClick={this.chooseQuestion}>
自选题目
</Button>
<Button
className="choose-btn"
className='choose-btn'
onClick={() => {
this.setState({
quickSortModalVisible: true,
});
}}
>
}}>
快捷排序
</Button>
</Space>
{questionCnt > 0 && (
<div
className="paper-info-tip"
style={{ margin: "0 auto 12px" }}
>
<div className='paper-info-tip' style={{ margin: '0 auto 12px' }}>
总计<span>{totalScore}</span>分,共
<span>{questionCnt}</span>
题。{" "}
{singleChoiceCnt > 0 &&
`单选题${singleChoiceCnt}题,共${singleChoiceScore}分;`}
{multiChoiceCnt > 0 &&
`多选题${multiChoiceCnt}题,共${multiChoiceScore}分;`}
题。 {singleChoiceCnt > 0 && `单选题${singleChoiceCnt}题,共${singleChoiceScore}分;`}
{multiChoiceCnt > 0 && `多选题${multiChoiceCnt}题,共${multiChoiceScore}分;`}
{judgeCnt > 0 && `判断题${judgeCnt}题,共${judgeScore}分,`}
{gapFillingCnt > 0 &&
`填空题${gapFillingCnt}题,共${gapFillingScore}分,`}
{indefiniteChoiceCnt > 0 &&
`不定项选择题${indefiniteChoiceCnt}题,共${indefiniteChoiceScore}分`}
{gapFillingCnt > 0 && `填空题${gapFillingCnt}题,共${gapFillingScore}分,`}
{indefiniteChoiceCnt > 0 && `不定项选择题${indefiniteChoiceCnt}题,共${indefiniteChoiceScore}分`}
</div>
)}
......@@ -900,18 +773,16 @@ class OperatePaper extends Component {
onChange={this.sortByQuestionType}
renderEmpty={{
image: paperEmpty,
description: <span style={{ display: 'block', paddingBottom: 24 }}>请在左上角添加题目</span>
description: <span style={{ display: 'block', paddingBottom: 24 }}>请在左上角添加题目</span>,
}}
/>
</Form>
</div>
<div className="footer">
<div className='footer'>
<Button onClick={this.handleGoBack}>取消</Button>
<Button onClick={this.previewPaper}>预览</Button>
<Button onClick={() => this.savePaper("saveToAddExam")}>
保存并组织考试
</Button>
<Button type="primary" onClick={() => this.savePaper()}>
<Button onClick={() => this.savePaper('saveToAddExam')}>保存并组织考试</Button>
<Button type='primary' onClick={() => this.savePaper()}>
保存
</Button>
</div>
......@@ -920,8 +791,8 @@ class OperatePaper extends Component {
{paperPreviewModal}
<Modal
maskClosable={false}
className="type-order-modal"
title="快捷排序"
className='type-order-modal'
title='快捷排序'
width={560}
visible={quickSortModalVisible}
onOk={() => {
......@@ -934,23 +805,21 @@ class OperatePaper extends Component {
}}
onCancel={() => {
this.setState({ quickSortModalVisible: false });
}}
>
}}>
<Radio.Group
onChange={(e) =>
this.setState({
sorterMethod: e.target.value,
})
}
value={sorterMethod}
>
<Radio value={"addOrder"}>按添加顺序排序</Radio>
<Radio value={"typeOrder"}>按题型排序</Radio>
value={sorterMethod}>
<Radio value={'addOrder'}>按添加顺序排序</Radio>
<Radio value={'typeOrder'}>按题型排序</Radio>
</Radio.Group>
{sorterMethod === "typeOrder" && (
{sorterMethod === 'typeOrder' && (
<Table
className="type-order-table"
style={{marginTop: '24px'}}
className='type-order-table'
style={{ marginTop: '24px' }}
showHeader={false}
columns={typeColumns}
dataSource={sorterTypeList}
......@@ -963,16 +832,7 @@ class OperatePaper extends Component {
<Route
path={`${match.url}/exam-operate-page`}
render={() => {
return (
<AddExam
paperInfo={formData}
type={
currentOperate === "edit"
? "editPaperToAddExam"
: "newPaperToAddExam"
}
/>
);
return <AddExam paperInfo={formData} type={currentOperate === 'edit' ? 'editPaperToAddExam' : 'newPaperToAddExam'} />;
}}
/>
</div>
......
......@@ -8,142 +8,147 @@
*/
export const menuList: any = [
{
groupName: "中心首页",
groupCode: "CloudPage",
groupName: '中心首页',
groupCode: 'CloudPage',
icon: '&#xe8a7;',
link: '/home',
img:'https://image.xiaomaiketang.com/xm/ni3BFJDT3a.png',
selectImg:'https://image.xiaomaiketang.com/xm/GRDztTAWaM.png'
img: 'https://image.xiaomaiketang.com/xm/ni3BFJDT3a.png',
selectImg: 'https://image.xiaomaiketang.com/xm/GRDztTAWaM.png',
},
{
groupName: "课程管理",
groupCode: "CloudCourse",
groupName: '课程管理',
groupCode: 'CloudCourse',
icon: '&#xe8a5;',
img:'https://image.xiaomaiketang.com/xm/jBGrGjM7HQ.png',
img: 'https://image.xiaomaiketang.com/xm/jBGrGjM7HQ.png',
selectImg: 'https://image.xiaomaiketang.com/xm/TTBGBpf3BJ.png',
children: [
{
groupName: "直播课",
groupCode: "CourseLiveClass",
link: '/live-course'
groupName: '直播课',
groupCode: 'CourseLiveClass',
link: '/live-course',
},
{
groupName: "视频课",
groupCode: "CourseVideoClass",
link: '/video-course'
groupName: '视频课',
groupCode: 'CourseVideoClass',
link: '/video-course',
},
{
groupName: "图文课",
groupCode: "GraphicLesson",
link: '/graphics-course'
groupName: '图文课',
groupCode: 'GraphicLesson',
link: '/graphics-course',
},
{
groupName: "线下课",
groupCode: "OfflineClass",
link: '/offline-course'
groupName: '线下课',
groupCode: 'OfflineClass',
link: '/offline-course',
},
]
],
},
{
groupName: "培训管理",
groupCode: "TrainManage",
groupName: '培训管理',
groupCode: 'TrainManage',
icon: '&#xe8a6;',
img:'https://image.xiaomaiketang.com/xm/Yy6pZ6G6kS.png',
img: 'https://image.xiaomaiketang.com/xm/Yy6pZ6G6kS.png',
selectImg: 'https://image.xiaomaiketang.com/xm/Z8G6NMQhaH.png',
children: [
{
groupName: "培训计划",
groupCode: "TrainPlan",
link: '/plan'
}
groupName: '培训计划',
groupCode: 'TrainPlan',
link: '/plan',
},
],
},
{
groupName: "助学工具",
groupCode: "AidTool",
groupName: '助学工具',
groupCode: 'AidTool',
icon: '&#xe8a9;',
img:'https://image.xiaomaiketang.com/xm/xsma4hx3b3.png',
img: 'https://image.xiaomaiketang.com/xm/xsma4hx3b3.png',
selectImg: 'https://image.xiaomaiketang.com/xm/3QZkdFMCS7.png',
children: [
{
groupName: "题库",
groupCode: "QuestionBank",
link: '/question-manage-index'
groupName: '题库',
groupCode: 'QuestionBank',
link: '/question-manage-index',
},
{
groupName: "试卷",
groupCode: "ExamPaper",
link: '/paper-manage-index'
groupName: '试卷',
groupCode: 'ExamPaper',
link: '/paper-manage-index',
},
{
groupName: "考试",
groupCode: "CloudExam",
link: '/examination-manage-index'
groupName: '考试',
groupCode: 'CloudExam',
link: '/examination-manage-index',
},
]
],
},
{
groupName: "知识库",
groupCode: "CloudKnowledge",
groupName: '知识库',
groupCode: 'CloudKnowledge',
icon: '&#xe8a8;',
link: '/knowledge-base',
img:'https://image.xiaomaiketang.com/xm/8sbP5rGQWh.png',
selectImg:'https://image.xiaomaiketang.com/xm/hJKCfibC22.png'
img: 'https://image.xiaomaiketang.com/xm/8sbP5rGQWh.png',
selectImg: 'https://image.xiaomaiketang.com/xm/hJKCfibC22.png',
},
{
groupName: "资料云盘",
groupCode: "CloudDisk",
groupName: '资料云盘',
groupCode: 'CloudDisk',
icon: '&#xe8aa;',
link: '/resource-disk',
img:'https://image.xiaomaiketang.com/xm/zGKbXJPzXx.png',
selectImg:'https://image.xiaomaiketang.com/xm/5sN4MzjxYc.png',
img: 'https://image.xiaomaiketang.com/xm/zGKbXJPzXx.png',
selectImg: 'https://image.xiaomaiketang.com/xm/5sN4MzjxYc.png',
},
{
groupName: "人员管理",
groupCode: "PersonManage",
groupName: '人员管理',
groupCode: 'PersonManage',
icon: '&#xe8a4;',
img:'https://image.xiaomaiketang.com/xm/PRCnrt35y8.png',
img: 'https://image.xiaomaiketang.com/xm/PRCnrt35y8.png',
selectImg: 'https://image.xiaomaiketang.com/xm/GhkwbdpwfK.png',
children: [
{
groupName: "员工管理",
groupCode: "ShopStaff",
link: '/college-employee'
groupName: '员工管理',
groupCode: 'ShopStaff',
link: '/college-employee',
},
{
groupName: "学员管理",
groupCode: "ShopUser",
link: '/college-user'
}
]
groupName: '学员管理',
groupCode: 'ShopUser',
link: '/college-user',
},
],
},
{
groupName: "学院管理",
groupCode: "CloudShop",
groupName: '学院管理',
groupCode: 'CloudShop',
icon: '&#xe8a4;',
img:'https://image.xiaomaiketang.com/xm/Q8i5RSMKNc.png',
img: 'https://image.xiaomaiketang.com/xm/Q8i5RSMKNc.png',
selectImg: 'https://image.xiaomaiketang.com/xm/pFFF3Wcy3t.png',
children: [
{
groupName: "学院信息",
groupCode: "ShopInfo",
link: '/college-info'
groupName: '学院信息',
groupCode: 'ShopInfo',
link: '/college-info',
},
{
groupName: "学院装修",
groupCode: "ShopDecoration",
link: '/store-decoration'
groupName: '学院装修',
groupCode: 'ShopDecoration',
link: '/store-decoration',
},
{
groupName: "分类管理",
groupCode: "CourseCategory",
link: '/course-category-manage'
groupName: '分类管理',
groupCode: 'CourseCategory',
link: '/course-category-manage',
},
{
groupName: "H5学院",
groupCode: "ShopDecorationH5",
link: '/store-decoration/h5'
groupName: 'H5学院',
groupCode: 'ShopDecorationH5',
link: '/store-decoration/h5',
},
{
groupName: "网页端学院",
groupCode: "ShopDecorationWeb",
link: '/store-decoration/web'
}
]
groupName: '网页端学院',
groupCode: 'ShopDecorationWeb',
link: '/store-decoration/web',
},
],
},
]
\ No newline at end of file
];
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