Commit f8f5536f by xuzhenghua

pull

parent c087d8df
import React from 'react';
import './banner.scss';
export default (props) => {
const { banner = '' } = props;
const style = () => {
if(banner) {
return {
backgroundImage: `url(${banner})`
}
}
return {};
}
return (
<div className="college-banner"></div>
)
}
\ No newline at end of file
.college-banner {
height: 100px;
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/college-banner.png');
background-size: cover;
}
\ No newline at end of file
import React, { Component } from 'react';
import { http } from '@/utils';
import { HeaderBar } from "@/common";
import CollegeBanner from './banner';
import CollegeHeader from './header';
import './courseList.scss';
import { Toast } from 'antd-mobile';
class CollegeCourse extends Component {
constructor(props) {
super(props);
this.state = {
header: '',
courseList: []
};
}
componentDidMount() {
this.fetchCourseList();
}
fetchCourseList = () => {
const { match } = this.props;
const id = match.params.id || 0;
http.get(`${API['home']}/sys/school/${id}`).then(res => {
const { code, data } = res.data;
if(code === 200) {
this.setState({
header: data.name,
courseList: data.data
});
}
});
}
toCourseDetail = (id) => {
const { history, match } = this.props;
const school_id = match.params.id || 0
http.post(`${API['home']}/sys/school/verify`, {
school_id,
course_id: id,
}).then(res => {
const { code, msg } = res.data;
if(code === 200) {
history.push(`/detail?id=${id}`);
}else if(code === 4030 || code === 4013) {
history.push('/passport/login');
}else {
Toast.info(`${msg}~`);
}
});
}
render() {
const { courseList = [], header } = this.state;
return (
<>
<HeaderBar title={'助学计划'} arrow={true}/>
<CollegeBanner />
<CollegeHeader
headerStyle={{
marginTop: '20px'
}}
title={header}
/>
<p className="college-course__desc">点击需要学习的课程封面,在课程详情页进入购买流程凭免单券免费兑换课程。</p>
<div className="college-course__body">
{
courseList.map(item => (
<div
className="college-course__item"
onClick={() => this.toCourseDetail(item.course_id)}
key={item.course_id}
>
<i className="college-course__cover" style={{'backgroundImage': `url(${item.image_name})`}}></i>
<p className="college-course__title">{item.course_title}</p>
</div>
))
}
</div>
</>
)
}
}
export default CollegeCourse;
\ No newline at end of file
.college-course__desc {
margin: 8px 19px 12px;
font-size: 12px;
color: #0099FF;
text-align: center;
line-height: 15px;
}
.college-course__body {
display: flex;
flex-wrap: wrap;
padding: 0 7px;
}
.college-course__item {
width: 165px;
height: 162px;
margin: 0 7px 15px;
border-radius: 4px;
background-color: #fff;
box-shadow: 0px 0px 8px 0px rgba(153,153,153,0.2);
overflow: hidden;
}
.college-course__cover {
display: block;
height: 119px;
background-repeat: no-repeat;
background-position: center;
background-size: auto 100%;
}
.college-course__title {
margin: 8px 0 0;
padding-left: 5px;
font-size: 14px;
color: #525C65;
line-height: 16px;
// text-overflow: ellipsis;
// white-space: nowrap;
// overflow: hidden;
}
\ No newline at end of file
import React from 'react';
import './header.scss';
export default (props) => {
const { title, isDecorate = true, headerStyle = {} } = props;
return (
<>
<div className="college-header" style={headerStyle}>
<i className="college-header__icon" data-direction="left"></i>
<h2 className="college-header__text">{title}</h2>
<i className="college-header__icon" data-direction="right"></i>
</div>
{
isDecorate &&
<i className="college-header__decorate"></i>
}
</>
);
};
.college-header {
display: flex;
justify-content: center;
align-items: center;
height: 34px;
}
.college-header__icon {
width: 44px;
height: 8px;
background-size: cover;
&[data-direction="left"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/icon-left.png');
background-position: right;
}
&[data-direction="right"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/icon-right.png');
background-position: left;
}
}
.college-header__text {
margin: 0;
padding: 0 10px;
font-size: 14px;
font-weight: 500;
color: #525C65;
line-height: 1;
}
.college-header__decorate {
display: block;
width: 39px;
height: 3px;
margin: 0 auto;
border-radius: 2px;
background: linear-gradient(90deg,rgba(0,153,255,1) 0%,rgba(55,117,239,1) 100%);
}
\ No newline at end of file
import React, { Component } from 'react';
import { http } from '@/utils';
import { HeaderBar } from "@/common";
import CollegeBanner from './banner';
import CollegeHeader from './header';
import './index.scss';
class CollegePage extends Component {
constructor(props) {
super(props);
this.state = {
sectionInfo: [
{
title: '系统赞助',
desc: '免费提供在线直播系统(屏幕实时演示),支持视频回放、互动答疑。',
icon: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/system.png',
},
{
title: '内容赞助 ',
desc: '免费开放相关课程的视频、课件、作业、考试、项目云平台(在线编译、在线批改)。',
icon: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/content.png',
},
],
schoolList: [],
};
}
componentDidMount() {
this.fetchSchoolList();
}
fetchSchoolList = () => {
http.get(`${API['home']}/sys/school/list`).then(res => {
const { code, data } = res.data;
if(code === 200) {
this.setState({
schoolList: data,
});
}
});
}
toCollegeCourse = (id) => {
const { history } = this.props;
history.push(`/college/${id}`);
}
render() {
const { sectionInfo = [], schoolList = [] } = this.state;
return (
<>
<HeaderBar title={'助学计划'} arrow={true}/>
<CollegeBanner />
<div className="college-page__banner"></div>
<CollegeHeader
headerStyle={{
marginTop: '20px'
}}
title="助力高校在线教学计划,在线不停课"
/>
<p className="college-page__contact">免费入驻联系周先生:18910848502(微信同)</p>
{
sectionInfo.map(({title, desc, icon}, index) => (
<div className="college-page__section" key={index}>
<i className="section__icon" style={{'backgroundImage': `url(${icon})`}}></i>
<div className="section__body">
<h2 className="section__title">{title}</h2>
<p className="section__desc">{desc}</p>
</div>
</div>
))
}
<CollegeHeader
headerStyle={{
marginTop: '20px'
}}
isDecorate={false}
title="入驻学院"
/>
<div className="college-page__college">
{
schoolList.map(({id, name, logo}) => (
<div
className="college__item"
onClick={() => this.toCollegeCourse(id)}
key={id}
>
<i className="college__iamge" style={{'backgroundImage': `url(${logo})`}}></i>
<p className="college__name">{name}</p>
</div>
))
}
</div>
</>
)
}
}
export default CollegePage;
\ No newline at end of file
.college-page__contact {
margin: 5px 0 0;
font-size: 12px;
color: #0099FF;
text-align: center;
line-height: 22px;
}
.college-page__section {
margin: 0 15px 10px;
border: 1px solid #ECECEC;
border-radius: 1px;
.section__icon {
float: left;
width: 30px;
height: 34px;
margin: 13px 0 0 15px;
background-size: 100% auto;
background-position: center;
background-repeat: no-repeat;
}
.section__body {
margin-left: 60px;
padding: 8px 0;
}
.section__title {
margin: 0 0 4px;
font-size: 14px;
font-weight: 400;
color: #0099FF;
line-height: 1;
}
.section__desc {
margin: 0;
font-size: 12px;
color: #333;
line-height: 15px;
}
}
.college-page__college {
display: flex;
flex-wrap: wrap;
padding: 4px 10px 0;
.college__item {
width: 166px;
margin: 0 5px 10px;
cursor: pointer;
&:hover {
.college__iamge {
border-color: #0099FF;
}
.college__name {
color: #0099ff;
}
}
}
.college__iamge {
display: block;
height: 60px;
border: 1px solid #ECECEC;
border-radius: 1px;
background-size: auto 100%;
background-position: center;
background-repeat: no-repeat;
}
.college__name {
margin: 6px 0 0;;
font-size: 12px;
color: #525C65;
text-align: center;
line-height: 16px;
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import ForgotPasswordEmail from './forgotPasswordEmail'
import { connect } from "react-redux"
import { compose } from "redux"
import { getParam } from "@/utils"
import StudentRoot from './studentRoot';
import account from './icons/account.png'
import qq from './icons/qq.png'
......@@ -103,6 +104,7 @@ class Passport extends Component {
from={'/passport'}
to={{...location, ...{pathname: '/passport/login'}}}
/>
<Route path={match.url + '/student-login'} component={StudentRoot}/>
<Route path={match.url + '/login'}
render={props => {
return <Login {...props} loginWays={this.state.loginWays}/>
......
......@@ -66,6 +66,11 @@ class Login extends Component {
}
}
toStudentRegister = () => {
const { history } = this.props;
history.push('/passport/student-login')
}
render() {
const {
loginWays,
......@@ -117,8 +122,12 @@ class Login extends Component {
onVerify={this.onVerify}
/>
<LoginButton active={values.tel && values.veriCode && isEmpty(errors)}/>
</Form>
{/* 助学计划 */}
<div className="student-root">
<a className="student-root__button" onClick={this.toStudentRegister}>助学计划</a>
</div>
</Form>
<LoginWays onClick={this.loginWaysClick} loginWays={loginWays}/>
</div>
)
......
......@@ -25,5 +25,15 @@
}
.student-root {
padding-top: 18px;
text-align: right;
}
.student-root__button {
font-size: 15px;
color: #0099FF;
line-height: 1;
cursor: pointer;
}
}
\ No newline at end of file
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { studentLogin } from '@/store/userAction';
import { http } from '@/utils';
import { Formik, Form, Field } from 'formik';
import { Toast } from "antd-mobile";
import { HeaderBar } from "@/common";
import Captcha from '@/common/Captcha'
import Header from '../common/Header';
import Input from '../common/inputWithCountryCodes';
import VeriCodeInput from '../common/veriCodeInput';
import './index.scss';
@connect(
state => ({
country: state.country,
}),
{
studentLogin
}
)
class StudentRoot extends PureComponent {
constructor(props) {
super(props);
this.state = {
validate: null,
captchaInstance: null,
disabled: false,
isSchool: false,
isCollege: false,
list: [],
schoolList: [],
schoolName: '',
};
}
componentDidMount() {
this.fetchSchoolInfo();
}
fetchSchoolInfo = () => {
http.get(`${API['home']}/sys/schools `).then(res => {
const { code, data } = res.data;
if(code === 200) {
const schoolList = [];
data.forEach(item => {
schoolList.push(item.school);
});
this.setState({
list: data,
schoolList
});
}
});
}
handleToLogin = ({ tel, password, code, school, college, uid, name}) => {
const { validate, captchaInstance } = this.state;
const { country, studentLogin, location } = this.props;
const from = location.state && location.state.from;
studentLogin({
validate,
tel,
password,
code,
num: `00${country.num}`,
school,
college,
uid,
name,
redirect: from && window.location.origin + from.pathname + from.search + from.hash,
}).then(res => {
if (res.hasError) {
captchaInstance.refresh();
Toast.info(res.msg, 2, null, false);
}
});
}
getCaptchaInstance = instance => {
this.setState({
captchaInstance: instance
});
}
onVerify = (err, data) => {
if (!err) {
this.setState({
validate: data.validate
});
}
}
selectSwitch = (key, value) => {
let param = {};
param[key] = value;
this.setState(param);
}
changeToCollege = (school = '') => {
this.setState({
schoolName: school
});
}
fetchCollegeInfo = () => {
const { list, schoolName } = this.state;
const data = list.filter(item => item['school'] === schoolName);
if(data.length > 0) {
return data[0]['colleges']
}
return [];
}
render() {
const { country } = this.props;
const { validate, captchaInstance, isSchool, isCollege, schoolList, schoolName } = this.state;
const collegeList = this.fetchCollegeInfo();
return (
<>
<HeaderBar title={'助学计划'} arrow={true}/>
<Header/>
<Formik
initialValues={{
tel: '',
password: '',
code: '',
school: '',
college: '',
uid: '',
name: '',
}}
validate={({ tel, password, code, school, college, uid, name }) => {
let errors = {}
if (!/\d/.test(tel)) {
errors.tip = '请填写正确格式的手机号~';
return errors;
}
if(password.length < 6 || !/[A-Za-z].*[0-9]|[0-9].*[A-Za-z]/.test(password)) {
errors.tip = '密码需要包含6-16位字母和数字~';
return errors;
}
if (!/[0-9]{6}/.test(code)) {
errors.tip = '请填写验证码(先滑块验证呦)~';
return errors;
}
if (!school) {
errors.tip = '请选择学校~';
return errors;
}
if (!college) {
errors.tip = '请选择学院~';
return errors;
}
if (!uid) {
errors.tip = '请填写学号~';
return errors;
}
if (!name) {
errors.tip = '请填写姓名~';
return errors;
}
return {};
}}
onSubmit={(values, errors) => {
this.handleToLogin(values);
}}
>
{props => {
const isSubmit = Object.values(props.values).join('') !== '' && props.errors.tip === undefined;
return (
<Form className="student-form">
<Field
name='tel'
render={({field}) => (
<Input
{...field}
type={'tel'}
placeholder={'手机号快捷登录(免注册)'}
wrapperClass={'tel-input'}
country={country}
/>
)}
/>
<div className="student-form__item">
<Field
className="student-form__input"
type="password"
name="password"
minLength="6"
maxLength="16"
placeholder="密码需要包含6-16位字母和数字"
/>
</div>
<Captcha
mrBtm={'15px'}
getInstance={this.getCaptchaInstance}
onVerify={this.onVerify}
/>
{
validate &&
<Field
type='number'
name='code'
render={({field}) => (
<VeriCodeInput
{...field}
className={'student-form__code'}
icon={<i className={'iconfont iconduanxin'} style={{fontSize: '20px', left: '12px'}}/>}
tel={props.values.tel}
challenge={validate}
errors={props.errors}
placeholder={'请输入验证码'}
instance={captchaInstance}
country={country}
/>
)}
/>
}
<div className="student-form__item">
<label className="student-form__label">学校</label>
<StudentSelect
name="school"
value={props.values.school}
schoolName={schoolName}
data={{
key: 'isSchool',
val: isSchool
}}
options={schoolList}
placeholder="请选择"
onChange={props.setFieldValue}
clearToCollege={() => {
props.setFieldValue('college', '')
}}
selectSwitch={this.selectSwitch}
changeToCollege={this.changeToCollege}
/>
</div>
<div className="student-form__item">
<label className="student-form__label">学院</label>
<StudentSelect
name="college"
value={props.values.college}
data={{
key: 'isCollege',
val: isCollege
}}
isClick={props.values.school !== ''}
options={collegeList}
placeholder="请先选择学校"
onChange={props.setFieldValue}
selectSwitch={this.selectSwitch}
/>
</div>
<div className="student-form__item">
<label className="student-form__label">学号</label>
<Field className="student-form__input" type="text" name="uid" placeholder="不区分大小写"/>
</div>
<div className="student-form__item">
<label className="student-form__label">姓名</label>
<Field className="student-form__input" type="text" name="name" placeholder="请准确填写"/>
</div>
<div className="student-form__footer">
{
props.errors.tip &&
<p className="student-form__tip">*{props.errors.tip}</p>
}
<button className="student-form__submit" type="submit" disabled={!isSubmit}>注册</button>
</div>
</Form>
)
}}
</Formik>
</>
)
}
}
const StudentSelect = (props) => {
const {
options = [],
data: { key = '' , val = false },
schoolName,
isClick = true,
name,
value,
onChange,
placeholder,
selectSwitch,
clearToCollege,
changeToCollege
} = props;
return (
<div className="student-select">
<input
className={classnames({'active': val})}
value={value}
type="text"
placeholder={placeholder}
readOnly
onClick={() => {
isClick && selectSwitch(key, true);
}}
/>
{
val &&
<ul className="student-select__list">
{
options.length > 0 && options.map((item, index) => (
<li
className="student-select__option"
key={index}
onClick={() => {
selectSwitch(key, false);
onChange(name, item);
if(schoolName !== item && typeof clearToCollege === 'function') {
clearToCollege();
}
typeof changeToCollege === 'function' && changeToCollege(item);
}}
>{item}</li>
))
}
</ul>
}
</div>
)
}
export default StudentRoot;
\ No newline at end of file
.student-form {
margin-top: -27px;
padding: 0 35px;
.input-with-country-codes {
margin-bottom: 15px;
}
}
.student-form__code {
margin: 0 0 15px;
&.input-wrapper {
width: 100%;
}
}
.student-form__item {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.student-form__label {
width: 44px;
font-size: 15px;
color: 333;
}
.student-form__input {
flex: 1;
height: 46px;
padding: 0 15px;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box;
font-size: 15px;
color: #999;
}
.student-form__footer {
position: relative;
margin-top: 35px;
}
.student-form__tip {
position: absolute;
top: -25px;
margin: 0;
padding-left: 60px;
font-size: 12px;
color: #FF1717;
line-height: 1;
}
.student-form__submit {
width: 100%;
height: 44px;
padding: 0;
border-style: none;
border-radius: 3px;
font-size: 18px;
color: #fff;
background-color: #0099FF;
&:disabled {
background-color: #ccc;
}
}
.student-select {
position: relative;
flex: 1;
height: 46px;
input {
width: 100%;
height: 100%;
padding: 0 15px;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box;
font-size: 15px;
color: #999;
&.active {
border-radius: 3px 3px 0 0;
}
}
}
.student-select__list {
position: absolute;
top: 45px;
width: 100%;
border: 1px solid #ccc;
border-radius: 0 0 3px 3px;
box-sizing: border-box;
background-color: #fff;
z-index: 99;
}
.student-select__option {
padding-left: 15px;
font-size: 14px;
color: #999;
line-height: 32px;
&:hover {
color: #0099FF;
}
}
\ No newline at end of file
......@@ -286,4 +286,17 @@ export default [
path: '/wxerr',
component: loadable(() => import(/* wx-err */ '@components/wxerr/index'))
},
// 助学计划落地页
{
path: '/college',
exact: true,
component: loadable(() => import('@/components/college'))
},
// 助学计划落地页
{
path: '/college/:id',
exact: true,
component: loadable(() => import('@/components/college/courseList'))
},
]
......@@ -14,6 +14,36 @@ const accountLogin = user => dispatch => {
})
}
const studentLogin = params => dispatch => {
return http.post(`${API['passport-api']}/phone_reg`, {
challenge: params.validate,
phone: params.tel,
password: params.password,
code: params.code,
area_code: params.num,
school_name: params.school,
college_name: params.college,
student_id: params.uid,
student_name: params.name,
redirect: encodeURIComponent(params.redirect),
type: 1,
}).then(res => {
const { errno, data } = res.data;
let result = {};
if(errno === 0) {
result = {
data: {
errno: 200,
data,
}
};
}else {
result = res;
}
return storeUser(result, dispatch);
});
}
const quickLogin = user => dispatch => {
return http.post(`${API['passport-api']}/m/login/quickLogin`, {
...user,
......@@ -75,6 +105,7 @@ const startFetchUser = () => ({
export {
accountLogin,
studentLogin,
SET_CURRENT_USER,
setCurrentUser,
quickLogin,
......
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