Commit 2d3a9245 by zhangleyuan

feat:处理创建培训计划

parent 4e8a79af
@font-face {
font-family: 'iconfont'; /* project id 2223403 */
src: url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.eot');
src: url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_fad4h32dwuo.svg#iconfont') format('svg');
src: url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.eot');
src: url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.woff2') format('woff2'),
url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.woff') format('woff'),
url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.ttf') format('truetype'),
url('//at.alicdn.com/t/font_2223403_325yz7wxu2d.svg#iconfont') format('svg');
}
.iconfont{
font-family:"iconfont" !important;
......
......@@ -2,7 +2,7 @@
* @Author: zhangleyuan
* @Date: 2021-02-21 16:08:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 10:21:33
* @LastEditTime: 2021-03-02 13:49:32
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -17,3 +17,9 @@ export function createTrainingPlan(params: object) {
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);
}
......@@ -2,11 +2,11 @@
* @Author: zhangleyuan
* @Date: 2021-02-21 16:15:38
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 10:22:19
* @LastEditTime: 2021-03-02 13:50:18
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import {getTrainingPlanPage,createTrainingPlan,updateStateTrainingPlan} from '@/data-source/plan/request-apis';
import {getTrainingPlanPage,createTrainingPlan,updateStateTrainingPlan,getTrainingPlanDetail,updateTrainingPlan} from '@/data-source/plan/request-apis';
export default class PlanService {
// 获取员工列表
static getTrainingPlanPage(params: any) {
......@@ -18,4 +18,10 @@ export default class PlanService {
static updateStateTrainingPlan(params: any) {
return updateStateTrainingPlan(params);
}
static getTrainingPlanDetail(params: any) {
return getTrainingPlanDetail(params);
}
static updateTrainingPlan(params: any) {
return updateTrainingPlan(params);
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-24 15:31:49
* @LastEditTime: 2021-03-02 15:16:04
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
......@@ -25,7 +25,7 @@
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="stylesheet" href="//at.alicdn.com/t/font_2223403_fad4h32dwuo.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
......
......@@ -2,7 +2,7 @@
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-24 15:31:42
* @LastEditTime: 2021-03-02 15:16:11
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
......@@ -25,7 +25,7 @@
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="stylesheet" href="//at.alicdn.com/t/font_2223403_fad4h32dwuo.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_325yz7wxu2d.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
......
......@@ -2,7 +2,7 @@
* @Author: zhangleyuan
* @Date: 2021-02-20 16:13:39
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-28 19:05:50
* @LastEditTime: 2021-03-02 20:11:09
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -16,8 +16,11 @@ import PlanService from '@/domains/plan-domain/planService'
import User from '@/common/js/user';
import _ from "underscore";
import './AddPlan.less'
const DEFAULT_BASIC_DATA = {
const defaultCover = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
const defaultBasicData = {
planName:"",
coverUrl: defaultCover,
coverId:null,
enableState:"YES",
selectOperatorList:[],
instro:'',
......@@ -25,9 +28,63 @@ const DEFAULT_BASIC_DATA = {
percentCompleteLive:80,
percentCompleteVideo:80
}
const defaultTaskList = [];
function AddPlan() {
const [basicData,setBasicData] = useState(DEFAULT_BASIC_DATA);
const [taskList,setTaskList] = useState([]);
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,
coverId,
enableState,
selectOperatorList:operateIds || [],
instro,
operateType,
percentCompleteLive,
percentCompleteVideo
})
setTaskList(trainingTaskList)
})
}
function handleChangeBasicInfo(field, value){
setBasicData( {
...basicData,
......@@ -40,7 +97,7 @@ function AddPlan() {
}
function submitInfo(){
const {planName,enableState,selectOperatorList,instro,operateType,percentCompleteLive,percentCompleteVideo} = basicData;
const {planName,enableState,selectOperatorList,instro,operateType,percentCompleteLive,percentCompleteVideo,coverId,coverUrl} = basicData;
if(!planName){
message.warning('请输入的培训计划名称');
return;
......@@ -49,6 +106,24 @@ function AddPlan() {
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(),
......@@ -58,20 +133,56 @@ function AddPlan() {
percentCompleteLive,
percentCompleteVideo,
planName,
scheduleMediaRequests:[],
scheduleMediaRequests,
storeId:User.getStoreId(),
trainingTaskList: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 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">
......@@ -87,7 +198,7 @@ function AddPlan() {
</div>
<div className="basic-info__wrap">
<div className="title">培训任务</div>
<TrainingTask onChange={handleChangeTaskInfo}/>
<TrainingTask data={taskList} onChange={handleChangeTaskInfo} />
</div>
</div>
</div>
......
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 './LearningData.less';
class LearningData extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
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">
六年级数学精品公开课快来参加二十个字展示
</div>
<div className="create-course">
<span className="createUser">创建人:张老师</span>
<span className="split">|</span>
<span className="course-total">课程总数量:88</span>
</div>
<div className="create-time">创建时间:2020-01-01 09:00</div>
</div>
</div>
<div className="join">
<div className="number">1999</div>
<div className="text">参培人数</div>
</div>
</div>
<div className="box">
<Tabs defaultActiveKey="employeeShareData">
<Tabs.TabPane tab="员工分享数据" key="employeeShareData">
<EmployeeShareData/>
</Tabs.TabPane>
<Tabs.TabPane tab="用户学习数据" key="userLearningData">
<UserLearningData/>
</Tabs.TabPane>
</Tabs>
</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
......@@ -2,66 +2,221 @@
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-25 17:48:12
* @LastEditTime: 2021-03-02 13:47:09
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
import React, { useState, useRef, useEffect } from 'react';
import { Button,Input,Switch,Radio,Row,Col} from 'antd';
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 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
};
}
function BasicInfo(props) {
const [operatorModalVisible,setOperatorModalVisible] = useState(false);
function handleShowSelectOperatorModal(){
setOperatorModalVisible(true);
handleShowSelectOperatorModal = () =>{
this.setState({
operatorModalVisible:true
})
}
function handleCloseSelectOperatorMOdal(){
setOperatorModalVisible(false);
handleCloseSelectOperatorMOdal = ()=>{
this.setState({
operatorModalVisible:false
})
}
function handleConfirmSelectOperator(selectOperatorList){
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')
}
}
props.onChange('selectOperatorList',selectOperatorList);
// 使用默认封面图
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);
}
setOperatorModalVisible(false);
//上传图片
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()
})
}
function enableStateChange(){
if(props.data.enableState==="NO"){
props.onChange('enableState','YES')
}else{
props.onChange('enableState','NO')
}, 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>
<span className="label"><span className="require">*</span>培训计划名称:</span>
<Input
value={props.data.planName}
value={planName}
placeholder="请输入培训计划名称,最多20字"
maxLength={20}
style={{ width: 240 }}
onChange={(e)=>props.onChange('planName', e.target.value)}
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='https://image.xiaomaiketang.com/xm/YNfi45JwFA.png' width="690"/>
}
<img src={coverUrl} width="690"/>
</div>
<div className="opt-btns">
<Button>上传图片</Button>
<Button onClick={() => {
this.setState({
showSelectFileModal:true
})
}}>上传图片</Button>
<span
className={`default-btn disabled`}
className={`default-btn ${isDefaultCover ? 'disabled' : ''}`}
onClick={this.handleResetCoverUrl}
>使用默认图</span>
<div className="tips">建议尺寸690*398像素,图片支持jpg、jpeg、png格式</div>
<div className="tips">建议尺寸1280*720px或16:9。封面图最大5M,支持jpg、jpeg和png</div>
</div>
</div>
</div>
......@@ -72,15 +227,15 @@ function BasicInfo(props) {
maxLength={200}
style={{ width: 480 }}
className="instro-textarea"
value={props.data.instro}
onChange={(e)=>props.onChange('instro', e.target.value)}
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={props.data.enableState==="YES"? true:false} onChange={enableStateChange}/>
<Switch checked={enableState==="YES"? true:false} onChange={()=> {this.enableStateChange()}}/>
</div>
<div>
<div className="instro-text">
......@@ -93,7 +248,7 @@ function BasicInfo(props) {
<div className="view-range" >
<span className="label"><span className="require">*</span>可见范围:</span>
<div className="content">
<Radio.Group value={props.data.operateType} onChange={(e) => { props.onChange('operateType', e.target.value) }}>
<Radio.Group value={operateType} onChange={(e) => { this.props.onChange('operateType', e.target.value) }}>
<Row style={{ marginBottom: '5px' }}>
<Col span={24}>
<Radio value="All_Operate">
......@@ -112,8 +267,8 @@ function BasicInfo(props) {
</Row>
</Radio.Group>
<div className="choose-business">
<Button onClick={handleShowSelectOperatorModal}>选择运营师</Button>
<span>已选择<span>{props.data.selectOperatorList.length}</span>名运营师</span>
<Button onClick={()=>{this.handleShowSelectOperatorModal()}}>选择运营师</Button>
<span>已选择<span>{selectOperatorList.length}</span>名运营师</span>
</div>
</div>
</div>
......@@ -122,23 +277,95 @@ function BasicInfo(props) {
<div>
<div>
<span className="icon iconfont">&#xe865;</span>
<span>直播课单个课程,用户学习进度达到<Input width="40" value={props.data.percentCompleteLive} onChange={(e) => { props.onChange('percentCompleteLive', e.target.value) }}/>% 即视为“已完成”学习</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 width="40" value={props.data.percentCompleteVideo} onChange={(e) => { props.onChange('percentCompleteVideo', e.target.value) }} />%即视为“已完成”学习</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={handleCloseSelectOperatorMOdal}
selectOperatorList={props.data.selectOperatorList}
onSelect={handleConfirmSelectOperator}
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: 100px;
width: 110px;
text-align: right;
display:inline-block;
.require {
......
import React from 'react';
import { withRouter } from "react-router-dom";
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import './EmployeeShareData.less';
const { Search } = Input;
const UserRole = {
StoreManager: {
name: "店铺管理员"
},
CloudManager: {
name:"管理员"
},
CloudOperator: {
name:'运营师'
},
CloudLecturer: {
name:"讲师"
},
};
class EmployeeShareData extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
}
}
handleFetchDataList = ()=>{
}
// 请求表头
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[val].name}
</div>
)
}
},
{
title: '手机号',
key: 'storeUserPhone',
dataIndex: 'storeUserPhone',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '最近分享成功时间',
key: 'recentlyForwardTime',
dataIndex: 'recentlyForwardTime',
sorter:true,
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>数据详情</span>
)
}
}
];
return columns;
}
render() {
const { dataSource,query,size,totalCount} = this.state;
return (
<div className="employee-share-data">
<div className="search-container">
<Search placeholder="搜索员工姓名手机号" style={{ width: 200 }} enterButton={<span className="icon iconfont">&#xe832;</span>}/>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parselumns()}
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()})
}}
/>
</div>
}
</div>
</div>
)
}
}
export default withRouter(EmployeeShareData);
\ No newline at end of file
.employee-share-data{
.search-container{
margin-bottom:12px;
}
}
\ No newline at end of file
......@@ -2,7 +2,7 @@
* @Author: zhangleyuan
* @Date: 2021-02-20 16:46:46
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 16:48:31
* @LastEditTime: 2021-03-02 15:55:04
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -115,7 +115,7 @@ function PlanList(props) {
render: (val, record) => {
return (
<div className="operate">
<div className="operate__item">学习数据</div>
<div className="operate__item" onClick={()=>toLearningDataPage(record)}>学习数据</div>
<span className="operate__item split"> | </span>
<div className="operate__item" onClick={() => {handleShowShareModal(record); }}>分享</div>
<span className="operate__item split"> | </span>
......@@ -246,7 +246,11 @@ function PlanList(props) {
pathname: `/create-plan?type=edit&id=${item.planId}`,
})
}
function toLearningDataPage(item){
window.RCHistory.push({
pathname: `/learning-data?id=${item.planId}`,
})
}
return (
<div className="plan-list">
<Table
......
......@@ -2,7 +2,7 @@
* @Author: zhangleyuan
* @Date: 2021-02-20 16:45:51
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-28 17:49:30
* @LastEditTime: 2021-03-02 20:20:28
* @Description: 描述一下
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -21,80 +21,23 @@ const DragHandle = sortableHandle(() => (
<span>移动</span>
</span>
));
const data = [
// {
// taskName: '培训计划名称A',
// index:0,
// type:'text',
// courserList:[
// {
// courseName:'培训计划课程A-1',
// type:'text',
// index:0,
// id:'00'
// },
// {
// courseName:'培训计划课程A-2',
// type:'text',
// index:1,
// id:'01'
// },
// {
// courseName:'培训计划课程A-3',
// type:'text',
// index:2,
// id:'02'
// }
// ]
// },
// {
// taskName: '培训计划名称B',
// index:1,
// type:'text',
// courserList:[
// {
// courseName:'培训计划课程B-1',
// index:0,
// type:'text',
// id:'10'
// },
// {
// courseName:'培训计划课程B-2',
// index:1,
// type:'text',
// id:'11'
// }
// ]
// },
// {
// taskName: '培训计划名称C',
// index:2,
// type:'text',
// courserList:[
// {
// courseName:'培训计划课程c-1',
// index:0,
// type:'text',
// id:'20'
// }
// ]
// },
];
const SortableItem = sortableElement(props => <tr {...props} />);
const SortableContainer = sortableContainer(props => <tbody {...props} />);
class TrainingTask extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: data,
dataSource:[],
selectedTaskIndex:0,
relatedCourseModalVisible:false
};
}
componentDidMount(){
console.log('data',this.props.data);
}
parseTaskColumns = ()=>{
const columns = [
{
......@@ -133,7 +76,7 @@ class TrainingTask extends React.Component {
<DragHandle />
<span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span>
<span onClick={(e)=>{const {dataSource}= this.state; record.type="input";this.setState(dataSource)}}>重命名</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>
......@@ -186,7 +129,7 @@ class TrainingTask extends React.Component {
<DragHandle />
<span className="operate__item">
<span className="icon iconfont">&#xe6f5;</span>
<span onClick={(e)=>{const {dataSource}= this.state; record.type="input";this.setState(dataSource)}}>重命名</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>
......@@ -227,61 +170,61 @@ class TrainingTask extends React.Component {
// };
addTask = () => {
const { dataSource } = this.state;
const { data } = this.props;
const taskObj={
taskName: '',
index:dataSource.length,
index:data.length,
type:'input',
courserList:[
courseList:[
]
}
const newData = [...dataSource,taskObj];
this.setState({ dataSource: newData });
const newData = [...data,taskObj];
this.props.onChange(newData);
}
handleRenameTaskName = (e,record) => {
const { value } = e.target;
const { dataSource } = this.state;
const { data } = this.props;
record.taskName = value;
this.props.onChange(dataSource);
this.props.onChange(data);
}
handleTaskNameBlur = (e,record)=>{
const { value } = e.target;
const {dataSource}= this.state;
const {data}= this.props;
if(value){
record.type="text";
this.setState({dataSource});
this.props.onChange(data);
}
}
handleRenameCourseName = (e,record) => {
const { value } = e.target;
const { dataSource } = this.state;
const { data } = this.props;
record.courseName = value;
this.props.onChange(this.state.dataSource);
this.props.onChange(data);
}
handleCourseNameBlur = (e,record)=>{
const { value } = e.target;
const {dataSource}= this.state;
const { data }= this.props;
if(value){
record.type="text";
this.setState({dataSource});
this.setState({data});
}
}
handleDeleteTask = (index)=>{
const {dataSource}= this.state;
const newData=[...dataSource];
const {data}= this.props;
const newData=[...data];
newData.splice(index,1);
this.setState({dataSource:newData},()=>{this.props.onChange(this.state.dataSource);});
this.props.onChange(newData);
}
handleDeleteCourse = (parentIndex,index)=>{
const {dataSource}= this.state;
const newData=[...dataSource];
const selectData = [...newData[parentIndex].courserList]
const {data}= this.props;
const newData=[...data];
const selectData = [...newData[parentIndex].courseList]
selectData.splice(index,1)
newData[parentIndex].courserList= selectData;
this.setState({dataSource:newData},()=>{this.props.onChange(this.state.dataSource);});
newData[parentIndex].courseList= selectData;
this.props.onChange(newData);
}
showRelatedCourseModal = (index)=>{
......@@ -296,35 +239,35 @@ class TrainingTask extends React.Component {
})
}
confirmSelectCourse = (selectList) =>{
const {dataSource,selectedTaskIndex}= this.state;
const newData=[...dataSource];
const selectData = [...newData[selectedTaskIndex].courserList]
const {selectedTaskIndex}= this.state;
const { data } = this.props
const newData=[...data];
const selectData = [...newData[selectedTaskIndex].courseList]
const _selectData =[...selectData,...selectList];
newData[selectedTaskIndex].courserList= _selectData;
newData[selectedTaskIndex].courseList= _selectData;
this.setState({
relatedCourseModalVisible:false,
dataSource:newData
},()=>{
this.props.onChange(dataSource);
this.props.onChange(newData);
})
}
render() {
const { dataSource,selectedTaskIndex,relatedCourseModalVisible} = this.state;
const { data } = this.props;
return (
<div className="training-task">
<Table
pagination={false}
dataSource={dataSource}
dataSource={data}
columns={this.parseTaskColumns()}
rowKey="index"
expandedRowRender={(record,index) => {
if (record.courserList.length !== 0){
if (record.courseList.length !== 0 ){
return <div>
<Table
pagination={false}
dataSource={record.courserList}
dataSource={record.courseList}
columns={this.parseCoursecolumns(index)}
rowKey="index"
// components={{
......@@ -339,6 +282,7 @@ class TrainingTask extends React.Component {
}else{
return <div><Button onClick={()=>{this.showRelatedCourseModal(index)}}><span>+</span><span>关联课程</span></Button></div>;
}
}}
// components={{
// body: {
......@@ -351,14 +295,12 @@ class TrainingTask extends React.Component {
{ relatedCourseModalVisible &&
<RelatedCourseModal
selectedTaskIndex={selectedTaskIndex}
data={dataSource}
data={data}
visible={relatedCourseModalVisible}
onClose={this.closeRelatedCourseModal}
onSelect={this.confirmSelectCourse}
/>
}
</div>
);
}
......
import React from 'react';
import { withRouter } from "react-router-dom";
import {Table, Modal,Input,message} from 'antd';
import { PageControl } from "@/components";
import UserLearningDataFilter from './UserLearningDataFilter';
import './EmployeeShareData.less';
import Item from 'antd/lib/list/Item';
const { Search } = Input;
const LearnState = {
UN_PLAY: {
text: "未开始"
},
UNDER_WAY: {
text:"进行中"
},
FINISH : {
text:'已完成'
}
};
class UserLearningData extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource:[],
size:10,
query: {
current: 1,
},
totalCount:0,
}
}
handleFetchDataList = ()=>{
}
// 请求表头
parselumns = () => {
const columns = [
{
title: '用户',
key: 'storeCustomerName',
dataIndex: 'storeCustomerName',
render: (val, record) => {
return (
<div>
{val}
</div>
)
}
},
{
title: '学习状态',
key: 'roleEnum',
dataIndex: 'roleEnum',
render: (val, record) => {
return (
<div>
{LearnState[val].text}
</div>
)
}
},
{
title: '负责人',
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: '学习进度',
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>
<span>学习详情</span>
<span>|</span>
<span>解绑</span>
</div>
)
}
}
];
return columns;
}
render() {
const { dataSource,query,size,totalCount} = this.state;
return (
<div className="user-learning-data">
<div className="search-container">
<UserLearningDataFilter/>
</div>
<div>
<Table
rowKey={record => record.id}
dataSource={dataSource}
columns={this.parselumns()}
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()})
}}
/>
</div>
}
</div>
</div>
)
}
}
export default withRouter(UserLearningData);
\ 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 './UserLearningDataFilter.less';
const { Search } = Input;
const { Option } = Select;
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(() => {
getCreatorList();
}, [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="search-name">用户:</span>
<Search
value={query.customerName}
placeholder="搜索用户名称"
onChange={(e) => { handleChangeQuery('customerName', 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.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="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.learnState}
onChange={(value) => { handleChangeQuery('learnState', 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(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;
.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
......@@ -28,7 +28,8 @@ class SelectOperatorModal extends React.Component {
const {query,size,totalCount} = this.state
const params ={
...query,
size
size,
roleCodes:['CloudOperator']
}
StoreService.getStoreUserBasicPage(params).then((res) => {
const { result = {} } = res ;
......
......@@ -64,7 +64,8 @@ class SelectOperatorModal extends React.Component {
handleFetchLiveDataList = () => {
const {liveQuery,liveSize} = this.state;
const { selectedTaskIndex } =this.props;
const currentLiveCourseListData =[...this.props.data[selectedTaskIndex].courserList]
console.log('selectedTaskIndex',selectedTaskIndex);
const currentLiveCourseListData =[...this.props.data[selectedTaskIndex].courseList]
const _currentLiveCourseListData = currentLiveCourseListData.map((item,index) => {
if(item.liveCourseId){
return item
......@@ -74,7 +75,7 @@ class SelectOperatorModal extends React.Component {
const params ={
...liveQuery,
liveSize,
excludeCourseIdList:_.pluck(_currentLiveCourseListData,'liveCourseId')
excludeCourseIdList:_.pluck(_currentLiveCourseListData,'courseId')
}
CourseService.getLiveCloudCourseBasePage(params).then((res) => {
......@@ -92,7 +93,7 @@ class SelectOperatorModal extends React.Component {
handleFetchVideoDataList = () => {
const {videoQuery,videoSize,videoTotalCount} = this.state;
const { selectedTaskIndex } =this.props;
const currentVideoCourseListData =[...this.props.data[selectedTaskIndex].courserList];
const currentVideoCourseListData =[...this.props.data[selectedTaskIndex].courseList];
const _currentVideoCourseListData = currentVideoCourseListData.map((item,index) => {
if(!item.liveCourseId){
return item
......@@ -101,7 +102,7 @@ class SelectOperatorModal extends React.Component {
const params ={
...videoQuery,
videoSize,
excludeCourseIdList:_.pluck(_currentVideoCourseListData,'id')
excludeCourseIdList:_.pluck(_currentVideoCourseListData,'courseId')
}
CourseService.videoScheduleBasePage(params).then((res) => {
......@@ -279,6 +280,24 @@ class SelectOperatorModal extends React.Component {
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;
......@@ -292,7 +311,7 @@ class SelectOperatorModal extends React.Component {
className="select-operator-modal"
closable={true}
width={800}
onOk={() => this.props.onSelect([...selectVideo,...selectLive]) }
onOk={() => this.props.onSelect([...this.handleSelectVideo(selectVideo),...this.handleSelectLive(selectLive)]) }
>
<div>
......
......@@ -2,7 +2,7 @@
* @Author: wufan
* @Date: 2020-11-27 16:21:49
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-03-01 19:22:10
* @LastEditTime: 2021-03-02 13:40:27
* @Description: Description
* @@Copyrigh: © 2020 杭州杰竞科技有限公司 版权所有
*/
......@@ -43,7 +43,7 @@ interface AddEmployeeModalProps {
function AddEmployeeModal(props: AddEmployeeModalProps) {
const [nickName, setName] = useState("");
const [phone, setPhone] = useState("");
const [role, setRole] = useState("CloudOperation");
const [role, setRole] = useState("CloudOperator");
const [avatar, setAvatar] = useState(
"https://image.xiaomaiketang.com/xm/rJeQaZxtc7.png"
);
......@@ -282,7 +282,7 @@ function AddEmployeeModal(props: AddEmployeeModalProps) {
}}
className="mt5"
>
<Radio value={"CloudOperation"} className="mt-4">
<Radio value={"CloudOperator"} className="mt-4">
<span style={{ color: "#333" }}>运营师</span>
<p className="radio-tip">
仅可查看/转发培训计划内容,并查看其负责的用户学习进度
......
......@@ -2,7 +2,7 @@
* @Author: 吴文洁
* @Date: 2020-04-29 10:26:32
* @LastEditors: zhangleyuan
* @LastEditTime: 2021-02-22 11:36:41
* @LastEditTime: 2021-03-02 15:56:22
* @Description: 内容线路由配置
*/
import EmployeesManagePage from '@/modules/store-manage/EmployeesManagePage';
......@@ -21,6 +21,8 @@ import ResourceDisk from '@/modules/resource-disk';
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 = [
{
......@@ -87,7 +89,18 @@ const mainRoutes = [
path: '/create-plan',
component:AddPlanPage,
name: '创建视频课'
},
{
path: '/store-info',
component:StoreInfoPage,
name: '店铺信息'
},
{
path: '/learning-data',
component:LearningDataPage,
name: '学习数据'
}
]
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 = [
{
groupName: "课程管理",
......@@ -23,11 +31,28 @@ export const menuList: any = [
link: '/resource-disk'
},
{
groupName: "培训管理",
groupCode: "TrainManage",
icon: '&#xe863;',
children: [
{
groupName: "培训计划",
groupCode: "TrainPlan",
link: '/plan'
}
]
},
{
groupName: "店铺管理",
groupCode: "CloudShop",
icon: '&#xe82e;',
children: [
{
groupName: "店铺信息",
groupCode: "ShopInfo",
link: '/store-info'
},
{
groupName: "员工管理",
groupCode: "ShopStaff",
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