Commit ee49d1c9 by wufan

Merge branch 'feature/yuananting/20210801/task-center' into 'master'

Feature/yuananting/20210801/task center

See merge request !96
parents 7928c3ee 670276f5
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-31 09:34:25 * @Date: 2020-08-31 09:34:25
* @LastEditors: Please set LastEditors * @LastEditors: yuananting
* @LastEditTime: 2021-07-20 17:21:16 * @LastEditTime: 2021-08-17 19:20:33
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
import Storage from './storage'; import Storage from './storage';
import { PREFIX, USER_PREFIX } from '@/domains/basic-domain/constants'; import { PREFIX, USER_PREFIX } from '@/domains/basic-domain/constants';
declare var window:any; declare var window: any;
class User { class User {
getExpirationTime() { getExpirationTime() {
return Storage.get(`${PREFIX}_expiration_time`) return Storage.get(`${PREFIX}_expiration_time`);
} }
getVersion() { getVersion() {
return Storage.getObj(`${PREFIX}_version`) return Storage.getObj(`${PREFIX}_version`);
} }
getStoreId(){ getStoreId() {
return window.currentStoreUserInfo.storeId || Storage.get(`${PREFIX}_storeId`) return window.currentStoreUserInfo.storeId || Storage.get(`${PREFIX}_storeId`);
} }
getEnterpriseId() { getEnterpriseId() {
...@@ -40,18 +39,22 @@ class User { ...@@ -40,18 +39,22 @@ class User {
return window.currentStoreUserInfo.storeUserId || Storage.get(`${PREFIX}_storeUserId`); return window.currentStoreUserInfo.storeUserId || Storage.get(`${PREFIX}_storeUserId`);
} }
getStoreUserName() {
return Storage.get(`${PREFIX}_storeUserName`);
}
getCustomerId() { getCustomerId() {
return Storage.get(`${PREFIX}_customerId`); return Storage.get(`${PREFIX}_customerId`);
} }
getUserId() { getUserId() {
return window.currentStoreUserInfo.userId || Storage.get(`${PREFIX}_userId`); return window.currentStoreUserInfo.userId || Storage.get(`${PREFIX}_userId`);
} }
getAvatar(){ getAvatar() {
return Storage.get(`${PREFIX}_avatar`); return Storage.get(`${PREFIX}_avatar`);
} }
getUserRole() { getUserRole() {
return Storage.get(`${PREFIX}_userRole`); return Storage.get(`${PREFIX}_userRole`);
} }
...@@ -64,16 +67,16 @@ class User { ...@@ -64,16 +67,16 @@ class User {
return Storage.get(`${PREFIX}_isAdmin`); return Storage.get(`${PREFIX}_isAdmin`);
} }
setExpirationTime(value:number) { setExpirationTime(value: number) {
return Storage.set(`${PREFIX}_expiration_time`,value) return Storage.set(`${PREFIX}_expiration_time`, value);
} }
setVersion(value:any) { setVersion(value: any) {
return Storage.setObj(`${PREFIX}_version`,value) return Storage.setObj(`${PREFIX}_version`, value);
} }
setStoreId(value:any){ setStoreId(value: any) {
return Storage.set(`${PREFIX}_storeId`,value) return Storage.set(`${PREFIX}_storeId`, value);
} }
setEnterpriseId(value: any) { setEnterpriseId(value: any) {
...@@ -92,6 +95,10 @@ class User { ...@@ -92,6 +95,10 @@ class User {
return Storage.set(`${PREFIX}_storeUserId`, value); return Storage.set(`${PREFIX}_storeUserId`, value);
} }
setStoreUserName(value: any) {
return Storage.set(`${PREFIX}_storeUserName`, value);
}
setCustomerId(value: any) { setCustomerId(value: any) {
return Storage.set(`${PREFIX}_customerId`, value); return Storage.set(`${PREFIX}_customerId`, value);
} }
......
@font-face { @font-face {
font-family: 'iconfont'; /* Project id 2223403 */ font-family: 'iconfont'; /* Project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_7261tsts1dc.woff2?t=1628475376853') format('woff2'), src: url('//at.alicdn.com/t/font_2223403_0b87tvtysw45.woff2?t=1629025918841') format('woff2'),
url('//at.alicdn.com/t/font_2223403_7261tsts1dc.woff?t=1628475376853') format('woff'), url('//at.alicdn.com/t/font_2223403_0b87tvtysw45.woff?t=1629025918841') format('woff'),
url('//at.alicdn.com/t/font_2223403_7261tsts1dc.ttf?t=1628475376853') format('truetype'); url('//at.alicdn.com/t/font_2223403_0b87tvtysw45.ttf?t=1629025918841') format('truetype');
} }
.iconfont { .iconfont {
font-family: 'iconfont' !important; font-family: 'iconfont' !important;
......
import React, { useState, useEffect } from 'react';
export default function RichText(props) {
const [text, setText] = useState('')
useEffect(() => {
console.log(props.url,'props.urlprops.url')
if (props.url) {
fetch(props.url, { method: 'GET' }).then((response) => {
return response.text();
}).then((res) => {
setText(res)
})
}
}, [props.url])
return <div className="text" style={{ wordBreak: 'break-all' }} dangerouslySetInnerHTML={{ __html: text }}></div>
}
\ No newline at end of file
...@@ -2,8 +2,6 @@ import React, { useRef, useLayoutEffect } from 'react' ...@@ -2,8 +2,6 @@ import React, { useRef, useLayoutEffect } from 'react'
export default function WWOpenDataCom({ type, openid }) { export default function WWOpenDataCom({ type, openid }) {
const ref = useRef(null) const ref = useRef(null)
useLayoutEffect(() => { useLayoutEffect(() => {
console.log('1111');
console.log(WWOpenData);
WWOpenData && WWOpenData.bind(ref.current) WWOpenData && WWOpenData.bind(ref.current)
}) })
return <ww-open-data ref={ref} type={type} openid={openid} /> return <ww-open-data ref={ref} type={type} openid={openid} />
......
...@@ -2,10 +2,14 @@ import React, { useEffect, useState } from 'react'; ...@@ -2,10 +2,14 @@ import React, { useEffect, useState } from 'react';
import { Empty, ConfigProvider, Table } from 'antd'; import { Empty, ConfigProvider, Table } from 'antd';
import Lottie from 'react-lottie'; import Lottie from 'react-lottie';
import * as nodata from '../modules/lottie/nodata/data.json'; import * as nodata from '../modules/lottie/nodata/data.json';
import college from '@/common/lottie/college';
function XMTable(props) { function XMTable(props) {
const [empty, setEmpty] = useState(props.renderEmpty || {}); const [empty, setEmpty] = useState(props.renderEmpty || {});
const [data, setData] = useState({}); const [data, setData] = useState({});
const imgType = {
college
}
useEffect(() => { useEffect(() => {
setEmpty(props.renderEmpty || {}) setEmpty(props.renderEmpty || {})
...@@ -17,7 +21,7 @@ function XMTable(props) { ...@@ -17,7 +21,7 @@ function XMTable(props) {
const defaultOptions = { const defaultOptions = {
loop: true, loop: true,
autoplay: true, autoplay: true,
animationData: empty.image || nodata, animationData: empty.image || imgType[props?.renderEmpty?.type] || nodata,
rendererSettings: { rendererSettings: {
preserveAspectRatio: 'xMidYMid slice' preserveAspectRatio: 'xMidYMid slice'
} }
...@@ -25,14 +29,14 @@ function XMTable(props) { ...@@ -25,14 +29,14 @@ function XMTable(props) {
return ( return (
<Empty <Empty
image={<div style={{ marginTop: 24 }}> image={<div style={{ marginTop: 24 }}>
<Lottie <Lottie
options={defaultOptions} options={defaultOptions}
height={150} height={150}
width={150} width={150}
isStopped={false} isStopped={false}
isPaused={false} isPaused={false}
/> />
</div>} </div>}
imageStyle={{ imageStyle={{
height: 150, height: 150,
}} }}
......
...@@ -514,6 +514,7 @@ mr0 { ...@@ -514,6 +514,7 @@ mr0 {
// 气泡 // 气泡
.ant-tooltip { .ant-tooltip {
max-width: 280px !important;
.ant-tooltip-content { .ant-tooltip-content {
.ant-tooltip-inner { .ant-tooltip-inner {
padding: 6px 12px !important; padding: 6px 12px !important;
......
...@@ -1706,22 +1706,40 @@ input:focus { ...@@ -1706,22 +1706,40 @@ input:focus {
background: transparent !important; background: transparent !important;
} }
.xm_search_item {
padding-right: 24px;
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
box-sizing: border-box;
.label{
margin-right: 8px;
flex-shrink: 0;
// height: 36px;
// line-height: 36px;
}
.search{
flex: 1;
}
.createQWCourse{ .createQWCourse{
.footer { .footer {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
height: 58px; height: 58px;
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
padding-right: 252px; padding-right: 252px;
background: #fff; background: #fff;
border-top: 1px solid #E8E8E8; border-top: 1px solid #E8E8E8;
z-index: 999; z-index: 999;
.ant-btn { .ant-btn {
margin-left: 10px; margin-left: 10px;
}
} }
} }
} }
\ No newline at end of file
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
z-index: 102; z-index: 102;
overflow: auto; overflow: auto;
margin: 0 16px; margin: 0 16px;
min-height: auto;
.box { .box {
&:first-child { &:first-child {
margin-bottom: 8px; margin-bottom: 8px;
......
/*
* @Author: yuananting
* @Date: 2021-08-06 17:35:35
* @LastEditors: yuananting
* @LastEditTime: 2021-08-16 20:52:16
* @Description: 任务中心接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from '@/common/js/service';
export function getTrainingTaskPage(params: object) {
return Service.Hades('public/hades/getTrainingTaskPage', params);
}
export function getStoreTaskNum(params: object) {
return Service.Hades('public/hades/getStoreTaskNum', params);
}
export function createTrainingExam(params: object) {
return Service.Hades('public/hades/createTrainingExam', params);
}
export function createTrainingTask(params: object) {
return Service.Hades('public/hades/createTrainingTask', params);
}
export function updateIssueStateTraining(params: object) {
return Service.Hades('public/hades/updateIssueStateTraining', params);
}
export function deleteTrainingTask(params: object) {
return Service.Hades('public/hades/deleteTrainingTask', params);
}
export function getTrainingTaskDetail(params: object) {
return Service.Hades('public/hades/getTrainingTaskDetail', params);
}
export function updateTrainingTask(params: object) {
return Service.Hades('public/hades/updateTrainingTask', params);
}
export function getTaskCustomerDetail(params: object) {
return Service.Hades('public/hades/getTaskCustomerDetail', params);
}
export function updateTrainingTaskAssign(params: object) {
return Service.Hades('public/hades/updateTrainingTaskAssign', params);
}
export function getTrainingCourseAutoCancel(params: object) {
return Service.Hades('public/hades/getTrainingCourseAutoCancel', params);
}
export function delTaskCancelContent(params: object) {
return Service.Hades('public/hades/delTaskCancelContent', params);
}
export function getStoreTaskAll(params: object) {
return Service.Hades('public/hades/getStoreTaskAll', params);
}
export function relatedCourseToTask(params: object) {
return Service.Hades('public/hades/relatedCourseToTask', params);
}
/* /*
* @Author: 陈剑宇 * @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01 * @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-08-11 22:52:04 * @LastEditTime: 2021-09-03 10:28:14
* @LastEditors: Please set LastEditors * @LastEditors: wufan
* @Description: * @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts * @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
*/ */
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-20 09:21:40 * @Date: 2020-08-20 09:21:40
* @LastEditors: Please set LastEditors * @LastEditors: wufan
* @LastEditTime: 2021-08-11 22:50:48 * @LastEditTime: 2021-09-03 10:28:17
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
......
/*
* @Author: yuananting
* @Date: 2021-08-06 17:32:41
* @LastEditors: yuananting
* @LastEditTime: 2021-08-16 20:56:36
* @Description: 任务中心-培训任务接口
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import {
getTrainingTaskPage,
getStoreTaskNum,
createTrainingExam,
createTrainingTask,
updateIssueStateTraining,
deleteTrainingTask,
getTrainingTaskDetail,
updateTrainingTask,
getTaskCustomerDetail,
updateTrainingTaskAssign,
getTrainingCourseAutoCancel,
delTaskCancelContent,
getStoreTaskAll,
relatedCourseToTask,
} from '@/data-source/taskCenter/request-apis';
export default class TaskCenterService {
// 获取培训任务列表
static getTrainingTaskPage(params: any) {
return getTrainingTaskPage(params);
}
// 获取学院任务数量
static getStoreTaskNum(params: any) {
return getStoreTaskNum(params);
}
// 培训任务创建考试
static createTrainingExam(params: any) {
return createTrainingExam(params);
}
// 企培创建培训任务
static createTrainingTask(params: any) {
return createTrainingTask(params);
}
// 发布/取消发布培训任务
static updateIssueStateTraining(params: any) {
return updateIssueStateTraining(params);
}
// 删除培训任务
static deleteTrainingTask(params: any) {
return deleteTrainingTask(params);
}
// 获取培训任务详情
static getTrainingTaskDetail(params: any) {
return getTrainingTaskDetail(params);
}
// 修改培训任务
static updateTrainingTask(params: any) {
return updateTrainingTask(params);
}
// 获取单个任务单个学员的学习进度详情
static getTaskCustomerDetail(params: any) {
return getTaskCustomerDetail(params);
}
// 修改培训任务的指派信息
static updateTrainingTaskAssign(params: any) {
return updateTrainingTaskAssign(params);
}
// 获取培训任务失效的课程
static getTrainingCourseAutoCancel(params: any) {
return getTrainingCourseAutoCancel(params);
}
// 删除失效课程
static delTaskCancelContent(params: any) {
return delTaskCancelContent(params);
}
// 获取培训任务学习基本信息
static getStoreTaskAll(params: any) {
return getStoreTaskAll(params);
}
// 课程关联培训任务
static relatedCourseToTask(params: any) {
return relatedCourseToTask(params);
}
}
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: Please set LastEditors * @LastEditors: wufan
* @LastEditTime: 2021-05-26 16:26:17 * @LastEditTime: 2021-08-15 19:12:48
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
--> -->
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_oqqm4z9s35j.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_0b87tvtysw45.css">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
/*
* @Author: wufan
* @Date: 2021-09-03 10:23:20
* @LastEditors: wufan
* @LastEditTime: 2021-09-03 10:24:30
* @Description: Description
* @@Copyrigh: © 2021 杭州杰竞科技有限公司 版权所有
*/
declare module 'jquery' declare module 'jquery'
declare module 'cropper' declare module 'cropper'
declare module 'ExamShareModal' declare module 'ExamShareModal'
declare module 'college'
declare module '@/common/lottie/college'
declare module 'routeHooks' declare module 'routeHooks'
// declare var this: any // declare var this: any
\ No newline at end of file
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: Please set LastEditors * @LastEditors: wufan
* @LastEditTime: 2021-08-11 20:25:11 * @LastEditTime: 2021-09-03 10:26:25
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
--> -->
...@@ -25,14 +25,14 @@ ...@@ -25,14 +25,14 @@
<!-- <link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/c4KiP2epBP.png" /> --> <!-- <link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/c4KiP2epBP.png" /> -->
<!-- <link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/WGWCtxiGzE.png"> --> <!-- <link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/WGWCtxiGzE.png"> -->
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_7261tsts1dc.css" /> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_7261tsts1dc.css" />
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build. It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML. Only files inside the `public` folder can be referenced from the HTML.
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</script> </script>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!-- <!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
...@@ -73,5 +73,5 @@ ...@@ -73,5 +73,5 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
</body> </body>
</html> </html>
...@@ -5,7 +5,6 @@ import './SetEmployeeModal.less' ...@@ -5,7 +5,6 @@ import './SetEmployeeModal.less'
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
const tagMap = { const tagMap = {
Cloud_Operator: '运营师',
Cloud_Lecturer: '讲师', Cloud_Lecturer: '讲师',
Cloud_Manager: '管理员', Cloud_Manager: '管理员',
} }
...@@ -105,12 +104,6 @@ export default class SetEmployeeModal extends React.Component { ...@@ -105,12 +104,6 @@ export default class SetEmployeeModal extends React.Component {
}} }}
className="mt5" className="mt5"
> >
<Radio value={"Cloud_Operator"} className="mt-4">
<span style={{ color: "#333" }}>运营师</span>
<p className="radio-tip">
仅可查看/转发培训计划内容,并查看负责的学员学习进度
</p>
</Radio>
<Radio value={"Cloud_Lecturer"} className="mt-4"> <Radio value={"Cloud_Lecturer"} className="mt-4">
<span style={{ color: "#333" }}>讲师</span> <span style={{ color: "#333" }}>讲师</span>
<p className="radio-tip"> <p className="radio-tip">
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: yuananting * @Author: yuananting
* @Date: 2021-07-05 10:47:19 * @Date: 2021-07-05 10:47:19
* @LastEditors: yuananting * @LastEditors: yuananting
* @LastEditTime: 2021-07-12 17:13:38 * @LastEditTime: 2021-08-02 17:54:13
* @Description: 描述一下咯 * @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
...@@ -49,7 +49,7 @@ class GraphicsEditor extends React.Component { ...@@ -49,7 +49,7 @@ class GraphicsEditor extends React.Component {
renderEditor() { renderEditor() {
const { editorId } = this.state; const { editorId } = this.state;
const { detail, onChange, isIntro, maxLimit, editorType } = this.props; const { detail, onChange, isIntro, maxLimit, editorType, placeholder = '请输入正文' } = this.props;
class ImageMenu extends BtnMenu { class ImageMenu extends BtnMenu {
constructor(editor) { constructor(editor) {
// data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述 // data-title属性表示当鼠标悬停在该按钮上时提示该按钮的功能简述
...@@ -89,6 +89,7 @@ class GraphicsEditor extends React.Component { ...@@ -89,6 +89,7 @@ class GraphicsEditor extends React.Component {
this.editorInt = new E(`#editor${editorId}`); this.editorInt = new E(`#editor${editorId}`);
this.editorInt.config.focus = false; this.editorInt.config.focus = false;
this.editorInt.config.showFullScreen = !isIntro; this.editorInt.config.showFullScreen = !isIntro;
this.editorInt.config.placeholder = placeholder;
this.editorInt.menus.extend('xmimage', ImageMenu); this.editorInt.menus.extend('xmimage', ImageMenu);
!isIntro && this.editorInt.menus.extend('xmvideo', VideoMenu); !isIntro && this.editorInt.menus.extend('xmvideo', VideoMenu);
this.editorInt.config.menus = isIntro this.editorInt.config.menus = isIntro
......
...@@ -2,8 +2,8 @@ import User from '@/common/js/user'; ...@@ -2,8 +2,8 @@ import User from '@/common/js/user';
import college from '@/common/lottie/college'; import college from '@/common/lottie/college';
import { PageControl, XMTable } from '@/components'; import { PageControl, XMTable } from '@/components';
import CourseService from '@/domains/course-domain/CourseService'; import CourseService from '@/domains/course-domain/CourseService';
import PlanService from '@/domains/plan-domain/planService'; import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import { Input, Modal } from 'antd'; import { Input, Modal, Tooltip } from 'antd';
import React from 'react'; import React from 'react';
import _ from 'underscore'; import _ from 'underscore';
import './RelatedPlanModal.less'; import './RelatedPlanModal.less';
...@@ -33,7 +33,7 @@ class RelatedPlanModal extends React.Component { ...@@ -33,7 +33,7 @@ class RelatedPlanModal extends React.Component {
size, size,
storeId: User.getStoreId(), storeId: User.getStoreId(),
}; };
PlanService.getStorePlanAll(params).then((res) => { TaskCenterService.getStoreTaskAll(params).then((res) => {
const { result = {} } = res; const { result = {} } = res;
const { records = [], total = 0 } = result; const { records = [], total = 0 } = result;
this.setState({ this.setState({
...@@ -65,11 +65,31 @@ class RelatedPlanModal extends React.Component { ...@@ -65,11 +65,31 @@ class RelatedPlanModal extends React.Component {
); );
}; };
renderTitle = () => {
return (
<div className='tip-title'>
<p>为了不影响学员学习,系统已对数据进行筛选</p>
<p>1、课程不能直接关联「已发布」的培训任务筛选;</p>
<p>2、一个课程不能重复出现在同一培训任务中</p>
</div>
);
};
renderTableTitle = () => {
return (
<div>
<Tooltip title={this.renderTitle()} overlayClassName='table-title-tooltip'>
培训任务<span className='icon iconfont table-title'>&#xe6f2;</span>
</Tooltip>
</div>
);
};
// 请求表头 // 请求表头
parsePlanColumns = () => { parsePlanColumns = () => {
const columns = [ const columns = [
{ {
title: '培训计划', title: this.renderTableTitle(),
key: 'planName', key: 'planName',
dataIndex: 'planName', dataIndex: 'planName',
render: (val, record) => { render: (val, record) => {
...@@ -93,38 +113,37 @@ class RelatedPlanModal extends React.Component { ...@@ -93,38 +113,37 @@ class RelatedPlanModal extends React.Component {
]; ];
return columns; return columns;
}; };
selectPlanList = (record, selected, planId) => { selectPlanList = (record, selected, taskId) => {
const { selectPlanList } = this.props; const { selectPlanList } = this.props;
let _selectPlanList = { ...selectPlanList }; let _selectPlanList = { ...selectPlanList };
if (selected) { if (selected) {
if (!_selectPlanList[planId]) { if (!_selectPlanList[taskId]) {
_selectPlanList[planId] = {}; _selectPlanList[taskId] = {};
} }
_selectPlanList[planId].taskBaseVOList = []; _selectPlanList[taskId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId; _selectPlanList[taskId].taskId = taskId;
_selectPlanList[planId].taskBaseVOList.push(record); _selectPlanList[taskId].taskBaseVOList.push({ ...record, stageId: record.taskId });
} else { } else {
if (!_selectPlanList[planId]) { if (!_selectPlanList[taskId]) {
_selectPlanList[planId] = {}; _selectPlanList[taskId] = {};
} }
_selectPlanList[planId].taskBaseVOList = []; _selectPlanList[taskId].taskBaseVOList = [];
_selectPlanList[planId].planId = planId; _selectPlanList[taskId].taskId = taskId;
} }
this.props.onChange(_selectPlanList); this.props.onChange(_selectPlanList);
// this.setState({selectPlanList:_selectPlanList});
}; };
handleSelectPlanListData(selectPlanList) { handleSelectPlanListData(selectPlanList) {
let _selectPlanList = []; let _selectPlanList = [];
for (let key in selectPlanList) { for (let key in selectPlanList) {
let item = {}; let item = {};
if (selectPlanList[key].taskBaseVOList) { if (selectPlanList[key].taskBaseVOList) {
item.planId = selectPlanList[key].planId; item.taskId = selectPlanList[key].taskId;
if (selectPlanList[key].taskBaseVOList[0]) { if (selectPlanList[key].taskBaseVOList[0]) {
item.taskId = selectPlanList[key].taskBaseVOList[0].taskId; item.stageId = selectPlanList[key].taskBaseVOList[0].stageId;
} }
} }
if (item.taskId) { if (item.stageId) {
_selectPlanList.push(item); _selectPlanList.push(item);
} }
} }
...@@ -134,10 +153,10 @@ class RelatedPlanModal extends React.Component { ...@@ -134,10 +153,10 @@ class RelatedPlanModal extends React.Component {
const { selectPlanList } = this.props; const { selectPlanList } = this.props;
const params = { const params = {
courseId: this.props.selectCourseId, courseId: this.props.selectCourseId,
relatedPlanList: this.handleSelectPlanListData(selectPlanList), relatedTaskList: this.handleSelectPlanListData(selectPlanList),
storeId: User.getStoreId(), storeId: User.getStoreId(),
}; };
CourseService.relatedCourseToPlan(params).then((res) => { TaskCenterService.relatedCourseToTask(params).then((res) => {
this.props.onConfirm(); this.props.onConfirm();
}); });
}; };
...@@ -159,7 +178,7 @@ class RelatedPlanModal extends React.Component { ...@@ -159,7 +178,7 @@ class RelatedPlanModal extends React.Component {
const { visible, selectPlanList } = this.props; const { visible, selectPlanList } = this.props;
return ( return (
<Modal <Modal
title='关联培训计划' title='关联培训任务'
onCancel={this.props.onClose} onCancel={this.props.onClose}
maskClosable={false} maskClosable={false}
visible={visible} visible={visible}
...@@ -170,7 +189,7 @@ class RelatedPlanModal extends React.Component { ...@@ -170,7 +189,7 @@ class RelatedPlanModal extends React.Component {
closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}> closeIcon={<span className='icon iconfont modal-close-icon'>&#xe6ef;</span>}>
<div className='search-container'> <div className='search-container'>
<Search <Search
placeholder='搜索培训计划名称' placeholder='搜索培训任务名称'
style={{ width: 207 }} style={{ width: 207 }}
onChange={(e) => { onChange={(e) => {
this.handleChangePlanName(e.target.value); this.handleChangePlanName(e.target.value);
...@@ -200,7 +219,7 @@ class RelatedPlanModal extends React.Component { ...@@ -200,7 +219,7 @@ class RelatedPlanModal extends React.Component {
image: college, image: college,
description: '暂无数据', description: '暂无数据',
}} }}
rowKey={(record) => record.planId} rowKey={(record) => record.taskId}
className='plan-table' className='plan-table'
dataSource={dataSource} dataSource={dataSource}
columns={this.parsePlanColumns()} columns={this.parsePlanColumns()}
...@@ -210,14 +229,12 @@ class RelatedPlanModal extends React.Component { ...@@ -210,14 +229,12 @@ class RelatedPlanModal extends React.Component {
return; return;
} }
if (_record.taskBaseVOList.length !== 0) { if (_record.taskBaseVOList.length !== 0) {
const selectPlan = selectPlanList[_record.planId]; const selectPlan = selectPlanList[_record.taskId];
let taskBaseVOList = []; let taskBaseVOList = [];
if (selectPlan) { if (selectPlan) {
taskBaseVOList = selectPlan.taskBaseVOList; taskBaseVOList = selectPlan.taskBaseVOList;
} }
console.log('taskBaseVOList', taskBaseVOList);
return ( return (
<div> <div>
<XMTable <XMTable
...@@ -232,9 +249,9 @@ class RelatedPlanModal extends React.Component { ...@@ -232,9 +249,9 @@ class RelatedPlanModal extends React.Component {
className='child-table' className='child-table'
rowSelection={{ rowSelection={{
type: 'checkbox', type: 'checkbox',
selectedRowKeys: _.pluck(taskBaseVOList, 'taskId'), selectedRowKeys: _.pluck(taskBaseVOList, 'stageId'),
onSelect: (record, selected) => { onSelect: (record, selected) => {
this.selectPlanList(record, selected, _record.planId); this.selectPlanList(record, selected, _record.taskId);
}, },
onSelectAll: (selected, _selectedRows, changeRows) => {}, onSelectAll: (selected, _selectedRows, changeRows) => {},
}} }}
......
.related-plan-modal{ .table-title-tooltip {
.search-container{ max-width: 600px!important;
margin-bottom:16px; }
.related-plan-modal {
.table-title {
margin-left: 8px;
margin-left: 5px;
cursor: pointer;
color: rgb(191, 191, 191);
font-size: 14px;
font-weight: normal;
}
.search-container {
margin-bottom: 16px;
}
.select-container {
margin-bottom: 12px;
.con {
background: #e9efff;
border-radius: 4px;
padding: 6px 16px;
display: inline-flex;
align-items: center;
justify-content: space-between;
.tip {
font-size: 14px;
color: #2966ff;
margin-right: 8px;
}
.text {
font-size: 14px;
color: #666;
margin-right: 30px;
}
.clear {
color: #2966ff;
font-size: 14px;
}
} }
.select-container{ }
margin-bottom:12px; .plan-table {
.con{ .taskName {
background: #E9EFFF; color: #666666;
border-radius: 4px; font-size: 14px;
padding:6px 16px;
display: inline-flex;
align-items: center;
justify-content: space-between;
.tip{
font-size:14px;
color:#2966FF;
margin-right:8px;
}
.text{
font-size:14px;
color:#666;
margin-right:30px;
}
.clear{
color:#2966FF;
font-size:14px;
}
}
} }
.plan-table{ .task-learn-percentage {
.taskName{ color: #666666;
color:#666666; font-size: 14px;
font-size:14px; }
} .course-info {
.task-learn-percentage{ margin-left: 57px;
color:#666666; .course-type {
font-size:14px; font-size: 11px;
color: #666666;
padding: 0px 6px;
line-height: 16px;
border: 1px solid #999999;
margin-right: 4px;
border-radius: 2px;
}
.course-name {
color: #666666;
font-size: 14px;
margin-right: 8px;
}
.tip {
font-size: 14px;
color: #ff4f4f;
margin-right: 2px;
}
.course-state {
color: #999;
font-size: 14px;
}
}
.ant-table-content {
border: 1px solid #e8e8e8;
tr {
td {
border: none;
} }
.course-info{ .child-table {
margin-left:57px; .ant-table-content {
.course-type{ border: none;
font-size:11px; thead {
color:#666666; display: none;
padding:0px 6px;
line-height: 16px;
border: 1px solid #999999;
margin-right:4px;
border-radius: 2px;
}
.course-name{
color:#666666;
font-size:14px;
margin-right:8px;
} }
.tip{ tbody tr td {
font-size:14px; border-bottom: none;
color:#FF4F4F;
margin-right:2px;
}
.course-state{
color:#999;
font-size:14px;
} }
}
} }
.ant-table-content{ }
border:1px solid #e8e8e8; .odd-row {
tr{ background: transparent;
td{ td {
border:none; background: #fff;
} }
.child-table{ & + .ant-table-expanded-row {
.ant-table-content{ background: transparent;
border:none; td {
thead{ background: #fff;
display:none; }
} }
tbody tr td{ &:hover {
border-bottom:none; & + .ant-table-expanded-row {
} background: transparent;
} td {
background: #f3f6fa !important;
}
}
.odd-row{
background:transparent;
td{
background: #FFF;
}
& + .ant-table-expanded-row{
background:transparent;
td{
background: #FFF;
}
}
&:hover{
& + .ant-table-expanded-row{
background:transparent;
td{
background: #F3f6fa !important;
}
}
}
} }
.even-row{ }
background:transparent; }
td{ }
background: #FAFAFA; .even-row {
} background: transparent;
& + .ant-table-expanded-row{ td {
background:transparent; background: #fafafa;
td{ }
background: #FAFAFA; & + .ant-table-expanded-row {
} background: transparent;
} td {
&:hover{ background: #fafafa;
& + .ant-table-expanded-row{ }
background:transparent; }
td{ &:hover {
background: #F3f6fa !important; & + .ant-table-expanded-row {
} background: transparent;
} td {
} background: #f3f6fa !important;
} }
}
} }
}
} }
} }
\ No newline at end of file }
...@@ -280,18 +280,19 @@ ...@@ -280,18 +280,19 @@
} }
.circle-tip { .circle-tip {
position: absolute; position: absolute;
left: 70%;
padding-left: 16px; padding-left: 16px;
&.unfinished { &.unfinished {
top: 152px; bottom: 2%;
left: 36%;
.spot { .spot {
background: #2966ff; background: #2966ff;
} }
} }
&.finished { &.finished {
top: 232px; bottom: 2%;
left: 18%;
.spot { .spot {
background: #ffbb54; background: #14cca7;
} }
} }
.spot { .spot {
...@@ -299,7 +300,7 @@ ...@@ -299,7 +300,7 @@
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 4px; border-radius: 4px;
top: 20px; top: 8px;
left: 0; left: 0;
} }
.number { .number {
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* @Description: * @Description:
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-12 14:49:40 * @Date: 2021-03-12 14:49:40
* @LastEditors: yuananting * @LastEditors: wufan
* @LastEditTime: 2021-07-15 14:07:42 * @LastEditTime: 2021-08-15 18:31:16
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* @Author: zangsuyun * @Author: zangsuyun
* @Date: 2021-03-13 11:48:24 * @Date: 2021-03-13 11:48:24
* @LastEditors: wufan * @LastEditors: wufan
* @LastEditTime: 2021-05-30 10:08:09 * @LastEditTime: 2021-08-15 18:31:21
* @Copyright: © 2020 杭州杰竞科技有限公司 版权所有 * @Copyright: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
......
...@@ -80,11 +80,19 @@ class BasicInfo extends React.Component { ...@@ -80,11 +80,19 @@ class BasicInfo extends React.Component {
this.props.onChange('coverId', null); this.props.onChange('coverId', null);
}, 1000); }, 1000);
}; };
handleSelectCover = (file) => { handleSelectCover = (file) => {
this.setState({
visible: true, if(file){
imageFile: file, this.setState({
}); visible: true,
imageFile: file,
});
}else{
message.warning('请选择文件!')
}
}; };
//获取resourceId //获取resourceId
getSignature = (blob, fileName) => { getSignature = (blob, fileName) => {
......
...@@ -6,66 +6,66 @@ ...@@ -6,66 +6,66 @@
* @Description: 大班直播分享弹窗 * @Description: 大班直播分享弹窗
*/ */
import React from 'react' import React from 'react';
import { Modal, Button, message } from 'antd' import { Modal, Button, message } from 'antd';
import domtoimage from 'dom-to-image' import domtoimage from 'dom-to-image';
import qrcode from '@/libs/qrcode/qrcode.js' import qrcode from '@/libs/qrcode/qrcode.js';
import User from '@/common/js/user' import User from '@/common/js/user';
import $ from 'jquery' import $ from 'jquery';
import CourseService from '@/domains/course-domain/CourseService' import CourseService from '@/domains/course-domain/CourseService';
import './SharePlanModal.less' import './SharePlanModal.less';
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png' const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
class ShareLiveModal extends React.Component { class ShareLiveModal extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props);
this.state = { this.state = {
shareUrl: '', shareUrl: '',
} };
} }
componentDidMount() { componentDidMount() {
// 获取短链接 // 获取短链接
this.handleConvertShortUrl() this.handleConvertShortUrl();
} }
handleConvertShortUrl = () => { handleConvertShortUrl = () => {
const { longUrl } = this.props.data const { longUrl } = this.props.data;
// 发请求 // 发请求
CourseService.getQrcode({ CourseService.getQrcode({
urls: [longUrl], urls: [longUrl],
}).then((res) => { }).then((res) => {
const { result = [] } = res const { result = [] } = res;
this.setState( this.setState(
{ {
shareUrl: result[0].shortUrl, shareUrl: result[0].shortUrl,
}, },
() => { () => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap') const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new qrcode({ const qrcodeNode = new qrcode({
text: this.state.shareUrl, text: this.state.shareUrl,
size: 98, size: 98,
}) });
qrcodeWrapDom.appendChild(qrcodeNode) qrcodeWrapDom.appendChild(qrcodeNode);
const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload') const qrcodeWrapDomDownload = document.querySelector('#qrcodeWrap-dowload');
const qrcodeNodeDownLoad = new qrcode({ const qrcodeNodeDownLoad = new qrcode({
text: this.state.shareUrl, text: this.state.shareUrl,
size: 196, size: 196,
}) });
qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad) qrcodeWrapDomDownload.appendChild(qrcodeNodeDownLoad);
} }
) );
}) });
} };
componentWillUnmount() { componentWillUnmount() {
// 页面销毁之前清空定时器 // 页面销毁之前清空定时器
clearTimeout(this.timer) clearTimeout(this.timer);
} }
// 下载海报 // 下载海报
...@@ -77,29 +77,29 @@ class ShareLiveModal extends React.Component { ...@@ -77,29 +77,29 @@ class ShareLiveModal extends React.Component {
}, },
() => { () => {
this.setState({ time: new Date().valueOf() }, () => { this.setState({ time: new Date().valueOf() }, () => {
let node = document.getElementById('poster-dowload') let node = document.getElementById('poster-dowload');
domtoimage.toPng(node).then((imgData) => { domtoimage.toPng(node).then((imgData) => {
const download = document.createElement('a') const download = document.createElement('a');
const { planName } = this.props.data const { planName } = this.props.data;
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click() $(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click();
}) });
}) });
} }
) );
} };
// 复制分享链接 // 复制分享链接
handleCopy = () => { handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText const textContent = document.getElementById('shareUrl').innerText;
window.copyText(textContent) window.copyText(textContent);
message.success('复制成功!') message.success('复制成功!');
} };
render() { render() {
const { data } = this.props const { data } = this.props;
const { planName, coverUrl = DEFAULT_COVER } = data const { planName, coverUrl = DEFAULT_COVER } = data;
const { shareUrl, showImg, time } = this.state const { shareUrl, showImg, time } = this.state;
return ( return (
<Modal <Modal
title={'分享培训计划'} title={'分享培训计划'}
...@@ -185,8 +185,8 @@ class ShareLiveModal extends React.Component { ...@@ -185,8 +185,8 @@ class ShareLiveModal extends React.Component {
</div> </div>
</div> </div>
</Modal> </Modal>
) );
} }
} }
export default ShareLiveModal export default ShareLiveModal;
...@@ -71,8 +71,10 @@ class FolderList extends React.Component { ...@@ -71,8 +71,10 @@ class FolderList extends React.Component {
handleSelect = (folder) => { handleSelect = (folder) => {
// 只有文件才有预览功能 // 只有文件才有预览功能
if (folder.folderType === 'FOLDER') { if (folder.folderType === 'FOLDER') {
console.log(1)
this.handleSelectFolder(folder) this.handleSelectFolder(folder)
} else { } else {
console.log(2)
this.handleScanFile(folder) this.handleScanFile(folder)
} }
} }
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2019-09-10 18:26:03 * @Date: 2019-09-10 18:26:03
* @LastEditors: Please set LastEditors * @LastEditors: wufan
* @LastEditTime: 2021-08-11 20:26:53 * @LastEditTime: 2021-09-03 10:32:44
* @Description: * @Description:
*/ */
import React, { useRef, useContext, useEffect, useState } from 'react'; import React, { useRef, useContext, useEffect, useState } from 'react';
...@@ -81,6 +81,7 @@ function Header(props) { ...@@ -81,6 +81,7 @@ function Header(props) {
setPhone(phone); setPhone(phone);
setAvatar(res.result.avatar); setAvatar(res.result.avatar);
User.setAvatar(res.result.avatar); User.setAvatar(res.result.avatar);
User.setStoreUserName(weChatAccount);
}); });
} }
...@@ -137,7 +138,7 @@ function Header(props) { ...@@ -137,7 +138,7 @@ function Header(props) {
<Tooltip title={nickName}> <Tooltip title={nickName}>
<div className='name'> <div className='name'>
{/* <span>{nickName}</span> */} {/* <span>{nickName}</span> */}
<WWOpenDataCom type="userName" openid={nickName}/> <WWOpenDataCom type='userName' openid={nickName} />
</div> </div>
</Tooltip> </Tooltip>
<span className='phone'>{phone}</span> <span className='phone'>{phone}</span>
...@@ -384,7 +385,7 @@ function Header(props) { ...@@ -384,7 +385,7 @@ function Header(props) {
/> />
<span className='name'> <span className='name'>
{/* {nickName} */} {/* {nickName} */}
<WWOpenDataCom type="userName" openid={nickName}/> <WWOpenDataCom type='userName' openid={nickName} />
</span> </span>
</div> </div>
</Dropdown> </Dropdown>
......
import React, { useEffect, useState } from 'react';
import { Tabs } from 'antd';
import { Route, withRouter } from 'react-router-dom';
import Service from '@/common/js/service';
import Breadcrumbs from "@/components/Breadcrumbs";
import UserLearningData from './UserLearningData';
import DataInfo from './components/DataInfo'
import CourseTable from './components/CourseTable';
import DataAnalysic from './components/DataAnalysic';
import ExamTable from './components/ExamTable';
import StudyTable from './components/StudyTable';
import './index.less'
const { TabPane } = Tabs;
function DataCenter(props: any) {
const { match } = props;
const { params: { taskId } } = match;
const [info, setInfo] = useState<any>({})
const [tabKey, setTabKey] = useState<any>('')
const tabList = [
{
name: '课程目录',
key: 'course',
compoment: CourseTable,
show:()=>{
return true
}
},
{
name: '学习详情',
key: 'study',
compoment: StudyTable,
show:()=>{
return info.courseNum
}
},
{
name: '考试详情',
key: 'exam',
compoment: ExamTable,
show:()=>{
return info.examNum
}
},
]
useEffect(() => {
getInfo()
}, [])
function getInfo() {
Service.Hades('/public/hades/getTrainingTaskDetail', { taskId }).then((res: any) => {
res.result.trainingStageList.map((item: any) => {
item.open = true
})
res.result.cover = res.result.courseMediaVOS.filter((item: any) => item.contentType === 'COVER')[0] || {};
res.result.intro = res.result.courseMediaVOS.filter((item: any) => item.contentType === 'INTRO')[0] || {};
setInfo(res.result)
})
}
return <div className="page train-data-center">
<Breadcrumbs
navList="培训任务详情"
goBack={() => {
props.history.goBack();
}}
/>
<div className="content">
<DataInfo info={info} />
<Tabs defaultActiveKey={'course'} onChange={() => { }}>
{
tabList.map((item: any) => {
return item.show() && <TabPane tab={item.name} key={item.key}>
{
<item.compoment info={info} taskId={taskId} />
}
</TabPane>
})
}
</Tabs>
</div>
<Route
path={`${match.url}/analysic/:id`}
render={() => {
return <DataAnalysic />;
}}
/>
<Route path={`${props.match.url}/user-learning-data/:storeCustomerId`} render={() => <UserLearningData taskId={taskId} />} />
</div>
}
export default withRouter(DataCenter)
/*
* @Author: yuananting
* @Date: 2021-08-05 10:55:49
* @LastEditors: wufan
* @LastEditTime: 2021-08-13 17:10:58
* @Description: 个人学习详情
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from 'react';
import { Tabs } from 'antd';
import Breadcrumbs from '@/components/Breadcrumbs';
import WholeData from './components/WholeData';
import { withRouter } from 'react-router-dom';
import TestLinkTaskData from './components/TestLinkTaskData';
import CustomerLearnBasicInfo from './components/CustomerLearnBasicInfo';
import User from '@/common/js/user';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import './UserLearningData.less';
const { TabPane } = Tabs;
function UserLearningData(props) {
const taskId = props.taskId;
const storeCustomerId = props.match.params.storeCustomerId.replace(/\?.+/, '');
const tabList = [
{
name: '全部',
key: 'whole',
compoment: WholeData,
},
{
name: '考试',
key: 'exam',
compoment: TestLinkTaskData, // todo
},
];
const [detail, setDetail] = useState({});
useEffect(() => {
getTaskCustomerDetail();
}, []);
function getTaskCustomerDetail() {
const params = {
storeCustomerId,
storeId: User.getStoreId(),
taskId,
};
TaskCenterService.getTaskCustomerDetail(params).then((res) => {
const { result = {} } = res;
setDetail(result);
});
}
return (
<div className='page user-learning-data'>
<Breadcrumbs
navList='学员学习数据'
goBack={() => {
props.history.goBack();
}}
/>
<CustomerLearnBasicInfo detail={detail} />
<div className='gap-line'></div>
<div className='content'>
<Tabs defaultActiveKey={'whole'} onChange={() => {}}>
{tabList.map((item) => {
return (
<TabPane tab={item.name} key={item.key}>
{<item.compoment detail={detail} taskId={taskId}/>}
</TabPane>
);
})}
</Tabs>
</div>
</div>
);
}
export default withRouter(UserLearningData);
.user-learning-data {
.header {
padding: 16px 28px;
.train-title {
font-size: 18px;
font-weight: 500;
color: #333333;
line-height: 25px;
position: relative;
&::before {
content: '';
position: absolute;
left: -8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 10px;
background: #2966ff;
}
&::after {
content: '';
position: absolute;
left: -8px;
top: 75%;
transform: translateY(-50%);
width: 4px;
height: 2px;
background: #0acca4;
}
}
.data-info {
display: flex;
color: #666666;
margin-top: 16px;
margin-bottom: 8px;
.user-name {
margin-right: 32px;
}
.complete-progress {
display: inline-flex;
margin-right: 24px;
}
img {
width: 20px;
height: 20px;
margin-right: 8px;
}
}
}
.gap-line {
height: 10px;
background-color: #f0f2f5;
}
.content {
.ant-tabs-tab {
padding: 20px 20px 14px !important;
// margin-left: 18px;
}
}
}
/*
* @Author: yuananting
* @Date: 2021-08-16 17:35:15
* @LastEditors: yuananting
* @LastEditTime: 2021-08-16 22:06:40
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from 'react';
import { withRouter } from 'react-router-dom';
import RichText from '@/components/RichText';
import ExpiredCourseList from '../../train-task/components/ExpiredCourseList';
import ENUM from '../../enum';
import './course.less';
function CourseTable(props: any) {
const taskId = props.match.params.taskId.replace(/\?.+/, '');
const IndexText = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
const [list, setList] = useState<any[]>([]);
useEffect(() => {
setList(props.info.trainingStageList || []);
}, [props.info.trainingStageList]);
return (
<div className='courseTabContent'>
<div className='tips'>
{`培训目的:`}
<RichText url={props.info?.intro?.mediaUrl} />
</div>
<div className='coursecontent'>
{list?.map((item: any, index: number) => {
return (
<div className='task'>
<div
className='title'
onClick={() => {
const _list = [...list];
_list[index].open = !_list[index].open;
setList(_list);
}}>
{item.open ? <span className='icon iconfont open'>&#xe614;</span> : <span className='icon iconfont'>&#xe614;</span>}
{`${IndexText[index]}、 `}
{item.stageName}
</div>
{item.open && (
<div className='taskItemList'>
{item.contentVOList.map((_item: any, _index: number) => {
return (
<div className={_index == item.contentVOList.length - 1 ? 'item noBorder' : 'item'}>
<div className='name'>
<img className='type-option-icon' src={ENUM.LearningContentIcon[_item.courseType || _item.contentType]} />
<span style={{ marginRight: 4 }}> {`${index + 1}.${_index + 1}`}</span>
{_item.contentName}
{_item.courseType == 'LIVE' && <span className='desc'>{ENUM.courseStateShow[_item.courseState].title}</span>}
{_item.courseType == 'VOICE' && <span className='desc'>{`(共${_item.courseChapterNum}节)`}</span>}
</div>
</div>
);
})}
</div>
)}
</div>
);
})}
</div>
<div className='expired'>
<ExpiredCourseList taskId={taskId} />
</div>
</div>
);
}
export default withRouter(CourseTable);
/*
* @Author: yuananting
* @Date: 2021-08-12 18:08:59
* @LastEditors: yuananting
* @LastEditTime: 2021-08-12 18:17:19
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Progress, Space } from 'antd';
import ENUM from '../../enum';
function CustomerLearnBasicInfo(props) {
return (
<div className='header'>
<div className='train-title'>{props.detail.taskName}</div>
<div className='data-info'>
<div className='user-name'>学员:{props.detail.storeCustomerName}</div>
<div className='complete-progress'>
任务完成率:
<div style={{ width: 120 }}>
<Progress size={120} strokeColor='#2966FF' trailColor='#EAEAEA' percent={props.detail.learnFinishPercentage} size='small' />
</div>
</div>
<Space size={24}>
<div>
<img src={ENUM.LearningContentIcon['COURSE']} />
课程:{props.detail.courseFinishNum || 0}/{props.detail.courseAllNum || 0}
</div>
<div>
<img src={ENUM.LearningContentIcon['EXAM']} />
考试:{props.detail.examFinishNum || 0}/{props.detail.examAllNum || 0}
</div>
</Space>
</div>
</div>
);
}
export default CustomerLearnBasicInfo;
import React, { useState, useRef, useEffect, useContext } from 'react'
import { Route, withRouter } from 'react-router-dom';
import Breadcrumbs from "@/components/Breadcrumbs";
import UserData from './UserData';
import ExamData from './ExamData'
import Service from "@/common/js/service";
import { Tabs } from 'antd';
import User from "@/common/js/user";
import './dataAnalysic.less'
const { TabPane } = Tabs;
function DataAnalysic(props: any) {
const examDetailInit: any = {};
const [selectKey, setSelectKey] = useState('user')
const [examDetail, setExamDetail] = useState(examDetailInit);
const { match } = props;
const examId =match.params.id;
useEffect(() => {
queryExamDetail();
}, [])
function queryExamDetail() {
Service.Hades("public/hades/queryExamDetail", {
examId:examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0
}).then((res) => {
const { result } = res
setExamDetail(result)
})
}
return <div className="page dataAnalysic">
<Breadcrumbs navList={"考试数据"} goBack={props.history.goBack} />
<div className="box">
<div className="titleBox ">
考试名称:{examDetail.examName}
</div>
</div>
<div className="box" style={{ paddingTop: 0 }}>
<Tabs activeKey={selectKey} onChange={(key: any) => {
setSelectKey(key)
}}>
<TabPane tab="考试人员数据" key="user">
<UserData examDetail ={examDetail } examId={examId} />
</TabPane>
<TabPane tab="题目数据" key="exam">
<ExamData examDetail ={examDetail } examId={examId}></ExamData>
</TabPane>
</Tabs>
</div>
</div>
}
export default withRouter(DataAnalysic);
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { withRouter } from "react-router-dom";
import { Progress } from 'antd';
import ENUM from '../../enum'
import moment from 'moment'
const defaultCover = 'https://image.xiaomaiketang.com/xm/rEAetaTEh3.png';
function DataInfo(props: any) {
return <div className="infoBox">
<div className="left">
<div className="banner">
<img src={props.info.cover?.mediaUrl || defaultCover} alt="" />
</div>
<div className="textBox">
<div className="nameBox">
<div className="name">{props.info.taskName}</div>
<div className="status" style={{ color: ENUM.trainStatus[props.info.taskState || 'UN_START'].color, background: ENUM.trainStatus[props.info.taskState || 'UN_START'].background }}>{ENUM.trainStatus[props.info.taskState || 'UN_START'].text}</div>
</div>
<div className="info">
<span> {`创建人: ${props.info.createName} `}</span>
<span style={{ marginLeft: 24 }}> {`培训时间:${moment(props.info.startTime).format('YYYY-MM-DD HH:mm')} 至 ${moment(props.info.endTime).format('YYYY-MM-DD HH:mm')}`}</span>
</div>
<div className="info">
<div className="item">{`任务数: ${props.info.contentNum}`}</div>
{
!!props.info.courseNum && <div className="item"><span className="icon"><img src="https://image.xiaomaiketang.com/xm/6C2GjSpnDp.png" alt="" /></span>{`课程:${props.info.courseNum}`}</div>
}
{/* <div className="item"><span className="icon"><img src="https://image.xiaomaiketang.com/xm/6C2GjSpnDp.png" alt="" /></span>{`作业:4`}</div> */}
{
!!props.info.examNum && <div className="item"><span className="icon"><img src="https://image.xiaomaiketang.com/xm/fCDPp2Eenc.png" alt="" /></span>{`考试:${props.info.examNum}`}</div>
}
</div>
</div>
</div>
<div className="right">
<div className="prog">
<Progress type="circle" width={85} percent={parseInt(props.info.finishCustomerNum / (props.info.cultureCustomerNum) * 100 as any) || 0} strokeWidth={10}
format={percent => <div className='wcl'>
{`${(parseInt((props.info.finishCustomerNum || 0) / (props.info.cultureCustomerNum || 0) * 100 as any)) || 0}% `}
<div>完成率</div>
</div>} />
{/* <div className='wcl'> */}
{/* {`${(parseInt(props.info.finishCustomerNum /(props.info.cultureCustomerNum)*100 as any))}% `} */}
{/* <div>完成率</div> */}
{/* </div> */}
</div>
<div className="num">
<div className="item"> {`指派人数:${props.info.cultureCustomerNum}`}</div>
<div className="item">{`学习人数:${props.info.learningCustomerNum}`}</div>
<div className="item">{`完成人数:${props.info.finishCustomerNum}`}</div>
</div>
</div>
</div>
}
export default withRouter(DataInfo)
\ No newline at end of file
import React, { useState, useRef, useEffect } from "react";
import Service from "@/common/js/service";
import { PageControl } from "@/components";
import { Input, Select, Tooltip, Button } from "antd";
import User from "@/common/js/user";
import { XMTable } from "@/components";
import college from "@/common/lottie/college.json";
import "./userData.less";
interface sortType {
type: "ascend" | "descend" | null | undefined;
}
function ExamData(props: any) {
const sortStatus: sortType = {
type: undefined,
};
const examDataInit: any = {};
const queryInit: any = { current: 1, size: 10, order: "SORT_ASC" };
const [examData, setUserData] = useState(examDataInit);
const [list, setList] = useState([]);
const [query, setQuery] = useState(queryInit);
const [total, setTotal] = useState(0);
const [field, setfield] = useState("");
const [allData, setAllData] = useState(0);
const [order, setOrder] = useState(sortStatus.type);
const questionTypeList = {
SINGLE_CHOICE: "单选题",
MULTI_CHOICE: "多选题",
JUDGE: "判断题",
GAP_FILLING: "填空题",
INDEFINITE_CHOICE: "不定项选择题",
};
const userTypeEnum = {
WORK_WE_CHAT: "企业微信",
WE_CHAT: "微信",
};
const userExamStateEnum = {
EXAM: "进行中",
LACK_EXAM: "缺考",
FINISH_EXAM: "已考试",
};
const orderEnum = {
currentAccuracy: {
ascend: "ACCURACY_ASC",
descend: "ACCURACY_DESC",
},
};
const queryRef = useRef({});
useEffect(() => {
queryExamUserData();
}, []);
useEffect(() => {
queryRef.current = query;
queryExamUserDataList();
}, [query]);
function queryExamUserData() {
Service.Hades("public/hades/queryExamQuestionData", {
examId: props.examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
setUserData(res.result);
});
}
function queryExamUserDataList() {
Service.Hades("public/hades/queryExamQuestionDataList", {
...query,
examId: props.examId,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
setList(res.result.records);
setTotal(parseInt(res.result.total));
if (!allData) {
setAllData(parseInt(res.result.total));
}
});
}
const columns = [
{
title: "序号",
dataIndex: "sort",
width: 60,
render: (text: any, record: any, index: any) => <span>{index + 1}</span>,
},
{
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, " ");
return (
<Tooltip
overlayClassName="aid-tool-list"
title={
<div style={{ maxWidth: 700, width: "auto" }}>{handleVal}</div>
}
placement="topLeft"
overlayStyle={{ maxWidth: 700 }}
>
{handleVal}
</Tooltip>
);
},
},
{
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,
};
}),
},
{
title: "本次正确率",
dataIndex: "currentAccuracy",
sorter: true,
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)",fontWeight: 400 }}
className="icon iconfont"
>
&#xe61d;
</span>
</Tooltip>
</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]);
let _query: any = { ...queryRef.current };
console.log(filters.questionType);
if (filters.questionType) {
console.log(233232);
_query.questionType = filters.questionType;
_query.current = 1;
} else {
delete _query.questionType;
}
_query.order = (orderEnum as any)[sorter.field][sorter.order];
setQuery(_query);
}
function download() {
Service.Hades("public/hades/exportExamData", {
// ...query,
examId: props.examId,
exportDataType: "EXAM_QUESTION_DATA",
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
source: 0,
}).then((res) => {
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>
</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>
</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">
<XMTable
renderEmpty={{
image: college,
description: '暂无数据'
}}
bordered
size="small"
columns={columns}
dataSource={list}
onChange={onChange}
pagination={false}
></XMTable>
{total > 0 && (
<PageControl
size="small"
current={query.current - 1}
pageSize={query.size}
total={total}
toPage={(page: any) => {
console.log(page);
let _query: any = { ...queryRef.current };
_query.current = page + 1;
setQuery(_query);
}}
/>
)}
</div>
</div>
);
}
export default ExamData;
import React, { useEffect, useState } from 'react';
import { Route, withRouter } from 'react-router-dom';
import { PageControl, XMTable } from '@/components';
import Service from '@/common/js/service';
import User from '@/common/js/user';
function ExamTable(props: any) {
const { match } = props;
console.log(match)
const [query, setQuery] = useState<any>({
current: 1, size: 10,
taskId: props.taskId,
source: 0,
tenantId: User.getStoreId(),
userId: User.getStoreUserId(),
});
const [list, setList] = useState<any[]>([]);
const [total, setTotal] = useState<any>(0);
useEffect(() => {
getList()
}, [query])
function getList() {
Service.Hades('public/hades/queryTrainingExamUserData', query).then((res: any) => {
setList(res.result.records);
setTotal(res.result.total);
})
}
const columns = [
{
title: '考试名称',
key: 'examName',
dataIndex: 'examName',
},
{
title: '完成人数',
key: 'finishCnt',
dataIndex: 'finishCnt',
},
{
title: '平均分',
key: 'averageScore',
dataIndex: 'averageScore',
render: (val: any, record: any) => {
return <div>{val || 0}</div>;
},
},
{
title: '及格人数',
key: 'passCnt',
dataIndex: 'passCnt',
render: (val: any, record: any) => {
return <div>{val || 0}</div>;
},
},
{
title: '及格率',
key: 'passPercentage',
dataIndex: 'passPercentage',
render: (val: any, record: any) => {
return <div>{val}%</div>;
},
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
render: (val: any, record: any) => {
return (
<div className='operate-area'>
<span className='operate-item' style={{ color: 'rgba(41, 102, 255, 1)', cursor: 'pointer' }} onClick={() => {
props.history.push({
pathname: `${match.url}/analysic/${record.examId}`,
});
}}>
查看数据
</span>
</div>
);
},
},
];
return <div className="study_Table">
<div style={{ marginTop: 12 }}>
<XMTable
renderEmpty={{
type: 'college',
description: '暂无数据',
}}
rowKey={(record: any) => record.storeCustomerId}
dataSource={list}
columns={columns}
pagination={false}
className='user-learning-table'
onChange={() => { }}
showSorterTooltip={false}
bordered
/>
{list.length > 0 && (
<div className='box-footer'>
<PageControl
current={query.current - 1}
pageSize={query.size}
total={total}
toPage={(page: any) => {
setQuery({ ...query, current: page + 1 })
}}
/>
</div>
)}
</div>
</div>
}
export default withRouter(ExamTable)
\ No newline at end of file
import React, { useState, useEffect } from "react";
import "./LeftStageList.less";
import ENUM from "../../enum";
function LeftStageList(props) {
const { stageExamList, activeExam, activeStage } = props;
const [stageList, setStageList] = useState(stageExamList || []);
// 展开和收起阶段下面的考试
function handleToggleExpand(stageIndex) {
let _taskList = [...stageList];
_taskList = _taskList.map((item, index) => {
if (stageIndex === index) {
item.isShowMoreCourse = !item.isShowMoreCourse;
}
return item;
});
setStageList(_taskList || []);
}
return (
<div className="left-stage-list">
{stageList.map((item, index) => {
return (
<div key={index} className="stage-list">
<div
className={`stage-item ${
item.stageId === activeStage.stageId ? "active" : ""
}`}
onClick={() => handleToggleExpand(index)}
>
<div className="icon">
{item.isShowMoreCourse ? (
<span className="icon iconfont edit-icon">&#xe677;</span>
) : (
<span className="icon iconfont edit-icon">&#xe600;</span>
)}
</div>
<div className="stage-name oneLineText">
{ENUM.IndexToSort[index + 1]}{item.stageName}
</div>
</div>
{!item.isShowMoreCourse && (
<div className="exam-list">
{item.contentVOList && item.contentVOList.map((examItem, examIdndex) => {
return (
<div
key={examIdndex}
className={`exam-item ${
examItem.contentId === activeExam.contentId ? "active" : ""
}`}
onClick={()=>{
props.changeActiveExam(examItem,item)
}}
>
<div className="exam-name">
{item.sequence}.{examItem.sequence}{' '}{examItem.contentName}
</div>
</div>
);
})}
</div>
)}
</div>
);
})}
</div>
);
}
export default LeftStageList;
.left-stage-list {
width: 229px;
.stage-list {
.stage-item {
display: flex;
width: 229px;
height: 44px;
background: rgba(255, 255, 255, 0.06);
color: #666;
font-size: 14px;
color: #666;
line-height: 44px;
margin-right: 8px;
cursor: pointer;
.iconfont {
line-height: 15px;
font-size: 10px;
margin: 0 8px;
display: inline-block;
color: #666;
}
&.active {
background: rgba(41, 102, 255, 0.06);
color: #2966ff;
}
}
.exam-list {
.exam-item {
width: 229px;
height: 44px;
background: rgba(255, 255, 255, 0.06);
width: 100%;
font-size: 14px;
color: #666666;
line-height: 44px;
padding-left: 48px;
cursor: pointer;
.exam-name {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
&.active {
background-color: RGBA(243, 246, 250, 1);
}
}
}
}
}
import React, { useState, useEffect } from "react";
import "./TestLinkTaskData.less";
import LeftStageList from "./LeftStageList";
import TestDetailPage from "@/modules/teach-tool/examination-manager/TestDetailPage";
import { Route, withRouter } from "react-router-dom";
import Service from "@/common/js/service";
import LottieIcon from "@/components/LottieIcon";
import User from '@/common/js/user';
function TestLinkTaskData(props) {
const [stageExamList, setStageExamList] = useState([]);
const [activeExam, setActiveExam] = useState({});
const [activeStage, setActiveStage] = useState({});
const [isShow, setIsShow] = useState(true);
const {taskId, detail: {storeCustomerId}} = props;
useEffect(() => {
getTrainingTaskDetail();
}, []);
function getTrainingTaskDetail() {
Service.Hades("public/hades/getTaskCustomerDetail", {
taskId: taskId,
storeId: User.getStoreId(),
storeCustomerId: storeCustomerId,
}).then((res) => {
const data = { ...res.result };
const { stageExamCustomerVOList = [] } = data;
if (!!stageExamCustomerVOList.length) {
setStageExamList(stageExamCustomerVOList);
setActiveStage(stageExamCustomerVOList[0]);
setActiveExam(stageExamCustomerVOList[0].contentVOList[0]);
const { contentId, paperId } = stageExamCustomerVOList[0].contentVOList[0];
const { match } = props;
const path = `${match.url}/test-detail/${contentId}?paperId=${paperId}&userId=${storeCustomerId}&fromTrainingTask=true`;
window.RCHistory.replace(path);
}
});
}
function changeActiveExam(_activeExam, _activeStage) {
console.log("_activeExam", _activeExam);
setActiveExam(_activeExam);
setActiveStage(_activeStage);
const { contentId, paperId } = _activeExam;
const { match } = props;
const path = `${
match.url
}/test-detail/${contentId}?paperId=${paperId}&userId=${111}`;
window.RCHistory.replace(path);
}
function toggleShowBar(diretion) {
if (diretion === "left") {
setIsShow(false);
} else {
setIsShow(true);
}
}
return (
<div>
{stageExamList.length ? (
<div className={`test-link-task-data ${!isShow ? 'hidden':''}`}>
<div className={`left-stage-list-box ${!isShow ? 'hidden':''}`}>
{isShow ? (
<div
className="left-bar"
onClick={() => {
toggleShowBar("left");
}}
></div>
) : (
<div
className="right-bar"
onClick={() => {
toggleShowBar("right");
}}
></div>
)}
<LeftStageList
stageExamList={stageExamList}
activeExam={activeExam}
changeActiveExam={changeActiveExam}
activeStage={activeStage}
/>
</div>
<div className={`right-exam-data-box ${!isShow ? 'hidden':''}`}>
{/* 未考过的考试显示暂无考试数据,否则请求接口会报错 */}
{
activeExam.learnFinishPercentage === 0 ?
<LottieIcon
title={<span className="desc">暂无考试数据</span>}
type="college"
size={150}
/> :
<Route
path={`${props.match.url}/test-detail/:testId`}
component={TestDetailPage}
/>
}
</div>
</div>
) : (
<LottieIcon
title={<span className="desc">暂无考试数据</span>}
type="college"
size={150}
/>
)}
</div>
);
}
export default withRouter(TestLinkTaskData);
.test-link-task-data {
position: relative;
height: calc(100vh - 300px);
border-right: 1px solid #cccccc;
display: flex;
.left-stage-list-box {
border-right: 1px solid #cccccc;
width: 245px;
height: 100%;
overflow-y: auto;
position: relative;
overflow-x: hidden;
&.hidden {
border-left: 1px solid #cccccc;
border-right: none;
width: 15px;
.left-stage-list {
display: none;
}
}
.left-bar {
width: 8px;
height: 68px;
background: #d6d6d6;
border-radius: 12px 0px 0px 12px;
position: absolute;
right: 0;
top: 50%;
&::before {
content: "";
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
width: 0;
height: 0;
transform: rotate(45deg);
background-color: #fff;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.2);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #fff;
}
&:hover {
width: 12px;
height: 68px;
background: #666666;
border-radius: 12px 0px 0px 12px;
}
}
.right-bar {
width: 8px;
height: 68px;
background: #d6d6d6;
border-radius: 0px 12px 12px 0px;
position: absolute;
left: 0;
top: 50%;
&::before {
content: "";
left: -190%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
width: 0;
height: 0;
transform: rotate(315deg);
background-color: #fff;
box-shadow: 0 0 6px 0 rgb(0 0 0 / 20%);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #fff;
}
&:hover {
width: 12px;
height: 68px;
background: #666666;
border-radius: 0px 12px 12px 0px;
&::before {
content: "";
left: -120%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
width: 0;
height: 0;
transform: rotate(315deg);
background-color: #fff;
box-shadow: 0 0 6px 0 rgb(0 0 0 / 20%);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #fff;
}
}
}
}
.right-exam-data-box {
width: calc(100vw - 450px);
height: 100%;
overflow-y: auto;
margin-top: -16px;
overflow-x: hidden;
@media screen and (max-width: 1400px) {
.lottie-icon {
text-align: center;
margin-top: 153px;
margin-bottom: 50px;
}
}
&.hidden {
width: calc(100vw - 200px);
}
}
}
/*
* @Author: yuananting
* @Date: 2021-08-05 11:26:25
* @LastEditors: yuananting
* @LastEditTime: 2021-08-18 18:27:06
* @Description: 个人学习详情-全部tab页
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useEffect } from 'react';
import { Collapse, Progress } from 'antd';
import './WholeData.less';
import ENUM from '../../enum';
const { Panel } = Collapse;
function WholeData(props) {
const SortConvert = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
const courseStateShow = {
UN_START: {
title: '待开播',
},
STARTING: {
title: '直播中',
},
FINISH: {
title: '回放',
},
EXPIRED: {
title: '未成功开课',
},
};
const [stageCustomerList, setStageCustomerList] = useState([]);
useEffect(() => {
setStageCustomerList(props.detail.stageCustomerVOList);
}, [props]);
// 渲染阶段信息
function renderStageInfo(item, index) {
return (
<div className='stage-info__item'>
<span className='stage-name'>
{SortConvert[index]}{item.stageName}
</span>
<span className='extra-info'>
{item.learnFinishNum || 0}/{item.learnAllNum || item.contentVOList.length}
</span>
</div>
);
}
// 渲染学习内容信息
function renderContentInfo(contentItem, contentIndex, index) {
const { contentName, contentType, courseChapterNum, courseState, learnFinishPercentage, courseType } = contentItem;
return (
<div className='content-info__item'>
<div className='basic-info'>
<img src={ENUM.LearningContentIcon[courseType || contentType]} />
<span className='content-name'>
{contentIndex + 1}.{index + 1} {contentName}
</span>
{courseType === 'LIVE' && <span className='extra-info'>{courseStateShow[courseState].title}</span>}
{courseType === 'VOICE' && <span className='extra-info'>(共{courseChapterNum || 1}小节)</span>}
</div>
<div className='percent-info'>
{learnFinishPercentage === 100 ? (
<span className='icon iconfont'>&#xe621;</span>
) : (
<Progress
width={20}
strokeWidth={12}
type='circle'
trailColor='#EAEAEA'
strokeColor={{
'0%': '#38B7F3',
'100%': '#1A80E2',
}}
percent={learnFinishPercentage || 0}
/>
)}
<span className='text'>{learnFinishPercentage === 100 ? '已完成' : `${learnFinishPercentage || 0}%`}</span>
</div>
</div>
);
}
return (
<div className='whole-data-container'>
<Collapse ghost>
{stageCustomerList?.length > 0 &&
stageCustomerList.map((item, index) => {
return (
<Panel header={renderStageInfo(item, index)} key={index}>
{item.contentVOList.map((contentItem, contentIndex) => {
return renderContentInfo(contentItem, contentIndex, index);
})}
</Panel>
);
})}
</Collapse>
</div>
);
}
export default WholeData;
.whole-data-container {
.ant-collapse-header {
padding: 15px 16px !important;
background-color: #f7f8f9;
}
.stage-info {
&__item {
width: calc(100% - 24px);
display: inline-flex;
align-items: center;
line-height: 20px;
.stage-name {
color: #333333;
}
.extra-info {
margin-left: 8px;
color: #666666;
}
}
}
.content-info {
&__item {
padding: 14px 16px;
margin-left: 40px;
border-bottom: 1px dotted #e8e8e8;
vertical-align: middle;
display: flex;
justify-content: space-between;
* {
vertical-align: middle;
}
.basic-info {
.content-name {
margin-left: 12px;
color: #333333;
}
.extra-info {
margin-left: 8px;
color: #999999;
}
img {
width: 20px;
height: 20px;
}
}
.percent-info {
width: 70px;
.ant-progress-text {
display: none;
}
.icon {
color: #2966ff;
font-size: 20px;
}
.text {
margin-left: 6px;
color: #666666;
}
}
}
}
}
.courseTabContent{
padding-bottom: 24px;
.tips{
// height: 48px;
background: #F7F8F9;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
padding: 14px 16px;
}
.coursecontent{
margin-top: 16px;
margin-bottom: 16px;
.title{
padding: 14px 16px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
background: #F7F8F9;
border-radius: 2px;
cursor: pointer;
.icon{
color: rgba(153, 153, 153, 1);
transform: scale(0.8);
display: inline-block;
transform-origin: 50% 50%;
margin-right: 4px;
&.open{
transform:rotate(180deg) scale(0.8);
}
}
}
.taskItemList{
margin-left: 64px;
.item{
padding: 16px 0px 16px 16px;
border-bottom: 1px dashed #E8E8E8;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
.name{
img{
width: 20px;
position: relative;
top: -2px;
}
}
&.noBorder{
border:none;
}
.desc{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
margin-left: 8px;
}
}
}
}
.expired{
.title{
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 22px;
}
.desc{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
margin: 12px 0;
}
.list{
.item{
display: flex;
justify-content: space-between;
background: #F7F8F9;
border-radius: 2px;
padding: 16px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
span{
margin-right: 12px;
}
.na{
color: #333333;
}
.ope{
color: rgba(41, 102, 255, 1);
cursor: pointer;
}
}
}
}
}
.dataAnalysic {
.titleBox {
position: relative;
font-size: 19px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 26px;
background: #ffffff;
&::before {
width: 4px;
height: 12px;
content: '';
background-image: linear-gradient(#2966ff 83.5%, #0acca4 16.5%);
display: inline-block;
margin-right: 8px;
}
}
.ant-tabs-content-holder {
margin-top: 8px;
}
}
.study_Table{
}
\ No newline at end of file
.dataPanal {
border-radius: 4px;
border: 1px solid #e8e8e8;
display: flex;
.item {
text-align: center;
// width: 29.9%;
position: relative;
flex: 1;
.num {
font-size: 26px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 26px;
margin-top: 12px;
}
.percent {
margin-top: 6px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 17px;
height: 20px;
margin-bottom: 18px;
}
.subTitle {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 12px;
}
.type {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
span {
color: rgba(153, 153, 153, 1);
}
.icon {
color: rgba(204, 204, 204, 1);
font-size: 16px;
margin-right: 4px;
position: relative;
top: 1px;
}
}
&:after {
content: '';
width: 0px;
height: 40px;
position: absolute;
width: 1px;
background-color: rgba(232, 232, 232, 1);
top: 40px;
right: 0px;
}
&:last-child {
&:after {
display: none;
}
}
}
.exstatus {
width: 4px;
height: 4px;
background: rgb(35, 143, 255);
display: inline-block;
border-radius: 50%;
position: relative;
top: -4px;
}
}
.answer-detail {
color: rgb(35, 143, 255);
}
.analysic-content {
.ant-table-bordered .ant-table-tbody tr {
&.analysic-content-row {
height: 50px;
}
}
}
.train-data-center{
.content{
margin: 0 16px;
}
.infoBox{
background: #FFFFFF;
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.05);
border-radius: 2px;
display: flex;
height: 141px;
justify-content: space-between;
margin: 0 0px 16px;
.left{
display: flex;
.banner{
padding: 16px;
img{
width: 194px;
height: 109px;
}
}
.textBox{
padding-top: 1px;
.nameBox{
margin-top: 12px;
margin-bottom: 34px;
display: flex;
.name{
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 25px;
}
.status{
padding: 0px 8px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #999999;
line-height: 20px;
text-align: center;
margin-left: 24px;
position: relative;
top: 2px;
height: 20px;
}
}
.info{
margin-bottom: 12px;
.icon{
position: relative;
top: -2px;
margin-right: 8px;
img{
width: 20px;
}
}
span,.item{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
display: inline-block;
}
.item{
margin-right: 24px;
}
}
}
}
.right{
display: flex;
margin-right: 32px;
.prog{
width: 85px;
margin-top: 27px;
margin-right: 24px;
position: relative;
.wcl{
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #666666;
line-height: 22px;
text-align: center;
// left: 0px;
// top: 20px;
// width: 100%;
// height: 100%;
// display: none;
// position: absolute;
// z-index: 1;
}
// &:hover{
// .wcl{
// display: block;
// }
// }
}
.num{
margin-top: 27px;
.item{
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 12px;
}
}
}
}
}
\ No newline at end of file
/*
* @Author: yuananting
* @Date: 2021-08-03 18:10:15
* @LastEditors: wufan
* @LastEditTime: 2021-08-09 18:24:21
* @Description: 描述一下咯
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
const ENUM: any = {
trainStatus: {
UN_START: {
text: "未开始",
color: "rgba(255, 178, 0, 1)",
background: "rgba(255, 178, 0, 0.1)",
},
STARTING: {
text: "进行中",
color: "rgba(41, 102, 255, 1)",
background: "rgba(41, 102, 255, 0.1)",
},
FINISH: {
text: "已结束",
color: "rgba(153, 153, 153, 1)",
background: "rgba(153, 153, 153, 0.1)",
},
},
learnState: {
UN_FINISH: {
text: "未完成",
},
FINISH: {
text: "已完成",
},
OVERDUE: {
text: "已逾期",
},
},
courseStateShow: {
UN_START: {
title: '待开播',
},
STARTING: {
title: '直播中',
},
FINISH: {
title: '回放',
},
EXPIRED: {
title: '未成功开课',
},
},
LearnState: {
UN_PLAY: {
text: '未开始',
},
UNDER_WAY: {
text: '进行中',
},
FINISH: {
text: '已完成',
},
},
LearningContentIcon: {
COURSE: 'https://image.xiaomaiketang.com/xm/6C2GjSpnDp.png',
LIVE: 'https://image.xiaomaiketang.com/xm/jyFhCtaKfi.png',
VOICE: 'https://image.xiaomaiketang.com/xm/2T2k5Tbmpy.png',
PICTURE: 'https://image.xiaomaiketang.com/xm/yzjNwGX6TY.png',
EXAM: 'https://image.xiaomaiketang.com/xm/fCDPp2Eenc.png',
HOMEWORK: 'https://image.xiaomaiketang.com/xm/hShsAzzppZ.png',
},
// index to 中文序号
IndexToSort: {
1: '一',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六',
7: '七',
8: '八',
9: '九',
10: '十',
11: '十一'
}
}
export default ENUM
\ No newline at end of file
.add-train-task {
.header-tab {
position: relative;
padding: 20px 0 12px 0;
text-align: center;
font-size: 16px;
color: #666666;
line-height: 22px;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 20px;
.tab-title {
position: relative;
cursor: pointer;
&:first-child {
margin-right: 44px;
}
.step-icon {
display: inline-block;
width: 16px;
height: 16px;
border-radius: 50%;
font-size: 12px;
text-align: center;
line-height: 14px;
margin-right: 8px;
&.default-icon {
border: 1px solid #999999;
color: #999999;
}
&.active-icon {
background-color: #2966ff;
border: 1px solid #2966ff;
color: #ffffff;
}
}
.active-text {
color: #2966ff;
}
.active-line {
position: absolute;
width: 32px;
height: 2px;
background: #2966ff;
top: 34px;
left: 16px;
z-index: 999;
}
}
&::after {
content: ' ';
height: 10px;
width: 10px;
border-width: 2px 2px 0 0;
border-color: #999999;
border-style: solid;
transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
position: absolute;
top: 50%;
left: calc(50% - 5px);
}
}
.footer {
position: fixed;
left: 196px;
bottom: 0;
height: 58px;
width: ~'calc(100% - 218px)';
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 72px;
background: #fff;
border-top: 1px solid #e8e8e8;
z-index: 9999;
.ant-btn {
margin-left: 10px;
}
}
}
/*
* @Author: yuananting
* @Date: 2021-07-28 11:25:58
* @LastEditors: yuananting
* @LastEditTime: 2021-08-18 12:36:21
* @Description: 任务中心-培训任务
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from 'react';
import TrainFilter from './components/TrainFilter';
import { Route, withRouter } from 'react-router-dom';
import TrainList from './components/TrainList';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
import DataCenter from '../data-center/Index';
import User from '@/common/js/user';
import Bus from '@/core/bus';
function TrainTaskPage(props) {
const [trainListData, setTrainListData] = useState([]); // 培训任务列表
const [query, setQuery] = useState({
current: 1,
endTime: null,
issueState: 'ALL', // 发布状态
myAssist: false, // 是否由我协同
size: 10,
sortMap: { UPDATED: 'SORT_DESC' }, // 排序(默认更新时间倒序)
startTime: null,
storeId: User.getStoreId(),
storeUserId: User.getStoreUserId(),
taskName: '',
});
const [totalCount, setTotalCount] = useState(0); // 总数
const [storeTaskNum, setStoreTaskNum] = useState({});
const { match } = props;
useEffect(() => {
getTrainingTaskPage();
}, [query]);
useEffect(() => {
getStoreTaskNum();
initPageData();
return () => {
removePageData();
};
}, []);
useEffect(() => {}, []);
const initPageData = () => {
Bus.bind('getTrainingTaskPage', (filterData) => {
getTrainingTaskPage(filterData);
});
Bus.bind('getStoreTaskNum', getStoreTaskNum);
};
const removePageData = () => {
Bus.unbind('getTrainingTaskPage', getTrainingTaskPage);
Bus.unbind('getStoreTaskNum', getStoreTaskNum);
};
// 获取计划列表
function getTrainingTaskPage(filterData) {
const _query = { ...query, ...filterData };
if (_query.issueState === 'ALL') {
delete _query.issueState;
}
TaskCenterService.getTrainingTaskPage(_query).then((res) => {
const {
result: { records = [], total },
} = res;
setTrainListData(records);
setTotalCount(total);
});
}
// 获取学院任务数量
function getStoreTaskNum() {
TaskCenterService.getStoreTaskNum({ storeId: User.getStoreId() }).then((res) => {
setStoreTaskNum(res.result);
});
}
// 搜索条件修改
function queryChange(_query) {
setQuery({ ...query, ..._query });
}
return (
<div className='page'>
<div className='content-header'>培训任务</div>
<div className='box'>
<TrainFilter onChange={queryChange} />
<TrainList trainListData={trainListData} storeTaskNum={storeTaskNum} query={query} totalCount={totalCount} onChange={queryChange} />
</div>
<Route path={`${match.url}/data/:taskId`} component={DataCenter} />
</div>
);
}
export default withRouter(TrainTaskPage);
.basic-info__form {
margin-bottom: 138px;
.cover {
display: flex;
margin-top: 24px;
&__wrap {
position: relative;
.img-box {
position: relative;
margin-top: 8px;
width: 300px;
height: 170px;
.default-tag {
border-radius: 2px;
background: #d6d6d6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #fff;
position: absolute;
top: 8px;
left: 8px;
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.opt-box {
.default-btn {
margin-left: 12px;
color: #2966ff;
cursor: pointer;
&.disabled {
color: #ccc;
cursor: not-allowed;
pointer-events: none;
}
}
.tips {
margin-top: 8px;
font-size: 14px;
color: #999;
}
}
}
}
.duration {
&__wrap {
.tips {
color: #999999;
display: inline-block;
margin-left: 16px;
}
.picker-box {
display: inline-block;
margin-left: 16px;
.ant-form-item {
margin-bottom: 0 !important;
}
}
}
}
.select-obj {
width: 600px;
max-height: 90px;
overflow-y: scroll;
padding: 12px;
border-radius: 4px;
margin-top: 10px;
border: 1px solid #e8e8e8;
color: #666666;
.obj-type-container {
display: flex;
overflow-wrap: normal;
.type-title {
width: 70px;
flex-shrink: 0;
}
}
.close-icon {
font-size: 14px;
color: #cccccc;
}
.disabled {
color: #ccc !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
}
.learning-model-tips {
margin-top: 8px;
color: #999999;
line-height: 20px;
}
.ant-form-item {
margin-bottom: 24px !important;
.ant-form-item-label > label {
width: 108px;
justify-content: flex-end;
}
&:not(:first-child) .ant-form-item-label > label {
height: 22px !important;
}
.ant-form-item-control-input {
min-height: 22px !important;
}
.ant-tag {
border: 1px solid #cccccc;
color: #666666;
margin-bottom: 10px;
font-size: 14px;
}
}
}
/*
* @Author: yuananting
* @Date: 2021-08-03 10:47:59
* @LastEditors: yuananting
* @LastEditTime: 2021-08-16 22:04:31
* @Description: 编辑培训任务-失效课程
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useEffect } from 'react';
import { List, message, Modal } from 'antd';
import './ExpiredCourseList.less';
import ENUM from '../../enum';
import moment from 'moment';
import TaskCenterService from '@/domains/task-center-domain/TaskCenterService';
function ExpiredCourseList(props) {
const [expiredCourseList, setExpiredCourseList] = useState([]); // 失效课程
useEffect(() => {
getTrainingCourseAutoCancel();
}, []);
// 获取培训任务失效的课程
function getTrainingCourseAutoCancel() {
TaskCenterService.getTrainingCourseAutoCancel({ taskId: props.taskId }).then((res) => {
setExpiredCourseList(res.result);
});
}
// 移除未成功开课课程
function deLExpiredCourse(contentId) {
const { taskId } = props;
Modal.confirm({
content: '你确定要删除该数据内容吗?',
okText: '确定',
cancelText: '取消',
icon: <span className='icon iconfont default-confirm-icon'>&#xe6f4;</span>,
onOk: () => {
const params = {
taskId,
contentIdList: [contentId],
};
TaskCenterService.delTaskCancelContent(params).then((res) => {
message.success('删除成功');
getTrainingCourseAutoCancel();
});
},
});
}
return (
<div>
{expiredCourseList.length > 0 && (
<div className='expired-list-page'>
<div className='tip'>为了不影响学员学习,「未成功开课」的课程已从任务中移出,具体课程如下所示:</div>
<List
dataSource={expiredCourseList}
renderItem={(item) => (
<List.Item>
<div className='item-detail'>
<span className='icon iconfont'>&#xe80b;</span>
<span className='content-status'>未成功开课</span>
<span className='stage-name'>{item.stageName}</span>
<span className='content-name'>
<img src={ENUM.LearningContentIcon[item.courseType]} />
<span>{item.courseName}</span>
</span>
<span className='teacher-name'>{item.teacherName}</span>
<span className='split'>|</span>
<span className='course-time'>
{moment(item.startTime).format('YYYY-MM-DD HH:mm')}~{moment(item.endTime).format('HH:mm')}
</span>
<span
className='del-btn'
onClick={() => {
deLExpiredCourse(item.courseId);
}}>
删除记录
</span>
</div>
</List.Item>
)}
/>
</div>
)}
</div>
);
}
export default ExpiredCourseList;
.expired-list-page {
.tip {
color: #999999;
line-height: 20px;
margin-top: 8px;
margin-bottom: 16px;
}
.ant-list-item {
padding: 16px;
border: none !important;
&:nth-child(even) {
background: #ffffff;
}
&:nth-child(odd) {
background: #f7f8f9;
}
.item-detail {
display: inline-flex;
width: 100%;
height: 20px;
.icon {
font-size: 14px;
color: #ff4f4f;
margin-right: 4px;
}
.content-status {
color: #999999;
margin-right: 24px;
}
.stage-name {
color: #333333;
margin-right: 12px;
}
.content-name {
color: #333333;
margin-right: 12px;
vertical-align: middle;
> * {
vertical-align: middle;
}
img {
width: 20px;
height: 20px;
margin-right: 12px;
}
}
.teacher-name,
.course-time {
color: #999999;
}
.split {
margin-left: 12px;
margin-right: 12px;
color: #999999;
}
.del-btn {
margin-left: auto;
color: #2966ff;
cursor: pointer;
}
}
}
}
.related-course-drawer {
.related-course-drawer-content {
padding-bottom: 20px;
}
.link-create-course {
color: #666666;
font-size: 14px;
width: 638px;
text-align: left;
display: inline-block;
span {
color: #2966ff;
}
}
.search-container {
margin-bottom: 16px;
}
.select-area {
margin-bottom: 12px;
display: flex;
justify-content: space-between;
.select-box {
display: inline-box;
width: 186px;
background: #e9efff;
border-radius: 4px;
padding: 6px 16px;
margin-right: 8px;
display: flex;
justify-content: space-between;
.tip-icon {
color: #2966ff;
font-size: 14px;
margin-right: 4px;
}
.select-num {
color: #666666;
font-size: 14px;
}
.clear-btn {
text-align: right;
color: #5289fa;
font-size: 14px;
}
}
.related-box {
padding: 6px 16px;
background: #e9efff;
border-radius: 4px;
flex: 1;
color: #666666;
font-size: 14px;
}
}
.course-info {
display: flex;
align-items: center;
.course-cover {
width: 97px;
height: 55px;
display: inline-block;
border-radius: 4px;
margin-right: 8px;
}
.course-name {
font-size: 14px;
color: #666;
text-overflow: -o-ellipsis-lastline;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
width: 140px;
}
}
.course-state {
* {
vertical-align: middle;
display: inline-block;
}
.status-point {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 4px;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
width: 720px;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 16px 24px;
background: #fff;
border-top: 1px solid #e8e8e8;
z-index: 9999;
.ant-btn {
margin-left: 8px;
}
}
}
.related-exam-drawer {
.module-title {
font-size: 16px;
color: #333333;
line-height: 22px;
margin-bottom: 24px;
}
.paper-title {
color: #333333;
line-height: 20px;
padding: 10px 12px;
margin-top: 12px;
margin-bottom: 8px;
img {
width: 20px;
height: 20px;
margin-right: 4px;
}
}
.paper-table {
border: 1px solid #e8e8e8;
width: 550px;
.header {
font-weight: 500;
.item {
padding: 10px 16px;
}
}
.body-list {
.item {
padding: 14px 16px;
}
}
.header,
.body-list {
display: flex;
color: #333333;
.item {
box-sizing: border-box;
width: 86px;
&.long {
width: 120px;
}
}
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
height: 50px;
width: 720px;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 24px;
background: #fff;
border-top: 1px solid #e8e8e8;
z-index: 9999;
.ant-btn {
margin-left: 8px;
}
}
.ant-form-item {
margin-bottom: 24px !important;
.graphics-editor-container {
width: 550px;
height: 130px;
.editor-warning {
top: 130px !important;
}
}
}
}
.train-content-page {
.train-content__warp {
.ant-collapse-header {
padding: 15px 16px !important;
background-color: #f7f8f9;
}
.add-stage-btn {
color: #2966ff;
height: 52px;
background: #f7f8f9;
padding: 16px;
cursor: pointer;
}
}
.expired-info__wrap {
margin-top: 24px;
.module-title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
}
}
.finish-standard__warp {
margin-top: 24px;
margin-bottom: 60px;
.module-title {
height: 22px;
font-size: 16px;
color: #333333;
line-height: 22px;
cursor: pointer;
.icon {
font-size: 12px;
margin-left: 8px;
color: #5e606a;
display: inline-block;
vertical-align: middle;
}
.rotate-arrow {
transform: (rotate(180deg));
}
}
.detail-container {
margin-top: 24px;
display: flex;
.title-text {
color: #333333;
line-height: 20px;
}
.detail-box {
margin-top: -4px;
.item-info {
color: #333333;
vertical-align: middle;
margin-bottom: 20px;
.ant-form-item {
display: inline-block;
margin-bottom: 0 !important;
vertical-align: baseline;
}
img {
width: 20px;
height: 20px;
margin-right: 8px;
}
.input-box {
width: 60px;
height: 32px;
border-radius: 2px;
border: 1px solid #e8e8e8;
margin: 0 4px;
}
}
}
}
}
}
.sort-stage-item {
width: calc(100% - 24px);
display: inline-flex;
align-items: center;
line-height: 20px;
.item-info {
color: #333333;
}
.item-operate {
display: none;
margin-left: 30px;
.operate__item {
cursor: pointer;
.icon {
color: #bfbfbf;
font-size: 14px;
}
.text {
color: #666666;
margin-left: 8px;
}
}
}
&:hover {
.item-operate {
display: block;
}
}
.ant-form {
display: inline-block;
.ant-form-item {
margin-bottom: 0 !important;
}
}
}
.sort-content-item {
display: flex;
padding: 15px 16px !important;
margin-left: 40px;
justify-content: space-between;
align-items: center;
border-bottom: 1px dotted #e8e8e8;
vertical-align: middle;
* {
vertical-align: middle;
height: 20px;
}
&:hover {
.content-operate {
display: block;
}
}
.content-info {
.content-name {
color: #333333;
font-size: 14px;
margin-left: 12px;
}
.extra-info {
color: #999999;
margin-left: 12px;
}
.tip {
font-size: 14px;
color: #ff4f4f;
margin-right: 2px;
}
}
.content-operate {
display: none;
margin-left: 26px;
.operate__item {
cursor: pointer;
color: #666666;
.icon {
font-size: 14px;
color: #bfbfbf;
}
.text {
margin-left: 8px;
}
}
}
}
.type-option-icon {
width: 20px;
height: 20px;
}
.type-option-text {
margin-left: 12px;
}
.drag-btn {
margin-left: auto;
color: #cccccc;
cursor: pointer;
}
.add-content-btn {
color: #2966ff;
height: 52px;
border-radius: 2px;
padding: 16px;
cursor: pointer;
display: inline-block;
&.disabled {
color: #ccc;
cursor: not-allowed;
pointer-events: none;
}
}
.ant-collapse-content {
padding-left: 24px !important;
> .ant-collapse-content-box {
padding: 0 !important;
}
}
/*
* @Author: 吴文洁
* @Date: 2020-07-14 15:41:30
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-23 13:45:16
* @Description: 大班直播、互动班课列表的筛选组件
*/
import React, { useState, useEffect } from 'react';
import { Row, Input, Select, Tooltip } from 'antd';
import RangePicker from '@/modules/common/DateRangePicker';
import moment from 'moment';
import StoreService from '@/domains/store-domain/storeService';
import './TrainFilter.less';
const { Search } = Input;
const DEFAULT_QUERY = {
// 头部筛选默认值
taskName: null,
createId: null, // 创建人
startTime: null,
endTime: null,
};
const DEFAULT_CREATOR_QUERY = {
// 创建人列表筛选默认值
size: 10,
current: 1,
nickName: null, // 搜索值
};
function TrainFilter(props) {
const [query, setQuery] = useState(DEFAULT_QUERY);
const [hasNext, setHasNext] = useState(false);
const [creatorQuery, setCreatorQuery] = useState(DEFAULT_CREATOR_QUERY);
const [creatorList, setCreatorList] = useState([]);
useEffect(() => {
getCreatorList();
}, []);
// 获取创建人列表
function getCreatorList(current = 1) {
const _query = {
...creatorQuery,
current,
};
StoreService.getStoreUserBasicPage(_query).then((res) => {
const { result = {} } = res;
const { records = [], hasNext } = result;
const list = current > 1 ? creatorList.concat(records) : records;
setHasNext(hasNext);
setCreatorList(list);
});
}
// 滑动加载更多创建人列表(讲师)
function handleScrollCreatorList(e) {
const container = e.target;
const scrollToBottom = container && container.scrollHeight <= container.clientHeight + container.scrollTop;
if (scrollToBottom && hasNext) {
const _creatorQuery = { ...creatorQuery };
_creatorQuery.current = creatorQuery.current + 1;
setCreatorQuery(_creatorQuery);
getCreatorList(creatorQuery.current + 1);
}
}
// 改变搜索条件
function handleChangeQuery(field, value) {
const _query = _.clone(query);
_query.current = 1;
if (field === 'createdDate') {
_query.startTime = value[0]?.startOf('day').valueOf();
_query.endTime = value[1]?.endOf('day').valueOf();
} else {
_query[field] = value;
}
setQuery(_query);
if (field === 'taskName') return;
props.onChange(_query);
}
// 重置搜索条件
function handleReset() {
setQuery(DEFAULT_QUERY);
props.onChange(DEFAULT_QUERY);
}
return (
<div className='train-filter-page'>
<Row>
<div className='search-condition'>
<div className='search-condition__item'>
<span>培训任务:</span>
<Search
value={query.taskName}
placeholder='搜索培训任务名称'
onChange={(e) => {
handleChangeQuery('taskName', e.target.value.trim());
}}
onSearch={() => {
props.onChange(query);
}}
style={{ width: 'calc(100% - 70px)' }}
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
</div>
<div className='search-condition__item'>
<span>创建人:</span>
<Select
placeholder='请选择创建人'
style={{ width: 'calc(100% - 70px)' }}
showSearch
allowClear
filterOption={(input, option) => option}
suffixIcon={
<span className='icon iconfont' style={{ fontSize: '12px', color: '#BFBFBF' }}>
&#xe835;
</span>
}
onPopupScroll={handleScrollCreatorList}
value={query.createId}
onChange={(value) => {
handleChangeQuery('createId', value);
}}
onSearch={(value) => {
creatorQuery.nickName = value;
setCreatorQuery(creatorQuery);
getCreatorList();
}}
onClear={() => {
setCreatorQuery(DEFAULT_CREATOR_QUERY);
getCreatorList();
}}>
{_.map(creatorList, (item, index) => {
return (
<Select.Option value={item.id} key={item.id}>
{item.nickName}
</Select.Option>
);
})}
</Select>
</div>
<div className='search-condition__item'>
<span className='search-date'>创建日期:</span>
<RangePicker
id='train_date_picker'
allowClear={false}
value={query.startTime ? [moment(query.startTime), moment(query.endTime)] : null}
format={'YYYY-MM-DD'}
onChange={(value) => {
handleChangeQuery('createdDate', value);
}}
style={{ width: 'calc(100% - 70px)' }}
/>
</div>
</div>
<div className='reset-fold-area'>
<Tooltip title='清空筛选'>
<span className='resetBtn iconfont icon' onClick={handleReset}>
&#xe61b;{' '}
</span>
</Tooltip>
</div>
</Row>
</div>
);
}
export default TrainFilter;
.train-filter-page {
.search-condition {
width: calc(100% - 80px);
display: flex;
align-items: center;
flex-wrap: wrap;
&__item {
width: 30%;
margin-right: 3%;
line-height: 32px;
}
}
.reset-fold-area {
position: absolute;
right: 12px;
.resetBtn {
color: #999999;
font-size: 18px;
margin-right: 8px;
}
}
}
.train-list-page {
.header-line {
display: flex;
justify-content: space-between;
margin-top: 16px;
margin-bottom: 12px;
.ant-radio-button-wrapper {
color: #9d9d9d;
&:first-child {
border-radius: 4px 0 0 4px;
}
&:last-child {
border-radius: 0 4px 4px 0;
}
}
.ant-radio-button-wrapper-checked {
color: #2966ff;
}
}
.train-list-table {
.train-task-name {
display: flex;
align-items: center;
cursor: pointer;
.train-cover {
width: 106px;
height: 60px;
border-radius: 2px;
margin-right: 8px;
flex-shrink: 0;
}
.train-name {
width: 230px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
height: 40px;
}
}
.task-state {
* {
vertical-align: middle;
display: inline-block;
}
.status-point {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 4px;
}
}
.operate {
display: flex;
&__item {
color: #2966ff;
cursor: pointer;
}
.split {
margin: 0 8px;
color: #bfbfbf;
}
.more-operate {
cursor: pointer;
.more-text {
color: #2966ff;
cursor: pointer;
}
}
.disabled {
color: #ccc !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
}
.tip {
cursor: pointer;
color: '#bfbfbf';
margin-left: 5px;
font-weight: normal;
}
thead.ant-table-thead {
tr {
th {
padding: 10px 16px;
}
}
}
tbody {
tr {
td.ant-table-cell {
padding: 20px 16px;
color: #333;
}
&:nth-child(even) {
background: transparent;
td {
background: #fff;
}
}
&:nth-child(odd) {
background: #fafafa;
td {
background: #fafafa;
}
}
&:hover {
td {
background: #f3f6fa;
}
}
}
}
}
}
.train-list-more-menu {
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
border-radius: 4px;
div {
line-height: 30px;
padding: 0 15px;
cursor: pointer;
&:hover {
background: #f3f6fa;
}
}
.disabled {
color: #ccc !important;
cursor: not-allowed !important;
pointer-events: none !important;
}
}
.choose-assignor-modal {
.assignor-container {
display: flex;
height: 417px;
.left-list {
position: relative;
width: 50%;
margin-right: 24px;
padding: 12px 0 12px 16px;
border: 1px solid #e8e8e8;
.ant-select-auto-complete .ant-select-clear {
font-size: 14px;
right: 15px;
}
.left-search-dropdown {
position: absolute;
padding: 16px;
background: #fff;
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
z-index: 999;
border-radius: 2px;
width: 274px;
max-height: 280px;
overflow-y: scroll;
.catalog-title {
font-size: 14px;
color: #666;
margin-bottom: 14px;
}
.search-result-item {
padding: 14px 0;
color: #333;
width: 220px;
display: flex;
justify-content: space-between;
.title-icon {
color: #999;
margin-right: 3px;
}
.search-result-item__left {
font-size: 14px;
}
.search-result-item__right {
font-size: 14px;
width: 84px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #999;
text-align: right;
}
}
.ant-select-dropdown-menu-item-group-title {
color: #666;
font-weight: bold;
}
.ant-select-item-option-grouped {
padding-left: 12px;
}
.empty-con {
text-align: center;
.empty-img {
width: 150px;
height: 150px;
}
.empty-text {
color: #666;
}
}
}
.data-body {
.ant-tabs-nav {
margin: 0 !important;
.ant-tabs-tab {
padding: 16px 0 10px 0 !important;
font-size: 14px !important;
}
}
.lottie-icon {
margin-top: 64px;
.lottie-icon__title {
color: #666666;
}
}
.tree-con {
overflow-y: scroll;
overflow-x: hidden;
max-height: 323px;
padding-right: 16px;
.ant-tree .ant-tree-treenode {
padding: 12px 0 !important;
}
.node-title-div {
display: flex;
justify-content: space-between;
white-space: nowrap;
.title-icon {
margin-right: 6px;
color: #999999;
}
.title-name {
position: absolute;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 55%;
left: 28px;
color: #666666;
}
}
.ant-tree.ant-tree-directory {
.ant-tree-treenode-selected:hover::before,
.ant-tree-treenode-selected::before {
background: #f3f6fa;
}
}
.ant-tree-switcher {
color: #999999 !important;
}
}
}
}
.right-list {
width: 50%;
border: 1px solid #e8e8e8;
.header-line {
display: flex;
justify-content: space-between;
border-bottom: 1px solid #e8e8e8;
padding: 12px 16px;
.tip-text {
font-weight: 500;
color: #333333;
}
.clear-btn {
color: #2966ff;
cursor: pointer;
}
}
.data-body {
overflow-y: scroll;
overflow-x: hidden;
max-height: 370px;
.lottie-icon {
margin-top: 76px;
.lottie-icon__title {
color: #666666;
}
}
.selected-item {
padding: 12px 12px 12px 16px;
display: flex;
justify-content: space-between;
.item-title {
width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.title-icon {
color: #999999;
margin-right: 6px;
}
.title-name {
color: #333333;
}
}
.clear-icon {
font-size: 16px;
color: #999999;
vertical-align: middle;
cursor: pointer;
}
}
}
}
}
}
/*
* @Author: yuananting
* @Date: 2021-08-12 16:27:38
* @LastEditors: yuananting
* @LastEditTime: 2021-08-17 19:10:02
* @Description: 新建培训任务-选择协同人员
* @Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useEffect } from 'react';
import { Modal, Input, Tooltip, List, Checkbox } from 'antd';
import StoreService from '@/domains/store-domain/storeService';
import './ChooseCollaboratorModal.less';
import WWOpenDataCom from '@/components/WWOpenDataCom';
import LottieIcon from '@/components/LottieIcon';
import _ from 'underscore';
const { Search } = Input;
function ChooseCollaboratorModal(props) {
const [searchKey, setSearchKey] = useState(''); // 搜索内容
const [employeeList, setEmployeeList] = useState([]); // 员工列表
const [checkedCollaboratorList, setCheckedCollaboratorList] = useState(props.currentCollaboratorList || []); // 勾选的协同人员
const [checkedCollaboratorKeys, setCheckedCollaboratorKeys] = useState(props.currentCollaboratorList.map((item) => item.checkedId) || []);
const [allChecked, setAllChecked] = useState(false);
useEffect(() => {
getEmployeeList();
}, [searchKey]);
function getEmployeeList() {
const params = {
current: 1,
size: 999,
nickName: searchKey,
phone: '',
roleCodes: [],
};
StoreService.getEmployeeList(params).then((res) => {
const { records } = res.result;
records.map((item) => {
item.checkedName = item.nickName;
item.checkedId = item.id;
item.checked = checkedCollaboratorKeys.includes(item.checkedId);
return item;
});
const checkedEmployeeList = records.filter((item) => item.checked);
const _checkedAll = records.length === checkedEmployeeList.length;
setEmployeeList(records);
setAllChecked(_checkedAll);
});
}
function renderDepItem(record) {
if (!record.depNameList) {
return <span>-</span>;
}
return record.depNameList.map((item, index) => {
return (
<span>
<WWOpenDataCom type='departmentName' openid={item} />
{index < record.depNameList.length - 1 ? '、' : ''}
</span>
);
});
}
function handleCheckedCollaborator(e) {
const { value, checked } = e.target;
let _checkedCollaboratorList = [...checkedCollaboratorList];
let _employeeList = [];
if (checked) {
_employeeList = employeeList.map((item) => {
if (item.checkedId === value.checkedId) {
item.checked = true;
}
return item;
});
_checkedCollaboratorList.push({ ...value, checked: true });
} else {
_employeeList = employeeList.map((item) => {
if (item.checkedId === value.checkedId) {
item.checked = false;
}
return item;
});
_checkedCollaboratorList = checkedCollaboratorList.filter((item) => item.checkedId !== value.checkedId);
}
const _checkedCollaboratorKeys = _checkedCollaboratorList.map((item) => item.checkedId);
const checkedEmployeeList = _employeeList.filter((item) => item.checked);
const _checkedAll = employeeList.length === checkedEmployeeList.length;
setEmployeeList(_employeeList);
setCheckedCollaboratorList(_checkedCollaboratorList);
setCheckedCollaboratorKeys(_checkedCollaboratorKeys);
setAllChecked(_checkedAll);
}
function removeCheckedCollaborator(record) {
const _employeeList = employeeList.map((item) => {
if (item.checkedId === record.checkedId) {
item.checked = false;
}
return item;
});
const _checkedCollaboratorList = checkedCollaboratorList.filter((item) => item.checkedId !== record.checkedId);
const _checkedCollaboratorKeys = _checkedCollaboratorList.map((item) => item.checkedId);
const checkedEmployeeList = _employeeList.filter((item) => item.checked);
const _checkedAll = employeeList.length === checkedEmployeeList.length;
setAllChecked(_checkedAll);
setCheckedCollaboratorList(_checkedCollaboratorList);
setCheckedCollaboratorKeys(_checkedCollaboratorKeys);
setEmployeeList(_employeeList);
}
function clearCheckedCollaborator() {
const _employeeList = employeeList.map((item) => {
item.checked = item.checkedId === props.createId;
return item;
});
const createrItem = checkedCollaboratorList.filter((item) => item.checkedId === props.createId);
setEmployeeList(_employeeList);
setCheckedCollaboratorList(createrItem);
setCheckedCollaboratorKeys([props.createId]);
setAllChecked(false);
}
function handleCheckedAll(e) {
let _employeeList = [];
let _checkedCollaboratorList = [];
if (e.target.checked) {
_employeeList = employeeList.map((item) => {
item.checked = true;
return item;
});
const filterList = employeeList.filter((item) => {
return !checkedCollaboratorKeys.includes(item.checkedId);
});
_checkedCollaboratorList = checkedCollaboratorList.concat([...filterList]);
} else {
_employeeList = employeeList.map((item) => {
item.checked = item.checkedId === props.createId;
return item;
});
const removeIds = _employeeList.filter((item) => !item.checked).map((childItem) => childItem.checkedId);
_checkedCollaboratorList = checkedCollaboratorList.filter((item) => {
return !removeIds.includes(item.checkedId);
});
}
const _checkedCollaboratorKeys = _checkedCollaboratorList.map((item) => item.checkedId);
setEmployeeList(_employeeList);
setCheckedCollaboratorList(_checkedCollaboratorList);
setCheckedCollaboratorKeys(_checkedCollaboratorKeys);
setAllChecked(e.target.checked);
}
return (
<Modal
className='choose-collaborator-modal'
title='选择协同者'
visible={true}
onCancel={props.onClose}
onOk={() => {
props.onConfirm(checkedCollaboratorList);
props.onClose();
}}
width={680}
maskClosable={false}>
<div className='collaborator-container'>
<div className='left-list'>
<Search
placeholder='搜索员工'
value={searchKey}
style={{ paddingRight: 16 }}
onChange={(e) => setSearchKey(e.target.value.trim())}
onSearch={getEmployeeList}
className='search search-input'
enterButton={<span className='icon iconfont'>&#xe832;</span>}
/>
<div className='data-body'>
<Choose>
<When condition={employeeList.length > 0}>
<List
header={
<Checkbox checked={allChecked} onChange={handleCheckedAll}>
全部
</Checkbox>
}
dataSource={employeeList}
renderItem={(item, index) => (
<List.Item>
<Checkbox
checked={item.checked}
value={item}
key={item.checkedId}
disabled={item.checkedId === props.createId}
onChange={handleCheckedCollaborator}>
<div className='employee-item'>
<span className='item-title'>
<span className='icon iconfont title-icon'>&#xe84a;</span>
<Tooltip title={<WWOpenDataCom type='userName' openid={item.checkedName} />}>
<span className='title-name'>
<WWOpenDataCom type='userName' openid={item.checkedName} />
</span>
</Tooltip>
</span>
<span className='dep-info'>{renderDepItem(item)}</span>
</div>
</Checkbox>
</List.Item>
)}
/>
</When>
<Otherwise>
<LottieIcon title='搜索无结果' type='search' size={150} />
</Otherwise>
</Choose>
</div>
</div>
<div className='right-list'>
<div className='header-line'>
<span className='tip-text'>已选择</span>
<span className='clear-btn' onClick={clearCheckedCollaborator}>
清空
</span>
</div>
<div className='data-body'>
{checkedCollaboratorList.map((item, index) => {
return (
<div className='selected-item'>
<span className='item-title'>
<span className='icon iconfont title-icon'>&#xe84a;</span>
<Tooltip title={<WWOpenDataCom type='userName' openid={item.checkedName} />}>
<span className='title-name'>
<WWOpenDataCom type='userName' openid={item.checkedName} />
</span>
</Tooltip>
</span>
<span
className={`icon iconfont clear-icon ${item.checkedId === props.createId && 'disabled'}`}
onClick={() => removeCheckedCollaborator(item)}>
&#xe717;
</span>
</div>
);
})}
</div>
</div>
</div>
</Modal>
);
}
export default ChooseCollaboratorModal;
.choose-collaborator-modal {
.collaborator-container {
display: flex;
height: 417px;
.left-list {
width: 50%;
margin-right: 24px;
padding: 12px 0 12px 16px;
border: 1px solid #e8e8e8;
.data-body {
overflow-y: scroll;
overflow-x: hidden;
max-height: 370px;
width: 288px;
.lottie-icon {
margin-top: 76px;
.lottie-icon__title {
color: #666666;
}
}
.ant-list {
.ant-list-item:hover {
background-color: #f3f6fa;
}
}
.employee-item {
display: flex;
justify-content: space-between;
.item-title {
width: 110px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.title-icon {
color: #0081f0;
margin-right: 6px;
}
.title-name {
color: #333333;
}
}
.dep-info {
width: 128px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 12px;
text-align: right;
}
}
}
}
.right-list {
width: 50%;
border: 1px solid #e8e8e8;
.header-line {
display: flex;
justify-content: space-between;
border-bottom: 1px solid #e8e8e8;
padding: 12px 16px;
.tip-text {
font-weight: 500;
color: #333333;
}
.clear-btn {
color: #2966ff;
cursor: pointer;
}
}
.data-body {
overflow-y: scroll;
overflow-x: hidden;
max-height: 370px;
.selected-item {
padding: 12px 12px 12px 16px;
display: flex;
justify-content: space-between;
.item-title {
width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.title-icon {
color: #0081f0;
margin-right: 6px;
}
.title-name {
color: #333333;
}
}
.clear-icon {
font-size: 16px;
color: #999999;
vertical-align: middle;
cursor: pointer;
&.disabled {
color: #ccc;
cursor: not-allowed;
pointer-events: none;
}
}
}
}
}
}
}
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