Commit 18dc52ef by zhangleyuan

feat:解决合并代码冲突

parents 0e33cd6a 39d9230d
@font-face { @font-face {
font-family: 'iconfont'; /* project id 2223403 */ font-family: 'iconfont'; /* project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_qb6r10go4s.eot'); src: url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.eot');
src: url('//at.alicdn.com/t/font_2223403_qb6r10go4s.eot?#iefix') format('embedded-opentype'), src: url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_qb6r10go4s.woff2') format('woff2'), url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_qb6r10go4s.woff') format('woff'), url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_qb6r10go4s.ttf') format('truetype'), url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_qb6r10go4s.svg#iconfont') format('svg'); url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.svg#iconfont') format('svg');
} }
.iconfont{ .iconfont{
font-family:"iconfont" !important; font-family:"iconfont" !important;
......
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-12-12 11:57:10 * @Date: 2020-12-12 11:57:10
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-06 20:18:16 * @LastEditTime: 2021-02-28 13:47:38
* @Description: Description * @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -52,6 +52,11 @@ export function turnOnOrOffLiveCloudCourse(params: object) { ...@@ -52,6 +52,11 @@ export function turnOnOrOffLiveCloudCourse(params: object) {
export function delLiveCloudCourse(params: object) { export function delLiveCloudCourse(params: object) {
return Service.Hades("public/courseCloud/delLiveCloudCourse", params); return Service.Hades("public/courseCloud/delLiveCloudCourse", params);
} }
//该接口主要用于培训计划关联直播课的接口(会筛选掉已关联的直播课)
export function getLiveCloudCourseBasePage(params: object) {
return Service.Hades("public/courseCloud/getLiveCloudCourseBasePage", params);
}
//视频课相关接口 //视频课相关接口
export function changeVideoShelfState(params: object) { export function changeVideoShelfState(params: object) {
return Service.Hades("public/hades/changeVideoShelfState", params); return Service.Hades("public/hades/changeVideoShelfState", params);
...@@ -76,4 +81,7 @@ export function videoSchedulePage(params: object) { ...@@ -76,4 +81,7 @@ export function videoSchedulePage(params: object) {
} }
export function videoWatchInfo(params: object) { export function videoWatchInfo(params: object) {
return Service.Hades("public/hades/videoWatchInfo", params); return Service.Hades("public/hades/videoWatchInfo", params);
}
export function videoScheduleBasePage(params: object) {
return Service.Hades("public/hades/videoScheduleBasePage", params);
} }
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-21 16:08:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-05 14:04:52
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import Service from "@/common/js/service";
export function getTrainingPlanPage(params: object) {
return Service.Hades("public/hades/getTrainingPlanPage", params);
}
export function createTrainingPlan(params: object) {
return Service.Hades("public/hades/createTrainingPlan", params);
}
export function updateStateTrainingPlan(params: object) {
return Service.Hades("public/hades/updateStateTrainingPlan", params);
}
export function getTrainingPlanDetail(params: object) {
return Service.Hades("public/hades/getTrainingPlanDetail", params);
}
export function updateTrainingPlan(params: object) {
return Service.Hades("public/hades/updateTrainingPlan", params);
}
export function deleteTrainingPlan(params: object) {
return Service.Hades("public/hades/deleteTrainingPlan", params);
}
export function getPlanUserRecordPage(params: object) {
return Service.Hades("public/hades/getPlanUserRecordPage", params);
}
export function getPlanCustomerRecordPage(params: object) {
return Service.Hades("public/hades/getPlanCustomerRecordPage", params);
}
export function getPlanCustomerDetail(params: object) {
return Service.Hades("public/hades/getPlanCustomerDetail", params);
}
export function getPlanCustomerAboutUser(params: object) {
return Service.Hades("public/hades/getPlanCustomerAboutUser", params);
}
export function removePlanCustomer(params: object) {
return Service.Hades("public/hades/removePlanCustomer", params);
}
export function getStorePlanAll(params: object) {
return Service.Hades("public/hades/getStorePlanAll", params);
}
\ No newline at end of file
/* /*
* @Author: 陈剑宇 * @Author: 陈剑宇
* @Date: 2020-05-07 14:43:01 * @Date: 2020-05-07 14:43:01
* @LastEditTime: 2021-01-28 16:28:08 * @LastEditTime: 2021-03-01 10:09:42
* @LastEditors: zhangleyuan * @LastEditors: zhangleyuan
* @Description: * @Description:
* @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts * @FilePath: /wheat-web-demo/src/domains/basic-domain/constants.ts
......
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-11-25 18:25:02 * @Date: 2020-11-25 18:25:02
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-06 20:17:40 * @LastEditTime: 2021-02-28 15:15:37
* @Description: Description * @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
import { import {
fetchLecturerData, fetchUserData, exportStudentCourseData, exportPlayBackCourseData, fetchPlaybackList, createLiveCloudCourse, getLiveCloudCoursePage, fetchLecturerData, fetchUserData, exportStudentCourseData, exportPlayBackCourseData, fetchPlaybackList, createLiveCloudCourse, getLiveCloudCoursePage,
getLiveCloudCourseDetail, updateLiveCloudCourse, turnOnOrOffLiveCloudCourse, delLiveCloudCourse, changeVideoShelfState, createVideoSchedule, delVideoSchedule, editVideoSchedule, userWatchInfo, videoSchedulePage, videoScheduleDetail, videoWatchInfo, getQrcode getLiveCloudCourseDetail, updateLiveCloudCourse, turnOnOrOffLiveCloudCourse, delLiveCloudCourse, changeVideoShelfState, createVideoSchedule, delVideoSchedule,
editVideoSchedule, userWatchInfo, videoSchedulePage, videoScheduleDetail, videoWatchInfo, getQrcode,getLiveCloudCourseBasePage,videoScheduleBasePage
} from '@/data-source/course/request-api'; } from '@/data-source/course/request-api';
export default class courseService { export default class courseService {
...@@ -84,4 +85,10 @@ export default class courseService { ...@@ -84,4 +85,10 @@ export default class courseService {
static videoWatchInfo(params: any) { static videoWatchInfo(params: any) {
return videoWatchInfo(params); return videoWatchInfo(params);
} }
static getLiveCloudCourseBasePage(params: any){
return getLiveCloudCourseBasePage(params);
}
static videoScheduleBasePage(params: any){
return videoScheduleBasePage(params);
}
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-20 09:21:40 * @Date: 2020-08-20 09:21:40
* @LastEditors: zhangleyuan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-27 19:53:17 * @LastEditTime: 2021-02-20 17:08:58
* @Description: * @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有 * @Copyright: 杭州杰竞科技有限公司 版权所有
*/ */
......
/*
* @Author: zhangleyuan
* @Date: 2021-03-01 15:33:02
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 15:35:09
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
/*
* @Author: zhangleyuan
* @Date: 2021-02-21 16:15:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-05 14:04:25
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import {getTrainingPlanPage,createTrainingPlan,updateStateTrainingPlan,getTrainingPlanDetail,updateTrainingPlan,deleteTrainingPlan,getPlanUserRecordPage,getPlanCustomerRecordPage,getPlanCustomerDetail,getPlanCustomerAboutUser,removePlanCustomer,getStorePlanAll} from '@/data-source/plan/request-apis';
export default class PlanService {
// 获取员工列表
static getTrainingPlanPage(params: any) {
return getTrainingPlanPage(params);
}
static createTrainingPlan(params: any) {
return createTrainingPlan(params);
}
static updateStateTrainingPlan(params: any) {
return updateStateTrainingPlan(params);
}
static getTrainingPlanDetail(params: any) {
return getTrainingPlanDetail(params);
}
static updateTrainingPlan(params: any) {
return updateTrainingPlan(params);
}
static deleteTrainingPlan(params: any) {
return deleteTrainingPlan(params);
}
static getPlanUserRecordPage(params: any) {
return getPlanUserRecordPage(params);
}
static getPlanCustomerRecordPage(params: any) {
return getPlanCustomerRecordPage(params);
}
static getPlanCustomerDetail(params: any) {
return getPlanCustomerDetail(params);
}
static getPlanCustomerAboutUser(params: any) {
return getPlanCustomerAboutUser(params);
}
static removePlanCustomer(params: any) {
return removePlanCustomer(params);
}
static getStorePlanAll(params: any) {
return getStorePlanAll(params);
}
}
\ No newline at end of file
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-12 13:52:14 * @LastEditTime: 2021-03-05 14:10:41
* @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_80qwxi5x2ed.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
<!-- <!--
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-08-24 12:20:57 * @Date: 2020-08-24 12:20:57
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-18 21:18:43 * @LastEditTime: 2021-03-02 15:16:11
* @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_80qwxi5x2ed.css"> <link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!-- <!--
Notice the use of %PUBLIC_URL% in the tags above. Notice the use of %PUBLIC_URL% in the tags above.
......
...@@ -272,21 +272,21 @@ class AddLiveBasic extends React.Component { ...@@ -272,21 +272,21 @@ class AddLiveBasic extends React.Component {
onClose={() => this.setState({ showCutModal: false })} onClose={() => this.setState({ showCutModal: false })}
reUpload={() => { this.state.currentInputFile.click() }} reUpload={() => { this.state.currentInputFile.click() }}
/> />
{showSelectFileModal && {showSelectFileModal &&
<SelectPrepareFileModal <SelectPrepareFileModal
key="basic" key="basic"
operateType="select" operateType="select"
multiple={false} multiple={false}
accept="image/jpeg,image/png,image/jpg" accept="image/jpeg,image/png,image/jpg"
selectTypeList={['JPG', 'JPEG', 'PNG']} selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png' tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectFileModal} isOpen={showSelectFileModal}
onClose={() => { onClose={() => {
this.setState({ showSelectFileModal: false }) this.setState({ showSelectFileModal: false })
}} }}
onSelect={this.handleSelectCover} onSelect={this.handleSelectCover}
/> />
} }
<Modal <Modal
title="设置图片" title="设置图片"
width={1080} width={1080}
......
...@@ -271,6 +271,37 @@ class LiveCourseList extends React.Component { ...@@ -271,6 +271,37 @@ class LiveCourseList extends React.Component {
}, },
}, },
{ {
title: '创建时间',
width: "9%",
key: "created",
dataIndex: "created",
sorted:true,
render: (val, item) => {
return (
<span>{formatDate('YYYY-MM-DD H:i', val)}</span>
);
},
},
{
title: '关联项',
width: "15%",
key: "planList",
dataIndex: "planList",
render: (val, record) => {
return (
<span>
{ record.relatedPlanList ?
record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
:
<span></span>
}
</span>
)
},
},
{
title: "操作", title: "操作",
width: "15%", width: "15%",
key: "operate", key: "operate",
...@@ -456,6 +487,37 @@ class LiveCourseList extends React.Component { ...@@ -456,6 +487,37 @@ class LiveCourseList extends React.Component {
); );
}, },
}, },
{
title: '创建时间',
width: "9%",
key: "created",
dataIndex: "created",
sorted:true,
render: (val, item) => {
return (
<span>{formatDate('YYYY-MM-DD H:i', val)}</span>
);
},
},
{
title: '关联项',
width: "15%",
key: "planList",
dataIndex: "planList",
render: (val, record) => {
return (
<span>
{ record.relatedPlanList ?
record.relatedPlanList.map((item,index)=>{
return <span>{item.planName} { (index < record.relatedPlanList.length-1)&&(<span></span>)} </span>
})
:
<span></span>
}
</span>
)
},
},
]; ];
} }
...@@ -477,6 +539,10 @@ class LiveCourseList extends React.Component { ...@@ -477,6 +539,10 @@ class LiveCourseList extends React.Component {
renderMoreOperate = (item) => { renderMoreOperate = (item) => {
return ( return (
<div className="live-course-more-menu"> <div className="live-course-more-menu">
<div
className="operate__item"
>关联培训计划</div>
<div <div
className="operate__item" className="operate__item"
onClick={()=>this.toEditCoursePage(item)} onClick={()=>this.toEditCoursePage(item)}
......
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
import './SelectOperatorModal.less';
import _ from "underscore";
const { Search } = Input;
class RelatedPlanModal extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
};
}
componentDidMount() {
this.handleFetchDataList();
}
// 获取运营师列表
handleFetchDataList = () => {
const {query,size,totalCount} = this.state
const params ={
...query,
size,
roleCodes:['CloudOperator']
}
PlanService.getStorePlanAll(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
}
handleChangePlanName = (value)=>{
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
query.phone = value;
query.nickName = null;
}else{
query.nickName = value;
query.phone = null;
}
query.current = 1;
this.setState({
query
})
}
onShowSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
size
},()=>{this.handleFetchDataList()})
}
// 请求表头
parseColumns = () => {
const columns = [
{
title: '姓名',
key: 'nickName',
dataIndex: 'nickName'
},
{
title: '手机号',
key: 'phone',
dataIndex: 'phone'
}
];
return columns;
}
render() {
const { size,dataSource,totalCount,query,selectOperatorList} = this.state;
const { visible } = this.props;
return (
<Modal
title="关联培训计划"
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="related-plan-modal"
closable={true}
width={800}
onOk={() => this.props.onSelect(selectOperatorList) }
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索培训计划名称" style={{ width: 200 }} onChange={(e) => { this.handleChangePlanName(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} />
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
/>
{dataSource.length >0 &&
<div className="box-footer">
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
</div>
</Modal>
)
}
}
export default SelectOperatorModal;
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-03 10:29:13
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from "react";
import { Button, message, Modal} from 'antd';
import ShowTips from "@/components/ShowTips";
import Breadcrumbs from "@/components/Breadcrumbs";
import BasicInfo from './components/BasicInfo';
import TrainingTask from './components/TrainingTask'
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user';
import _ from "underscore";
import './AddPlan.less'
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
const defaultBasicData = {
planName:"",
coverUrl: defaultCover,
coverId:null,
enableState:"YES",
selectOperatorList:[],
instro:'',
operateType:'All_Operate',
percentCompleteLive:80,
percentCompleteVideo:80
}
const defaultTaskList = [];
function AddPlan() {
const id = getParameterByName("id");
const type = getParameterByName("type");
const [basicData,setBasicData] = useState(defaultBasicData);
const [taskList,setTaskList] = useState(defaultTaskList);
useEffect(()=>{
if(type==='edit'){
getPlanDetail();
}
},id)
function getPlanDetail (){
PlanService.getTrainingPlanDetail({
planId: id
}).then((res) => {
const {
planName,
enableState,
operateType,
operateIds,
percentCompleteLive,
percentCompleteVideo,
courseMediaVOS,
trainingTaskList
} = res.result;
let coverId;
let coverUrl;
let instro;
courseMediaVOS.map((item) => {
switch (item.contentType){
case "COVER":
coverId = item.mediaContent;
coverUrl = item.mediaUrl;
break;
case "INTRO":
instro = item.mediaContent;
break;
default:
break;
}
return item;
})
setBasicData({
planName,
coverUrl:coverUrl || defaultCover,
coverId,
enableState,
selectOperatorList:operateIds || [],
instro,
operateType,
percentCompleteLive,
percentCompleteVideo
})
setTaskList(trainingTaskList)
})
}
function handleChangeBasicInfo(field, value){
setBasicData( {
...basicData,
[field]: value,
})
}
function handleChangeTaskInfo(value){
setTaskList(value)
}
function submitInfo(){
const {planName,enableState,selectOperatorList,instro,operateType,percentCompleteLive,percentCompleteVideo,coverId,coverUrl} = basicData;
if(!planName){
message.warning('请输入的培训计划名称');
return;
}
if(taskList.length === 0){
message.warning('请输入培训计划内容');
return;
}
let scheduleMediaRequests = [];
let coverObj ={
contentType:'COVER',
mediaContent:coverId,
mediaType:'PICTURE',
mediaUrl: coverUrl,
}
if(coverId){
scheduleMediaRequests = [...scheduleMediaRequests,coverObj];
}
let instroObj = {
contentType:"INTRO",
mediaType: 'TEXT',
mediaContent:instro,
}
if(instro){
scheduleMediaRequests = [...scheduleMediaRequests,instroObj];
}
const params = {
createId:User.getStoreUserId(),
enableState,
operateIds:_.pluck(selectOperatorList,'id'),
operateType,
percentCompleteLive,
percentCompleteVideo,
planName,
scheduleMediaRequests,
storeId:User.getStoreId(),
trainingTaskList:handleSubmitTaskData(taskList)
}
if (type === 'add') {
PlanService.createTrainingPlan(params).then((res) => {
if (res.success){
message.success("新建成功");
window.RCHistory.goBack();
}
});
}else{
const _params = {
...params,
id
}
PlanService.updateTrainingPlan(_params).then((res) => {
if (res.success){
message.success("更新成功");
window.RCHistory.goBack();
}
});
}
}
function handleSubmitTaskData(taskData){
return taskData.map((item,index)=>{
let _item = {};
_item.taskId = item.taskId;
_item.taskName =item.taskName;
_item.courseList = item.courseList.map((childItem,index)=>{
let _childItem = {}
_childItem.courseId = childItem.courseId;
_childItem.courseName = childItem.courseName;
_childItem.courseType = childItem.courseType;
return _childItem;
});
return _item;
})
}
// 取消编辑并返回上一级路由
function handleGoBack (){
if (!_.isEqual(basicData, defaultBasicData) || !_.isEqual(taskList, defaultTaskList)
) {
Modal.confirm({
title: '确定要返回吗?',
content: '返回后,本次编辑的内容将不被保存',
okText: '确认返回',
cancelText: '留在本页',
icon: <span className="icon iconfont default-confirm-icon">&#xe6f4;</span>,
onOk: () => {
window.RCHistory.goBack();
}
})
} else {
window.RCHistory.goBack();
}
}
return (
<div className="page add-plan-page">
<Breadcrumbs
navList={type == "add" ? "新建培训计划" : "编辑培训计划"}
goBack={handleGoBack}
/>
<div className="box">
<div className="show-tips">
<ShowTips message="请遵守国家相关规定,切勿上传低俗色情、暴力恐怖、谣言诈骗、侵权盗版等相关内容,小麦企培保有依据国家规定及平台规则进行处理的权利" />
</div>
<div className="add-plan-page__form">
<div className="basic-info__wrap">
<div className="title">基本信息</div>
<BasicInfo
data={basicData}
onChange={handleChangeBasicInfo}
/>
</div>
<div className="basic-info__wrap">
<div className="title">培训任务</div>
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} />
</div>
</div>
</div>
<div className="footer">
<Button>取消</Button>
<Button type="primary" onClick={submitInfo}>保存</Button>
</div>
</div>
)
}
export default AddPlan;
\ No newline at end of file
.add-plan-page {
position:relative !important;
.box {
margin-bottom: 66px !important;
}
.add-plan-page__form {
margin-top: 16px;
.title {
font-size: 16px;
color: #333;
font-weight: 500;
line-height: 22px;
margin-bottom:8px;
}
}
.footer {
position: fixed;
bottom: 0;
height: 58px;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 252px;
background: #fff;
border-top: 1px solid #E8E8E8;
z-index: 9999;
.ant-btn {
margin-left: 10px;
}
}
}
\ No newline at end of file
import React from 'react';
import { withRouter } from "react-router-dom";
import { Tabs } from 'antd';
import Breadcrumbs from "@/components/Breadcrumbs";
import EmployeeShareData from './components/EmployeeShareData';
import UserLearningData from './components/UserLearningData';
import PlanService from '@/domains/plan-domain/planService';
import User from '@/common/js/user'
import Bus from '@/core/bus';
import './LearningData.less';
const userRole = User.getUserRole();
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
class LearningData extends React.Component {
constructor(props) {
super(props);
const id = getParameterByName("id");
this.state = {
id,
planName:"",
coverUrl:defaultCover,
courseNum:0,
created:"",
cultureCustomerNum:0,
activeKey:"employeeShareData"
}
}
componentDidMount(){
this.getPlanDetail();
Bus.bind('watchDataView',() =>{this.setState({activeKey:"userLearningData"}) })
}
getPlanDetail = ()=>{
const { id } = this.state;
PlanService.getTrainingPlanDetail({
planId: id
}).then((res) => {
const {
planName,
courseMediaVOS,
courseNum,
created,
cultureCustomerNum
} = res.result;
let coverUrl;
courseMediaVOS.map((item) => {
if(item.contentType === "COVER"){
coverUrl = item.mediaUrl;
}
return item;
})
this.setState({
planName,
coverUrl: coverUrl || defaultCover,
courseNum,
created,
cultureCustomerNum:0
})
})
}
render() {
const {planName,coverUrl,courseNum,created,cultureCustomerNum,activeKey} = this.state;
return (
<div className="page plan-learn-data-list">
<Breadcrumbs
navList="学习数据"
goBack={() => {
RCHistory.goBack();
}}
/>
<div className="plan-info">
<div className="plan-intro">
<div className="plan-img-con">
<img src="https://image.xiaomaiketang.com/xm/YNfi45JwFA.png"/>
</div>
<div>
<div className="plan-name">
{planName}
</div>
<div className="create-course">
<span className="createUser">创建人:张老师</span>
<span className="split">|</span>
<span className="course-total">课程总数量:{courseNum}</span>
</div>
<div className="create-time">创建时间:{formatDate('YYYY-MM-DD H:i', created)} </div>
</div>
</div>
<div className="join">
<div className="number">{cultureCustomerNum}</div>
<div className="text">参培人数</div>
</div>
</div>
<div className="box">
{ (userRole === "CloudManager" || userRole === "StoreManager")?
(<Tabs activeKey={activeKey} onChange={(activeKey)=>{this.setState({activeKey})}}>
<Tabs.TabPane tab="员工分享数据" key="employeeShareData">
<EmployeeShareData/>
</Tabs.TabPane>
<Tabs.TabPane tab="用户学习数据" key="userLearningData">
<UserLearningData/>
</Tabs.TabPane>
</Tabs>)
:
(<UserLearningData/>)
}
</div>
</div>
)
}
}
export default withRouter(LearningData);
\ No newline at end of file
.plan-learn-data-list{
.plan-info{
margin:16px;
padding:16px;
background: #FFF;
display:flex;
justify-content: space-between;
align-items: center;
.plan-intro{
display: flex;
align-items: center;
.plan-img-con{
margin-right:8px;
img{
width:136px;
height:77px;
}
}
.plan-name{
font-size: 16px;
font-weight: 500;
color: #333333;
line-height: 22px;
margin-bottom:8px;
}
.create-course{
font-size: 14px;
color: #666666;
.split{
margin:0 8px;
color:#666;
}
margin-bottom:4px;
}
.create-time{
font-size:14px;
color:#666;
}
}
.join{
margin-right:144px;
min-width:54px;
text-align:center;
.number{
font-size: 26px;
font-weight: 500;
color: #333333;
margin-botttom:4px;
}
.text{
font-size: 14px;
color:#999;
}
}
}
}
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 13:41:08
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useEffect, useState } from "react";
import PlanFilter from './components/PlanFilter';
import PlanOpt from './components/PlanOpt';
import PlanList from './components/PlanList';
import PlanService from "@/domains/plan-domain/planService";
function PlanPage() {
const [planListData, setPlanListData] = useState([]);
const [query,setQuery] = useState({
current:1,
size:10,
});
useEffect(() => {
handleFetchPlanList();
}, [query]);
const [totalCount,setTotalCount] = useState(0);
function queryChange(_query){
const params = {
...query,
..._query,
};
setQuery(params);
}
function handleFetchPlanList(_query){
const params = {
...query,
..._query,
};
//动态获取计划列表
PlanService.getTrainingPlanPage(params).then((res) => {
const { result: { records = [], total } } = res;
setPlanListData(records);
setTotalCount(total);
})
}
return (
<div className="page">
<div className="content-header">培训计划</div>
<div className="box">
<PlanFilter onChange={handleFetchPlanList}/>
<PlanOpt/>
<PlanList
planListData={planListData}
query={query}
totalCount={totalCount}
onChange={queryChange}
/>
</div>
</div>
)
}
export default PlanPage;
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-03 10:53:36
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Button,Input,Switch,Radio,Row,Col,Modal,message} from 'antd';
import { withRouter } from 'react-router-dom';
import SelectOperatorModal from '../modal/SelectOperatorModal';
import { ImgCutModalNew } from '@/components';
import SelectPrepareFileModal from '@/modules/prepare-lesson/modal/SelectPrepareFileModal';
import Upload from '@/core/upload';
import PhotoClip from 'photoclip'
import './BasicInfo.less';
const { TextArea } = Input;
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
let cutFlag = false;
let timer = null
class BasicInfo extends React.Component{
constructor(props) {
super(props);
this.state = {
operatorModalVisible: false,
showSelectFileModal:false,
cutImageBlob: null
};
}
handleShowSelectOperatorModal = () =>{
this.setState({
operatorModalVisible:true
})
}
handleCloseSelectOperatorMOdal = ()=>{
this.setState({
operatorModalVisible:false
})
}
handleConfirmSelectOperator = (selectOperatorList)=> {
console.log("selectOperatorList",selectOperatorList);
this.props.onChange('selectOperatorList',selectOperatorList);
this.setState({
operatorModalVisible:false
})
}
enableStateChange = ()=> {
if(this.props.data.enableState==="NO"){
this.props.onChange('enableState','YES')
}else{
this.props.onChange('enableState','NO')
}
}
// 使用默认封面图
handleResetCoverUrl = ()=> {
const { data: { coverUrl } } = this.props;
const isDefaultCover = coverUrl === defaultCover;
// 如果已经是默认图的话,不做任何任何处理
if (isDefaultCover) return;
message.success('已替换为默认图');
this.props.onChange('coverUrl',defaultCover);
setTimeout(()=>{
this.props.onChange('coverId', null);
},1000)
}
handleSelectCover = (file)=> {
this.uploadImage(file);
}
//上传图片
uploadImage = (imageFile) => {
const { folderName } = imageFile;
const fileName = window.random_string(16) + folderName.slice(folderName.lastIndexOf("."));
const self = this;
this.setState(
{
visible: true,
},
() => {
setTimeout(() => {
const okBtnDom = document.querySelector("#headPicModal");
const options = {
size: [500, 282],
ok: okBtnDom,
maxZoom: 3,
style: {
jpgFillColor: "transparent",
},
done: function (dataUrl) {
clearTimeout(self.timer);
self.timer = setTimeout(() => {
if ((self.state.rotate != this.rotate()) || (self.state.scale != this.scale())) {
console.log(this.scale(), 'scale')
const _dataUrl = this.clip()
const cutImageBlob = self.convertBase64UrlToBlob(_dataUrl);
self.setState({
cutImageBlob,
dataUrl: _dataUrl,
rotate: this.rotate(),
scale: this.scale()
})
}
}, 500)
const cutImageBlob = self.convertBase64UrlToBlob(dataUrl);
self.setState({
cutImageBlob,
dataUrl
})
setTimeout(() => {
cutFlag = false;
}, 2000);
},
fail: (failInfo) => {
message.error("图片上传失败了,请重新上传");
},
loadComplete: function (img) {
setTimeout(() => {
const _dataUrl = this.clip()
self.setState({
dataUrl: _dataUrl,
hasImgReady: true
})
}, 100)
},
};
const imgUrl = `${imageFile.ossUrl}?${new Date().getTime()}`
if (!this.state.photoclip) {
const _photoclip = new PhotoClip("#headPicModal", options);
_photoclip.load(imgUrl);
this.setState({
photoclip: _photoclip,
});
} else {
this.state.photoclip.clear();
this.state.photoclip.load(imgUrl);
}
}, 200);
}
);
};
//获取resourceId
getSignature = (blob, fileName) => {
Upload.uploadBlobToOSS(blob, 'cover' + (new Date()).valueOf(),null,'signInfo').then((signInfo) => {
this.setState({
coverClicpPath:signInfo.fileUrl,
coverId:signInfo.resourceId,
visible: false
},()=>this.updateCover())
});
};
updateCover = () =>{
const {coverClicpPath,coverId} = this.state
this.setState({
showSelectFileModal: false
})
this.props.onChange('coverUrl', coverClicpPath);
setTimeout(()=>{
this.props.onChange('coverId', coverId);
},1000)
}
// base64转换成blob
convertBase64UrlToBlob = (urlData) => {
const bytes = window.atob(urlData.split(",")[1]);
const ab = new ArrayBuffer(bytes.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: "image/png" });
};
render() {
const { operatorModalVisible ,showSelectFileModal,visible,hasImgReady,cutImageBlob} = this.state;
const { data} = this.props;
const { planName,coverUrl,instro,enableState,operateType,selectOperatorList,percentCompleteLive,percentCompleteVideo} = data;
// 当前是否使用的是默认图片
const isDefaultCover = coverUrl === defaultCover;
return (
<div className="plan-basic-info">
<div className="plan-name">
<span className="label"><span className="require">*</span>培训计划名称:</span>
<Input
value={planName}
placeholder="请输入培训计划名称,最多20字"
maxLength={20}
style={{ width: 240 }}
onChange={(e)=>this.props.onChange('planName', e.target.value)}
/>
</div>
<div className="cover">
<span className="label">封面图:</span>
<div className="cover__wrap">
<div className="img-content">
{ isDefaultCover &&
<span className="tag">默认图</span>
}
<img src={coverUrl} width="690"/>
</div>
<div className="opt-btns">
<Button onClick={() => {
this.setState({
showSelectFileModal:true
})
}}>上传图片</Button>
<span
className={`default-btn ${isDefaultCover ? 'disabled' : ''}`}
onClick={this.handleResetCoverUrl}
>使用默认图</span>
<div className="tips">建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png。</div>
</div>
</div>
</div>
<div className="introduction">
<span className="label">简介:</span>
<TextArea
placeholder="请输入培训计划简介"
maxLength={200}
style={{ width: 480 }}
className="instro-textarea"
value={instro}
onChange={(e)=>this.props.onChange('instro', e.target.value)}
/>
</div>
<div className="wether-use">
<span className="label">是否启用:</span>
<div className="content">
<div>
<Switch checked={enableState==="YES"? true:false} onChange={()=> {this.enableStateChange()}}/>
</div>
<div>
<div className="instro-text">
<div>开启:此培训计划可以分享给用户进行学习</div>
<div>关闭:此培训计划暂不可分享给用户进行学习,后续可开启</div>
</div>
</div>
</div>
</div>
<div className="view-range" >
<span className="label"><span className="require">*</span>可见范围:</span>
<div className="content">
<Radio.Group value={operateType} onChange={(e) => { this.props.onChange('operateType', e.target.value) }}>
<Row style={{ marginBottom: '5px' }}>
<Col span={24}>
<Radio value="All_Operate">
所有运营师
<span className="playback__text">后续新增的运营师都有权限可见</span>
</Radio>
</Col>
</Row>
<Row>
<Col span={8}>
<Radio value="Assign_Operate">
指定运营师
<span className="playback__text">仅被选择的运营师有权限可见</span>
</Radio>
</Col>
</Row>
</Radio.Group>
<div className="choose-business">
<Button onClick={()=>{this.handleShowSelectOperatorModal()}}>选择运营师</Button>
<span>已选择<span>{selectOperatorList.length}</span>名运营师</span>
</div>
</div>
</div>
<div className="done-standard">
<span className="label"><span className="require">*</span>完成标准:</span>
<div>
<div>
<span className="icon iconfont">&#xe865;</span>
<span>直播课单个课程,用户学习进度达到<Input type="number" width="40" value={percentCompleteLive} onChange={(e) => { this.props.onChange('percentCompleteLive', e.target.value) }}/>% 即视为“已完成”学习</span>
</div>
<div>
<span className="icon iconfont">&#xe864;</span>
<span>视频课单个课程,用户学习进度达到<Input type="number" width="40" value={percentCompleteVideo} onChange={(e) => { this.props.onChange('percentCompleteVideo', e.target.value) }} />%即视为“已完成”学习</span>
</div>
</div>
</div>
{ operatorModalVisible &&
<SelectOperatorModal
visible={operatorModalVisible}
onClose={this.handleCloseSelectOperatorMOdal}
selectOperatorList={selectOperatorList}
onSelect={this.handleConfirmSelectOperator}
/>
}
{showSelectFileModal &&
<SelectPrepareFileModal
key="basic"
operateType="select"
multiple={false}
accept="image/jpeg,image/png,image/jpg"
selectTypeList={['JPG', 'JPEG', 'PNG']}
tooltip='支持文件类型:jpg、jpeg、png'
isOpen={showSelectFileModal}
onClose={() => {
this.setState({
showSelectFileModal:false
})
}}
onSelect={this.handleSelectCover}
/>
}
<Modal
title="设置图片"
width={1080}
visible={visible}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
onCancel={() => {
this.setState({ visible: false });
}}
zIndex={10001}
footer={[
<Button
key="back"
onClick={() => {
this.setState({ visible: false });
}}
>
重新上传
</Button>,
<Button
key="submit"
type="primary"
disabled={!hasImgReady}
onClick={() => {
if (!cutFlag) {
cutFlag = true;
this.refs.hiddenBtn.click();
}
this.getSignature(cutImageBlob);
}}
>
确定
</Button>,
]}
>
<div className="clip-box">
<div
id="headPicModal"
ref="headPicModal"
style={{
width: "500px",
height: "430px",
marginBottom: 0,
}}
></div>
<div id="clipBtn" style={{ display: "none" }} ref="hiddenBtn"></div>
<div className="preview-img">
<div className="title">效果预览</div>
<div id="preview-url-box" style={{width:500,height:282}}>
<img src={this.state.dataUrl} style={{ width: '100%' }} alt="" />
</div>
</div>
</div>
</Modal>
</div>
);
}
}
export default withRouter(BasicInfo)
.plan-basic-info{
.label {
width: 110px;
text-align: right;
display:inline-block;
.require {
color: #EC4B35;
}
}
.cover {
display: flex;
margin-top: 16px;
&__wrap {
position: relative;
.tag {
border-radius: 2px;
background: #D6D6D6;
font-size: 12px;
height: 18px;
width: 52px;
text-align: center;
color: #FFF;
position: absolute;
top: 8px;
left: 8px;
}
}
.cover__wrap {
display: flex;
flex-direction: row;
}
.img-content {
margin-right: 20px;
width: 299px;
height: 169px;
img {
width: 100%;
height: 100%;
object-fit: contain;
border: 1px solid #E8e8e8;
}
}
}
.introduction{
margin-top:16px;
.instro-textarea{
vertical-align: top;
}
}
.wether-use{
display:flex;
margin-top:16px;
.instro-text{
color:#999;
margin-left:12px;
}
.content{
display:flex;
}
}
.view-range{
display:flex;
margin-top:16px;
.instro-text{
color:#999;
margin-left:12px;
}
.choose-business{
margin-top:16px;
}
}
.done-standard{
display: flex;
margin-top:22px;
input{
display:inline-block;
width:90px;
height:32px;
}
}
}
import React from 'react';
import { withRouter } from "react-router-dom";
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user';
import Bus from '@/core/bus';
import './EmployeeShareData.less';
const { Search } = Input;
const UserRole = {
Store_Manager: {
text: "店铺管理员"
},
Cloud_Manager: {
text:"管理员"
},
Cloud_Operator: {
text:'运营师'
},
Cloud_Lecture: {
text:"讲师"
},
};
class EmployeeShareData extends React.Component {
constructor(props) {
super(props);
const id = getParameterByName("id");
this.state = {
id,
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
}
}
componentDidMount(){
this.handleFetchDataList();
}
handleFetchDataList = ()=>{
const { query ,size,id} = this.state;
const params ={
...query,
size,
planId:id,
storeId:User.getStoreId(),
}
PlanService.getPlanUserRecordPage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
}
onShowSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
size
},()=>{this.handleFetchDataList()})
}
handleChangeTable = (pagination, filters, sorter)=> {
const { columnKey, order } = sorter;
const { query } = this.state;
let _columnKey;
let _order;
if (columnKey === 'learnNum' && order === 'ascend') { _columnKey="LEARN_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnNum' && order === 'descend') { _columnKey="LEARN_NUM"; _order = 'SORT_DESC'; }
if (columnKey === 'learnFinishNum' && order === 'ascend') { _columnKey="FINISH_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnFinishNum' && order === 'descend') { _columnKey="FINISH_NUM"; _order = 'SORT_DESC'; }
if (columnKey === 'learnNoFinishNum' && order === 'ascend') { _columnKey="NOT_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnNoFinishNum' && order === 'descend') { _columnKey="NOT_NUM"; _order = 'SORT_DESC'; }
const _query = {
...query,
sortMap:{}
};
_query.sortMap[_columnKey]=_order;
this.setState({
query:_query
},()=>this.handleFetchDataList())
}
handleChangNickname = (value)=>{
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
query.userPhone = value;
query.userName = null;
}else{
query.userName = value;
query.userPhone = null;
}
query.current = 1;
this.setState({
query
})
}
watchDataView = (record)=>{
Bus.trigger('watchDataView',record.storeUserId);
}
// 请求表头
parselumns = () => {
const columns = [
{
title: '员工',
key: 'storeUserName',
dataIndex: 'storeUserName',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '角色',
key: 'roleEnum',
dataIndex: 'roleEnum',
render: (val, record) => {
return (
<div>
{UserRole[record.roleEnum].text}
</div>
)
}
},
{
title: '手机号',
key: 'storeUserPhone',
dataIndex: 'storeUserPhone',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '最近分享成功时间',
key: 'recentlyForwardTime',
dataIndex: 'recentlyForwardTime',
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
},
{
title: '学习人数',
key: 'learnNum',
dataIndex: 'learnNum',
sorter:true,
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '已学完',
key: 'learnFinishNum',
dataIndex: 'learnFinishNum',
sorter:true,
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '未学完',
key: 'learnNoFinishNum',
dataIndex: 'learnNoFinishNum',
sorter:true,
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
render: (val, record) => {
return (
<span className="operate-item" onClick={()=>this.watchDataView(record)}>数据详情</span>
)
}
}
];
return columns;
}
render() {
const { dataSource,query,size,totalCount} = this.state;
return (
<div className="employee-share-data">
<div className="search-container">
<Search placeholder="搜索员工姓名手机号" onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} style={{ width: 200 }} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parselumns()}
pagination={false}
onChange={this.handleChangeTable}
bordered
/>
{dataSource.length >0 &&
<div className="box-footer">
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
</div>
</div>
)
}
}
export default withRouter(EmployeeShareData);
\ No newline at end of file
.employee-share-data{
.search-container{
margin-bottom:12px;
}
.operate-item{
font-size:14px;
color:#5289FA;
cursor: pointer;
}
}
\ No newline at end of file
/*
* @Author: 吴文洁
* @Date: 2020-07-14 15:41:30
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-23 13:45:16
* @Description: 大班直播、互动班课列表的筛选组件
*/
import React, { useState, useRef, useEffect } from 'react';
import { withRouter } from 'react-router-dom';
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 './PlanFilter.less';
const { Search } = Input;
const { Option } = Select;
const DEFAULT_QUERY = {
planName: null,
startTime: null,
endTime:null,
enableState:null,
createId: null
}
const defaultCreatorQuery = {
size: 10,
current: 1,
nickName:null
}
function PlanFilter(props) {
const [expandFilter, setExpandFilter] = useState(false);
const [query,setQuery] = useState(DEFAULT_QUERY);
const [hasNext,setHasNext] = useState(false);
const [creatorQuery,setCreatorQuery] = useState(defaultCreatorQuery);
const [creatorList,setCreatorList] = useState([]);
useEffect(() => {
getCreatorList();
}, [creatorQuery]);
// 改变搜索条件
function handleChangeQuery(field, value){
const _query ={
...query,
[field]: value,
current: 1,
}
setQuery(_query);
if (field === 'planName') return;
props.onChange( _query);
}
function handleChangeDates (dates){
const _query = _.clone(query);
if (_.isEmpty(dates)) {
delete _query.startTime;
delete _query.endTime;
} else {
_query.startTime = dates[0].valueOf();
_query.endTime = dates[1].valueOf();
}
const param ={
..._query,
current: 1,
}
setQuery(param);
props.onChange(param);
}
// 重置搜索条件
function handleReset(){
setQuery(DEFAULT_QUERY);
props.onChange(DEFAULT_QUERY);
}
function getCreatorList(current = 1, selectList){
const _query = {
...creatorQuery,
current,
size:10
};
StoreService.getStoreUserBasicPage( _query).then((res) => {
const { result = {} } = res;
const { records = [], total = 0, 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) {
getCreatorList(creatorQuery.current + 1);
}
}
return (
<div className="plan-filter">
<Row>
<div className="search-condition">
<div className="search-condition__item">
<span className="search-name">培训计划:</span>
<Search
value={query.planName}
placeholder="搜索培训计划名称"
onChange={(e) => { handleChangeQuery('planName', e.target.value)}}
onSearch={ () => { props.onChange(query) } }
style={{ width: "calc(100% - 70px)" }}
/>
</div>
<div className="search-condition__item">
<span>创建人:</span>
<Select
placeholder="请选择创建人"
style={{width:"calc(100% - 70px)"}}
showSearch
allowClear
filterOption={(input, option) => option}
onPopupScroll={handleScrollCreatorList}
value={query.createId}
onChange={(value) => {
handleChangeQuery('createId', value)
}}
onSearch={(value) => {
creatorQuery.nickName = value
setCreatorQuery(creatorQuery)
getCreatorList()
}
}
onClear ={(value)=>{
setCreatorQuery({
size: 10,
current: 1,
nickName:null
})
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="course_date_picker"
allowClear={false}
value={ query.startTime ? [moment(query.startTime), moment(query.endTime)] : null }
format={"YYYY-MM-DD"}
onChange={(dates) => { handleChangeDates(dates) }}
style={{ width: "calc(100% - 70px)" }}
/>
</div>
{ expandFilter &&
<div className="search-condition__item">
<span className="shelf-status">当前状态:</span>
<Select
style={{ width: "calc(100% - 70px)" }}
placeholder="请选择当前状态"
allowClear={true}
value={query.enableState}
onChange={(value) => { handleChangeQuery('enableState', value) }}
>
<Option value="YES">开启</Option>
<Option value="NO">关闭</Option>
</Select>
</div>
}
</div>
<div className="reset-fold-area">
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={handleReset}>&#xe61b; </span></Tooltip>
<span style={{ cursor: 'pointer' }} className="fold-btn" onClick={() => {
setExpandFilter(!expandFilter)
}}>{expandFilter ? <span><span>收起</span><span className="iconfont icon fold-icon" >&#xe82d; </span> </span> : <span>展开<span className="iconfont icon fold-icon" >&#xe835; </span></span>}</span>
</div>
</Row>
</div>
)
}
export default withRouter(PlanFilter);
\ No newline at end of file
.plan-filter {
position: relative;
.ant-input-search-button{
border-left:none;
}
.search-condition {
width: calc(100% - 80px);
display: flex;
align-items: center;
flex-wrap: wrap;
&__item {
width: 30%;
margin-right: 3%;
margin-bottom: 12px;
.search-name{
vertical-align: middle;
display:inline-block;
height:32px;
line-height:32px;
}
}
}
.reset-fold-area {
position: absolute;
right: 12px;
}
.resetBtn {
color: #999999;
font-size: 18px;
margin-right: 8px;
}
.fold-btn {
font-size: 14px;
color: #666666;
line-height: 20px;
.fold-icon {
font-size: 12px;
margin-left:4px;
}
}
}
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:46:46
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-05 10:31:11
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useRef, useEffect } from 'react';
import { Table, Modal, message , Tooltip,Switch,Dropdown} from 'antd';
import { withRouter } from 'react-router-dom';
import { PageControl } from "@/components";
import PlanService from "@/domains/plan-domain/planService";
import SharePlanModal from '../modal/SharePlanModal';
import {LIVE_SHARE} from '@/domains/course-domain/constants';
import User from '@/common/js/user';
import './PlanList.less';
const { confirm } = Modal;
const userRole = User.getUserRole();
function PlanList(props) {
const [sharePlanModal, setSharePlanModal] = useState(null);
function parseColumns(){
const columns = [
{
title: '培训计划',
key: 'planName',
dataIndex: 'planName',
width:'15%',
render: (val, record) => {
return (
<div className="plan__name">
{val}
</div>
)
}
},
{
title: '课程总数量',
key: 'courseNum',
dataIndex: 'courseNum',
width: '10%',
render: (val, record) => {
return (
<div className="course-number">
{val}
</div>
)
}
},
{
title: '当前状态',
width: '10%',
dataIndex: "status",
render: (val, item, index) => {
return (
<Switch defaultChecked={item.enableState==="NO"? false:true} onChange={()=>changeEnableState(item)} disabled={(userRole === "CloudManager" || userRole === "StoreManager")?false:true}/>
)
},
},
{
title: '创建人',
key: 'createName',
dataIndex: 'createName',
width: '10%',
render: (val) => {
return (
<div className="create-name">
{val}
</div>
)
}
},
{
title: '创建时间',
width: "10%",
key: 'created',
dataIndex: 'created',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
},
{
title: '更新时间',
width: "10%",
key: 'updated',
dataIndex: 'updated',
sorter: true,
render: (val) => {
return formatDate('YYYY-MM-DD H:i', val)
}
},
{
title: '参培人数',
width: "10%",
key: 'cultureCustomerNum',
dataIndex: 'cultureCustomerNum',
sorter: true,
render: (val) => {
return (
<div className="join-number">
{val}
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
width: '25%',
render: (val, record) => {
return (
<div className="operate">
<div className="operate__item" onClick={()=>toLearningDataPage(record)}>学习数据</div>
<span className="operate__item split"> | </span>
<div className="operate__item" onClick={() => {handleShowShareModal(record); }}>分享</div>
{(userRole === "CloudManager" || userRole === "StoreManager") &&
<>
<span className="operate__item split"> | </span>
<Dropdown overlay={renderMoreOperate(record)}>
<span className="more-operate">
<span className="operate-text">更多</span>
<span
className="iconfont icon"
style={{ color: "#5289FA" }}
>
&#xe824;
</span>
</span>
</Dropdown>
</>
}
</div>
)
}
}
];
return columns;
}
function renderMoreOperate(item){
return (
<div className="live-course-more-menu">
<div className="operate__item"
onClick={()=>toEditPlanPage(item)}
>编辑</div>
<div
className="operate__item" onClick={()=>handleDelete(item)}
>删除</div>
</div>
)
}
function handleChangeTable(pagination, filters, sorter){
const { columnKey, order } = sorter;
const { query } = props;
let _columnKey;
let _order;
// 按创建时间升序排序
if (columnKey === 'created' && order === 'ascend') {_columnKey="UPDATED"; _order = 'SORT_ASC'; }
// 按创建时间降序排序
if (columnKey === 'created' && order === 'descend') { _columnKey="UPDATED"; _order = 'SORT_DESC';}
// 按更新时间升序排序
if (columnKey === 'updated' && order === 'ascend') { _columnKey="CREATED"; _order = 'SORT_ASC'; }
// 按更新时间降序排序
if (columnKey === 'updated' && order === 'descend') { _columnKey="CREATED"; _order = 'SORT_DESC'; }
// 按更新时间升序排序
if (columnKey === 'cultureCustomerNum' && order === 'ascend') { _columnKey="CUSTOMER_NUM"; _order = 'SORT_ASC'; }
// 按更新时间降序排序
if (columnKey === 'cultureCustomerNum' && order === 'descend') { _columnKey="CUSTOMER_NUM"; _order = 'SORT_DESC'; }
const _query = {
...query,
sortMap:{}
};
_query.sortMap[_columnKey]=_order;
props.onChange(_query);
}
// 显示分享弹窗
function handleShowShareModal(item) {
const htmlUrl = `${LIVE_SHARE}training_plan_detail/${item.planId}?storeId=${User.getStoreId()}&storeUserId=${User.getStoreUserId()}`;
const longUrl = htmlUrl
const shareData = { ...item, longUrl };
const sharePlanModal = (
<SharePlanModal
data={shareData}
type="liveClass"
close={() => {
setSharePlanModal(null)
}}
/>
)
setSharePlanModal(sharePlanModal)
}
//改变上架状态
function changeEnableState(item){
let _enableState = item.enableState
if(_enableState==='NO'){
_enableState = "YES";
item.enableState = "YES"
}else{
_enableState = "NO"
item.enableState = "NO"
}
const params={
"planId": item.planId,
"enableState":_enableState
}
PlanService.updateStateTrainingPlan(params).then((res)=>{
if(res.success){
if(_enableState === "YES"){
message.success("已启用此计划");
}else{
message.success("已禁用此计划");
}
props.onChange();
}
})
// let _enableState = record.enableState
// if(_enableState==='NO'){
// // _enableState = "YES";
// const params={
// "planId": record.planId,
// "enableState":"YES"
// }
// PlanService.updateStateTrainingPlan(params).then((res)=>{
// if(res.success){
// // if(_enableState === "YES"){
// record.enableState = "YES";
// message.success("已启用此计划");
// // }
// }
// })
// }else{
// // _enableState = "NO";
// // item.enableState = "YES";
// return confirm({
// title: "确定要禁用培训计划吗?",
// content: "禁用后,培训计划不再支持新用户加入,已参与培训的用户可继续培训",
// icon: (
// <span className="icon iconfont default-confirm-icon">&#xe839; </span>
// ),
// okText: "确定",
// okType: "danger",
// cancelText: "取消",
// onOk: () => {
// const params={
// "planId": record.planId,
// "enableState":"NO"
// }
// PlanService.updateStateTrainingPlan(params).then((res)=>{
// if(res.success){
// // if(_enableState === "NO"){
// record.enableState = "NO";
// message.success("已禁用此计划");
// // }
// }
// })
// },
// });
// }
}
function toEditPlanPage(item){
window.RCHistory.push({
pathname: `/create-plan?type=edit&id=${item.planId}`,
})
}
function toLearningDataPage(item){
window.RCHistory.push({
pathname: `/learning-data?id=${item.planId}`,
})
}
function handleDelete(record){
return confirm({
title: '你确定要删除吗?',
content: '删除后,此培训计划的用户将无法继续学习,所有学习数据将同步删除不可恢复',
icon: <span className="icon iconfont default-confirm-icon">&#xe839; </span>,
okText: '删除',
okType: 'danger',
cancelText: '取消',
width:440,
height:188,
onOk: () => {
if(record.enableState === "YES"){
Modal.warning({
title: '无法删除',
content: '培训计划启用中,无法直接删除',
});
}else{
deleteConfirm(record);
}
}
})
}
function deleteConfirm(item){
const params={
"planId": item.planId,
}
PlanService.deleteTrainingPlan(params).then((res)=>{
if(res.success){
message.success("删除成功");
props.onChange();
}
})
}
function onShowSizeChange(current, size){
if (current === size) {
return
}
let _query = props.query
_query.size = size;
props.onChange(_query)
}
return (
<div className="plan-list">
<Table
rowKey={record => record.id}
dataSource={props.planListData}
columns={ parseColumns() }
pagination={false}
onChange={handleChangeTable}
bordered
/>
<div className="box-footer">
<PageControl
current={props.query.current - 1}
pageSize={props.query.size}
total={props.totalCount}
toPage={(page) => {
const _query = {...props.query, current: page + 1};
props.onChange(_query)
}}
onShowSizeChange={onShowSizeChange}
/>
</div>
{sharePlanModal }
</div>
)
}
export default withRouter(PlanList);
\ No newline at end of file
.plan-list{
margin-top:12px;
.operate-text {
color: #5289FA;
cursor: pointer;
}
.operate {
display: flex;
&__item {
color: #5289FA;
cursor: pointer;
&.split {
margin: 0 8px;
color: #BFBFBF;
}
}
}
}
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 16:43:16
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Button } from 'antd';
import { withRouter } from 'react-router-dom';
import User from '@/common/js/user';
import './PlanOpt.less';
const userRole = User.getUserRole();
function PlanOpt() {
function handleCreatePlan(){
window.RCHistory.push({
pathname: '/create-plan?type=add',
})
}
return (
<div className="plan-opt">
{ (userRole === "CloudManager" || userRole === "StoreManager") &&
<Button
type="primary"
className="mr12"
onClick={handleCreatePlan}
>新建培训计划</Button>
}
</div>
);
}
export default withRouter(PlanOpt)
.plan-opt{
margin-top:4px;
}
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-03 10:06:14
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React from 'react';
import { Table ,Button,Input,Form} from 'antd';
import { sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc';
import { MenuOutlined } from '@ant-design/icons';
import arrayMove from 'array-move';
import RelatedCourseModal from '../modal/relatedCourseModal'
import { withRouter } from 'react-router-dom';
import './TrainingTask.less';
const DragHandle = sortableHandle(() => (
<span style={{ cursor: 'pointer', color: '#999' }} >
<span className="icon iconfont">&#xe7cd;</span>
<span>移动</span>
</span>
));
const SortableItem = sortableElement(props => <tr {...props} />);
const SortableContainer = sortableContainer(props => <tbody {...props} />);
class TrainingTask extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
selectedTaskIndex:0,
relatedCourseModalVisible:false
};
}
componentDidMount(){
console.log('data',this.props.data);
}
parseTaskColumns = ()=>{
const columns = [
{
title: 'taskName',
dataIndex: 'taskName',
className: 'drag-visible',
render: (val, record,index) => {
return (
<div>
{record.type==='input'?
<Form>
<Form.Item
validateTrigger={['onChange', 'onBlur']}
name={['taskName']}
rules={[
{
required: true,
message: "请输入任务名称",
},
]}>
<Input defaultValue={record.taskName} style={{ width: 300 }} placeholder="请输入任务名称(20字以内)" maxLength={20} onChange={(e) => { this.handleRenameTaskName(e,record)}} onBlur={(e)=>{this.handleTaskNameBlur(e,record)}}/>
</Form.Item>
</Form>
:
<span>{val}</span>
}
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
width: '25%',
render: (val, record,index) => {
return (
<div className="operate">
<DragHandle />
<span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span>
<span onClick={(e)=>{const { data }= this.props; record.type="input";this.props.onChange(data);}}>重命名</span>
</span>
<span className="operate__item" onClick={()=>{this.handleDeleteTask(index)}} >
<span className="icon iconfont">&#xe6f6;</span>
<span>删除</span>
</span>
</div>
)
}
}
];
return columns;
}
parseCoursecolumns = (parentIndex)=>{
const coursecolumns = [
{
title: 'courseName',
dataIndex: 'courseName',
className: 'drag-visible',
render: (val, record) => {
return (
<div>
{record.type==='input'?
<Form>
<Form.Item
validateTrigger={['onChange', 'onBlur']}
name={['courseName']}
rules={[
{
required: true,
message: "请输入课程名称",
},
]}>
<Input defaultValue={record.courseName} style={{ width: 300 }} placeholder="请输入任务名称(40字以内)" maxLength={40} onChange={(e) => { this.handleRenameCourseName(e,record)}} onBlur={(e)=>{this.handleCourseNameBlur(e,record)}}/></Form.Item>
</Form>
:
<span>{record.courseName}</span>
}
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
width: '25%',
render: (val, record,index) => {
return (
<div className="operate">
<DragHandle />
<span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span>
<span onClick={(e)=>{const { data } = this.props; record.type="input";this.props.onChange(data);}}>重命名</span>
</span>
<span className="operate__item" onClick={()=>{this.handleDeleteCourse(parentIndex,index)}}>
<span className="icon iconfont">&#xe6f6;</span>
<span>删除</span>
</span>
</div>
)
}
}
];
return coursecolumns
}
// onSortEnd = ({ oldIndex, newIndex }) => {
// const { dataSource } = this.state;
// if (oldIndex !== newIndex) {
// const newData = arrayMove([].concat(dataSource), oldIndex, newIndex).filter(el => !!el);
// console.log('Sorted items: ', newData);
// this.setState({ dataSource: newData });
// }
// };
// DraggableContainer = props => (
// <SortableContainer
// useDragHandle
// disableAutoscroll
// helperClass="row-dragging"
// onSortEnd={this.onSortEnd}
// {...props}
// />
// );
// DraggableBodyRow = ({ className, style, ...restProps }) => {
// const { dataSource } = this.state;
// // function findIndex base on Table rowKey props and should always be a right array index
// const index = dataSource.findIndex(x => x.index === restProps['data-row-key']);
// return <SortableItem className={className} index={index} {...restProps} style={style}/>;
// };
addTask = () => {
const { data } = this.props;
const taskObj={
taskName: '',
index:data.length,
type:'input',
courseList:[
]
}
const newData = [...data,taskObj];
this.props.onChange(newData);
}
handleRenameTaskName = (e,record) => {
const { value } = e.target;
const { data } = this.props;
record.taskName = value;
this.props.onChange(data);
}
handleTaskNameBlur = (e,record)=>{
const { value } = e.target;
const {data}= this.props;
if(value){
record.type="text";
this.props.onChange(data);
}
}
handleRenameCourseName = (e,record) => {
const { value } = e.target;
const { data } = this.props;
record.courseName = value;
this.props.onChange(data);
}
handleCourseNameBlur = (e,record)=>{
const { value } = e.target;
const { data }= this.props;
if(value){
record.type="text";
this.setState({data});
}
}
handleDeleteTask = (index)=>{
const {data}= this.props;
const newData=[...data];
newData.splice(index,1);
this.props.onChange(newData);
}
handleDeleteCourse = (parentIndex,index)=>{
const {data}= this.props;
const newData=[...data];
const selectData = [...newData[parentIndex].courseList]
selectData.splice(index,1)
newData[parentIndex].courseList= selectData;
this.props.onChange(newData);
}
showRelatedCourseModal = (index)=>{
this.setState({
selectedTaskIndex:index,
relatedCourseModalVisible:true
})
}
closeRelatedCourseModal = (index)=>{
this.setState({
relatedCourseModalVisible:false
})
}
confirmSelectCourse = (selectList) =>{
const {selectedTaskIndex}= this.state;
const { data } = this.props
const newData=[...data];
const selectData = [...newData[selectedTaskIndex].courseList]
const _selectData =[...selectData,...selectList];
newData[selectedTaskIndex].courseList= _selectData;
this.setState({
relatedCourseModalVisible:false,
},()=>{
this.props.onChange(newData);
})
}
render() {
const { dataSource,selectedTaskIndex,relatedCourseModalVisible} = this.state;
const { data } = this.props;
return (
<div className="training-task">
<Table
pagination={false}
dataSource={data}
columns={this.parseTaskColumns()}
rowKey="index"
expandedRowRender={(record,index) => {
if (record.courseList.length !== 0 ){
return <div>
<Table
pagination={false}
dataSource={record.courseList}
columns={this.parseCoursecolumns(index)}
rowKey="index"
// components={{
// body: {
// wrapper: this.DraggableContainer,
// row: this.DraggableBodyRow,
// },
// }}
/>
<div><Button onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></Button></div>
</div>
}else{
return <div><Button onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></Button></div>;
}
}}
// components={{
// body: {
// wrapper: this.DraggableContainer,
// row: this.DraggableBodyRow,
// },
// }}
/>
<div><Button onClick={()=>this.addTask()}><span>+</span><span>添加任务</span></Button></div>
{ relatedCourseModalVisible &&
<RelatedCourseModal
selectedTaskIndex={selectedTaskIndex}
data={data}
visible={relatedCourseModalVisible}
onClose={this.closeRelatedCourseModal}
onSelect={this.confirmSelectCourse}
/>
}
</div>
);
}
}
export default withRouter(TrainingTask)
.training-task{
thead{
display:none;
}
}
import React from 'react';
import { withRouter } from "react-router-dom";
import {Table, Modal,Input,message,Tooltip} from 'antd';
import { PageControl } from "@/components";
import UserLearningDataFilter from './UserLearningDataFilter';
import PlanService from '@/domains/plan-domain/planService';
import UserLearnDetailModal from "../modal/UserLearnDetailModal";
import UnbundEmployeeModal from '../modal/UnbundEmployeeModal'
import User from '@/common/js/user';
import './UserLearningData.less';
const userRole = User.getUserRole();
const { Search } = Input;
const { confirm } = Modal;
const LearnState = {
UN_PLAY: {
text: "未开始"
},
UNDER_WAY: {
text:"进行中"
},
FINISH : {
text:'已完成'
}
};
class UserLearningData extends React.Component {
constructor(props) {
super(props);
const id = getParameterByName("id");
this.state = {
id,
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
userLearnDetailModalSHow:false,
unbundEmployeeModalVisible:false
}
}
componentDidMount(){
this.handleFetchDataList();
}
handleFetchDataList = (_query)=>{
const { query ,size,id} = this.state;
const params ={
...query,
..._query,
size,
planId:id,
storeId:User.getStoreId(),
storeUserId:User.getStoreUserId(),
}
this.setState({ query: params });
PlanService.getPlanCustomerRecordPage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
}
onShowSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
size
},()=>{this.handleFetchDataList()})
}
handleChangeTable = (pagination, filters, sorter)=> {
const { columnKey, order } = sorter;
const { query } = this.state;
let _columnKey;
let _order;
if (columnKey === 'latelyLearnTime' && order === 'ascend') {_columnKey="LATE_LEARN_TIME"; _order = 'SORT_ASC'; }
if (columnKey === 'latelyLearnTime' && order === 'descend') { _columnKey="LATE_LEARN_TIME"; _order = 'SORT_DESC';}
if (columnKey === 'startLearnTime' && order === 'ascend') { _columnKey="START_LEARN_TIME"; _order = 'SORT_ASC'; }
if (columnKey === 'startLearnTime' && order === 'descend') { _columnKey="START_LEARN_TIME"; _order = 'SORT_DESC'; }
if (columnKey === 'learnNum' && order === 'ascend') { _columnKey="LEARN_NUM"; _order = 'SORT_ASC'; }
if (columnKey === 'learnNum' && order === 'descend') { _columnKey="LEARN_NUM"; _order = 'SORT_DESC'; }
const _query = {
...query,
sortMap:{}
};
_query.sortMap[_columnKey]=_order;
this.setState({
query:_query
}, ()=>this.handleFetchDataList())
}
watchDetail = (record)=>{
this.setState({
userLearnDetailModalSHow:true,
storeCustomerId:record.storeCustomerId,
planId:record.planId
})
}
closeUserLearnDetailModal = ()=>{
this.setState({
userLearnDetailModalSHow:false,
})
}
UnbundEmployee = (record)=>{
if(userRole==='CloudOperator'){
return confirm({
title: "你确定要解绑与用户的关系吗?",
content: "解绑后,用户该培训计划的学习数据将同步移出",
icon: (
<span className="icon iconfont default-confirm-icon">&#xe839; </span>
),
okText: "删除",
okType: "danger",
cancelText: "取消",
onOk: () => {
this.handleConfirmUnbundEmployee(record.storeCustomerId);
},
});
}else{
this.setState({
unbundEmployeeModalVisible:true,
storeCustomerId:record.storeCustomerId
})
}
}
handleConfirmUnbundEmployee=(storeCustomerId)=>{
let removeUserIds = [];
removeUserIds.push(storeCustomerId)
const params = {
planId:getParameterByName("id"),
removeUserIds,
storeCustomerId:storeCustomerId,
storeId:User.getStoreId(),
storeUserId:User.getStoreUserId()
}
PlanService.removePlanCustomer(params).then((res) => {
message.success('解绑成功');
});
}
handleCloseUnbundEmployeeModal = ()=>{
this.setState({
unbundEmployeeModalVisible:false,
})
}
// 请求表头
parselumns = () => {
let columns;
if(userRole === "CloudManager" || userRole === "StoreManager"){
columns = [
{
title: '用户',
key: 'storeCustomerName',
dataIndex: 'storeCustomerName',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '学习状态',
key: 'learnState',
dataIndex: 'learnState',
render: (val, record) => {
return (
<div>
{LearnState[val].text}
</div>
)
}
},
{
title: <span>
<span>负责人</span>
<Tooltip title="用户加入学习时,培训计划的分享者"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip>
</span>,
key: 'userNameList',
dataIndex: 'userNameList',
render: (val, record) => {
return (
<div>
{record.userNameList.map((item,index)=>{
return <span>{item} { (index < record.userNameList.length-1)&&(<span></span>)} </span>
})}
</div>
)
}
},
{
title: '最近学习时间',
key: 'latelyLearnTime',
dataIndex: 'latelyLearnTime',
sorter:true,
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
},
{
title: '开始学习时间',
key: 'startLearnTime',
dataIndex: 'startLearnTime',
sorter:true,
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
},
{
title: <span>
<span>学习进度</span>
<Tooltip title="用户培训计划中达到“已完成”状态的课程数/总课程数"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip>
</span>,
key: 'learnNum',
dataIndex: 'learnNum',
sorter:true,
render: (val, record) => {
return (
<div>
<span>{record.courseFinishNum}</span>
<span>/</span>
<span>{record.courseNum}</span>
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
render: (val, record) => {
return (
<div className="operate-area">
<span className="operate-item" onClick={()=>this.watchDetail(record)}>学习详情</span>
<span className="split">|</span>
<span className="operate-item" onClick={()=>{this.UnbundEmployee(record)}}>解绑</span>
</div>
)
}
}
];
}else{
columns = [
{
title: '用户',
key: 'storeCustomerName',
dataIndex: 'storeCustomerName',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '学习状态',
key: 'learnState',
dataIndex: 'learnState',
render: (val, record) => {
return (
<div>
{LearnState[val].text}
</div>
)
}
},
{
title: '最近学习时间',
key: 'latelyLearnTime',
dataIndex: 'latelyLearnTime',
sorter:true,
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
},
{
title: '开始学习时间',
key: 'startLearnTime',
dataIndex: 'startLearnTime',
sorter:true,
render: (val, record) => {
return (
<div>
{formatDate('YYYY-MM-DD H:i', val)}
</div>
)
}
},
{
title: <span>
<span>学习进度</span>
<Tooltip title="用户培训计划中达到“已完成”状态的课程数/总课程数"><i className="icon iconfont" style={{ marginLeft: '5px',cursor:'pointer',color:'#bfbfbf',fontSize:'14px'}}>&#xe61d;</i></Tooltip>
</span>,
key: 'learnNum',
dataIndex: 'learnNum',
sorter:true,
render: (val, record) => {
return (
<div>
<span>{record.courseFinishNum}</span>
<span>/</span>
<span>{record.courseNum}</span>
</div>
)
}
},
{
title: '操作',
key: 'operate',
dataIndex: 'operate',
render: (val, record) => {
return (
<div className="operate-area">
<span className="operate-item" onClick={()=>this.watchDetail(record)}>学习详情</span>
<span className="split">|</span>
<span className="operate-item" onClick={()=>{this.UnbundEmployee(record)}}>解绑</span>
</div>
)
}
}
];
}
return columns;
}
render() {
const { dataSource,query,size,totalCount,userLearnDetailModalSHow,storeCustomerId,planId,unbundEmployeeModalVisible} = this.state;
return (
<div className="user-learning-data">
<div className="search-container">
<UserLearningDataFilter onChange={this.handleFetchDataList}/>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parselumns()}
pagination={false}
className="user-learning-table"
onChange={this.handleChangeTable}
bordered
/>
{dataSource.length >0 &&
<div className="box-footer">
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
</div>
{ userLearnDetailModalSHow &&
<UserLearnDetailModal storeCustomerId={storeCustomerId} planId={planId} visible={userLearnDetailModalSHow} onClose={this.closeUserLearnDetailModal}/>
}
{unbundEmployeeModalVisible &&
<UnbundEmployeeModal
visible={unbundEmployeeModalVisible}
onClose={this.handleCloseUnbundEmployeeModal}
storeCustomerId={storeCustomerId}
/>
}
</div>
)
}
}
export default withRouter(UserLearningData);
\ No newline at end of file
.user-learning-data{
.user-learning-table{
margin-top:4px;
}
.operate-area{
.operate-item{
font-size:14px;
color:#5289FA;
cursor: pointer;
}
.split{
margin:0 4px;
color: #BFBFBF;
}
}
}
\ No newline at end of file
/*
* @Author: 吴文洁
* @Date: 2020-07-14 15:41:30
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-23 13:45:16
* @Description: 大班直播、互动班课列表的筛选组件
*/
import React, { useState, useRef, useEffect } from 'react';
import { withRouter } from 'react-router-dom';
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 User from '@/common/js/user'
import Bus from '@/core/bus';
import './UserLearningDataFilter.less';
const { Search } = Input;
const { Option } = Select;
const userRole = User.getUserRole();
const DEFAULT_QUERY = {
customerName: null,
startTime: null,
endTime:null,
learnState:null,
operateId: null
}
const defaultCreatorQuery = {
size: 10,
current: 1,
nickName:null
}
function UserLearningDataFilter(props) {
const [expandFilter, setExpandFilter] = useState(false);
const [query,setQuery] = useState(DEFAULT_QUERY);
const [hasNext,setHasNext] = useState(false);
const [creatorQuery,setCreatorQuery] = useState(defaultCreatorQuery);
const [creatorList,setCreatorList] = useState([]);
useEffect(() => {
Bus.bind('watchDataView',(value) => handleChangeCreatorQuery(value))
}, []);
useEffect(() => {
getCreatorList();
}, [creatorQuery]);
function handleChangeCreatorQuery (value){
const _creatorQuery = {...creatorQuery};
_creatorQuery.operateId = value;
setCreatorQuery(_creatorQuery);
}
// 改变搜索条件
function handleChangeQuery(field, value){
const _query ={
...query,
[field]: value,
current: 1,
}
setQuery(_query);
if (field === 'customerName') return;
props.onChange( _query);
}
function handleChangeDates (dates){
const _query = _.clone(query);
if (_.isEmpty(dates)) {
delete _query.startTime;
delete _query.endTime;
} else {
_query.startTime = dates[0].valueOf();
_query.endTime = dates[1].valueOf();
}
const param ={
..._query,
current: 1,
}
setQuery(param);
props.onChange(param);
}
// 重置搜索条件
function handleReset(){
setQuery(DEFAULT_QUERY);
props.onChange(DEFAULT_QUERY);
}
function getCreatorList(current = 1, selectList){
const _query = {
...creatorQuery,
current,
size:10
};
StoreService.getStoreUserBasicPage( _query).then((res) => {
const { result = {} } = res;
const { records = [], total = 0, 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) {
getCreatorList(creatorQuery.current + 1);
}
}
return (
<div className="user-learn-data-filter">
<Row>
<div className="search-condition">
<div className="search-condition__item">
<span className="label customer-label">用户:</span>
<Search
value={query.customerName}
placeholder="搜索用户名称"
onChange={(e) => { handleChangeQuery('customerName', e.target.value)}}
onSearch={ () => { props.onChange(query) } }
style={{ width: "calc(100% - 70px)" }}
enterButton={<span className="icon iconfont">&#xe832;</span>}
/>
</div>
{(userRole === "CloudManager" || userRole === "StoreManager")&&
<div className="search-condition__item">
<span className="label lead-label">负责人:</span>
<Select
id="leadSelect"
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.operateId}
onChange={(value) => {
handleChangeQuery('operateId', value)
}}
onSearch={(value) => {
creatorQuery.nickName = value
setCreatorQuery(creatorQuery)
getCreatorList();
}
}
onClear ={(value)=>{
setCreatorQuery({
size: 10,
current: 1,
nickName:null
})
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="label learn-date-label">最近学习日期:</span>
<RangePicker
id="course_date_picker"
allowClear={false}
value={ query.startTime ? [moment(query.startTime), moment(query.endTime)] : null }
format={"YYYY-MM-DD"}
onChange={(dates) => { handleChangeDates(dates) }}
style={{ width: "calc(100% - 98px)" }}
/>
</div>
{ ((expandFilter && (userRole === "CloudManager" || userRole === "StoreManager")) || userRole === "CloudOperator")&&
<div className="search-condition__item">
<span className="label learn-status-label">学习状态:</span>
<Select
style={{ width: "calc(100% - 70px)" }}
placeholder="请选择当前状态"
allowClear={true}
value={query.learnState}
onChange={(value) => { handleChangeQuery('learnState', value) }}
>
<Option value="YES">开启</Option>
<Option value="NO">关闭</Option>
</Select>
</div>
}
</div>
{(userRole === "CloudManager" || userRole === "StoreManager")&&
<div className="reset-fold-area">
<Tooltip title="清空筛选"><span className="resetBtn iconfont icon" onClick={handleReset}>&#xe61b; </span></Tooltip>
<span style={{ cursor: 'pointer' }} className="fold-btn" onClick={() => {
setExpandFilter(!expandFilter)
}}>{expandFilter ? <span><span>收起</span><span className="iconfont icon fold-icon" >&#xe82d; </span> </span> : <span>展开<span className="iconfont icon fold-icon" >&#xe835; </span></span>}</span>
</div>
}
</Row>
</div>
)
}
export default withRouter(UserLearningDataFilter);
\ No newline at end of file
.user-learn-data-filter {
position: relative;
.ant-input-search-button{
border-left:none;
}
.search-condition {
width: calc(100% - 80px);
display: flex;
align-items: center;
flex-wrap: wrap;
&__item {
width: 30%;
margin-right: 3%;
margin-bottom: 12px;
.label{
font-size:14px;
color:#666;
vertical-align: middle;
width:70px;
text-align:right;
display:inline-block;
}
.learn-date-label{
width:98px;
text-align:right;
display:inline-block;
}
.ant-input-group-wrapper{
vertical-align: middle;
}
}
}
.reset-fold-area {
position: absolute;
right: 12px;
}
.resetBtn {
color: #999999;
font-size: 18px;
margin-right: 8px;
}
.fold-btn {
font-size: 14px;
color: #666666;
line-height: 20px;
.fold-icon {
font-size: 12px;
margin-left:4px;
}
}
}
\ No newline at end of file
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import StoreService from "@/domains/store-domain/storeService";
import User from '@/common/js/user'
import './SelectOperatorModal.less';
import _ from "underscore";
const { Search } = Input;
class SelectOperatorModal extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
selectOperatorList:props.selectOperatorList
};
}
componentDidMount() {
this.handleFetchDataList();
}
// 获取运营师列表
handleFetchDataList = () => {
const {query,size,totalCount} = this.state
const params ={
...query,
size,
roleCodes:['CloudOperator']
}
StoreService.getStoreUserBasicPage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
this.setState({
dataSource: records,
totalCount: Number(total)
});
});
}
handleChangNickname = (value)=>{
const isPhone = (value || '').match(/^\d+$/);
const { query } = this.state;
if(isPhone){
query.phone = value;
query.nickName = null;
}else{
query.nickName = value;
query.phone = null;
}
query.current = 1;
this.setState({
query
})
}
onShowSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
size
},()=>{this.handleFetchDataList()})
}
// 请求表头
parseColumns = () => {
const columns = [
{
title: '姓名',
key: 'nickName',
dataIndex: 'nickName'
},
{
title: '手机号',
key: 'phone',
dataIndex: 'phone'
}
];
return columns;
}
selectOperator = (record,selected) =>{
const {selectOperatorList} = this.state;
let _list = [];
if (selected || !_.find(selectOperatorList, (item) => item.id == record.id)) {
_list = _.uniq(selectOperatorList.concat([record]), false, (item) => item.id);
} else {
_list = _.reject(selectOperatorList, (item) => item.id === record.id);
}
this.setState({selectOperatorList:_list});
}
render() {
const { size,dataSource,totalCount,query,selectOperatorList} = this.state;
const { visible } = this.props;
return (
<Modal
title="选择运营师"
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="select-operator-modal"
closable={true}
width={800}
onOk={() => this.props.onSelect(selectOperatorList) }
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="search-container">
<Search placeholder="搜索运营师/手机号" style={{ width: 200 }} onChange={(e) => { this.handleChangNickname(e.target.value)}} onSearch={ () => { this.handleFetchDataList()}} />
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(selectOperatorList, 'id'),
onSelect: (record, selected) => {
this.selectOperator(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
if (selected) {
_list = _.uniq(selectOperatorList.concat(changeRows), false, (item) => item.id);
} else {
_list = _.reject(selectOperatorList, (item) => _.find(changeRows, (data) => data.id === item.id));
}
this.setState({selectOperatorList:_list});
},
}}
/>
{dataSource.length >0 &&
<div className="box-footer">
<PageControl
current={query.current - 1}
pageSize={size}
total={totalCount}
toPage={(page) => {
const _query = {...query, current: page + 1};
this.setState({
query:_query
},()=>{ this.handleFetchDataList()})
}}
onShowSizeChange={this.onShowSizeChange}
/>
</div>
}
</div>
</Modal>
)
}
}
export default SelectOperatorModal;
\ No newline at end of file
.select-operator-modal{
.search-container{
margin-bottom:16px;
}
}
\ No newline at end of file
/*
* @Author: 吴文洁
* @Date: 2020-07-20 19:12:49
* @Last Modified by: 吴文洁
* @Last Modified time: 2020-07-20 20:25:13
* @Description: 大班直播分享弹窗
*/
import React from 'react';
import { Modal, Input, Button, message } from 'antd';
import domtoimage from 'dom-to-image';
import qrcode from "@/libs/qrcode/qrcode.js";
import User from '@/common/js/user';
import $ from 'jquery';
import CourseService from "@/domains/course-domain/CourseService";
import './SharePlanModal.less';
const storeName = User.getStoreName();
const DEFAULT_COVER = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
class ShareLiveModal extends React.Component {
constructor(props) {
super(props);
this.state = {
shareUrl: ''
}
}
componentDidMount() {
// 获取短链接
this.handleConvertShortUrl();
}
handleConvertShortUrl = () => {
const { longUrl } = this.props.data;
// 发请求
CourseService.getQrcode({
urls: [longUrl]
}).then((res) => {
const { result = [] } = res;
this.setState({
shareUrl: result[0].shortUrl
}, () => {
const qrcodeWrapDom = document.querySelector('#qrcodeWrap');
const qrcodeNode = new qrcode({
text: this.state.shareUrl,
size: 98,
})
qrcodeWrapDom.appendChild(qrcodeNode);
});
})
}
componentWillUnmount() {
// 页面销毁之前清空定时器
clearTimeout(this.timer);
}
// 下载海报
handleDownloadPoster = () => {
this.setState({
showImg:true,
time:new Date().valueOf()
},()=>{
this.setState({time:new Date().valueOf()},()=>{
let node = document.getElementById('poster');
domtoimage.toPng(node)
.then((imgData) => {
console.log(imgData)
const download = document.createElement('a');
const { planName } = this.props.data;
$(download).attr('href', imgData).attr('download', `${planName}.png`).get(0).click();
})
})
})
}
// 复制分享链接
handleCopy = () => {
const textContent = document.getElementById('shareUrl').innerText;
window.copyText(textContent);
message.success('复制成功!');
}
render() {
const {data} = this.props;
const { planName, coverUrl = DEFAULT_COVER} = data;
const { shareUrl,showImg,time} = this.state;
return (
<Modal
title={'分享培训计划'}
width={680}
visible={true}
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="share-live-modal"
onCancel={this.props.close}
>
<div className="left">
<div id="poster">
<div className="store-name">
<span className="text">{storeName}</span>
</div>
<div className="course-name-title">邀请你参与培训:</div>
<div class="live-couse-name">{planName}</div>
{
showImg ? <img
crossOrigin='*'
src={coverUrl+`?=${time}`}
className="course-cover"
/>: <img
src={coverUrl+`?=${time}`}
className="course-cover"
/>
}
<div className="qrcode-wrap">
<div className="qrcode-wrap__left">
<div className="text">长按识别二维码进入观看</div>
<img className="finger" src="https://image.xiaomaiketang.com/xm/thpkWDwJsC.png"/>
</div>
<div className="qrcode-wrap__right" id="qrcodeWrap">
</div>
</div>
</div>
</div>
<div className="right">
<div className="share-poster right__item">
<div className="title">① 海报分享</div>
<div className="sub-title">用户可通过微信扫描海报二维码,查看培训计划</div>
<div className="content" onClick={this.handleDownloadPoster}>下载海报</div>
</div>
<div className="share-url right__item">
<div className="title">② 链接分享</div>
<div className="sub-title">用户可通过微信打开以下链接,查看培训计划</div>
<div className="content url-content">
<div className="share-url" id="shareUrl">{shareUrl}</div>
<Button type="primary" onClick={this.handleCopy}>复制</Button>
</div>
</div>
</div>
</Modal>
)
}
}
export default ShareLiveModal;
.share-live-modal {
.ant-modal-body {
display: flex;
#poster{
background: #FFF;
margin:0;
padding: 20px;
}
.left {
width: 303px;
margin: 0 32px 0 16px;
box-shadow:0px 2px 10px 0px rgba(0,0,0,0.05);
border-radius: 12px;
.course-name-title {
font-size: 14px;
color: #333;
line-height: 20px;
margin-bottom: 4px;
}
.live-couse-name{
font-size:16px;
color:#333333;
font-weight: 600;
}
.course-name {
color: #333;
font-size: 16px;
font-weight: 600;
line-height: 20px;
}
.course-cover {
width: 263px;
height: 143px;
border-radius: 6px;
margin-top: 8px;
}
.qrcode-wrap {
padding: 0 16px;
display: flex;
align-items: center;
margin: 24px 0 16px 0;
&__left {
width: 98px;
text-align: center;
margin-right: 22px;
.text {
line-height: 20px;
}
.finger {
width: 40px;
height: 40px;
margin-top: 8px;
}
}
&__right {
width: 110px;
height: 110px;
padding: 6px
}
}
.store-name {
// padding: 8px 16px;
display: flex;
align-items: center;
margin-bottom: 8px;
.text {
font-size: 12px;
color: #999;
font-size: 14px;
line-height: 20px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
}
}
}
.right {
.title {
color: #333;
font-weight: 500;
}
.sub-title {
color: #999;
margin-top: 16px;
}
.content {
display: flex;
align-items: center;
margin-top: 8px;
.share-url {
width: 212px;
overflow: hidden;
height: 28px;
line-height: 28px;
border-radius: 4px 0 0 4px;
padding-left: 12px;
white-space: nowrap;
color: #999999;
background: #EFEFEF;
}
.ant-btn {
margin-left: -2px;
}
}
.url-content{
position:relative;
&:after{
content:'';
width: 12px;
height: 22px;
background: #EFEFEF;
position:absolute;
right:71px;
}
}
.share-poster {
margin-bottom: 40px;
.content {
color:rgba(82, 137, 250, 1);
cursor: pointer;
}
}
}
}
}
\ No newline at end of file
import React from 'react';
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
import './UnbundEmployeeModal.less';
import _ from "underscore";
const { Search } = Input;
const UserRole = {
StoreManager: {
text: "店铺管理员"
},
CloudManager: {
text:"管理员"
},
CloudOperator: {
text:'运营师'
},
Cloud_Lecture: {
text:"讲师"
},
};
class UnbundEmployeeModal extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
selectOperatorList:[]
};
}
componentDidMount() {
this.handleFetchDataList();
}
// 获取绑定员工列表
handleFetchDataList = () => {
const params ={
planId: getParameterByName("id"),
storeCustomerId: this.props.storeCustomerId,
storeId:this.props.storeId
}
PlanService.getPlanCustomerAboutUser(params).then((res) => {
const { result = {} } = res ;
this.setState({
dataSource: result,
});
});
}
// 请求表头
parseColumns = () => {
const columns = [
{
title: '员工',
key: 'storeUserName',
dataIndex: 'storeUserName',
},
{
title: '角色',
key: 'role',
dataIndex: 'role',
render:(val,record)=>{
return <span>{UserRole[val].text}</span>
}
},
{
title: '手机号',
key: 'storeUserPhone',
dataIndex: 'storeUserPhone'
}
];
return columns;
}
selectOperator = (record,selected) =>{
const {selectOperatorList} = this.state;
let _list = [];
if (selected || !_.find(selectOperatorList, (item) => item.storeUserId == record.storeUserId)) {
_list = _.uniq(selectOperatorList.concat([record]), false, (item) => item.storeUserId);
} else {
_list = _.reject(selectOperatorList, (item) => item.storeUserId === record.storeUserId);
}
this.setState({selectOperatorList:_list});
}
confirmUnbund = ()=>{
const { selectOperatorList } = this.state;
const params = {
planId:getParameterByName("id"),
removeUserIds:_.pluck(selectOperatorList, 'storeUserId'),
storeCustomerId:this.props.storeCustomerId,
storeId:User.getStoreId(),
storeUserId:User.getStoreUserId()
}
PlanService.removePlanCustomer(params).then((res) => {
message.success('解绑成功');
this.props.onClose();
});
}
render() {
const { size,dataSource,totalCount,query,selectOperatorList} = this.state;
const { visible } = this.props;
return (
<Modal
title="选择需解绑的员工"
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="select-operator-modal"
closable={true}
width={800}
onOk={() => {this.confirmUnbund()}}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div>
<Table
rowKey={record => record.storeUserId}
dataSource={dataSource}
columns={this.parseColumns()}
pagination={false}
bordered
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(selectOperatorList, 'storeUserId'),
onSelect: (record, selected) => {
this.selectOperator(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
if (selected) {
_list = _.uniq(selectOperatorList.concat(changeRows), false, (item) => item.storeUserId);
} else {
_list = _.reject(selectOperatorList, (item) => _.find(changeRows, (data) => data.id === item.storeUserId));
}
this.setState({selectOperatorList:_list});
}
}}
/>
</div>
</Modal>
)
}
}
export default UnbundEmployeeModal;
\ No newline at end of file
import React from 'react';
import {Table, Modal,Input} from 'antd';
import { PageControl } from "@/components";
import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user'
import './UserLearnDetailModal.less';
import _ from "underscore";
const { Search } = Input;
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
const CourseType = {
LIVE: {
text: "直播课"
},
VOICE : {
text:"视频课"
},
RECORD : {
text:'录播课'
}
};
const courseStateShow = {
UN_START: {
title: "待开课",
},
STARTING: {
title: "上课中",
},
FINISH: {
title: "已完成",
},
EXPIRED: {
title: "未成功开课",
},
};
class UserLearnDetailModal extends React.Component {
constructor(props) {
super(props);
this.state = {
planDataSource:[],
taskDataSource:[],
taskSize:10,
taskQuery: {
current: 1,
},
taskTotalCount:0,
courseDataSource:[],
storeCustomerName:'',
storeCustomerPhone:''
};
}
componentDidMount() {
this.getPlanCustomerDetail();
}
getPlanCustomerDetail = ()=>{
PlanService.getPlanCustomerDetail({
planId:getParameterByName("id"),
storeCustomerId:this.props.storeCustomerId,
storeId:User.getStoreId()
}).then((res) => {
const {
storeCustomerName,
storeCustomerPhone,
planName,
learnFinishPercentage,
taskCustomerVOList,
courseMediaVOS,
}=res.result;
let coverUrl;
courseMediaVOS.map((item) => {
if(item.contentType === "COVER"){
coverUrl = item.mediaUrl;
}
return item;
})
const planDataSource = [{
planName,
learnFinishPercentage,
coverUrl:coverUrl || defaultCover
}]
this.setState({
storeCustomerName,
storeCustomerPhone,
planDataSource,
taskDataSource:taskCustomerVOList
})
})
}
parsePlanColumns = () => {
const columns = [
{
title: '培训计划名称',
key: 'planInfo',
dataIndex: 'planInfo',
render: (val, record) => {
return (
<div className="plan-instro">
<div className="img-con">
<img src={record.coverUrl}/>
</div>
<div className="plan-name">{record.planName}</div>
</div>
)
}
},
{
title: '学习进度',
key: 'learnFinishPercentage',
dataIndex: 'learnFinishPercentage',
width:167,
render: (val, record) => {
return (
<div className="plan-learn-percentage">
{val}%
</div>
)
}
}
];
return columns;
}
parseTaskColumns = () => {
const columns = [
{
title: '培训任务',
key: 'taskName',
dataIndex: 'taskName',
render: (val, record,index) => {
return (
<div className="taskName">
{index + 1}.{record.taskName}
</div>
)
}
},
{
title: '学习进度',
key: 'learnFinishPercentage',
dataIndex: 'learnFinishPercentage',
width:167,
render: (val, record) => {
return (
<div className="task-learn-percentage">
{ val === 100 ?<span>已完成</span>:<span>{val}%</span>}
</div>
)
}
}
];
return columns;
}
parseCoursecolumns = (parentIndex) => {
const columns = [
{
title: '课程',
key: 'courseName',
dataIndex: 'courseName',
render: (val, record,index) => {
return (
<div className="course-info">
<span className="course-type">{CourseType[record.courseType].text}</span>
<span className="course-name">{parentIndex + 1}.{index + 1}{record.courseName}</span>
{record.courseState === "EXPIRED" &&
<span className="icon iconfont tip">&#xe834;</span>
}
<span className="course-state">{courseStateShow[record.courseState].title}</span>
</div>
)
}
},
{
title: '学习进度',
key: 'learnFinishPercentage',
dataIndex: 'learnFinishPercentage',
width:152,
render: (val, record) => {
return (
<div className="course-learn-percentage">
{ record.learnState === "FINISH" ?<span>已完成</span>:<span>{val}%</span>}
</div>
)
}
}
];
return columns;
}
render() {
const {storeCustomerName,storeCustomerPhone,planDataSource,taskDataSource,taskQuery,taskTotalCount} = this.state;
const { visible } = this.props;
return (
<Modal
title="用户学习详情"
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="user-Learn-modal"
closable={true}
width={800}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div className="customer-info">
<span className="customer-name">
<span>用户:</span>
<span>{storeCustomerName}</span>
</span>
<span className="customer-phone">
<span>手机号:</span>
<span>{storeCustomerPhone}</span>
</span>
</div>
<div>
<Table
dataSource={planDataSource}
columns={this.parsePlanColumns()}
pagination={false}
bordered
className="plan-table"
/>
</div>
<div>
<Table
rowKey={(record) => record.taskId}
className="task-table"
dataSource={taskDataSource}
columns={this.parseTaskColumns()}
pagination={false}
expandedRowRender={(record,index) => {
if(!record.courseVOList){
return
}
if (record.courseVOList.length !== 0 ){
return <div>
<Table
pagination={false}
dataSource={record.courseVOList}
columns={this.parseCoursecolumns(index)}
className="child-table"
/>
</div>
}
}}
rowClassName={(record,index)=>{if(index%2===0){return 'odd-row'}else{ return 'even-row'}}}
/>
</div>
</Modal>
)
}
}
export default UserLearnDetailModal;
\ No newline at end of file
.user-Learn-modal{
.customer-info{
margin-bottom:16px;
.customer-name{
font-size:14px;
color:#333;
margin-right:32px;
}
.customer-phone{
font-size:14px;
color:#333;
}
}
.plan-table{
margin-bottom:8px;
.plan-instro{
display: flex;
align-items: center;
.img-con{
margin-right:8px;
img{
width: 97px;
height: 55px;
display: inline-block;
border-radius:4px;
}
}
.plan-name{
color:#666666;
font-size:14px;
}
.plan-learn-percentage{
color:#666666;
font-size:14px;
}
}
}
.task-table{
.taskName{
color:#666666;
font-size:14px;
}
.task-learn-percentage{
color:#666666;
font-size:14px;
}
.course-info{
margin-left:57px;
.course-type{
font-size:11px;
color:#666666;
padding:1px 8px;
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;
}
.child-table{
.ant-table-content{
border:none;
thead{
display:none;
}
tbody tr td{
border-bottom:none;
}
}
}
}
.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;
}
& + .ant-table-expanded-row{
background:transparent;
td{
background: #FAFAFA;
}
}
&:hover{
& + .ant-table-expanded-row{
background:transparent;
td{
background: #F3f6fa !important;
}
}
}
}
}
}
}
\ No newline at end of file
import React from 'react';
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import CourseService from "@/domains/course-domain/CourseService";
import User from '@/common/js/user';
import { Tabs } from 'antd';
import './SelectOperatorModal.less';
import _ from "underscore";
import dealTimeDuration from "../../course-manage/utils/dealTimeDuration";
const { Search } = Input;
const { TabPane } = Tabs;
const courseStateShow = {
UN_START: {
code: 1,
title: "待开课",
color: "#FFB714",
},
STARTING: {
code: 2,
title: "上课中",
color: "#238FFF",
},
FINISH: {
code: 3,
title: "已完成",
color: "#3BBDAA",
},
EXPIRED: {
code: 4,
title: "未成功开课",
color: "#999",
},
};
class SelectOperatorModal extends React.Component {
constructor(props) {
super(props);
this.state = {
liveDataSource:[],
liveSize:10,
liveQuery: {
current: 1,
},
liveTotalCount:0,
selectLive:[],//弹窗内已选择的直播课程
currentLiveCourseListData:[], //页面中已关联的直播课程
videoDataSource:[],
videoSize:10,
videoQuery: {
current: 1,
},
videoTotalCount:0,
selectVideo:[], //弹窗内已选择的视频课程
currentVideoCourseListData:[], //页面中已关联的视频课程
}
}
componentDidMount() {
this.handleFetchLiveDataList();
this.handleFetchVideoDataList();
}
// 获取直播课列表
handleFetchLiveDataList = () => {
const {liveQuery,liveSize} = this.state;
const { selectedTaskIndex } =this.props;
console.log('selectedTaskIndex',selectedTaskIndex);
const currentLiveCourseListData =[...this.props.data[selectedTaskIndex].courseList]
const _currentLiveCourseListData = currentLiveCourseListData.map((item,index) => {
if(item.liveCourseId){
return item
}
});
const params ={
...liveQuery,
liveSize,
// excludeCourseIdList:_.pluck(_currentLiveCourseListData,'courseId')
}
CourseService.getLiveCloudCourseBasePage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
this.setState({
liveDataSource: records,
liveTotalCount: Number(total),
currentLiveCourseListData
});
});
}
// 获取视频课列表
handleFetchVideoDataList = () => {
const {videoQuery,videoSize,videoTotalCount} = this.state;
const { selectedTaskIndex } =this.props;
const currentVideoCourseListData =[...this.props.data[selectedTaskIndex].courseList];
const _currentVideoCourseListData = currentVideoCourseListData.map((item,index) => {
if(!item.liveCourseId){
return item
}
});
const params ={
...videoQuery,
videoSize,
// excludeCourseIdList:_.pluck(_currentVideoCourseListData,'courseId')
}
CourseService.videoScheduleBasePage(params).then((res) => {
const { result = {} } = res ;
const { records = [], total = 0 } = result;
console.log('records',records);
this.setState({
videoDataSource: records,
videoTotalCount: Number(total)
});
});
}
// handleChangNickname = (value)=>{
// const isPhone = (value || '').match(/^\d+$/);
// const { query } = this.state;
// if(isPhone){
// query.phone = value;
// query.nickName = null;
// }else{
// query.nickName = value;
// query.phone = null;
// }
// query.current = 1;
// this.setState({
// query
// },()=>{this.handleFetchLiveDataList()})
// }
onShowLiveSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
liveSize:size
},()=>{this.handleFetchLiveDataList()})
}
onShowVideoSizeChange = (current, size) => {
if (current == size) {
return
}
this.setState({
videoSize:size
},()=>{this.handleFetchLiveDataList()})
}
// 请求表头
parseLiveColumns = () => {
const columns = [
{
title: '课程信息',
key: 'course',
dataIndex: 'course',
render: (val, record) => {
let hasCover = false;
return (
<div className="record__item">
{
record.courseMediaVOS.map((item,index)=>{
if( item.contentType === "COVER"){
hasCover = true;
return <img className="course-cover" src={item.mediaUrl} style={{width:'100px',height:'50px'}}/>
}
})
}
{ !hasCover &&
<img className="course-cover" src={'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png'} style={{width:'100px',height:'50px'}} />
}
<div className="course-name">{record.courseName}</div>
</div>
)
}
},
{
title: '课程状态',
key: 'courseState',
dataIndex: 'courseState',
render: (val, record) => {
return (
<span className="course-status">{courseStateShow[record.courseState].title}</span>
)
}
},
{
title: '上课时间',
key: 'courseTime',
dataIndex: 'courseTime',
render: (val, record) => {
return (
<span>{formatDate('YYYY-MM-DD H:i', record.startTime) - formatDate('YYYY-MM-DD H:i', record.endTime) }</span>
)
}
}
];
return columns;
}
// 请求表头
parseVideoColumns = () => {
const columns = [
{
title: '课程信息',
key: 'course',
dataIndex: 'course',
render: (val, record) => {
const { coverUrl, scheduleVideoUrl } = record;
return (
<div className="record__item">
{/* 上传了封面的话就用上传的封面, 没有的话就取视频的第一帧 */}
<img className="course-cover" src={coverUrl || `${scheduleVideoUrl}?x-oss-process=video/snapshot,t_0,m_fast`} style={{width:'100px',height:'50px'}}/>
<div className="course-name">{record.courseName}</div>
</div>
)
}
},
{
title: '课程时长',
key: 'courseTime',
dataIndex: 'courseTime',
render: (val, record) => {
return (
<span className="course-status">{dealTimeDuration(record.videoDuration)}</span>
)
}
},
{
title: '课程大小',
key: 'size',
dataIndex: 'size',
render: (val, record) => {
return (
<span>{(val/1024).toFixed(1)}M</span>
)
}
}
];
return columns;
}
selectLiveList = (record,selected) =>{
const {selectVideo,currentVideoCourseListData,currentLiveCourseListData,selectLive} = this.state;
let _list = [];
if (selected || !_.find(selectLive, (item) => item.liveCourseId == record.liveCourseId)) {
_list = _.uniq(selectLive.concat([record]), false, (item) => item.liveCourseId);
} else {
_list = _.reject(selectLive, (item) => item.liveCourseId === record.liveCourseId);
}
if(_list.length + currentVideoCourseListData.length + currentLiveCourseListData.length + selectVideo> 20){
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
this.setState({selectLive:_list});
}
selectVideoList = (record,selected) =>{
const {selectVideo,currentVideoCourseListData,currentLiveCourseListData,selectLive} = this.state;
let _list = [];
if (selected || !_.find(selectVideo, (item) => item.id == record.id)) {
_list = _.uniq(selectVideo.concat([record]), false, (item) => item.id);
} else {
_list = _.reject(selectVideo, (item) => item.id === record.id);
}
if(_list.length + currentVideoCourseListData.length + currentLiveCourseListData.length + selectLive> 20){
message.warning('无法继续选择,一个任务最多关联20个课程');
return;
}
this.setState({selectVideo:_list});
}
clearSelectCourse = ()=>{
this.setState({
selectLive:[],
selectVideo:[]
})
}
handleSelectVideo = (selectVideo)=>{
return selectVideo.map((item,index)=>{
let _item = {};
_item.courseId = item.id;
_item.courseType = "VOICE";
_item.courseName = item.courseName;
return _item;
})
}
handleSelectLive = (selectLive)=>{
return selectLive.map((item,index)=>{
let _item = {};
_item.courseId = item.liveCourseId;
_item.courseType = "LIVE";
_item.courseName = item.courseName;
return _item;
})
}
render() {
const { visible } = this.props;
const { liveDataSource,liveSize,liveQuery,liveTotalCount,selectLive,currentLiveCourseListData,videoDataSource,videoSize,videoQuery,videoTotalCount,selectVideo,currentVideoCourseListData} = this.state;
return (
<Modal
title="关联课程"
onCancel={this.props.onClose}
maskClosable={false}
visible={visible}
className="select-operator-modal"
closable={true}
width={800}
onOk={() => this.props.onSelect([...this.handleSelectVideo(selectVideo),...this.handleSelectLive(selectLive)]) }
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
>
<div>
<Tabs defaultActiveKey="1">
<TabPane tab="视频课" key="1">
<div className="search-container">
<Search placeholder="搜索课程名称" style={{ width: 200 }} />
</div>
<div>
<span>
<span className="iconfont">&#xe61d;</span>
<span>已选择{selectVideo.length + selectLive.length}</span>
<span onClick={this.clearSelectCourse}>清空</span>
</span>
<span>该任务已关联{currentVideoCourseListData.length + currentLiveCourseListData.length}个课程,可继续选择{20-(currentVideoCourseListData.length + currentLiveCourseListData.length )}</span>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={videoDataSource}
columns={this.parseVideoColumns()}
pagination={false}
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(selectVideo, 'id'),
onSelect: (record, selected) => {
this.selectVideoList(record, selected);
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
if (selected) {
_list = _.uniq(selectVideo.concat(changeRows), false, (item) => item.id);
} else {
_list = _.reject(selectVideo, (item) => _.find(changeRows, (data) => data.id === item.id));
}
this.setState({selectVideo:_list});
},
}}
/>
{videoDataSource.length >0 &&
<div className="box-footer">
<PageControl
current={videoQuery.current - 1}
pageSize={videoSize}
total={videoTotalCount}
toPage={(page) => {
const _query = {...videoQuery, current: page + 1};
this.setState({
videoQuery:_query
},()=>{ this.handleFetchVideoDataList()})
}}
onShowVideoSizeChange={this.onShowVideoSizeChange}
/>
</div>
}
</div>
</TabPane>
<TabPane tab="直播课" key="2">
<div className="search-container">
<Search placeholder="搜索课程名称" style={{ width: 200 }} />
</div>
<div>
<span>
<span className="iconfont">&#xe61d;</span>
<span>已选择{selectVideo.length + selectLive.length}</span>
<span onClick={this.clearSelectCourse}>清空</span>
</span>
<span>该任务已关联{currentVideoCourseListData.length + currentLiveCourseListData.length}个课程,可继续选择{20-(currentVideoCourseListData.length + currentLiveCourseListData.length )}</span>
</div>
<div>
<Table
rowKey={record => record.liveCourseId}
dataSource={liveDataSource}
columns={this.parseLiveColumns()}
pagination={false}
rowSelection={{
type: 'checkbox',
selectedRowKeys: _.pluck(selectLive, 'liveCourseId'),
onSelect: (record, selected) => {
this.selectLiveList(record, selected)
},
onSelectAll: (selected, _selectedRows, changeRows) => {
let _list = [];
if (selected) {
_list = _.uniq(selectLive.concat(changeRows), false, (item) => item.liveCourseId);
} else {
_list = _.reject(selectLive, (item) => _.find(changeRows, (data) => data.liveCourseId === item.liveCourseId));
}
this.setState({selectLive:_list});
},
}}
/>
{liveDataSource.length >0 &&
<div className="box-footer">
<PageControl
current={liveQuery.current - 1}
pageSize={liveSize}
total={liveTotalCount}
toPage={(page) => {
const _query = {...liveQuery, current: page + 1};
this.setState({
liveQuery:_query
},()=>{ this.handleFetchLiveDataList()})
}}
onShowLiveSizeChange={this.onShowLiveSizeChange}
/>
</div>
}
</div>
</TabPane>
</Tabs>
</div>
</Modal>
)
}
}
export default SelectOperatorModal;
\ No newline at end of file
...@@ -60,7 +60,7 @@ class OperateArea extends React.Component { ...@@ -60,7 +60,7 @@ class OperateArea extends React.Component {
// 显示创建文件夹弹窗 // 显示创建文件夹弹窗
handleToggleCreateFolderModal = async () => { handleToggleCreateFolderModal = async () => {
const { instId } = window.currentUserInstInfo; const { instId } = window.currentUserInstInfo;
const ultimateRes = await axios.Business('public/inst/checkInstProduct', { const ultimateRes = await axios.Business('public/inst/checkInstProduct', {
instId: instId || LS.get("instId"), instId: instId || LS.get("instId"),
productCodeList: ['ULTIMATESELL', 'PIP_TO_ULTIMATE', 'HIGH_TO_ULTIMATE'] productCodeList: ['ULTIMATESELL', 'PIP_TO_ULTIMATE', 'HIGH_TO_ULTIMATE']
}); });
......
/* /*
* @Author: wufan * @Author: wufan
* @Date: 2020-11-27 16:21:49 * @Date: 2020-11-27 16:21:49
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-25 21:14:48 * @LastEditTime: 2021-03-02 13:40:27
* @Description: Description * @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有 * @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/ */
...@@ -40,11 +40,10 @@ interface AddEmployeeModalProps { ...@@ -40,11 +40,10 @@ interface AddEmployeeModalProps {
onClose: () => void; onClose: () => void;
isWorkWechat: boolean; isWorkWechat: boolean;
} }
function AddEmployeeModal(props: AddEmployeeModalProps) { function AddEmployeeModal(props: AddEmployeeModalProps) {
const [nickName, setName] = useState(""); const [nickName, setName] = useState("");
const [phone, setPhone] = useState(""); const [phone, setPhone] = useState("");
const [role, setRole] = useState("CloudLecturer"); const [role, setRole] = useState("CloudOperator");
const [avatar, setAvatar] = useState( const [avatar, setAvatar] = useState(
"https://image.xiaomaiketang.com/xm/rJeQaZxtc7.png" "https://image.xiaomaiketang.com/xm/rJeQaZxtc7.png"
); );
...@@ -64,16 +63,15 @@ function AddEmployeeModal(props: AddEmployeeModalProps) { ...@@ -64,16 +63,15 @@ function AddEmployeeModal(props: AddEmployeeModalProps) {
const [form] = Form.useForm(); const [form] = Form.useForm();
useEffect(() => { useEffect(() => {
if (props.choosedItem.nickName) { if (props.choosedItem.nickName) {
console.log("props.choosedItem", props.choosedItem); console.log("props.choosedItem", props.choosedItem);
setName(props.choosedItem.nickName); setName(props.choosedItem.nickName);
console.log('choosedItem',props.choosedItem);
props.choosedItem.phone && setPhone(props.choosedItem.phone); props.choosedItem.phone && setPhone(props.choosedItem.phone);
props.choosedItem.role && setRole(props.choosedItem.role[0]); props.choosedItem.role && setRole(props.choosedItem.role[0]);
props.choosedItem.avatar && setAvatar(props.choosedItem.avatar); props.choosedItem.avatar && setAvatar(props.choosedItem.avatar);
const _role = const _role = props.choosedItem.role[0];
props.choosedItem.role[0] === "CloudLecturer"
? "CloudLecturer"
: "CloudManager";
form.setFieldsValue({ form.setFieldsValue({
nickName: props.choosedItem.nickName, nickName: props.choosedItem.nickName,
role: _role, role: _role,
...@@ -284,8 +282,13 @@ function AddEmployeeModal(props: AddEmployeeModalProps) { ...@@ -284,8 +282,13 @@ function AddEmployeeModal(props: AddEmployeeModalProps) {
}} }}
className="mt5" className="mt5"
> >
<Radio value={"CloudLecturer"} className="mt-4" <Radio value={"CloudOperator"} className="mt-4">
> <span style={{ color: "#333" }}>运营师</span>
<p className="radio-tip">
仅可查看/转发培训计划内容,并查看其负责的用户学习进度
</p>
</Radio>
<Radio value={"CloudLecturer"} className="mt-4">
<span style={{ color: "#333" }}>普通讲师</span> <span style={{ color: "#333" }}>普通讲师</span>
<p className="radio-tip"> <p className="radio-tip">
仅可查看/使用与自己相关的文件和课表,并进行上课 仅可查看/使用与自己相关的文件和课表,并进行上课
......
/* /*
* @Author: 吴文洁 * @Author: 吴文洁
* @Date: 2020-04-29 10:26:32 * @Date: 2020-04-29 10:26:32
* @LastEditors: wufan * @LastEditors: zhangleyuan
* @LastEditTime: 2021-01-18 21:23:08 * @LastEditTime: 2021-03-02 15:56:22
* @Description: 内容线路由配置 * @Description: 内容线路由配置
*/ */
import Home from '@/modules/home/Home'; import Home from '@/modules/home/Home';
...@@ -15,10 +15,15 @@ import LiveCoursePage from '@/modules/course-manage/LiveCoursePage'; ...@@ -15,10 +15,15 @@ import LiveCoursePage from '@/modules/course-manage/LiveCoursePage';
import AddLivePage from '@/modules/course-manage/AddLive' import AddLivePage from '@/modules/course-manage/AddLive'
import VideoCoursePage from '@/modules/course-manage/video-course' import VideoCoursePage from '@/modules/course-manage/video-course'
import AddVideoCoursePage from '@/modules/course-manage/video-course/AddVideoCourse' import AddVideoCoursePage from '@/modules/course-manage/video-course/AddVideoCourse'
import DataList from '@/modules/course-manage/DataList/DataList'; //TODO
import ClassBook from '@/modules/resource-disk'; // import DataList from '@/modules/course-manage/DataList/DataList';
// import ClassBook from '@/modules/resource-disk';
import ResourceDisk from '@/modules/resource-disk'; import ResourceDisk from '@/modules/resource-disk';
import SwitchRoute from '@/modules/root/SwitchRoute'; import SwitchRoute from '@/modules/root/SwitchRoute';
import PlanPage from '@/modules/plan-manage/PlanPage';
import AddPlanPage from '@/modules/plan-manage/AddPlan';
import LearningDataPage from '@/modules/plan-manage/LearningData';
import StoreInfoPage from '@/modules/store-manage/StoreInfo';
const mainRoutes = [ const mainRoutes = [
{ {
...@@ -80,7 +85,28 @@ const mainRoutes = [ ...@@ -80,7 +85,28 @@ const mainRoutes = [
path: '/switch-route', path: '/switch-route',
component: SwitchRoute, component: SwitchRoute,
name: '登录后跳转承载页' name: '登录后跳转承载页'
},
{
path:'/plan',
component: PlanPage,
name: '培训计划'
},
{
path: '/create-plan',
component:AddPlanPage,
name: '创建视频课'
},
{
path: '/store-info',
component:StoreInfoPage,
name: '店铺信息'
},
{
path: '/learning-data',
component:LearningDataPage,
name: '学习数据'
} }
] ]
export default mainRoutes; export default mainRoutes;
\ No newline at end of file
/*
* @Author: zhangleyuan
* @Date: 2021-01-19 11:27:56
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-02 15:18:12
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
export const menuList: any = [ export const menuList: any = [
{ {
groupName: "中心首页", groupName: "中心首页",
...@@ -29,11 +37,28 @@ export const menuList: any = [ ...@@ -29,11 +37,28 @@ export const menuList: any = [
link: '/resource-disk' link: '/resource-disk'
}, },
{ {
groupName: "培训管理",
groupCode: "TrainManage",
icon: '&#xe863;',
children: [
{
groupName: "培训计划",
groupCode: "TrainPlan",
link: '/plan'
}
]
},
{
groupName: "店铺管理", groupName: "店铺管理",
groupCode: "CloudShop", groupCode: "CloudShop",
icon: '&#xe82e;', icon: '&#xe82e;',
children: [ children: [
{ {
groupName: "店铺信息",
groupCode: "ShopInfo",
link: '/store-info'
},
{
groupName: "员工管理", groupName: "员工管理",
groupCode: "ShopStaff", groupCode: "ShopStaff",
link: '/employees-manage' link: '/employees-manage'
......
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