Commit be08c70b by xuzhenghua

pull

parent c3f7e8b4
......@@ -281,7 +281,8 @@ module.exports = function(webpackEnv) {
'react-native': 'react-native-web',
'@': path.resolve(__dirname, '../src'),
'@common': path.resolve(__dirname, '../src/common'),
'@components': path.resolve(__dirname, '../src/components')
'@components': path.resolve(__dirname, '../src/components'),
'@assets': path.resolve(__dirname, '../src/assets'),
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
......
......@@ -1027,9 +1027,9 @@
}
},
"@babel/runtime": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
"version": "7.7.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz",
"integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==",
"requires": {
"regenerator-runtime": "^0.13.2"
},
......
......@@ -5,6 +5,7 @@
"dependencies": {
"@babel/core": "7.2.2",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/runtime": "^7.7.7",
"@loadable/component": "^5.10.1",
"@svgr/webpack": "4.1.0",
"antd-mobile": "^2.3.1",
......@@ -41,7 +42,9 @@
"jest-pnp-resolver": "1.0.2",
"jest-resolve": "23.6.0",
"jest-watch-typeahead": "^0.2.1",
"js-base64": "^2.5.1",
"js-cookie": "^2.2.0",
"json-stringify-safe": "^5.0.1",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"lodash": "^4.17.15",
......
......@@ -3,12 +3,13 @@ import Routes from './router'
import cookie from 'js-cookie'
import { connect } from "react-redux"
import { setCurrentUser, startFetchUser } from "@/store/userAction"
import {initialState} from "@/store/userReducer"
import { initialState } from "@/store/userReducer"
import { withRouter, Link } from 'react-router-dom'
import { compose } from 'redux'
import { getParam, http, browser } from "@/utils"
import { Toast } from "antd-mobile"
import { addDays } from 'date-fns'
import stringify from 'json-stringify-safe'
//拦截ajax请求,返回mock数据
......@@ -33,19 +34,22 @@ class App extends Component {
pathnameBlacklist = ['/country', '/passport']
firstLoad = true
componentWillMount(){
componentWillMount() {
if (!getParam('version')) {
if(!localStorage.getItem('qimoClientIdedu')){
if (!cookie.get('qimoClientIdedu')) {
this.getqimoClientId()
} else {
window.qimoClientId = localStorage.getItem('qimoClientIdedu')
window.qimoClientId = cookie.get('qimoClientIdedu')
}
}
}
componentDidMount() {
this.umengStatistic()
//平台信息
cookie.set('plat', '5', {domain: '.julyedu.com'})
!getParam('version') && cookie.set('plat', '5', {domain: '.julyedu.com'})
if (browser.isWeixin && browser.isIOS) {
sessionStorage.setItem('enter_url', window.location.href)
......@@ -70,7 +74,7 @@ class App extends Component {
this.getUser()
}
if (location.pathname === '/passport') {
window.localStorage.setItem('binding_redirect', JSON.stringify(this.previousLocation))
window.localStorage.setItem('binding_redirect', stringify(this.previousLocation))
}
const {pathname, state} = location
if (pathname.startsWith('/passport')) {
......@@ -285,19 +289,19 @@ class App extends Component {
// 七陌客服代码 start
getqimoClientId =()=>{
getqimoClientId = () => {
http.post(`${API['home']}/qiMoUserId`)
.then(res => {
if (res.data.code == 200) {
window.qimoClientId = res.data.data.userId
localStorage.setItem('qimoClientIdedu', res.data.data.userId)
}
})
.catch(function () {
})
.then(res => {
if (res.data.code == 200) {
window.qimoClientId = res.data.data.userId
cookie.set('qimoClientIdedu', res.data.data.userId, {domain: '.julyedu.com'})
}
})
.catch(function () {
})
}
QimoClient=()=>{
QimoClient = () => {
let utm_source = getParam('utm_source')
let utm_medium = getParam('utm_medium')
let utm_campaign = getParam('utm_campaign')
......@@ -307,10 +311,10 @@ class App extends Component {
var src = ''
// 知乎投放
if (utm_term && utm_source && utm_medium && utm_campaign && utm_content) {
src = 'https://webchat.7moor.com/javascripts/7moorInit.js?accessId=e2ca4f90-2d04-11ea-84c3-43908ae47640&autoShow=true&language=ZHCN'
src = 'https://webchat.7moor.com/javascripts/7moorInit.js?accessId=e2ca4f90-2d04-11ea-84c3-43908ae47640&autoShow=false&language=ZHCN'
} else {
// m端
src = 'https://webchat.7moor.com/javascripts/7moorInit.js?accessId=fbcf9aa0-2d03-11ea-84c3-43908ae47640&autoShow=true&language=ZHCN'
src = 'https://webchat.7moor.com/javascripts/7moorInit.js?accessId=fbcf9aa0-2d03-11ea-84c3-43908ae47640&autoShow=false&language=ZHCN'
}
var script = document.createElement('script')
......@@ -320,12 +324,17 @@ class App extends Component {
document.body.appendChild(script)
}
// 七陌客服代码 end
// 七陌客服代码 end
render() {
return <Routes/>
return <>
<Routes/>
{/*<Link className={'year19-index'} to="/year/yearindex">*/}
{/*<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/PC/pc-yearindex.png" alt=""/>*/}
{/*</Link>*/}
</>
}
}
......
......@@ -512,3 +512,18 @@ input[type="radio"]:checked:before {
color: #333;
font-size: 15px;
}
.year19-index {
display: block;
width: 58px;
height: 70px;
position: fixed;
top: 50%;
right: 0;
margin-top: -35px;
z-index: 9;
img {
width: 100%;
height: 100%;
}
}
\ No newline at end of file
......@@ -19,7 +19,7 @@ const options = {
},
appstore: "https://itunes.apple.com/cn/app/id1102275343?mt=8",
yingyongbao: "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline",
fallback: "http://www.julyedu.com/weekend/redir1"
fallback: "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline"
}
class OpenApp extends Component {
......@@ -38,7 +38,7 @@ class OpenApp extends Component {
},
callback: () => {
window.location.href = "http://www.julyedu.com/weekend/redir1";
window.location.href = "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline";
}
});
}
......
#give-courses {
@mixin px2px($name, $px) {
#{$name}: round($px / 2) * 1px;
}
@function px2rem($px) {
@return #{$px}px
}
background: #3d1aaf;
padding-bottom: px2rem(45);
.banner {
width: 100%;
height: 187.5px;
img {
width: 100%;
height: 100%;
}
}
.give-box {
background-color: #1c008c;
box-shadow: 0 0 4px 0 #1c008c;
margin: px2rem(10);
padding: px2rem(10) px2rem(5) 0 px2rem(5);
.give-course {
width: 100%;
height: px2rem(125);
background-color: #fff;
border-radius: 2px;
margin-bottom: px2rem(10);
padding: px2rem(8) px2rem(5);
display: flex;
justify-content: space-between;
.give-course-img {
width: px2rem(150);
height: px2rem(108);
img {
width: 100%;
height: 100%;
}
}
.give-course-mess {
width: px2rem(178);
height: px2rem(108);
margin-left: px2rem(7);
position: relative;
text-align: left;
.course-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.course-title a {
color: #353535;
@include px2px(font-size, 26);
}
.course-teacher {
@include px2px(font-size, 24);
color: #666;
margin-top: px2rem(12);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.course-time {
@include px2px(font-size, 24);
color: #666;
margin-top: px2rem(7);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tag {
display: inline-block;
color: #555;
background-color: #f2f2f2;
margin-right: px2rem(4);
padding: px2rem(2) px2rem(4);
}
.toreceive {
width: px2rem(93);
height: px2rem(24);
@include px2px(font-size, 24);
color: #fff;
background-color: #09f;
border-radius: 4px;
line-height: px2rem(24);
text-align: center;
position: absolute;
bottom: 0;
}
}
}
.rule-title {
color: #2ff8ff;
@include px2px(font-size, 32);
text-align: left;
padding-left: px2rem(5);
}
.rule-list {
text-align: left;
color: #fff;
@include px2px(font-size, 24);
padding-left: px2rem(5);
}
.marquee_box {
width: px2rem(355);
height: px2rem(30);
overflow: hidden;
background-color: rgba(0, 0, 0, .5);
margin-left: px2rem(-5);
margin-top: px2rem(15);
.marquee_list {
li {
color: #fff;
height: px2rem(30);
line-height: px2rem(30);
@include px2px(font-size, 24);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
img {
width: px2rem(18);
height: px2rem(18);
vertical-align: middle;
border-radius: 50%;
}
span {
color: #0099ff;
display: inline-block;
margin: 0 5px;
}
}
}
.marquee_top {
transition: all .5s;
margin-top: -30px;
}
}
}
.set-prize {
background-color: #FCECB1;
border-radius: px2rem(3);
margin: px2rem(20) px2rem(10) 0 px2rem(10);
padding: px2rem(18) px2rem(15) px2rem(20) px2rem(15);
.set-prize-title {
width: 100%;
text-align: center;
color: #F75E18;
@include px2px(font-size, 28);
img {
width: px2rem(19);
height: px2rem(19);
margin-right: px2rem(10);
vertical-align: sub;
}
}
.set-prize-content {
text-align: left;
color: #6E1517;
@include px2px(font-size, 24);
display: flex;
flex-wrap: wrap;
p {
margin-top: px2rem(12);
}
}
}
.ranking-list {
margin: px2rem(30) px2rem(10) 0 px2rem(10);
.ranking_title {
width: 100%;
text-align: center;
img {
width: px2rem(18);
height: px2rem(20);
vertical-align: text-bottom;
}
span {
color: #fff;
@include px2px(font-size, 28);
display: inline-block;
margin-left: px2rem(10);
}
p {
color: #fee600;
@include px2px(font-size, 24);
margin-top: 10px;
}
}
table {
text-align: center;
margin-top: px2rem(15);
box-shadow: 0 0 5px 0 rgba(0, 0, 0, .3);
width: 100%;
border-radius: 6px 6px 0 0;
overflow: hidden;
thead tr {
background-color: #fadc7f;
height: px2rem(30);
line-height: px2rem(30);
@include px2px(font-size, 26);
color: #ad4700;
}
tbody tr:nth-of-type(even) {
background-color: #fadc7f;
height: px2rem(35);
}
tbody tr:nth-of-type(odd) {
background-color: #ffe794;
height: px2rem(35);
}
tbody tr {
color: #333;
@include px2px(font-size, 24);
}
tbody tr td {
width: 30%;
}
tbody tr td:nth-child(2) {
text-align: left;
padding-left: px2rem(15);
}
tbody tr td:nth-child(2) span {
display: inline-block;
width: 55%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
position: relative;
top: 5px;
margin-left: px2rem(5);
}
tbody tr td:nth-child(1) img {
vertical-align: middle;
width: px2rem(18);
height: px2rem(22);
}
tbody tr td:nth-child(2) img {
width: px2rem(20);
height: px2rem(20);
vertical-align: middle;
border-radius: 50%;
}
tbody .ismylist td {
background-color: #ad4700;
color: #fff;
}
}
.btm {
width: 100%;
height: px2rem(10);
background-color: #ffe794;
border-radius: 0 0 6px 6px;
}
}
.julynotice {
margin: px2rem(60) auto px2rem(30) auto;
color: #FEE600;
@include px2px(font-size, 13);
text-align: center;
}
.code-mbc {
position: fixed;
left: 0;
width: 100%;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
z-index: 9;
text-align: center;
.code-box {
width: 80%;
background-color: #fff;
border-radius: px2rem(10);
text-align: center;
margin: px2rem(200) auto 0 auto;
padding-top: px2rem(15);
.code-title {
color: #333;
@include px2px(font-size, 36);
i {
color: #f00;
@include px2px(font-size, 36);
font-style: normal;
}
}
#qrCodeWpay {
width: px2rem(150);
height: px2rem(150);
margin: px2rem(10) auto 0 auto;
}
.btmmess {
color: #666;
padding-bottom: px2rem(15);
margin-top: px2rem(-5);
}
}
.close {
display: inline-block;
@include px2px(font-size, 60);
color: #fff;
margin-top: px2rem(20);
}
}
}
import React, { Component } from 'react'
import './give-courses.scss'
import { getParam, http } from "@/utils"
import { Toast } from "antd-mobile"
import { Link } from "react-router-dom"
function showToast(text) {
Toast.info(text, 2, null, false)
}
class GiveCourses extends Component {
state = {
banner: '',
courses: [],
rule: '',
awardstext: '',
rankList: {},
isShow: false,
animate: false,
marqueeList: []
}
componentDidMount() {
http.get(`${API["base-api"]}/assistance/detail`)
.then(res => {
const {data, errno, msg} = res.data
if (errno === 200) {
this.setState({
banner: data["active_info"]["m_banner"],
courses: data.course_info,
rule: data["active_info"]["rule"],
awardstext: data["active_info"]["awards_text"].split('\n\n'),
query: data["active_info"]["activity_name"]
})
this.isRouter(data["active_info"]["activity_name"])
} else {
showToast(msg)
}
})
this.getRankList()
this.getMarqueeList()
setInterval(this.showMarquee, 5000)
setInterval(this.getMarqueeList, 60000)
}
getRankList = () => {
http.get(`${API["base-api"]}/assistance/ranking_list/50`)
.then(res => {
const {data, errno, msg} = res.data
if (errno === 200) {
this.setState({
rankList: {
list: data.list,
first: data.list[0],
second: data.list[1],
third: data.list[2],
other: data.list.slice(3, 50),
isMyList: data["nickname"],
myList: data["nickname"] && data
}
})
} else {
showToast(msg)
}
})
}
getMarqueeList = () => {
http.get(`${API["base-api"]}/assistance/roll_tip`)
.then(res => {
const {errno, msg, data} = res.data
if (errno === 200) {
this.setState({
marqueeList: data
})
} else {
showToast(msg)
}
})
}
showMarquee = () => {
this.setState({
animate: true
}, () => {
setTimeout(() => {
const [first, second] = this.state.marqueeList
this.setState({
animate: false,
marqueeList: [second, first]
})
}, 500)
})
}
isRouter = param => {
if (decodeURIComponent(getParam('activename')) != param) {
this.props.history.push('/')
}
}
render() {
const {
banner,
courses,
rule,
awardstext,
rankList,
isShow,
animate,
marqueeList
} = this.state
return (
<div id={'give-courses'}>
<div className="banner">
<img src={banner} alt=""/>
</div>
<div className='give-box'>
{
courses.map(course => {
return (
<div className='give-course' key={course.id}>
<Link className='give-course-img' to={`/detail?id=${course.course_id}`}>
<img src={course.image_name} alt=""/>
</Link>
<ul className="give-course-mess">
<li className='course-title'>
<a href="javascript:">{course.course_title}</a>
</li>
<li className='course-teacher'><span className='tag'>讲师</span>{course["teachers"]}</li>
<li className='course-time'><span className='tag'>开课时间</span>{course["start_time"]}</li>
<li className='toreceive' onClick={() => {
_czc && _czc.push(["_trackEvent", `课程id=${course.course_id}`, '免费领取课程'])
console.log(course.course_id)
this.setState({isShow: true})
}}>免费领取课程
</li>
</ul>
</div>
)
})
}
<p className="rule-title">活动规则</p>
<div className="rule-list" dangerouslySetInnerHTML={{__html: rule}}/>
<div className="marquee_box">
<ul className={`marquee_list ${animate ? 'marquee_top' : ''}`}>
{
marqueeList.length ?
marqueeList.map((item, index) => {
return (
<li key={index}>
<img src={item.head_img} alt=""/>
<span>{item["nickname"]}</span>
{item["word"]}
</li>
)
})
: null
}
</ul>
</div>
</div>
<div className="set-prize">
<p className="set-prize-title">
<img src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018_1111/jpsz_icon.png"
alt=""/>奖品设置
</p>
<div className="set-prize-content">
{
awardstext && awardstext.length && awardstext.map((item, index) => <p key={index}>{item}</p>)
}
</div>
</div>
<div className="ranking-list">
<div className="ranking_title">
<img src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018_1111/jbei_icon.png" alt=""/>
<span>排行榜</span>
<p>数据实时更新 只显示Top50</p>
</div>
<table border="0" cellPadding="0" cellSpacing="0">
<thead>
<tr>
<td>排名</td>
<td>用户</td>
<td>邀请人数</td>
</tr>
</thead>
<tbody>
{
rankList.list && rankList.list.length > 0
?
<tr>
<td><img src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018_1111/jin1_icon.png" alt=""/></td>
<td><img src={rankList.first.head_img} alt=""/><span>{rankList.first.name}</span></td>
<td>{rankList.first.num}</td>
</tr>
: null
}
{
rankList.list && rankList.list.length > 1
?
<tr>
<td><img src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018_1111/yin2_icon.png" alt=""/></td>
<td><img src={rankList.second.head_img} alt=""/><span>{rankList.second.name}</span></td>
<td>{rankList.second.num}</td>
</tr>
: null
}
{
rankList.list && rankList.list.length > 2
?
<tr>
<td><img src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018_1111/tong_icon.png" alt=""/></td>
<td><img src={rankList.third.head_img} alt=""/><span>{rankList.third.name}</span></td>
<td>{rankList.third.num}</td>
</tr>
: null
}
{
rankList.list && rankList.list.length && rankList.list.length > 3 ? rankList.other.map((item, index) => {
return (
<tr key={index}>
<td>{index + 4}</td>
<td><img src={item.head_img} alt=""/><span>{item.name}</span></td>
<td>{item.num}</td>
</tr>
)
})
: null
}
{
rankList.isMyList &&
<tr className="ismylist">
<td>{rankList.myList["ranking"]}</td>
<td><img src={rankList.myList.head_img} alt=""/><span>{rankList.myList["nickname"]}</span></td>
<td>{rankList.myList["inviter_num"]}</td>
</tr>
}
</tbody>
</table>
<p className="btm"/>
</div>
<p className="julynotice">*本活动最终解释权归七月在线所有*</p>
{
isShow &&
<div className="code-mbc">
<div className="code-box">
<p className="code-title">进入服务号回复<i>77</i>免费领取课程</p>
<img id="qrCodeWpay" src="//julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018christyear/h5/qrcode.jpg" alt=""/>
<p className="btmmess">长按扫码进入服务号</p>
</div>
<i className="close iconfont iconiconfront-2" onClick={() => {
this.setState({isShow: false})
}}/>
</div>
}
</div>
)
}
}
export default GiveCourses
import React, { Component } from 'react'
import './share-content.scss'
import Poster from "./poster/poster"
import Prizes from './prizes/prizes'
import {WithFullSize} from '@/HOCs'
class ShareContent extends Component {
state = {
activeIndex: 0,
tabs: ['分享海报', '活动奖品'],
poster: '',
rankingList: []
}
render() {
const {
tabs,
activeIndex,
poster,
rankingList
} = this.state
return (
<div id={'share-content'}>
<ul className="tab">
{
tabs.map((item, index) => {
return (
<li key={index} className={activeIndex === index ? 'active' : ''} onClick={() => {
this.setState({activeIndex: index})
}}>{item}</li>
)
})
}
</ul>
{
activeIndex === 0
? <Poster
poster={poster}
savePoster={poster => {
this.setState({poster})
}}/>
: <Prizes rankingList={rankingList} saveRankingList={rankingList => {this.setState({rankingList})}}/>
}
</div>
)
}
}
export default WithFullSize(ShareContent)
import React, { Component } from 'react'
import QRCode from "qrcode"
import './poster.scss'
import { getParam, http } from "@/utils"
import { Toast } from "antd-mobile"
function showToast(text) {
Toast.info(text, 2, null, false)
}
class Poster extends Component {
state = {
redirectUrl: '',
backgroundUrl: '',
avatar: '',
username: '',
poster: this.props.poster,
}
componentDidMount() {
const {poster} = this.props
if (!poster) {
http.get(`${API["base-api"]}/assistance/active_haibao_info/${getParam('assis_word')}`)
.then(res => {
const {data, errno, msg} = res.data
if (errno === 200) {
this.setState({
avatar: data.head_img,
username: data.user_name,
redirectUrl: data.m_redirect_code,
backgroundUrl: data.background_img_url
}, () => {
this.generatePoster()
})
} else {
showToast(msg)
}
})
}
}
generatePoster = async () => {
let canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
posterWidth = 270,
canvasWidth = 540
const {backgroundUrl, avatar, username, redirectUrl} = this.state
const bg = await this.getImage(backgroundUrl)
canvas.style.width = posterWidth + 'px'
canvas.style.height = posterWidth * bg.height / bg.width + 'px'
canvas.width = canvasWidth
canvas.height = canvasWidth * bg.height / bg.width
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height)
let r = 33
const avatarImage = await this.getImage(avatar)
const ax = 40
const ay = 16
ctx.save()
ctx.beginPath()
ctx.arc(ax + r, ay + r, r, 0, Math.PI * 2)
ctx.clip()
ctx.drawImage(avatarImage, ax, ay, 66, 66)
ctx.restore()
ctx.font = `24px serif`
ctx.fillStyle = '#fff'
ctx.fillText(username, 2 * r + ax + 10, ay + r - 5)
let qrcode = await this.getQRCodeCanvas(redirectUrl, posterWidth)
ctx.drawImage(qrcode, 380, 796, 140, 140)
this.setState({
poster: canvas.toDataURL()
}, () => {
const {savePoster} = this.props
savePoster && savePoster(this.state.poster)
})
}
getImage = url => {
return new Promise(resolve => {
const img = new Image()
img.setAttribute('crossOrigin', 'anonymous')
img.onload = function () {
resolve(this)
}
img.src = url
})
}
getQRCodeCanvas = (text, posterWidth) => {
const canvas = document.createElement('canvas')
let codeWidth = 100, codeHeight = 100,
codeSize = posterWidth * (100 / (window.innerWidth * 0.72))
canvas.style.width = `${codeSize}px`
canvas.style.height = `${codeSize}px`
canvas.width = codeWidth
canvas.height = codeHeight
return QRCode.toCanvas(canvas, text)
}
render() {
const {poster} = this.state
return (
<div id='poster'>
<div className="placard-desc">
长按下方海报,分享给好友~
</div>
<div className="total-container">
<div className="placard-img-container" id='imgWrapper'>
<img src={poster} alt='分享海报'/>
</div>
</div>
</div>
)
}
}
export default Poster
#poster {
width: 100%;
height: 100%;
overflow: hidden;
background-color: #470bc8;
-webkit-overflow-scrolling: touch;
-webkit-touch-callout: none;
.placard-desc {
width: 100%;
color: #fff;
background-color: #470bc8;
font-size: 14px;
margin-bottom: 15px;
}
.total-container {
width: 100%;
height: 90%;
position: relative;
.placard-img-container {
width: 100%;
position: relative;
img {
width: 72%;
}
}
}
}
import React, { Component } from 'react'
import './prizes.scss'
import { http } from "@/utils"
import { Toast } from "antd-mobile"
function showToast(text) {
Toast.info(text, 2, null, false)
}
class Prizes extends Component {
state = {
list: [
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-1.png',
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-2.png',
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-3.png',
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-4.png',
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-5.png',
'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/prize-6.png',
],
isShowRule: false,
rankList: []
}
componentDidMount() {
const {rankingList} = this.props
console.log(rankingList)
if (!rankingList || !rankingList.length) {
this.getRankingList()
} else {
this.setState({
rankingList
})
}
}
getRankingList = () => {
const {saveRankingList} = this.props
http.get(`${API["base-api"]}/assistance/ranking_list/50`)
.then(res => {
const {data, msg, errno} = res.data
if (errno === 200) {
this.setState({
rankList: data.list
})
saveRankingList && saveRankingList(data.list)
} else {
showToast(msg)
}
})
}
render() {
const {
list,
rankList,
isShowRule
} = this.state
return (
<div id={'prizes'}>
<div className="content">
<i className="part-title__decorate"/>
<div className="part-title__txt">活动奖品</div>
<i className="part-title__decorate"/>
<div className="prize-img">
{
list.map((item, index) => {
return (
<div className='img-box' key={index}>
<img src={item} alt=""/>
</div>
)
})
}
</div>
<span className="rule" onClick={() => {
this.setState({isShowRule: true})
}}>活动规则</span>
</div>
<div className="content" style={{padding: `20px 0 30px 0`}}>
<i className="part-title__decorate"/>
<div className="part-title__txt">排行榜</div>
<i className="part-title__decorate"/>
<div className="tip">
数据实时更新 只显示Top50
</div>
<table className="ranking" cellSpacing="0" cellPadding="0" border="0">
<thead>
<tr>
<td>排名</td>
<td>用户名称</td>
<td>邀请人数</td>
</tr>
</thead>
<tbody>
{
rankList.map((item, index) => {
return (
<tr key={index}>
<td>{index + 1}</td>
<td>{item.name}</td>
<td>{item.num}</td>
</tr>
)
})
}
</tbody>
</table>
</div>
{
isShowRule ?
<div className="mbc-box">
<div className="rele-content">
<div className="title">活动规则</div>
<p>1、进入七月在线服务号<a
style={{fontSize: `16px`, color: `rgba(47, 248, 255, 1)`, display: `inline-block`}}>回复77</a>获取活动海报,将上方课程赠送给好友,每有1名好友领取后,您和好友均可免费学习1课时该课
</p>
<p>2、每多1名好友领取,您获得的课时数+1,直到获得全部课时,届时仍可赠送给好友;</p>
<p>3、活动结束后,邀请好友数超过50人且排行榜前20名用户会得到<a
style={{fontSize: `16px`, color: `rgba(47, 248, 255, 1)`, display: `inline-block`}}>19VIP年会员、樱桃键盘</a>等大奖
</p>
</div>
<img className="close"
onClick={() => {this.setState({isShowRule: false})}}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/close-btn.png" alt=""/>
</div>
: null
}
</div>
)
}
}
export default Prizes
#prizes {
padding: 0 10px;
height: auto;
.content {
padding: 20px 10px 30px 10px;
background-color: #3F07B8;
border: 1px solid #5970FF;
border-radius: 4px;
text-align: center;
margin-bottom: 30px;
}
.part-title__decorate {
display: block;
width: 232px;
height: 2px;
margin: 0 auto;
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 48%, rgba(255, 255, 255, 0) 100%);
}
.part-title__txt {
display: inline-block;
height: 32px;
line-height: 32px;
font-size: 16px;
color: #fff;
font-weight: 400;
letter-spacing: 2px;
}
.prize-img {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-top: 20px;
.img-box {
width: 31.3%;
height: 112px;
margin-bottom: 5px;
img {
width: 100%;
height: 100%;
}
}
}
.rule {
display: inline-block;
width: 101px;
height: 28px;
line-height: 28px;
border: 1px solid rgba(254, 228, 29, 1);
border-radius: 14px;
margin-top: 15px;
color: #FEE41D;
font-size: 15px;
}
.tip {
margin-top: 15px;
font-size: 12px;
color: #fff;
}
.ranking {
width: 100%;
margin-top: 20px;
tr {
height: 44px;
line-height: 44px;
}
thead {
color: #FEE41D;
font-size: 14px;
font-weight: 500;
background-color: #490AD1;
}
tbody {
color: #fff;
font-weight: 400;
font-size: 12px;
tr {
td:first-child {
font-size: 14px;
}
}
tr:nth-child(odd) {
background-color: #470AC9;
}
tr:nth-child(even) {
background-color: #490AD1;
}
}
}
}
.mbc-box{
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, .6);
.rele-content {
display: inline-block;
width: 320px;
background: #fff;
border-radius: 5px;
padding: 20px 28px 30px 28px;
box-sizing: border-box;
margin: 145px auto 30px auto;
.title {
font-size: 18px;
color: #111;
margin-bottom: 20px;
font-weight: normal;
}
p {
text-align: left;
font-size: 14px;
color: #666666;
line-height: 18px;
margin-bottom: 3px;
}
}
.close {
display: block;
margin: auto;
width: 33px;
height: 33px;
}
}
#share-content {
width: 100%;
height: auto;
min-height: 100%;
background-color: #470bc8;
padding: 30px 0 40px 0;
position: relative;
text-align: center;
font-family: Microsoft YaHei,Helvetica Neue,STHeiti,Helvetica,Arial,sans-serif;
.tab {
display: flex;
justify-content: center;
margin: 0 auto 20px auto;
li {
width: 83px;
height: 32px;
line-height: 32px;
color: #FEE41D;
border: 1px solid #FEE41D;
font-size: 16px;
background-color: #470bc8;
}
li:first-child {
border-radius: 4px 0 0 4px;
}
li:last-child {
border-radius: 0 4px 4px 0;
}
.active {
color: #090F08;
background: #FEE41D;
border: 1px solid #FEE41D;
}
}
}
......@@ -114,7 +114,7 @@ class Prizes extends Component {
item.grant_max
?
<div className='bottom-bar'>
<div className="progress-bar"><span className={'progress'}/></div>
<div className="progress-bar"><span className={'progress'} style={{width: `${(item.surplus / item.grant_max) * 100}%`}}/></div>
<span>限量{item.grant_max}</span>
</div>
: <span className='no-limit'>不限量</span>
......
......@@ -178,7 +178,7 @@
position: absolute;
right: 0;
top: 0;
width: 80%;
//width: 80%;
height: 100%;
background: linear-gradient(-90deg, rgba(255, 140, 0, 1) 0%, rgba(255, 23, 0, 1) 100%);
border-radius: 1px;
......
......@@ -43,7 +43,7 @@ class MyTreasure extends Component {
componentDidMount() {
this.fetchMyTreasure()
this.fetchActivityStatus()
document.title = '七月在线年终大回馈,幸运宝箱随你开,100%有奖!人气好课免费学,精品课程1分抢!';
document.title = '七月在线新春献礼,幸运宝箱随你开,100%有奖!“薪”年好课免费学,精品课程1分秒!';
}
componentWillReceiveProps(nextProp) {
......
......@@ -141,7 +141,7 @@ class LiveRoom extends Component {
)
}
<CommonContainer title='大咖直播' id="year-live">
<CommonContainer title='“薪”年好课免费学' id="year-live">
<ul className='live__list'>
{
list.length && (
......@@ -153,39 +153,39 @@ class LiveRoom extends Component {
<span>主题:</span>
<p>{item.title}</p>
</div>
<div className="info__item">
<span>时间:</span>
<p>{item.date}</p>
</div>
{/*<div className="info__item">*/}
{/*<span>时间:</span>*/}
{/*<p>{item.date}</p>*/}
{/*</div>*/}
<div className="info__item">
<span>讲师:</span>
<p>{item.teacher}</p>
</div>
{
item.on_live == 0 && item.is_end == 0 && item.is_subscribe == 0 && (
<span onClick={ ()=>this.subscribe(item.live_id)} className="order__btn">立即预约</span>
)
}
{/*{*/}
{/*item.on_live == 0 && item.is_end == 0 && item.is_subscribe == 0 && (*/}
{/*<span onClick={ ()=>this.subscribe(item.live_id)} className="order__btn">立即预约</span>*/}
{/*)*/}
{/*}*/}
{
item.is_end == 1 && item.transcribe_url=='' && (
<span className="order__btn live__end">已结束</span>
)
}
{/*{*/}
{/*item.is_end == 1 && item.transcribe_url=='' && (*/}
{/*<span className="order__btn live__end">已结束</span>*/}
{/*)*/}
{/*}*/}
{/*{*/}
{/*item.on_live == 0 && item.is_end == 0 && item.is_subscribe == 1 && (*/}
{/*<span onClick={this.tip} className="order__btn subscribed">已预约</span>*/}
{/*)*/}
{/*}*/}
{/*{*/}
{/*item.on_live == 1 && item.is_end == 0 && (*/}
{/*<span onClick={()=>this.toLivingRoom(item.room_url)} className="order__btn living">正在直播</span>*/}
{/*)*/}
{/*}*/}
{
item.on_live == 0 && item.is_end == 0 && item.is_subscribe == 1 && (
<span onClick={this.tip} className="order__btn subscribed">已预约</span>
)
}
{
item.on_live == 1 && item.is_end == 0 && (
<span onClick={()=>this.toLivingRoom(item.room_url)} className="order__btn living">正在直播</span>
)
}
{
item.is_end == 1 && item.transcribe_url != '' && (
<span onClick={() => this.checkVideo(item.transcribe_url)} className="order__btn check__video">查看回放</span>
item.transcribe_url != '' && (
<span onClick={() => this.checkVideo(item.transcribe_url)} className="order__btn check__video">点击查看</span>
)
}
......
......@@ -10,6 +10,36 @@
margin: 12px auto 0;
}
.course-conter {
.course-item {
width: 168px;
padding: 4px;
background-color: #fff;
position: relative;
border-radius: 2px;
span {
position: absolute;
right: 4px;
top: 4px;
width: 60px;
display: block;
text-align: center;
padding-left: 5px;
border-radius: 0 0 0 10px;
background-color: #EB1612;
color: #fff;
font-size: 12px;
}
a {
width: 160px;
height: 104px;
img {
width: 100%;
height: 100%;
}
}
}
}
.course-box {
display: flex;
justify-content: space-between;
......
......@@ -29,7 +29,7 @@ export default class index extends Component {
userInfo: {},
isAppUpdate: false,
showMark: false,
banner: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/m_banner_bj%402x.png'
banner: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/new-year/m-banner.png'
}
componentDidMount() {
......@@ -39,7 +39,7 @@ export default class index extends Component {
_this.loginInfo(result)
}
_this.getStage()
document.title = '七月在线年终大回馈,幸运宝箱随你开,100%有奖!人气好课免费学,精品课程1分抢!';
document.title = '七月在线新春献礼,幸运宝箱随你开,100%有奖!“薪”年好课免费学,精品课程1分秒!';
}
// 获取app登录数据
......@@ -147,7 +147,7 @@ export default class index extends Component {
})
} else {
this.setState({
banner: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/m_banner_bj%402x.png',
banner: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/new-year/m-banner.png',
})
}
} else {
......
......@@ -17,9 +17,9 @@
}
.banner-treasure__decorate {
height: 35px;
background-size: cover;
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/m_nv_bj%402x.png');
height: 20px;
//background-size: cover;
//background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/m_nv_bj%402x.png');
}
.sub__code_container {
......
......@@ -13,20 +13,24 @@ class TreasureNav extends Component {
navs: [
{
id: 'year-live',
name: '大咖直播'
name: '好课免费学'
},
{
id: 'year-treasure',
name: '组队开宝箱'
},
{
id: 'year-course',
name: '重磅好课'
id: 'year-wuhan',
name: '驰援武汉'
},
{
id: 'year-free',
name: '免费学'
id: 'year-course',
name: '重磅好课'
},
// {
// id: 'year-free',
// name: '免费学'
// },
{
id: 'year-group',
name: '1分拼团'
......
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-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/college-plan/m-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
});
document.title = `助力高校在线教学计划之${data.name}专区 - 七月在线`
}
});
}
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 {
display: -webkit-box;
margin: 8px 0 0;
padding-left: 5px;
font-size: 14px;
color: #525C65;
line-height: 16px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
}
\ 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();
document.title = `免费提供在线教学系统 助力全国高校在线教学 - 七月在线`
}
fetchSchoolList = () => {
const { schoolList } = this.state;
http.get(`${API['home']}/sys/school/list`).then(res => {
const { code, data } = res.data;
if(code === 200) {
this.setState({
schoolList: data.concat(schoolList),
});
}
});
}
toCollegeCourse = (id) => {
const { history } = this.props;
if(id) {
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;
}
.college__iamge {
display: block;
height: 60px;
border: 1px solid #ECECEC;
border-radius: 1px;
background-size: auto 95%;
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
import React, { PureComponent } from "react"
import './accountLogin.scss'
import { Link } from "react-router-dom";
import { withFormik, FastField, Form } from "formik";
import { compose } from 'redux';
import { accountLogin } from '@/store/userAction';
import { connect } from "react-redux";
import { Link } from "react-router-dom"
import { withFormik, FastField, Form } from "formik"
import { compose } from 'redux'
import { accountLogin } from '@/store/userAction'
import { connect } from "react-redux"
import { isEmpty } from 'lodash'
import { HeaderBar } from "@/common";
import { HeaderBar } from "@/common"
import Header from "../common/Header";
import Header from "../common/Header"
import Input from '../common/Input'
import LoginButton from '../common/LoginButton'
import PasswordInput from '../common/passwordInput'
import { Toast } from "antd-mobile";
import { Toast } from "antd-mobile"
class AccountLogin extends PureComponent {
render() {
const {
errors,
values
} = this.props
return (
<div className={'account-login'}>
<HeaderBar arrow={true} title={'登录'}/>
<Header/>
<Form className="login-info">
<FastField
name='account'
render={({field}) => (
<Input
{...field}
type={'text'}
placeholder={'手机/邮箱/昵称'}
wrapperClass={'tel-input'}
icon={
<i className={'iconfont iconshouji'}
style={{fontSize: '22px', left: '10px'}}
/>
}
/>
)}
/>
<FastField
name='password'
render={({field}) => (
<PasswordInput
{...field}
autoComplete={'on'}
placeholder={'密码'}
icon={
<i className={'iconfont iconiconfront-74 lock-icon'}/>
}
/>
)}
/>
<LoginButton active={values.account && values.password && isEmpty(errors)}/>
<Link className={'forgot-password-btn'} to='/passport/forgot-password'>忘记密码</Link>
</Form>
</div>
);
}
render() {
const {
errors,
values
} = this.props
return (
<div className={'account-login'}>
<HeaderBar arrow={true} title={'登录'}/>
<Header/>
<Form className="login-info">
<FastField
name='account'
render={({field}) => (
<Input
{...field}
type={'text'}
placeholder={'手机/邮箱/昵称'}
wrapperClass={'tel-input'}
icon={
<i className={'iconfont iconshouji'}
style={{fontSize: '22px', left: '10px'}}
/>
}
/>
)}
/>
<FastField
name='password'
render={({field}) => (
<PasswordInput
{...field}
autoComplete={'on'}
placeholder={'密码'}
icon={
<i className={'iconfont iconiconfront-74 lock-icon'}/>
}
/>
)}
/>
<LoginButton active={values.account && values.password && isEmpty(errors)}/>
<Link className={'forgot-password-btn'} to='/passport/forgot-password'>忘记密码</Link>
</Form>
</div>
)
}
}
const formikConfig = {
mapPropsToValues: () => ({
account: '',
password: ''
}),
handleSubmit(values, formikBag) {
const {account: username, password} = values
const {props, props: {history}} = formikBag
const from = props.location.state && props.location.state.from
props.accountLogin({
username, password,redirect: from && window.location.origin + from.pathname + from.search + from.hash
}).then(res => {
if (res.hasError) {
Toast.info(res.msg, 2, null, false)
}
})
mapPropsToValues: () => ({
account: '',
password: ''
}),
handleSubmit(values, formikBag) {
const {account: username, password} = values
const {props, props: {history}} = formikBag
const from = props.location.state && props.location.state.from
props.accountLogin({
username, password, redirect: from && window.location.origin + from.pathname + from.search + from.hash
}).then(res => {
if (res.hasError) {
Toast.info(res.msg, 2, null, false)
}
})
},
validate: values => {
const errors = {}
if (!values.account) {
errors.account = '账号不能为空'
} else if (!values.password) {
errors.password = '密码不能为空'
}
return errors
}
}
export default compose(
connect(
state => ({user: state.user}),
{accountLogin}
),
withFormik(formikConfig)
connect(
state => ({user: state.user}),
{accountLogin}
),
withFormik(formikConfig)
)(AccountLogin)
......@@ -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'
......@@ -48,13 +49,13 @@ class Passport extends Component {
{
logo: qq,
text: 'QQ',
url: `${API["passport-api"]}/mob/qqlogin?redirect_url=${encodeURIComponent(this.redirectURL)}`,
url: `${API["passport-api"]}/m/login/qqLogin?redirect_url=${encodeURIComponent(this.redirectURL)}`,
id: 'qq'
},
{
logo: sina,
text: '新浪',
url: `${API['passport-api']}/mob/sinalogin?redirect_url=${encodeURIComponent(this.redirectURL)}`,
url: `${API['passport-api']}/m/login/sinaLogin?redirect_url=${encodeURIComponent(this.redirectURL)}`,
id: 'sina'
}
]
......@@ -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}/>
......
......@@ -43,7 +43,7 @@ class Login extends Component {
window.location.assign(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=${encodeURIComponent(redirectURI)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`)
break;
case 'QQ':
window.location.assign(`${API["passport-api"]}/mob/qqlogin?redirect_url=${encodeURIComponent(redirectURI)}`);
window.location.assign(`${API["passport-api"]}/m/login/qqLogin?redirect_url=${encodeURIComponent(redirectURI)}`);
break;
default:
window.location.assign(item.url)
......@@ -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)}/>
{/* 助学计划 */}
<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
......@@ -69,7 +69,9 @@ const formikConfig = {
},
handleSubmit: (values, {props}) => {
let from = props.location.state && props.location.state.from || {pathname: '/'}
const {location} = props
let from = location.state && location.state.records && location.state.records[location.state.records.length - 2] || {pathname: '/'}
if (from.pathname.includes('forgot-password')) {
forgotPasswordReset(values, props)
} else {
......
.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
import React, {Component} from 'react'
import {http, getParam, SendMessageToApp} from '@/utils'
import {http, getParam, browser, SendMessageToApp} from '@/utils'
import PythonDes from './pythomDes'
import PythonStudy from './pythonStudy'
import {connect} from "react-redux"
......@@ -19,7 +19,8 @@ class Python extends Component {
this.state = {
isPay: '',
userInfoList: [],
isAppUpdate: false
isAppUpdate: false,
backwardVersion: false, // 默认是新版本
}
}
......@@ -60,10 +61,12 @@ class Python extends Component {
this.setState({
isAppUpdate: true
})
this.fetchCourseInfo();
}
this.props.setCurrentUser(this.transformUser(this.state.userInfoList))
this.props.setCurrentUser(this.transformUser(this.state.userInfoList));
}
transformUser = res => {
let payload
......@@ -89,19 +92,53 @@ class Python extends Component {
http.get(`${API.home}/m/course/detail/${id}`).then((res) => {
const {data, code} = res.data
if (code === 200) {
this.setState({
isPay: data.course_info.is_pay
})
let version = getParam('version')
if(version) {
version = version.replace(/\./g, '').slice(0, 3)
if(browser.isAndroidApp && version < 453) { // 安卓的低版本
this.setState({
backwardVersion: true,
isPay: 0,
})
}else if(browser.isIOSApp && version < 380) { // ISO的低版本
this.setState({
backwardVersion: true,
isPay: 0,
})
}else{ // 安卓/IOS 的高版本
if(data.course_info.is_pay === 1) { // 在APP内未登录-去登陆-登录后还显示此页;如果是已购买的用户 就需要跳转到 APP已购买的原生页面
SendMessageToApp('toSyllabusChapter', id); // 跳转到APP的已购买详情页 id 是课程ID
return;
}
this.setState({
backwardVersion: false,
isPay: data.course_info.is_pay
})
}
} else {
this.setState({
backwardVersion: false,
isPay: data.course_info.is_pay
})
}
}
})
}
render() {
const {isPay, isAppUpdate} = this.state
const {isPay, isAppUpdate, backwardVersion} = this.state;
// 旧版本 无论购买未购买 都跳转到 未购买的详情页; 如果是已购买就提示更新APP
return (
<div>
{
isPay === 0 && <PythonDes history={this.props.history} isAppUpdate={isAppUpdate}></PythonDes>
isPay === 0 && (
<PythonDes
backwardVersion={backwardVersion}
history={this.props.history}
isAppUpdate={isAppUpdate}
isPay={isPay}
/>
)
}
{
(isPay === 1 && !getParam('version')) && <PythonStudy isAppUpdate={isAppUpdate}/>
......
......@@ -8,7 +8,7 @@ export default class Description extends Component {
<img className={'left'} src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/left.png" alt=""/>
<img className={'right'} src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/right.png" alt=""/>
<p className={'title'}> / / / </p>
<p className={'sub__title'}>对话式交互教学+课后实操,20节课全面掌握Python基础语法</p>
<p className={'sub__title'}>对话式交互教学+课后实操,17节课全面掌握Python基础语法</p>
<img src={'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/diannao.png'} />
{
this.props.list.map((item, index)=>{
......
......@@ -3,18 +3,41 @@ import './index.scss'
export default class Experience extends Component {
render() {
const { isOnline, tryLearn } = this.props;
return (
<div className={'experience_container'}>
<p className={'title'}> / / / </p>
<div className={'experience'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/L%402x.png" alt=""/>
<div>
<p>限时福利免费试学</p>
<p>开始颠覆你想象的学习</p>
</div>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/r%402x.png" alt=""/>
</div>
<div className={'btn'} onClick={this.props.tryLearn}>立即体验</div>
{
isOnline
? (
<>
<div className={'experience'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/L%402x.png" alt=""/>
<div>
<p>限时福利免费试学</p>
<p>开始颠覆你想象的学习</p>
</div>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/r%402x.png" alt=""/>
</div>
<div className={'btn'} onClick={tryLearn}>立即体验</div>
</>
)
: (
<>
<div className={'experience'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/L%402x.png" alt=""/>
<div>
<p>上架后可免费试学</p>
<p>体验颠覆你想象的学习</p>
</div>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/r%402x.png" alt=""/>
</div>
<div className={'btn btn--online'}>即将上架敬请期待</div>
</>
)
}
</div>
)
}
......
......@@ -31,14 +31,22 @@
.btn {
width: 150px;
height: 36px;
background: rgba(0, 153, 255, 1);
box-shadow: 0px 6px 12px 0px rgba(0, 153, 255, 0.06);
margin: 0 auto;
border-radius: 18px;
font-size: 16px;
font-weight: 600;
color: rgba(255, 255, 255, 1);
line-height: 36px;
text-align: center;
margin: 0 auto;
line-height: 36px;
background: rgba(0, 153, 255, 1);
box-shadow: 0px 6px 12px 0px rgba(0, 153, 255, 0.06);
}
.btn--online {
width: 200px;
font-size: 15px;
color: #fff;
letter-spacing: 4px;
background-color: rgba(82,92,101,.6);
}
}
......@@ -16,7 +16,7 @@ export default class Progream extends Component {
q: '学习过程中遇到了困难怎么办?',
a: '我们为学员配备了专门的助教,有问题随时可以咨询助教老师。'
},{
q: '课程学习周期是多久?',
q: '需要自己安装代码运行环境吗?',
a: '在平台上学习不需要安装任何环境,该课程包含的知识点实操以及课后实操都可以在平台上完成。'
}
]
......
......@@ -2,13 +2,34 @@ import React, { Component } from 'react'
import './index.scss'
export default class Study extends Component {
Change = (data) => {
let cn = ["一", "二", "三", "四", "五", "六", "七", "八", "九", '十'];
let newStr = ''
let str = data.toString()
let ci = Number(str) % 10
let cs = Math.floor(Number(str) / 10)
let cv = ''
if (str.length > 1) {
if(cs==1){
cv = ci >= 1 ? cn[9] + cn[ci - 1] : cn[9]
} else {
cv = ci > 1 ? cn[cs - 1] + cn[9] + cn[ci - 1] : cn[cs - 1] + cn[9]
}
} else {
cv = cn[ci - 1]
}
newStr = str.replace(str, cv)
return newStr;
}
render() {
return (
<div className={'study__container'}>
<img className={'learn_left'} src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/learn_left.png" alt=""/>
<img className={'learn_bottom'} src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/learn_bottom.png" alt=""/>
<p className={'title'}> / / / </p>
<p className={'sub__title'}>20节课堂教学,带你掌握Python技术入门知识</p>
<p className={'sub__title'}>17节课堂教学,带你掌握Python技术入门知识</p>
<div className={'step__container'}>
<div className={'item__container'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/wanzheng.png" alt=""/>
......@@ -37,13 +58,14 @@ export default class Study extends Component {
this.props.syllabus.map((item, index)=>{
return (
<div className={'item__container'} key={index}>
<div className={'item__title'}>{item.name}</div>
<div className={'item__title'}>{
'第' + this.Change(index + 1) + '阶段 ' +item.name}</div>
{
item.lessons.map((item, index)=>{
return (
<div className={'item__content'} key={index}>
<div className={'content order_number number_center'}>{item.number}</div>
<div className={'content chapter'}>{item.number}</div>
<div className={'content chapter'}>{item.name}</div>
<div className={'content points'}>{item.info.name}</div>
</div>
)
......
......@@ -8,23 +8,23 @@ export default class Team extends Component {
teacherList: [
{
avatar: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/teacher1.png',
name: 'A老师',
des: '七月在线AI Lab负责人兼科学家。历任浪潮集团数七月在线AI Lab负责人兼科学家。历任浪潮集团数七月在线AI Lab负责人兼科学家。历任浪潮集团数据科学家,国家电网人工智能行业应用方向团队负责人。参与过一国家863项目,且曾主持一山东省自主创新及成果转化专项,发明专利十余项,专业论文十余年哈哈塑料袋科技发牢骚'
name: '陈博士',
des: '七月在线AI Lab负责人兼科学家。历任浪潮集团数据科学家,国家电网人工智能行业应用方向团队负责人。参与过一国家863项目,且曾主持一山东省自主创新及成果转化专项,发明专利十余项,专业论文十余篇。'
},
{
avatar: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/teacher2.png',
name: 'A老师',
des: '七月在线AI Lab负责人兼科学家。历任浪潮集团数据科学家,'
name: '老师',
des: '10多年开发经验,先后任职国内知名互联网企业,从事人工智能方向的实施,擅长全栈开发,将机器学习/深度学习结合到实际生产运营中。'
},
{
avatar: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/teacher3.png',
name: 'A老师',
des: '七月在线AI Lab负责人兼科学家。历任浪潮集团数据科学家,'
name: '老师',
des: '计算机专业毕业,多年开发经验,熟悉 Python,熟悉 linux 环境下系统编程,具有多年的 Web 后端开发经验,授课思路清晰明了,通俗易懂,幽默风趣的讲课方式将枯燥的知识以通俗的方式展现,深受学生好评。'
},
{
avatar: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/teacher4.png',
name: 'A老师',
des: '七月在线AI Lab负责人兼科学家。历任浪潮集团数据科学家,国家电网人工智能行业应用方向团队负责人。参与过一国家863项目,且曾主持一山东省自主创新及成果转化专项,发明专利十余项,专业论文十余年哈哈塑料袋科技发牢骚'
name: '珊酱',
des: '班主任的工作是一项非常辛苦和琐碎的工作,不付出艰辛的劳动和辛苦的汗水是不会有收获的。历经数万名学员的一对一服务,始终坚持给学员做到最好的服务和最严格的要求,学员的点滴进步和成长就是我最大的期望!'
}
]
}
......
......@@ -35,12 +35,12 @@ export default class Test extends Component {
<div className={'table_body'}>
{
this.props.practice.map((item, index)=>{
return <div key={index}>
return <div className="stage-item" key={index}>
<div className='stage'>{`第${this.Change(item.stage)}阶段 ${item.name}`}</div>
{
item.questions.map((question, index)=>{
return <div key={index} className={'line'}>
<span>{index}</span>
<span>{question.number}</span>
<div className={'test_name'}>
<img src={question.icon} alt=""/>
<p>{question.name}</p>
......
......@@ -49,6 +49,10 @@
.table_body {
background: #34AFFF;
.stage-item {
border-bottom: 1px solid #0099FF;
}
.stage {
height: 38px;
font-size: 14px;
......@@ -59,6 +63,7 @@
justify-content: center;
align-items: center;
}
.line {
display: flex;
justify-content: space-between;
......@@ -94,7 +99,6 @@
.table_bottom {
height: 36px;
background: #3DB1FF;
border-top: 1px solid #0099FF;
div {
display: flex;
justify-content: center;
......
......@@ -74,8 +74,8 @@ class PythonDes extends Component {
},
{
url: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/rumen.png',
title: '20天零基础入门',
subTitle: '降低学习门槛,20天入门人工智能'
title: '17天零基础入门',
subTitle: '降低学习门槛,17天入门人工智能'
},
],
worryList: [
......@@ -87,7 +87,7 @@ class PythonDes extends Component {
{
url: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/xuexi.png',
title: '想学习,没有坚持下来',
subTitle: '以故事性的方式编写学习内容, 把生硬的知识点有趣化。精美的图片+幽默的文字+代码编写让你越学越学。'
subTitle: '以故事性的方式编写学习内容, 把生硬的知识点有趣化。精美的图片+幽默的文字+代码编写让你越学越学。'
},
{
url: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/zhishidian.png',
......@@ -95,6 +95,8 @@ class PythonDes extends Component {
subTitle: '每节课的知识点都设有代码编写部分,每节课课后都有课后实操。有多次代码编写练习的机会,助你真正学以致用。'
},
],
payMoney: 0,
isOnline: true, //课程是否上架
}
}
......@@ -119,6 +121,7 @@ class PythonDes extends Component {
}
toLearn = () => {
const {backwardVersion} = this.props;
http.post(`${API['home']}/m/it/user/trialCourse`, {course_id: getParam('id')}).then((res) => {
const {code, msg} = res.data
if (code == 200) {
......@@ -127,7 +130,11 @@ class PythonDes extends Component {
toApp: true
})
} else { // APP
SendMessageToApp("toLearn")
if(backwardVersion) {
Toast.info('当前版本不支持该课程模式,请升级到最新版本或前往PC端体验', 2)
} else {
SendMessageToApp("toLearn", getParam('id'))
}
}
} else {
Toast.info(msg, 2)
......@@ -142,6 +149,7 @@ class PythonDes extends Component {
}
toDetail = () => {
const {backwardVersion} = this.props;
const id = getParam('id')
if (!getParam('version')) { // H5
http.get(`${API['base-api']}/m/cart/addtopreorder/[${id}]`).then((res) => {
......@@ -152,7 +160,16 @@ class PythonDes extends Component {
}
})
} else { // APP
SendMessageToApp('toPay', id)
if(backwardVersion) {
Toast.info('当前版本不支持该课程模式,请升级到最新版本或前往PC端购买', 2);
let onlyoneUse = setTimeout(() => {
clearTimeout(onlyoneUse);
onlyoneUse = null;
SendMessageToApp('toPay', id)
}, 2000);
} else {
SendMessageToApp('toPay', id)
}
}
}
......@@ -202,6 +219,7 @@ class PythonDes extends Component {
allSyllabus: data.syllabus,
practice: data.practice.slice(0, 2),
allPractice: data.practice,
payMoney: data.course_info.price1,
})
} else {
Toast.info(msg)
......@@ -211,6 +229,11 @@ class PythonDes extends Component {
componentDidMount() {
this.getStatus()
const {backwardVersion, isPay} = this.props;
if(backwardVersion && isPay === 1) {
Toast.info('当前版本不支持该课程模式,请升级到最新版本或前往PC端学习', 2);
}
}
......@@ -223,7 +246,18 @@ class PythonDes extends Component {
}
render() {
const {defineList, desList, worryList, syllabus, practice, allSyllabusShow, allPracticeShow, toApp} = this.state
const {
defineList,
desList,
worryList,
syllabus,
practice,
allSyllabusShow,
allPracticeShow,
toApp,
payMoney,
isOnline
} = this.state
return (
<div className={'python__des'}>
<div className={'des__start'}></div>
......@@ -240,31 +274,64 @@ class PythonDes extends Component {
}
</div>
<div className={'course__introduce'}></div>
<div className={'bottom__btn_group'}>
<div className={'course__price'}>
<div className={'money'}>199<span style={{fontSize: '18px'}}></span></div>
<div className={'discount'}>
限时特惠
<div className={'triangle'}></div>
{
isOnline
? (
<div className={'bottom__btn_group'}>
<div className={'course__price'}>
<div className={'money'}>{payMoney}<span style={{fontSize: '18px'}}></span></div>
<div className={'discount'}>
限时特惠
<div className={'triangle'}></div>
</div>
</div>
<div className={'btn__group'}>
<div className={'try__study'} onClick={this.tryLearn}>免费试学</div>
<div className={'sign__now'} onClick={this.signUp}>立即报名</div>
</div>
</div>
</div>
)
: (
<div className="bottom__btn_group">
<p className="online__label">Python人工智能</p>
<button className="online__button">即将上架 敬请期待</button>
</div>
)
}
{/* 课程介绍 */}
<Description list={desList} />
<div className={'btn__group'}>
<div className={'try__study'} onClick={this.tryLearn}>免费试学</div>
<div className={'sign__now'} onClick={this.signUp}>立即报名</div>
</div>
</div>
<Description list={desList}></Description>
<NoWorry list={worryList}></NoWorry>
<Study syllabus={syllabus} allSyllabusShow={allSyllabusShow} show={this.showAll}
hide={this.hideSome}></Study>
<Test practice={practice} allPracticeShow={allPracticeShow} show={this.showAll}
hide={this.hideSome}></Test>
{/* 课后实操 */}
<Test
practice={practice}
allPracticeShow={allPracticeShow}
show={this.showAll}
hide={this.hideSome}
/>
<Team/>
<Experience tryLearn={this.tryLearn}/>
<img style={{display: 'block', width: '100%', height: '8px'}}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/changjian.png" alt=""/>
<Progream/>
{/* 试学体验 */}
<Experience tryLearn={this.tryLearn} isOnline={isOnline} />
<img
style={{
display: 'block',
width: '100%',
height: '8px'
}}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/h5_python_class/changjian.png"
alt=""
/>
{/* 常见问题 */}
<Progream />
{
toApp && (<Mask type={1} closePop={this.closePop}/>)
}
......
......@@ -86,6 +86,24 @@
padding: 0 14px;
background: #fff;
.online__label {
font-size: 18px;
font-weight: 600;
color: #525C65;
}
.online__button {
width: 160px;
height: 37px;
border-style: none;
border-radius: 19px;
font-size: 15px;
font-weight: 600;
color: #fff;
background-color: rgba(82,92,101,.6);
outline: none;
}
.course__price {
width: 100px;
font-size: 24px;
......
......@@ -59,9 +59,34 @@ html:not([data-scale]) .am-accordion.python-study__stage .am-accordion-item .am-
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
height: 55px;
padding: 0 12px;
background-color: #1A9BFC;
&::after {
content: '';
position: absolute;
left: -60px;
top: 0;
width: 146px;
height: 100%;
background-repeat: no-repeat;
background-size: 100% auto;
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/pythonCourse/pc/python-title-bg-1.png');
}
&::before {
content: '';
position: absolute;
right: -60px;
bottom: -20px;
width: 106px;
height: 100%;
background-repeat: no-repeat;
background-size: 100% auto;
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/pythonCourse/pc/python-title-bg-0.png');
}
}
.python-study__course-name {
......
......@@ -83,23 +83,15 @@ class PythonClass extends Component {
handleToSend = (params) => {
const { history } = this.props;
const { isShare, entryMode } = this.state;
const { isShare } = this.state;
if(browser.isWeixin) {
history.push(`/pythonShare?id=${getParam('id')}&type=${getParam('type')}&ques=${getParam('ques')}&origin=python`);
this.setState({
isGuide: true
});
let title = '';
let labelName = this.formatTitle(params);
if(entryMode !== 0 && !isShare) {
title = `我在${params.course_name}${labelName}遇到了困难`;
}
if(entryMode !== 0 && isShare) {
title = `我已在【${params.course_name}】上运行了行代码了${params.code_lines}`
}
wxShare({
title,
desc: labelName,
title: isShare? `我已在【${params.course_name}】上运行了行代码了${params.code_lines}` : `我在${params.course_name}${this.formatTitle(params)}遇到了困难`,
desc: this.formatTitle(params),
link: encodeURI(location.href),
imgUrl: params.course_img,
});
......@@ -155,7 +147,7 @@ class PythonClass extends Component {
data={data}
labelName={this.formatTitle(data)}
handleToExecute={this.handleToExecute}
handleToSend={this.handleToSend}
handleToSend={() => this.handleToSend(data)}
copyToSuccess={this.copyToSuccess}
handleToHide={this.handleToHide}
/>
......@@ -203,7 +195,7 @@ function PythonContent(props) {
<div className="python-popup" onClick={handleToHide}>
<div className="python-header">
<p className="python-wechat__title">请点击右上角分享</p>
<i className="iconfont iconyindao"></i>
<i className="iconfont"></i>
</div>
</div>
}
......
......@@ -11,22 +11,26 @@
.python-header {
position: relative;
height: 100px;
padding-top: 34px;
padding-top: 36px;
box-sizing: border-box;
.iconfont {
position: absolute;
top: -6px;
top: 10px;
right: 40px;
font-size: 38px;
color: #fff;
width: 50px;
height: 38px;
background: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/pythonCourse/h5/share_arrow.png') center center no-repeat;
background-size: 100% 100%;
}
}
.python-wechat__title {
margin: 0;
font-size: 17px;
color: #FCFF1D;
color: #FFF;
text-align: center;
line-height: 1;
}
......@@ -172,7 +176,7 @@
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .3);
background-color: rgba(0, 0, 0, .6);
z-index: 99;
}
......
......@@ -149,6 +149,7 @@ class CampTest extends Component {
data.answer_record = this.answerList;
http.post(`${API.home}/m/aist/submit`, data).then((res) => {
if(res.data.code === 200) {
this.body = document.getElementsByTagName('body')[0];
this.body.style.position = 'static';
this.props.history.push(`/campResolve?keshi_id=${this.keshi_id}&qid=${this.qid}`, {from: this.props.location.state.from})
}else{
......
......@@ -13,7 +13,6 @@ import { Modal } from "antd-mobile"
import { Loading } from '@/common'
import { connect } from "react-redux"
import jsCookie from 'js-cookie'
import io from 'socket.io-client'
import Single from "@/components/detail/single";
import SingleSuccess from "../detail/single/singleSuccess";
import './CustomPlayButton'
......@@ -83,8 +82,6 @@ class Video extends Component {
isCurrentVideoFirstPlay = true
WATCHTIME = "watch_time"
COUNTSCHEDULE = "count_schedule"
RECENTLEARN = "recent_learn"
state = {
......@@ -136,7 +133,6 @@ class Video extends Component {
this.token = jsCookie.get('token')
this.getVideoList()
this.getDatumCatalog()
this.setupRecord()
}
// 直接购买
......@@ -341,40 +337,6 @@ class Video extends Component {
})
}
setupRecord = () => {
this.recordSocket = io(API.record, {
transports: ['websocket'],
forceNew: true
})
// this.recordSocket = io(API.record)
// this.recordSocket.on('seek', time => {
// this.player.currentTime(time)
// })
// 开启定时器 每5秒发送一次学习记录 --删除
this.recordTimer = setInterval(() => {
this.sendRecord()
}, 5000)
}
// 发送学习记录
sendRecord = () => {
if (this.recordSocket && this.player) {
this.recordSocket.emit('addRecord', this.recordUserInfo())
}
}
// 返回学习记录的数据
recordUserInfo = () => {
let {uid} = this.props.user.data
return {
uid,
course_id: this.courseID,
video_id: this.state.videoList[this.state.activeIndex]['id'],
video_time: parseInt(this.player.currentTime()),
plat: 5
}
}
// 9502 初始化 监听事件
setupWS = () => {
this.ws = new WebSocket(API["process-api"]);
......@@ -524,9 +486,6 @@ class Video extends Component {
}
})
this.player.addChild('CustomPlayButtonCover')
this.player.on('ready', () => {
this.recordSocket.emit('load', this.recordUserInfo())
})
this.player.on('play', () => {
const {videoList, activeIndex, vCourseId, course = {}} = this.state
// 当视频播放时 看是否是第一次播放(初次进入页面 刷新页面 切换视频 都是第一次播放 需要获取上次的播放时间)
......@@ -566,9 +525,6 @@ class Video extends Component {
clearInterval(this.timer);
this.timer = null;
})
this.player.on('seeked', () => {
this.sendRecord()
})
}
sendLastRecord = () => {
......@@ -895,6 +851,7 @@ class Video extends Component {
activeIndex={this.state.activeIndex}
selectVideo={this.selectVideo}
videoCatalog={videoList}
isAist={course.is_aist}
{...props}
/>
);
......
......@@ -34,7 +34,8 @@ class VideoCatalog extends Component {
)}/>
</div>
{
(item.practice && item.practice.qid)
this.props.isAist ? (
(item.practice && item.practice.qid)
? <Link to={{
pathname: item.practice.is_tested ? '/campResolve' : '/campTest',
search: `?keshi_id=${item.id}&qid=${item.practice.qid}`,
......@@ -46,6 +47,22 @@ class VideoCatalog extends Component {
</div>
</Link>
: null
) : (
item.practice_common.map((commonItem, index) => {
return (
<Link to={{
pathname: commonItem.is_tested ? '/campResolve' : '/campTest',
search: `?keshi_id=${item.id}&qid=${commonItem.qid}`,
state: {from: `/play/video${window.location.search}`}
}} key={index}>
<div className="exercise">
课后练习:{commonItem.title}
<i className={classnames('iconfont',!item.video_auth ? 'iconiconfront-74' : commonItem.is_tested ? 'iconiconfront-3' : '')}/>
</div>
</Link>
)
})
)
}
</li>
)
......
......@@ -33,11 +33,16 @@
}
}
a:last-child {
.exercise {
margin-bottom: 15px;
}
}
.exercise {
padding-left: 15px;
height: 35px;
background: #F5FBFF;
margin-bottom: 15px;
line-height: 35px;
font-size: $font_12;
color: #000;
......@@ -52,12 +57,12 @@
.title {
display: inline-block;
width: 50%;
width: 66%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: $font_14;
margin-right: 26px;
margin-right: 12px;
}
.duration {
......
import React from 'react'
import WxErrImage from '@assets/image/wx_error.png'
import './wxerr.scss'
const WxErr = () => {
return (
<div className={'wxerr'}>
<img src={WxErrImage} alt=''/>
<p className="openLink" style={{
fontSize: `16px`,
marginTop: `40px`
}}>请在微信客户端打开链接</p>
</div>
)
}
export default WxErr
.wxerr{
margin-top: 40px;
display: flex;
flex-flow: column;
align-items: center;
.openLink{
font-size: 16px;
margin-top:40px;
}
}
......@@ -4,7 +4,7 @@ import jsCookie from 'js-cookie'
const accountLogin = user => dispatch => {
return http.post(`${API['passport-api']}/user_login`, {
return http.post(`${API['passport-api']}/m/login/accountLogin`, {
user_name: user.username,
password: encrypt(user.password),
is_encrypt: 1,
......@@ -14,8 +14,38 @@ 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']}/quick_login`, {
return http.post(`${API['passport-api']}/m/login/quickLogin`, {
...user,
plat: 5
}).then(res => {
......@@ -26,7 +56,7 @@ const quickLogin = user => dispatch => {
const storeUser = (res, dispatch) => {
const data = res.data
let payload
if (data.errno === 0) {
if (data.errno === 200) {
const {user_name: username, avatar_file: avatar,is_vip: isVIP, ...rest} = data.data.user_info
payload = {
hasError: false,
......@@ -75,6 +105,7 @@ const startFetchUser = () => ({
export {
accountLogin,
studentLogin,
SET_CURRENT_USER,
setCurrentUser,
quickLogin,
......
import { http, browser } from "@/utils"
const url = `https://res.wx.qq.com/open/js/jweixin-1.4.0.js`
// const url = `https://res.wx.qq.com/open/js/jweixin-1.4.0.js`
const url = `https://res.wx.qq.com/open/js/jweixin-1.6.0.js`
const appId = 'wx23dac6775ac82877'
const jsApiList = ['updateAppMessageShareData','updateTimelineShareData','onMenuShareAppMessage', 'onMenuShareTimeline']
const jsApiList = ['updateAppMessageShareData', 'updateTimelineShareData', 'onMenuShareAppMessage', 'onMenuShareTimeline']
export const getSignature = async (config = {}) => {
let res = await http.post(`${API['base-api']}/m/sale/signature`, {
url: browser.isWeixin && browser.isIOS ? sessionStorage.getItem('enter_url') : window.location.href.split('#')[0],
})
return wx.config({
debug: false, // 开启调试模式,
appId, // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonce_str, // 必填,生成签名的随机串
signature: res.data.signature,// 必填,签名,见附录1
jsApiList,
...config
})
let res = await http.post(`${API['base-api']}/m/sale/signature`, {
url: browser.isWeixin && browser.isIOS ? sessionStorage.getItem('enter_url') : window.location.href.split('#')[0],
})
return wx.config({
debug: false, // 开启调试模式,
appId, // 必填,公众号的唯一标识
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonce_str, // 必填,生成签名的随机串
signature: res.data.signature,// 必填,签名,见附录1
jsApiList,
...config
})
}
export const getWXObject = () => {
return new Promise(resolve => {
if (!window.wx) {
return loadScript(url).then(() => resolve())
}
resolve()
})
return new Promise(resolve => {
if (!window.wx) {
return loadScript(url).then(() => resolve())
}
resolve()
})
}
function loadScript(url) {
return new Promise(resolve => {
var head = document.head || document.getElementsByTagName('head')[0]
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
if (!('onload' in script)) {
script.onreadystatechange = function () {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
resolve()
}
}
script.onload = function () {
this.onload = null
resolve()
}
head.appendChild(script)
})
}
\ No newline at end of file
return new Promise(resolve => {
var head = document.head || document.getElementsByTagName('head')[0]
var script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
if (!('onload' in script)) {
script.onreadystatechange = function () {
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
this.onreadystatechange = null
resolve()
}
}
script.onload = function () {
this.onload = null
resolve()
}
head.appendChild(script)
})
}
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