Commit cff4cd0f by chenshu

feat:初始化

parent 553cbc40
...@@ -27,7 +27,7 @@ import ShowTips from "@/components/ShowTips"; ...@@ -27,7 +27,7 @@ import ShowTips from "@/components/ShowTips";
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from "@/components/Breadcrumbs";
import SelectStudent from '../modal/select-student'; import SelectStudent from '../modal/select-student';
import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal'; import SelectPrepareFileModal from '../../prepare-lesson/modal/SelectPrepareFileModal';
import PreviewGraphicsModal from '../modal/PreviewGraphicsModal'; import PreviewOfflineModal from './modal/PreviewOfflineModal';
import StoreService from "@/domains/store-domain/storeService"; import StoreService from "@/domains/store-domain/storeService";
import Service from '@/common/js/service'; import Service from '@/common/js/service';
import { randomString } from '@/domains/basic-domain/utils'; import { randomString } from '@/domains/basic-domain/utils';
...@@ -304,24 +304,25 @@ class AddOfflineCourse extends React.Component { ...@@ -304,24 +304,25 @@ class AddOfflineCourse extends React.Component {
const { const {
coverUrl, coverUrl,
courseName, courseName,
courseMedia, startTime,
endTime,
introduce, introduce,
categoryName, categoryName,
offlinePlace,
} = this.state; } = this.state;
const courseBasinInfo = { const data = {
coverUrl, coverUrl,
courseName, courseName,
categoryName startTime,
} endTime,
const courseIntroInfo = { categoryName,
courseMedia,
introduce, introduce,
offlinePlace,
} }
const previewGraphicsModal = ( const previewGraphicsModal = (
<PreviewGraphicsModal <PreviewOfflineModal
courseBasicInfo={courseBasinInfo} data={data}
courseIntroInfo={courseIntroInfo}
close={() => { close={() => {
this.setState({ this.setState({
previewGraphicsModal: null previewGraphicsModal: null
...@@ -749,10 +750,29 @@ class AddOfflineCourse extends React.Component { ...@@ -749,10 +750,29 @@ class AddOfflineCourse extends React.Component {
<div className="course-catalog"> <div className="course-catalog">
<span className="label"><span className="require">*</span>课程分类:</span> <span className="label"><span className="require">*</span>课程分类:</span>
{ (pageType === 'add') && { (pageType === 'add') &&
<Cascader defaultValue={categoryName ? [categoryName] : undefined} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}/> <Cascader
showSearch
defaultValue={categoryName ? [categoryName] : undefined}
options={courseCatalogList}
displayRender={ label => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{ fontSize: '12px', color: '#BFBFBF' }}>&#xe835;</span>}
/>
} }
{ (pageType === 'edit' && categoryName ) && { (pageType === 'edit' && categoryName ) &&
<Cascader defaultValue={[categoryName]} options={courseCatalogList} displayRender={ label => label.join('-')} fieldNames={fieldNames} onChange={this.catalogChange} style={{ width: 240 }} placeholder="请选择课程分类" suffixIcon={<span className="icon iconfont" style={{fontSize:'12px',color:'#BFBFBF'}}>&#xe835;</span>}/> <Cascader
showSearch
defaultValue={[categoryName]}
options={courseCatalogList}
displayRender={ label => label.join('-')}
fieldNames={fieldNames}
onChange={this.catalogChange}
style={{ width: 240 }}
placeholder="请选择课程分类"
suffixIcon={<span className="icon iconfont" style={{ fontSize: '12px', color: '#BFBFBF' }}>&#xe835;</span>}
/>
} }
</div> </div>
<div className="course-catalog"> <div className="course-catalog">
......
import React from 'react'; import React from 'react';
import { Tooltip, Input, Radio, Table } from 'antd'; import { Tooltip, Input, Radio, Table } from 'antd';
import moment from 'moment'; import moment from 'moment';
import _ from 'underscore';
import Breadcrumbs from "@/components/Breadcrumbs"; import Breadcrumbs from "@/components/Breadcrumbs";
import PageControl from '@/components/PageControl'; import PageControl from '@/components/PageControl';
import Service from "@/common/js/service"; import Service from "@/common/js/service";
import User from '@/common/js/user';
import './OfflineCourseData.less'; import './OfflineCourseData.less';
const { Search } = Input; const { Search } = Input;
...@@ -11,19 +13,29 @@ const { Search } = Input; ...@@ -11,19 +13,29 @@ const { Search } = Input;
export default class OfflineCourseData extends React.Component { export default class OfflineCourseData extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const courseId = window.getParameterByName('id');
this.state = { this.state = {
courseId: window.getParameterByName('id'), courseId,
query: { query: {
size: 10, size: 10,
current: 1, current: 1,
courseId,
storeId: User.getStoreId(),
joinInState: 'YES',
joinOutState: 'YES',
}, },
loading: false, loading: false,
data: [], data: [],
total: 0, total: 0,
courseName: '', courseName: '',
calendarTime: [], calendarTime: [],
currentIndex: 0,
fullJoin: 0, fullJoin: 0,
totalJoin: 0, totalJoin: 0,
fullJoinNum: 0,
joinInNum: 0,
joinNum: 0,
joinOutNum: 0,
} }
} }
...@@ -39,25 +51,52 @@ export default class OfflineCourseData extends React.Component { ...@@ -39,25 +51,52 @@ export default class OfflineCourseData extends React.Component {
const { result } = res; const { result } = res;
this.setState({ this.setState({
courseName: result.courseName, courseName: result.courseName,
fullJoin: result.fullJoin, fullJoin: result.fullJoin || 0,
totalJoin: result.totalJoin, totalJoin: result.totalJoin || 0,
}); });
} }
}) })
} }
getOfflineCalendar = () => { getOfflineCalendar = () => {
const { courseId } = this.state; const { courseId, currentIndex } = this.state;
Service.Hades('public/customerHades/offlineDateList', { courseId }).then((res) => { Service.Hades('public/customerHades/offlineDateList', { courseId }).then((res) => {
if (res.success) { if (res.success) {
console.log(result.calendarTime, _.groupBy(result.calendarTime, item => moment(item).format('YYYY-MM')), 1111111) const timeList = _.sortBy(_.pluck(res.result, 'date'));
const group = _.groupBy(timeList, item => moment(item).format('YYYY年M月'));
const calendarTime = _.map(group, (value, key) => ({ key, value }));
const currentDate = calendarTime[currentIndex].value[0];
this.setState({ this.setState({
calendarTime: _.pluck(res.result, 'date'), calendarTime,
}) currentDate,
}, () => this.getDateDetail())
} }
}); });
} }
getDateDetail = (current = 1) => {
const { query, currentDate, courseId } = this.state;
if (currentDate !== query.date) {
Service.Hades('public/hades/getOfflineDateJoinStatistics', { courseId, date: currentDate }).then((res) => {
if (res.success) {
this.setState({
fullJoinNum: res.result.fullJoinNum || 0,
joinInNum: res.result.joinInNum || 0,
joinNum: res.result.joinNum || 0,
joinOutNum: res.result.joinOutNum || 0,
})
}
})
}
query.current = current;
query.date = currentDate;
Service.Hades('public/hades/getOfflineDateJoinPage', query).then((res) => {
if (res.success) {
this.setState({ data: res.result.records, total: res.result.total });
}
})
}
getColumns = () => { getColumns = () => {
const columns = [ const columns = [
{ {
...@@ -72,26 +111,26 @@ export default class OfflineCourseData extends React.Component { ...@@ -72,26 +111,26 @@ export default class OfflineCourseData extends React.Component {
}, },
{ {
title: "手机号", title: "手机号",
key: "teacher", key: "phone",
dataIndex: "teacher", dataIndex: "phone",
render: (val, item) => { render: (val, item) => {
return ( return (
<div>{item.teacherName}</div> <div>{item.phone}</div>
) )
}, },
}, },
{ {
title: '报名时间', title: '报名时间',
key: 'updated', key: 'joinTime',
dataIndex: 'updated', dataIndex: 'joinTime',
render: (val, item) => { render: (val, item) => {
return item.startTimeApply ? `${formatDate('MM-DD H:i', item.startTimeApply)} ~ ${formatDate('MM-DD H:i', item.endTimeApply)}` : '-' return <div>{formatDate('YYYY-MM-DD H:i', item.joinTime)}</div>
} }
}, },
{ {
title: '签到时间', title: '签到时间',
key: 'signIn', key: 'joinInTime',
dataIndex: 'signIn', dataIndex: 'joinInTime',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val) return formatDate('YYYY-MM-DD H:i', val)
...@@ -99,8 +138,8 @@ export default class OfflineCourseData extends React.Component { ...@@ -99,8 +138,8 @@ export default class OfflineCourseData extends React.Component {
}, },
{ {
title: '签退时间', title: '签退时间',
key: 'signOut', key: 'joinOutTime',
dataIndex: 'signOut', dataIndex: 'joinOutTime',
sorter: true, sorter: true,
render: (val) => { render: (val) => {
return formatDate('YYYY-MM-DD H:i', val) return formatDate('YYYY-MM-DD H:i', val)
...@@ -123,7 +162,16 @@ export default class OfflineCourseData extends React.Component { ...@@ -123,7 +162,16 @@ export default class OfflineCourseData extends React.Component {
courseName, courseName,
fullJoin, fullJoin,
totalJoin, totalJoin,
calendarTime,
currentIndex,
currentDate,
fullJoinNum,
joinInNum,
joinNum,
joinOutNum,
} = this.state; } = this.state;
const calendarLength = calendarTime.length;
const dateList = (calendarTime[currentIndex] || {}).value || [];
return ( return (
<div className="page offline-course-data"> <div className="page offline-course-data">
<Breadcrumbs <Breadcrumbs
...@@ -143,29 +191,59 @@ export default class OfflineCourseData extends React.Component { ...@@ -143,29 +191,59 @@ export default class OfflineCourseData extends React.Component {
<div className="left-box"> <div className="left-box">
<div className="left-title">上课日期</div> <div className="left-title">上课日期</div>
<div className="left-calendar"> <div className="left-calendar">
<div className="icon-box"> <div
className="icon-box"
onClick={() => {
const index = currentIndex - 1;
if (index >= 0 && index < calendarLength) {
this.setState({ currentIndex: index, currentDate: calendarTime[index][0] }, () => {
this.getDateDetail();
});
}
}}
>
<span className="icon iconfont">&#xe79c;</span> <span className="icon iconfont">&#xe79c;</span>
</div> </div>
<div className="calendar-text">2021年5月</div> <div className="calendar-text">{(calendarTime[currentIndex] || {}).key}</div>
<div className="icon-box"> <div
className="icon-box"
onClick={() => {
const index = currentIndex + 1;
if (index >= 0 && index < calendarLength) {
this.setState({ currentIndex: index, currentDate: calendarTime[index][0] }, () => {
this.getDateDetail();
});
}
}}
>
<span className="icon iconfont">&#xe79b;</span> <span className="icon iconfont">&#xe79b;</span>
</div> </div>
</div> </div>
<div className="date-list"> <div className="date-list">
<div className="date-item">5月1日(周五)</div> {dateList.map(item => (
<div
className={`date-item${item === currentDate ? ' selected' : ''}`}
key={item}
onClick={() => {
this.setState({ currentDate: item }, () => {
this.getDateDetail();
})
}}
>{window.formatDate('MM月DD日(WW)', item)}</div>
))}
</div> </div>
</div> </div>
<div className="right-box"> <div className="right-box">
<div className="selected-date">5月1日</div> <div className="selected-date">5月1日</div>
<div className="detail-data"> <div className="detail-data">
<span className="icon iconfont">&#xe89f;</span> <span className="icon iconfont">&#xe89f;</span>
<span className="data-text">报名人数:100</span> <span className="data-text">报名人数:{joinNum}</span>
<span className="icon iconfont">&#xe89e;</span> <span className="icon iconfont">&#xe89e;</span>
<span className="data-text">完成考勤数<Tooltip title="当日在规定时间内完成签到和签退的用户数"><span className="icon iconfont">&#xe7c4;</span></Tooltip>99</span> <span className="data-text">完成考勤数<Tooltip title="当日在规定时间内完成签到和签退的用户数"><span className="icon iconfont">&#xe7c4;</span></Tooltip>{fullJoinNum}</span>
<span className="icon iconfont">&#xe8a0;</span> <span className="icon iconfont">&#xe8a0;</span>
<span className="data-text">签到人数:99</span> <span className="data-text">签到人数:{joinInNum}</span>
<span className="icon iconfont">&#xe89d;</span> <span className="icon iconfont">&#xe89d;</span>
<span className="data-text">签退人数:99</span> <span className="data-text">签退人数:{joinOutNum}</span>
</div> </div>
<div className="detail-filter"> <div className="detail-filter">
<Search <Search
...@@ -173,11 +251,28 @@ export default class OfflineCourseData extends React.Component { ...@@ -173,11 +251,28 @@ export default class OfflineCourseData extends React.Component {
placeholder="搜索用户姓名/手机号" placeholder="搜索用户姓名/手机号"
style={{ width: 200, marginRight: 24 }} style={{ width: 200, marginRight: 24 }}
enterButton={<span className="icon iconfont">&#xe832;</span>} enterButton={<span className="icon iconfont">&#xe832;</span>}
onSearch={(value) => {
const _query = { ...query };
if (value) {
const isPhone = (value || "").match(/^\d+$/);
const name = isPhone ? "storeCustomerPhone" : "storeCustomerName";
_query.storeCustomerName = "";
_query.storeCustomerPhone = "";
_query[name] = value;
} else {
_query.storeCustomerName = "";
_query.storeCustomerPhone = "";
}
this.setState({ query: _query }, () => this.getDateDetail());
}}
/> />
<div className="filter-box"> <div className="filter-box">
<span className="label">签到情况:</span> <span className="label">签到情况:</span>
<Radio.Group <Radio.Group
defaultValue="YES" value={query.joinInState}
onChange={(e) => {
this.setState({ query: { ...query, joinInState: e.target.value } }, () => this.getDateDetail());
}}
> >
<Radio value="YES">已签到</Radio> <Radio value="YES">已签到</Radio>
<Radio value="NO">未签到</Radio> <Radio value="NO">未签到</Radio>
...@@ -186,7 +281,10 @@ export default class OfflineCourseData extends React.Component { ...@@ -186,7 +281,10 @@ export default class OfflineCourseData extends React.Component {
<div className="filter-box"> <div className="filter-box">
<span className="label">签退情况:</span> <span className="label">签退情况:</span>
<Radio.Group <Radio.Group
defaultValue="YES" value={query.joinOutState}
onChange={(e) => {
this.setState({ query: { ...query, joinOutState: e.target.value } }, () => this.getDateDetail());
}}
> >
<Radio value="YES">已签退</Radio> <Radio value="YES">已签退</Radio>
<Radio value="NO">未签退</Radio> <Radio value="NO">未签退</Radio>
...@@ -208,9 +306,7 @@ export default class OfflineCourseData extends React.Component { ...@@ -208,9 +306,7 @@ export default class OfflineCourseData extends React.Component {
pageSize={query.size} pageSize={query.size}
total={total} total={total}
toPage={(page) => { toPage={(page) => {
const queryStates = _.clone(query); this.getDateDetail(page);
queryStates.current = page;
this.setState({ query: queryStates });
}} }}
/> />
</div> </div>
......
...@@ -84,6 +84,9 @@ ...@@ -84,6 +84,9 @@
background: #F3F6FA; background: #F3F6FA;
cursor: pointer; cursor: pointer;
} }
&.selected {
background: rgba(255, 183, 20, 0.06);
}
} }
} }
} }
......
import React from 'react';
import { Button, Modal, Select } from 'antd';
import './PreviewModal.less';
const { Option } = Select;
export default class PreviewModal extends React.Component {
constructor(props) {
super(props);
}
render() {
const { visible, onCancel } = this.props;
return (
<Modal
title="预览"
width={680}
visible={visible}
footer={null}
onCancel={() => onCancel()}
className="offline-preview-modal"
>
<div className="image-box">
<img src="https://image.xiaomaiketang.com/xm/xYSpX2y6ri.png" className="image" />
</div>
</Modal>
)
}
}
\ No newline at end of file
.offline-preview-modal {
}
\ No newline at end of file
import React from 'react';
import { Modal } from 'antd';
import './PreviewOfflineModal.less';
const defaultCoverUrl = 'https://image.xiaomaiketang.com/xm/YNfi45JwFA.png';
class PreviewOfflineModal extends React.Component {
constructor(props) {
super(props);
}
render() {
const { data } = this.props;
const { coverUrl, courseName, categoryName, introduce } = data;
return (
<Modal
title="预览"
visible={true}
width={680}
onCancel={this.props.close}
footer={null}
maskClosable={false}
closeIcon={<span className="icon iconfont modal-close-icon">&#xe6ef;</span>}
className="preview-live-graphics-modal"
>
<div className="container__wrap">
<div className="container">
<div className="container__header">
<img src={coverUrl || defaultCoverUrl} className="course-cover" />
</div>
<div className="container__body">
<div className="title__name">{courseName}</div>
<div className="title__categery">课程分类:{categoryName}</div>
<div className="title__categery">上课时间:{categoryName}</div>
<div className="title__categery">上课地点{categoryName}</div>
</div>
<div className="container__introduction">
<div className="title">线下课简介</div>
<div className="container__introduction__list editor-box">
<div
className="intro-item text"
dangerouslySetInnerHTML={{
__html: introduce
}}
/>
</div>
</div>
</div>
</div>
</Modal>
)
}
}
export default PreviewOfflineModal;
.preview-live-graphics-modal {
.ant-modal-body {
background-image: url('https://image.xiaomaiketang.com/xm/xZWdziTCAf.png');
background-size: 100% 100%;
}
.container__wrap {
width: 340px;
height: 618px;
padding: 67px 46px 48px 47px;
margin: auto;
background-image: url('https://image.xiaomaiketang.com/xm/DHMzHiGc2E.png');
background-size: 100% 100%;
}
.container {
overflow: scroll;
height: 100%;;
.course-cover, .course-url {
width: 100%;
height: 141px;
background: #000;
}
&__body {
background-color: #FFF;
padding: 7px 0 11px 0;;
.title__name {
color: #333333;
font-weight: 500;
}
.title__categery {
font-size: 12px;
color: #999999;
}
}
&__introduction {
margin-top: 10px;
padding: 12px 0;
position: relative;
&::after {
content: '';
position: absolute;
width: 241px;
top: -10px;
height: 10px;
background: #F4F6FA;
}
.title {
height: 24px;
display: flex;
align-items: center;
font-size: 12px;
color: #333333;
padding: 0 10px;
border-bottom: 1px solid #E8E8E8;
.title-word {
position: relative;
margin-right: 15px;
cursor: pointer;
}
.selected {
color: #FFB714;
&::after {
content: '';
position: absolute;
bottom: -4px;
width: 20px;
height: 1px;
background: #FFB714;
left: 50%;
transform: translateX(-50%);
}
}
}
&__list {
margin-top: 12px;
.intro-item:not(:first-child) {
margin-top: 13px;
}
color: #666;
p {
font-size: 12px;
}
img {
max-width: 100%;
}
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment