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";
}
});
}
......
#assistance {
width: 100%;
background: $white;
position: relative;
padding-bottom: px2rem(55);
-webkit-overflow-scrolling: touch;
text-align: center;
$mt15: 15px;
@function px2rem($px) {
@return #{$px}px;
}
@mixin px2px($prop, $value) {
#{$prop}: #{$value/2}px
}
@mixin page-big($bg, $color, $shadow) {
border-radius: 40px;
border: 1px solid transparent;
outline: none;
text-align: center;
background: $bg;
color: $color;
font-weight: bold;
box-shadow: $shadow;
}
@mixin border-b-1px($color) {
position: relative;
&::after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-bottom: 1px solid $color;
content: '';
}
}
.barrage-wrapper {
width: 100%;
height: px2rem(30);
background-color: rgba(0, 0, 0, .5);
position: absolute;
top: 0;
z-index: 99;
overflow: hidden;
.barrage-list {
height: px2rem(30);
li {
height: px2rem(30);
position: relative;
display: flex;
align-items: center;
justify-content: left;
margin: 0 10px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
.person-left {
display: flex;
align-items: center;
line-height: px2rem(30);
margin-right: 5px;
.avatar {
min-width: px2rem(20);
height: px2rem(20);
border-radius: 50%;
margin-right: 5px;
}
.name {
color: $active;
max-width: px2rem(100);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.title {
color: $white;
}
.person-right {
margin-right: 58px;
.time {
@include px2px(font-size, 22);
color: $color_666;
}
}
}
}
}
.bg-image {
position: relative;
width: 100%;
height: 0;
padding-top: 60%;
transform-origin: top;
background-size: cover;
.ranklist-btn {
height: px2rem(30);
line-height: px2rem(30);
padding: 0 10px;
background-color: #2FF8FF;
box-shadow: 0 0 5px 0 rgba(47, 248, 255, .4);
border-radius: 15px 0 0 15px;
position: fixed;
top: 160px;
right: 0;
z-index: 999;
color: #15006A;
@include px2px(font-size, 20);
-webkit-appearance: none;
outline: 0;
border: none;
}
}
.christ-getwrap {
overflow: hidden;
.title {
margin-top: 10px;
margin-left: $mt15;
text-align: left;
@include px2px(font-size, 28);
.active {
color: $active;
}
}
.btn-get-wrap {
margin-top: 15px;
.btn-get {
width: px2rem(300);
height: px2rem(38);
line-height: px2rem(34);
display: block;
margin: 0 auto;
@include px2px(font-size, 32);
@include page-big($active, $white, none);
border-radius: 5px !important;
}
}
.person-tolearn {
margin-top: 10px;
color: $color_333;
font-size: 14px;
}
.person-getwarp {
width: 100%;
margin-top: $mt15;
.person-get {
height: px2rem(30);
line-height: px2rem(30);
background-color: $bg_f4f4f4;
text-align: left;
padding-left: 15px;
color: $color_333;
@include px2px(font-size, 24);
}
.person-null {
height: px2rem(105);
line-height: px2rem(105);
text-align: center;
overflow: hidden;
}
.person-list {
height: px2rem(105);
overflow: hidden;
background-color: $bg_f4f4f4;
li {
height: px2rem(35);
line-height: px2rem(35);
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
@include border-b-1px(#eae7f4);
margin: 0 10px;
.person-left {
display: flex;
align-items: center;
overflow: hidden;
.avatar {
min-width: px2rem(20);
height: px2rem(20);
border-radius: 50%;
margin-right: 5px;
}
.name {
display: inline-block;
max-width: px2rem(150);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.person-right {
.time {
@include px2px(font-size, 22);
color: $color_666;
}
}
}
}
.person-encourage {
color: #FA952F;
@include px2px(font-size, 24);
margin-top: 10px;
}
.btn-morewap {
margin-top: 10px;
.btn-more {
width: px2rem(300);
height: px2rem(38);
line-height: px2rem(34);
display: block;
margin: 0 auto;
@include px2px(font-size, 32);
@include page-big(#3D1AAF, $white, none);
border-radius: 5px !important;
}
}
.more-title {
margin-top: 10px;
margin-bottom: 15px;
color: #FA952F;
@include px2px(font-size, 24);
}
.course-wrap {
padding: 0 10px;
overflow: hidden;
display: flex;
justify-content: space-between;
flex-flow: wrap;
li {
display: inline-block;
width: px2rem(160);
height: px2rem(106);
margin-right: 10px;
margin-bottom: 20px;
}
li:nth-child(2) {
margin-right: 0;
}
li:last-child {
margin-right: 0;
}
}
}
}
.christ-toactive {
width: 100%;
height: px2rem(35);
line-height: px2rem(35);
text-align: center;
color: $white;
@include px2px(font-size, 36);
font-weight: bold;
background-color: #3D1AAF;
position: fixed;
bottom: 0;
}
.module-dialog {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
height: 100%;
font-size: .8rem;
.box-mask {
z-index: 900;
position: fixed;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .6);
display: flex;
align-items: center;
justify-content: center;
}
}
.box-container {
border-radius: 10px;
background-color: $white;
width: 80%;
position: relative;
padding-bottom: 15px;
.box-content {
.box-title {
@include px2px(font-size, 44);
line-height: px2rem(44);
height: px2rem(44);
margin-top: 10px;
color: $active;
i {
color: #f00;
@include px2px(font-size, 36);
font-style: normal;
}
.text1 {
color: $color_333;
@include px2px(font-size, 36);
}
.icon-right {
@include px2px(font-size, 80);
display: inline-block;
vertical-align: middle;
margin-top: -3px;
}
}
.box-text {
.text1 {
color: $color_333;
@include px2px(font-size, 36);
text-align: center;
margin: 15px auto 20px;
padding: 0 10px;
}
.text2 {
color: $color_333;
@include px2px(font-size, 28);
margin: 0 auto;
text-align: left;
max-width: 260px;
margin-bottom: 5px;
}
.text3 {
color: $active;
@include px2px(font-size, 24);
margin: 0 auto;
text-align: left;
max-width: 260px;
}
}
}
.box-bottom1 {
width: 100%;
img {
width: 50% !important;
}
p {
font-size: 12px;
color: $color_666;
margin-top: -15px;
}
}
.box-bottom {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.activeallcourse {
padding: 10px 30px;
border-radius: 5px;
min-width: 260px;
border: 1px solid transparent;
background-color: #09f;
color: $white;
font-size: 18px;
}
img {
width: 50% !important;
}
.bottom-item {
height: px2rem(40);
padding: 0 54px;
color: $white;
background-color: $active;
border-radius: 30px;
@include px2px(font-size, 44);
display: flex;
align-items: center;
justify-content: center;
margin-top: 14px;
}
}
.box-close {
position: absolute;
left: 50%;
bottom: -60px;
color: $white;
margin-left: -15px;
.f30 {
font-size: 30px;
}
}
}
.gift-package {
display: block;
width: 58px;
height: 72px;
position: fixed;
right: 0;
top: 50%;
margin-top: -25px;
img {
width: 100%;
height: 100%;
}
}
.iconfont{
font-size: 30px;
}
}
import React, { Component } from 'react'
import { browser, getParam, http } from "@/utils"
import { Toast } from "antd-mobile"
import { withRouter } from 'react-router-dom'
import './assistance.scss'
import { Base64 } from "js-base64"
import { connect } from 'react-redux'
import Swiper from 'swiper'
function showToast(text) {
Toast.info(text, 2, null, false)
}
@connect(({user}) => ({
user
}))
class GiveCourseAssistance extends Component {
scrollContainer = null
cloned = false
animationId = 0
swiper = null
state = {
allCourse: [],
activeName: '',
activeUrl: '',
isAwards: '',
count: '',
assistItems: [],
name: '',
courseImg: '',
code: getParam('magic_word'),
getBtnStatus: '1',
disabled: false,
successOneStatus: false,
errorStatus: false,
activeStatus: false,
friendStatus: false,
posY: 0,
scrollStyle: {
transform: `translateY(${0}px)`,
transition: 'all linear 0ms',
overflow: 'hidden'
},
headItems: []
}
componentDidMount() {
this.giveDetail()
this.assistantScroll()
this.requestHeadItems()
let baseCode = decodeURIComponent(Base64.decode(this.state.code)).split(',')
this.setState({
baseCode: baseCode[0],
getBtnStatus: this.props.user.data.uid == baseCode[0] ? '2' : '1'
})
}
componentWillUpdate(nextProps) {
const {user} = this.props
if (nextProps.user.hasError !== user.hasError) {
this.setState({
getBtnStatus: nextProps.user.data.uid == this.state.baseCode ? '2' : '1'
})
}
}
componentWillUnmount() {
window.cancelAnimationFrame(this.animationId)
this.swiper && this.swiper.destroy()
}
giveDetail = () => {
http.get(`${API["base-api"]}/assistance/detail`)
.then(res => {
const {data, msg, errno} = res.data
if (errno === 200) {
this.setState({
allCourse: data.course_info,
activeName: data.active_info.activity_name,
activeUrl: data.active_info.activity_url,
isAwards: data.is_awards
})
} else {
showToast(msg)
}
})
}
assistantScroll = () => {
http.get(`${API["base-api"]}/assistance/active_detail?code=${getParam('magic_word')}`)
.then(res => {
const {data, msg, errno} = res.data
if (errno === 0) {
this.setState({
count: data.assistant_count,
assistItems: data.assistants,
name: data.invitation_user_nickname,
courseImg: data.course_image_name
}, this.displayScrollArea)
} else {
showToast(msg)
}
})
}
getCourse = () => {
_czc && _czc.push(["_trackEvent", '中间页免费领取课程', '中间页免费领取课程'])
const {history, user} = this.props
if (browser.isWeixin) {
if (user.hasError) {
history.push('/passport')
return
}
this.setState({
disabled: true
})
http.post(`${API["base-api"]}/assistance/assistant`, {
code: this.state.code,
})
.then(res => {
const {errno, msg} = res.data
let success = errno == 200
this.setState({
successOneStatus: success,
errorStatus: !success,
disabled: false
})
})
} else {
history.push('/wxerr')
}
}
toGetDetail = courseId => {
_czc && _czc.push(["_trackEvent", `课程id=${courseId}`, '中间页查看课程详情'])
window.location.href = this.state.activeUrl
}
displayScrollArea = () => {
let {
assistItems: list,
posY
} = this.state
if (list.length < 3) return
if (!this.cloned) {
const cloneNode = this.scrollContainer.firstChild.firstChild.cloneNode(true)
this.scrollContainer.firstChild.appendChild(cloneNode)
this.cloned = true
}
const h = this.scrollContainer.firstChild.offsetHeight / 2
if (Math.abs(posY) >= h) posY = 0
posY -= 0.5
this.setState({
posY,
scrollStyle: {
transform: `translateY(${posY}px)`,
transition: 'all linear 0ms',
overflow: 'hidden'
}
})
this.animationId = window.requestAnimationFrame(this.displayScrollArea)
}
requestHeadItems = () => {
http.get(`${API["base-api"]}/assistance/roll_tip`)
.then(res => {
const {errno, data, msg} = res.data
if (errno === 200) {
this.setState({
headItems: data
}, this.setupHeadItemsSwiper)
} else {
showToast(msg)
}
})
}
setupHeadItemsSwiper = () => {
this.swiper = new Swiper('.barrage-wrapper', {
autoplay: {
delay: 5000
},
direction: 'vertical',
loop: true,
allowTouchMove: false
})
}
render() {
const {
isAwards,
courseImg,
activeName,
name,
getBtnStatus,
disabled,
count,
allCourse,
successOneStatus,
errorStatus,
activeStatus,
friendStatus,
assistItems,
scrollStyle,
headItems
} = this.state
const {history} = this.props
return (
<div id='assistance'>
{<div className="barrage-wrapper">
<ul className="barrage-list swiper-wrapper">
{
headItems.map((item, index) => {
return (
<li key={index} className={'swiper-slide'}>
<div className="person-left">
<img className="avatar" src={item.head_img} alt=""/>
<span className="name">{item["nickname"]}</span>
</div>
<div className="title">{item["word"]}</div>
</li>
)
})
}
</ul>
</div>}
<div className="bg-image" style={{backgroundImage: `url(${courseImg})`}}>
{
isAwards == 1 && <button className="ranklist-btn" type="button" onClick={() => {
_czc && _czc.push(["_trackEvent", '中间页大奖排行榜', '中间页面大奖排行榜'])
history.push(`/active/givecourse?activename=${activeName}`)
}}>大奖排行榜>></button>
}
</div>
<div className="christ-getwrap">
{
getBtnStatus === '1' &&
<div className="title">
<span className="active">@{name}</span> 赠送您一门课
</div>
}
<div className="btn-get-wrap">
{
getBtnStatus === '1'
?
<button type="button" className="btn-get" disabled={disabled} onClick={this.getCourse}>
免费领取
</button>
:
<button type="button" className="btn-get" onClick={() => {
_czc && _czc.push(["_trackEvent", '中间页继续邀请好友', '中间页继续邀请好友'])
this.setState({
friendStatus: true
})
}}>
继续邀请好友
</button>
}
</div>
<p className="person-tolearn">领取的课程可前往七月在线APP或官网学习</p>
<div className="person-getwarp">
<div className="person-get">
已有
<span>{count}</span>
人通过助力领取课程
</div>
{
assistItems.length ?
<div className='scroll-container person-list' ref={el => this.scrollContainer = el}>
<div className="wrapper" style={scrollStyle}>
<ul>
{
assistItems.map(item => {
return (
<li key={item["assistant_uid"]}>
<div className="person-left">
<img className="avatar" src={item["avatar_file"]} alt=""/>
<span className="name">{item.user_name}</span>
</div>
<div className="person-right">
<span className="time">{item["add_time"]}前领取成功</span>
</div>
</li>
)
})
}
</ul>
</div>
</div>
:
<p className="person-null">目前暂无邀请</p>
}
<div className="btn-morewap">
<button type="button" className="btn-more" onClick={() => {
_czc && _czc.push(["_trackEvent", '中间页参与活动领更多课时', '中间页参与活动领更多课时'])
this.setState({activeStatus: true})
}}>参与活动领更多课时
</button>
</div>
<p className="more-title">参与活动可免费领取以下课程</p>
<ul className="course-wrap">
{
allCourse.map((item) => {
return (
<li key={item.course_id} onClick={this.toGetDetail.bind(this, item.course_id)}>
<img src={item.image_name} alt=""/>
</li>
)
})
}
</ul>
</div>
</div>
{
successOneStatus &&
<div className="module-dialog">
<div className="box-mask">
<div className="box-container">
<div className="box-content">
<div className="box-title">
<i className="iconfont icon-chenggong icon-right"/>
领取成功
</div>
<div className="box-text">
<p className="text1">恭喜您获得第一课的免费学习权限</p>
</div>
</div>
<div className="box-bottom">
<button type="button" className="activeallcourse" onClick={() => {
this.setState({activeStatus: true, successOneStatus: false})
}}>进入服务号领取全部课时
</button>
</div>
</div>
</div>
</div>
}
{
errorStatus &&
<div className="module-dialog">
<div className="box-mask">
<div className="box-container">
<div className="box-content">
<div className="box-title" style={{color: `#f00`}}>
领取失败
</div>
<div className="box-text">
<p className="text1">只能免费领取1次,想获得更多课时快去参与活动吧!</p>
</div>
</div>
<div className="box-bottom">
<button type="button" className="activeallcourse" onClick={() => {
this.setState({errorStatus: false, friendStatus: true})
}}>确定
</button>
</div>
</div>
</div>
</div>
}
{
activeStatus &&
<div className="module-dialog">
<div className="box-mask">
<div className="box-container">
<div className="box-content">
<div className="box-title">
<p className="text1">进入服务号回复<i>77</i>免费领取课程</p>
</div>
<div className="box-bottom1">
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018christyear/h5/qrcode.jpg" alt=""/>
<p>长按扫码进入服务号</p>
</div>
</div>
<div className="box-close" onClick={() => {
this.setState({activeStatus: false})
}}>
<i className="iconfont iconiconfront-2"/>
</div>
</div>
</div>
</div>
}
{
friendStatus &&
<div className="module-dialog">
<div className="box-mask">
<div className="box-container">
<div className="box-content">
<div className="box-title">
<p className="text1">进入服务号回复<i>77</i>免费领取课程</p>
</div>
<div className="box-bottom1">
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/2018christyear/h5/qrcode.jpg" alt=""/>
<p>长按扫码进入服务号</p>
</div>
</div>
<div className="box-close" onClick={() => {
this.setState({friendStatus: false})
}}>
<i className="iconfont iconiconfront-2"/>
</div>
</div>
</div>
</div>
}
</div>
)
}
}
export default withRouter(GiveCourseAssistance)
#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;
......
......@@ -15,606 +15,642 @@ import { FadeLoader } from "react-spinners"
class Landing extends Component {
closeIcon = 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/close-btn.png'
createTeamSuccessPopup = null
joinSuccessPopup = null
shareTipPopup = null
swiper = null
shareTitle = ''
shareDesc = ''
state = {
teamData: {
member: []
},
origin: sessionStorage.getItem('isCaptain') ? 1 : getParam('origin'),
treasure_code: getParam('treasure_code'),
activityEnd: false,
notices: [],
rule: '',
isCaptain: getParam('origin') == 1,
isLoading: this.props.user.isFetching
closeIcon = 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/close-btn.png'
createTeamSuccessPopup = null
joinSuccessPopup = null
shareTipPopup = null
swiper = null
shareTitle = ''
shareDesc = ''
state = {
teamData: {
member: []
},
origin: sessionStorage.getItem('isCaptain') ? 1 : getParam('origin'),
treasure_code: getParam('treasure_code'),
activityEnd: false,
notices: [],
rule: '',
isCaptain: getParam('origin') == 1,
isLoading: this.props.user.isFetching
}
componentDidMount() {
this.getStage();
this.fetchPageData()
window.showFollowAlert = this.remind.bind(this, 'create')
document.title = '七月在线新春献礼,幸运宝箱随你开,100%有奖!“薪”年好课免费学,精品课程1分秒!'
const landing = document.querySelector('#landing')
const boxContainer = document.querySelector('.box-container')
if (boxContainer.offsetHeight < window.innerHeight) {
landing.style.minHeight = `${window.innerHeight}px`
boxContainer.style.minHeight = `${window.innerHeight}px`
}
}
componentDidMount() {
this.fetchPageData()
window.showFollowAlert = this.remind.bind(this, 'create')
document.title = '七月在线年终大回馈,幸运宝箱随你开,100%有奖!'
const landing = document.querySelector('#landing')
const boxContainer = document.querySelector('.box-container')
if (boxContainer.offsetHeight < window.innerHeight) {
landing.style.minHeight = `${window.innerHeight}px`
boxContainer.style.minHeight = `${window.innerHeight}px`
}
componentDidUpdate(prevProps) {
if (prevProps.user.hasError !== this.props.user.hasError && !this.props.user.hasError) {
this.fetchPageData()
}
componentDidUpdate(prevProps) {
if (prevProps.user.hasError !== this.props.user.hasError && !this.props.user.hasError) {
this.fetchPageData()
if(prevProps.user.isFetching !== this.props.user.isFetching && !this.props.user.isFetching){
this.setState({
isLoading: this.props.user.isFetching
})
}
}
componentWillUnmount() {
this.shareTipPopup && this.shareTipPopup.remove()
}
// 获取活动以及宝箱的阶段
getStage = () => {
http.get(`${API.home}/activity/stage`).then(res => {
const {code, data, msg} = res.data
if (code === 200) {
if(Number(data.activity_stage) === 0) {
this.props.history.push('/');
return;
}
} else {
Toast.info(msg, 2)
}
if(prevProps.user.isFetching !== this.props.user.isFetching && !this.props.user.isFetching){
this.setState({
isLoading: this.props.user.isFetching
})
}
createMeta = (title,dec,imgname) => {
let meta = document.createElement('meta');
meta.setAttribute('name', 'description')
meta.setAttribute('itemprop', 'description')
meta.setAttribute('content', '这里是自定义分享的描述')
let meta2 = document.createElement('meta');
meta2.setAttribute('itemprop', 'name')
meta2.setAttribute('content', '这里是标题')
let meta3 = document.createElement('meta');
meta3.setAttribute('itemprop', 'image')
meta3.setAttribute('content', 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png')
let head = document.getElementsByTagName('head')[0];
head.appendChild(meta);
head.appendChild(meta2);
head.appendChild(meta3);
}
createMeta = (title, dec, imgname) => {
let meta = document.createElement('meta')
meta.setAttribute('name', 'description')
meta.setAttribute('itemprop', 'description')
meta.setAttribute('content', dec)
let meta2 = document.createElement('meta')
meta2.setAttribute('itemprop', 'name')
meta2.setAttribute('content', title)
let meta3 = document.createElement('meta')
meta3.setAttribute('itemprop', 'image')
meta3.setAttribute('content', imgname)
let head = document.getElementsByTagName('head')[0]
head.appendChild(meta)
head.appendChild(meta2)
head.appendChild(meta3)
}
fetchPageData = ({origin = this.state.origin, treasure_code = this.state.treasure_code} = {}) => {
const {location} = this.props
http.post(`${API.home}/sys/treasure/team`, {
treasure_code,
origin
})
.then(res => {
const {data, code, msg} = res.data
if (code == 200) {
this.setState({
teamData: data,
isCaptain: sessionStorage.getItem('isCaptain') ? sessionStorage.getItem('isCaptain') : getParam('origin') == 1,
activityEnd: data.status == 5
})
this.setupWxShareConfig(data.member)
if (browser.isWeixin) {
wxShare({
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
})
}
}
componentWillUnmount() {
this.shareTipPopup && this.shareTipPopup.remove()
}
// history.replace(`${match.url}?treasure_code=${getParam('treasure_code')}&origin=2`)
} else {
this.createMeta(this.shareTitle, this.shareDesc, 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png')
}
createMeta = (title, dec, imgname) => {
let meta = document.createElement('meta')
meta.setAttribute('name', 'description')
meta.setAttribute('itemprop', 'description')
meta.setAttribute('content', dec)
let searchParams = new URLSearchParams(window.location.search)
if (searchParams.get('origin') === '1' && !browser.isWeixin) {
searchParams.set('origin', '2')
sessionStorage.setItem('isCaptain', '1')
window.history.replaceState(null, '', `landing?${searchParams.toString()}`)
}
let meta2 = document.createElement('meta')
meta2.setAttribute('itemprop', 'name')
meta2.setAttribute('content', title)
this.showSharePromptWithParam()
let meta3 = document.createElement('meta')
meta3.setAttribute('itemprop', 'image')
meta3.setAttribute('content', imgname)
let head = document.getElementsByTagName('head')[0]
head.appendChild(meta)
head.appendChild(meta2)
head.appendChild(meta3)
}
fetchPageData = ({origin = this.state.origin, treasure_code = this.state.treasure_code} = {}) => {
const {location} = this.props
http.post(`${API.home}/sys/treasure/team`, {
treasure_code,
origin
})
.then(res => {
const {data, code, msg} = res.data
if (code == 200) {
this.setState({
teamData: data,
isCaptain: sessionStorage.getItem('isCaptain') ? sessionStorage.getItem('isCaptain') : getParam('origin') == 1,
activityEnd: data.status == 5
})
this.setupWxShareConfig(data.member)
if (browser.isWeixin) {
wxShare({
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
})
// history.replace(`${match.url}?treasure_code=${getParam('treasure_code')}&origin=2`)
} else {
this.createMeta(this.shareTitle, this.shareDesc, 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png')
}
let searchParams = new URLSearchParams(window.location.search)
if (searchParams.get('origin') === '1' && !browser.isWeixin) {
searchParams.set('origin', '2')
sessionStorage.setItem('isCaptain', '1')
window.history.replaceState(null, '', `landing?${searchParams.toString()}`)
}
this.showSharePromptWithParam()
} else {
Toast.info(msg, 2, null, false)
}
})
} else {
Toast.info(msg, 2, null, false)
}
})
}
setupWxShareConfig = member => {
const len = member.length
let count = 5 - len <= 0 ? 4 : 5 - len
this.shareTitle = `还差${count}人即可开宝箱,100%有奖!`
this.shareDesc = '加入我的队伍,机械键盘,纸质书籍等超多奖品等你拿!--七月在线'
}
showSharePromptWithParam = () => {
if (sessionStorage.getItem('showShareTip') && !sessionStorage.getItem('closedRemind')) {
this.showShareTip()
}
}
setupWxShareConfig = member => {
const len = member.length
let count = 5 - len <= 0 ? 4 : 5 - len
this.shareTitle = `还差${count}人即可开宝箱,100%有奖!`
this.shareDesc = '加入我的队伍,机械键盘,纸质书籍等超多奖品等你拿!--七月在线'
joinTeam = () => {
if (this.requiredLogin()) {
return
}
const {treasure_code} = this.state
http.post(`${API.home}/sys/treasure/joinTeam`, {
treasure_code
})
.then(res => {
const {data, code, msg} = res.data
if (code === 200) {
/*
* status
* 1-成功加入
* 2-成功加入组队成功
* 3-已加入当前队伍
* 4-已加入其他队伍
* */
switch (data.status) {
case 1:
case 2:
this.joinSuccess(data)
break
default:
Toast.info(msg, 2, null, false)
}
showSharePromptWithParam = () => {
if (sessionStorage.getItem('showShareTip') && !sessionStorage.getItem('closedRemind')) {
this.showShareTip()
} else {
Toast.info(msg, 2, null, false)
}
})
}
handleToMyTreasure = id => {
const {history} = this.props
this.joinSuccessPopup && this.joinSuccessPopup.remove()
if (getParam('version')) {
window.location.assign(`https://m.julyedu.com/year/yearTreasure?id=${id}&version=${getParam('version')}`)
} else {
window.location.assign(`https://m.julyedu.com/year/yearTreasure?id=${id}`)
}
joinTeam = () => {
if (this.requiredLogin()) {
return
}
joinSuccess = ({id, status, my_team: {team_num, lack_member, is_team, treasure_code}}) => {
this.joinSuccessPopup = Popup({
title: <div className={'join-success'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/check-icon.png" alt=""/>
<div>成功加入队伍</div>
</div>,
className: 'landing-join-success-wrapper',
content: <div>
<div className={'tip'}>
{
status === 2
? '组队成功,恭喜你获得一个宝箱!'
: is_team
? <>您的{<span>{team_num}</span>}号队伍还差{<span>{lack_member}</span>}人即可获得宝箱</>
: '自己当队长,宝箱内有专属奖品哦~'
}
</div>
{
status === 2
? <img style={{width: '150px', marginTop: '23px'}}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/treasure-split.gif"
alt=""
onClick={this.handleToMyTreasure.bind(this, id)}
/>
: is_team
? <button type='button'
onClick={this.inviteMembers.bind(this, treasure_code)}>继续组队</button>
: <button type="button" onClick={this.createMyTeam}>创建我的队伍</button>
}
const {treasure_code} = this.state
http.post(`${API.home}/sys/treasure/joinTeam`, {
treasure_code
})
.then(res => {
const {data, code, msg} = res.data
if (code === 200) {
/*
* status
* 1-成功加入
* 2-成功加入组队成功
* 3-已加入当前队伍
* 4-已加入其他队伍
* */
switch (data.status) {
case 1:
case 2:
this.joinSuccess(data)
break
default:
Toast.info(msg, 2, null, false)
}
} else {
Toast.info(msg, 2, null, false)
}
})
</div>,
closeIcon: this.closeIcon,
close: () => {
this.fetchPageData()
this.getFollowStatus()
.then(isFollow => {
!isFollow && this.remind('join', this.state.treasure_code, 2)
})
}
})
}
inviteMembers = treasure_code => {
const {history, match, user, location} = this.props
if (getParam('version')) {
let data = {
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${treasure_code}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
}
SendMessageToApp("toShare", data)
}
sessionStorage.setItem('showShareTip', '1')
history.replace(`${match.url}?treasure_code=${treasure_code}&origin=${1}`)
this.createTeamSuccessPopup && this.createTeamSuccessPopup.remove() && (this.createTeamSuccessPopup = null)
this.joinSuccessPopup && this.joinSuccessPopup.remove() && (this.joinSuccessPopup = null)
this.fetchPageData({treasure_code, origin: 1})
this.setState({
isCaptain: true
})
}
getFollowStatus = () => http.get(`${API.home}/sys/user/isFollowWeChat`)
.then(res => {
const {code, data} = res.data
return code === 200 && data['is_follow']
})
createTeamSuccess = ({member, team_num, lack_member, treasure_code}) => {
this.joinSuccessPopup && this.joinSuccessPopup.remove() && (this.joinSuccessPopup = null)
this.getFollowStatus()
.then(isFollow => {
this.createTeamSuccessPopup = Popup({
title: '创建成功',
className: 'landing-create-success',
content: <div>
<div className="tip">{<span>{team_num}</span>}号队伍 还差{<span>{lack_member}</span>}名队员即可获得宝箱哦~</div>
<img className={'treasure-box'}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/treasure-box-icon.png" alt=""/>
<ul className='members'>
{
new Array(5).fill('a').map((item, index) => {
const _member = member[index]
return <li key={index}>
{_member && _member['is_captain'] && <sup>队长</sup>}
<img
className={'avatar'}
src={
_member
? _member['head_img']
: "https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/not-joined-placeholder.png"
}
alt=""/>
</li>
})
}
</ul>
<button type={'button'} onClick={this.inviteMembers.bind(this, treasure_code)}>邀请好友加入队伍</button>
</div>,
closeIcon: this.closeIcon,
afterClose: () => {
if (!isFollow) {
this.remind('create', treasure_code)
}
}
})
handleToMyTreasure = id => {
const {history} = this.props
this.joinSuccessPopup && this.joinSuccessPopup.remove()
if (getParam('version')) {
window.location.assign(`https://m.julyedu.com/year/yearTreasure?id=${id}&version=${getParam('version')}`)
} else {
window.location.assign(`https://m.julyedu.com/year/yearTreasure?id=${id}`)
}
})
}
// 安卓手机保存二维码
saveImage = url => {
let version = getParam('version')
version = typeof version === 'string' ? version.replace('.', '').replace('.', '').slice(0, 3) : ''
if (version && parseInt(version) < 451) {
Toast.info('当前不支持此功能,升级到最新版本app可以点击保存二维码!', 3, null, false)
} else {
SendMessageToApp('generateQRCode', url)
}
joinSuccess = ({id, status, my_team: {team_num, lack_member, is_team, treasure_code}}) => {
this.joinSuccessPopup = Popup({
title: <div className={'join-success'}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/check-icon.png" alt=""/>
<div>成功加入队伍</div>
</div>,
className: 'landing-join-success-wrapper',
}
remind = (type = 'create', treasure_code, origin = 1) => {
const {user} = this.props
http.get(`${API["base-api"]}/wx/user_temporary_qrcode/${user.data.uid}`)
.then(res => {
const {data} = res.data
return data.url
}).then(text => {
QRCode.toDataURL(text)
.then(url => {
Popup({
title: '提醒服务',
className: 'landing-remind',
closeIcon: this.closeIcon,
content: <div>
<div className={'tip'}>
{
status === 2
? '组队成功,恭喜你获得一个宝箱!'
: is_team
? <>您的{<span>{team_num}</span>}号队伍还差{<span>{lack_member}</span>}人即可获得宝箱</>
: '自己当队长,宝箱内有专属奖品哦~'
}
</div>
{
status === 2
? <img style={{width: '150px', marginTop: '23px'}}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/treasure-split.gif"
alt=""
onClick={this.handleToMyTreasure.bind(this, id)}
/>
: is_team
? <button type='button'
onClick={this.inviteMembers.bind(this, treasure_code)}>继续组队</button>
: <button type="button" onClick={this.createMyTeam}>创建我的队伍</button>
}
<div className="des">{type === 'create' ? '有好友加入队伍后第一时间通知我~' : '获得宝箱时第一时间通知我~'}</div>
<img src={url} alt=""/>
{browser.isAndroidApp ? <div>
<button className={'android-qr-code-btn'} onClick={this.saveImage.bind(this, text)}>保存二维码</button>
</div> : null}
<div className="des">长按识别/扫码 关注【七月在线】服务号即可预约</div>
</div>,
closeIcon: this.closeIcon,
close: () => {
this.fetchPageData()
this.getFollowStatus()
.then(isFollow => {
!isFollow && this.remind('join', this.state.treasure_code, 2)
})
// history.replace(`${match.path}?treasure_code=${treasure_code}&origin=1`)
sessionStorage.setItem('closedRemind', '1')
this.fetchPageData({
treasure_code,
origin
})
}
})
})
}
inviteMembers = treasure_code => {
const {history, match, user, location} = this.props
if (getParam('version')) {
let data = {
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${treasure_code}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
}
SendMessageToApp("toShare", data)
}
sessionStorage.setItem('showShareTip', '1')
history.replace(`${match.url}?treasure_code=${treasure_code}&origin=${1}`)
this.createTeamSuccessPopup && this.createTeamSuccessPopup.remove() && (this.createTeamSuccessPopup = null)
this.joinSuccessPopup && this.joinSuccessPopup.remove() && (this.joinSuccessPopup = null)
this.fetchPageData({treasure_code, origin: 1})
this.setState({
isCaptain: true
})
}
getFollowStatus = () => http.get(`${API.home}/sys/user/isFollowWeChat`)
.then(res => {
const {code, data} = res.data
return code === 200 && data['is_follow']
})
createTeamSuccess = ({member, team_num, lack_member, treasure_code}) => {
this.joinSuccessPopup && this.joinSuccessPopup.remove() && (this.joinSuccessPopup = null)
this.getFollowStatus()
.then(isFollow => {
this.createTeamSuccessPopup = Popup({
title: '创建成功',
className: 'landing-create-success',
content: <div>
<div className="tip">{<span>{team_num}</span>}号队伍 还差{<span>{lack_member}</span>}名队员即可获得宝箱哦~</div>
<img className={'treasure-box'}
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/treasure-box-icon.png" alt=""/>
<ul className='members'>
{
new Array(5).fill('a').map((item, index) => {
const _member = member[index]
return <li key={index}>
{_member && _member['is_captain'] && <sup>队长</sup>}
<img
className={'avatar'}
src={
_member
? _member['head_img']
: "https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/not-joined-placeholder.png"
}
alt=""/>
</li>
})
}
</ul>
<button type={'button'} onClick={this.inviteMembers.bind(this, treasure_code)}>邀请好友加入队伍</button>
</div>,
closeIcon: this.closeIcon,
afterClose: () => {
if (!isFollow) {
this.remind('create', treasure_code)
}
}
})
})
}
// 安卓手机保存二维码
saveImage = url => {
let version = getParam('version')
version = typeof version === 'string' ? version.replace('.', '').replace('.', '').slice(0, 3) : ''
if (version && parseInt(version) < 451) {
Toast.info('当前不支持此功能,升级到最新版本app可以点击保存二维码!', 3, null, false)
} else {
SendMessageToApp('generateQRCode', url)
})
}
showShareTip = () => {
const {user, location} = this.props
if (browser.isAndroidApp || browser.isIOSApp) {
SendMessageToApp('toShare', {
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
})
} else {
this.shareTipPopup = Popup({
title: <div>还差<span>{this.state.teamData.lack_member}</span>名队友即可获得宝箱 快分享给好友吧~</div>,
className: `landing-share-tip ${browser.isWeixin ? 'wechat' : ''}`,
closable: false,
close: () => {
sessionStorage.removeItem('showShareTip')
if (!user.hasError) {
this.getFollowStatus()
.then(isFollow => {
!isFollow && this.remind('create', getParam('treasure_code'), 1)
})
}
}
})
}
remind = (type = 'create', treasure_code, origin = 1) => {
const {user} = this.props
http.get(`${API["base-api"]}/wx/user_temporary_qrcode/${user.data.uid}`)
.then(res => {
const {data} = res.data
return data.url
}).then(text => {
QRCode.toDataURL(text)
.then(url => {
Popup({
title: '提醒服务',
className: 'landing-remind',
closeIcon: this.closeIcon,
content: <div>
<div className="des">{type === 'create' ? '有好友加入队伍后第一时间通知我~' : '获得宝箱时第一时间通知我~'}</div>
<img src={url} alt=""/>
{browser.isAndroidApp ? <div>
<button className={'android-qr-code-btn'} onClick={this.saveImage.bind(this, text)}>保存二维码</button>
</div> : null}
<div className="des">长按识别/扫码 关注【七月在线】服务号即可预约</div>
</div>,
close: () => {
// history.replace(`${match.path}?treasure_code=${treasure_code}&origin=1`)
sessionStorage.setItem('closedRemind', '1')
this.fetchPageData({
treasure_code,
origin
})
}
})
})
})
}
showOpenInBrowserTip = () => {
Popup({
title: <div>点击右上角,选择“在浏览器打开”</div>,
className: 'landing-open-in-browser-tip',
closable: false,
})
}
createMyTeam = () => {
if (this.requiredLogin()) {
return
}
http.get(`${API.home}/sys/treasure/createMyTeam`)
.then(res => {
const {data, code, msg} = res.data
if (code == 200) {
showShareTip = () => {
const {user, location} = this.props
if (browser.isAndroidApp || browser.isIOSApp) {
SendMessageToApp('toShare', {
title: this.shareTitle,
desc: this.shareDesc,
link: `${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=2`,
imgUrl: 'https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/wx-share-icon.png'
})
this.createTeamSuccess(data)
} else {
this.shareTipPopup = Popup({
title: <div>还差<span>{this.state.teamData.lack_member}</span>名队友即可获得宝箱 快分享给好友吧~</div>,
className: `landing-share-tip ${browser.isWeixin ? 'wechat' : ''}`,
closable: false,
close: () => {
sessionStorage.removeItem('showShareTip')
if (!user.hasError) {
this.getFollowStatus()
.then(isFollow => {
!isFollow && this.remind('create', getParam('treasure_code'), 1)
})
}
}
})
Toast.info(msg, 2, null, false)
}
})
}
requiredLogin = () => {
const {history, user, location} = this.props
if (user.hasError) {
if (browser.isWeixin) {
window.location.assign(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=${encodeURIComponent(`${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=${getParam('origin')}&aa=bb`)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`)
} else {
history.push('/passport')
}
return true
}
showOpenInBrowserTip = () => {
Popup({
title: <div>点击右上角,选择“在浏览器打开”</div>,
className: 'landing-open-in-browser-tip',
closable: false,
})
}
toSquare = () => {
const {history} = this.props
if (!getParam('version')) {
window.location.assign('https://m.julyedu.com/year/yearindex')
} else {
window.location.assign('https://m.julyedu.com/year/yearindex?version=' + getParam('version'))
}
createMyTeam = () => {
if (this.requiredLogin()) {
return
}
render() {
const {
teamData: {
head_img,
member,
status,
lack_member,
team_num,
prize_info,
my_team,
standby_plan
},
activityEnd,
isCaptain,
isLoading
} = this.state
/*
* status
* 1-自己的队伍
* 2-未加入队伍
* 3-已加入当前队伍
* 4-已加入别的队伍
* */
return (
<div id={'landing'}>
{
!(standby_plan && browser.isWeixin) &&
<div id="to-square">
<a href='javascript:' onClick={this.toSquare}>前往活动会场,享更多福利! >></a>
</div>
}
http.get(`${API.home}/sys/treasure/createMyTeam`)
.then(res => {
const {data, code, msg} = res.data
if (code == 200) {
this.createTeamSuccess(data)
} else {
Toast.info(msg, 2, null, false)
}
})
}
requiredLogin = () => {
const {history, user, location} = this.props
if (user.hasError) {
if (browser.isWeixin) {
window.location.assign(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=${encodeURIComponent(`${window.location.origin}${location.pathname}?treasure_code=${getParam('treasure_code')}&origin=${getParam('origin')}&aa=bb`)}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`)
} else {
history.push('/passport')
}
return true
}
}
toSquare = () => {
const {history} = this.props
if (!getParam('version')) {
window.location.assign('https://m.julyedu.com/year/yearindex')
} else {
window.location.assign('https://m.julyedu.com/year/yearindex?version=' + getParam('version'))
}
}
render() {
const {
teamData: {
head_img,
member,
status,
lack_member,
team_num,
prize_info,
my_team,
standby_plan
},
activityEnd,
isCaptain,
isLoading
} = this.state
/*
* status
* 1-自己的队伍
* 2-未加入队伍
* 3-已加入当前队伍
* 4-已加入别的队伍
* */
return (
<div id={'landing'}>
<BoxContainer>
<i className={`snow-deco ${standby_plan && browser.isWeixin ? 'standby' : ''}`}/>
{
standby_plan && browser.isWeixin &&
<>
<WhiteSpace className={'standby-space'}/>
<div className={'standby-tip'}>点击右上角,选择“在浏览器打开”</div>
</>
}
<img className='main-avatar'
src={head_img} alt=""/>
{
status === 1
? <div className="des">邀请好友加入队伍,开宝箱领取丰厚奖品~</div>
: <div className="des">加入我的<span>{team_num}</span>号队伍,一起开宝箱领取丰厚奖品~</div>
}
<Prizes showSystemNotices={false}/>
<BulletScreen/>
<div className="group">
<ul className={'member'}>
{
member && member.length &&
new Array(5).fill('a').map((item, index) => {
const _member = member[index]
return (
_member
? <li key={index}>
<div className='avatar-wrapper'>
{
_member.is_captain ? <sup>队长</sup> : null
}
<img
src={_member.head_img}
alt=""
className="avatar"/>
</div>
</li>
:
<li key={index}>
<div className="avatar-wrapper">
<img
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/default-member-avatar.png"
alt=""
className="avatar"/>
</div>
</li>
)
})
}
</ul>
<ul className={'bars'}>
{
member && member.length &&
new Array(5).fill('a').map((item, index) => {
return (
<li key={index} className={`${index < member.length ? 'active' : ''}`}/>
)
})
}
</ul>
{
status === 1 && !activityEnd &&
<>
<div className="group-des">
还差 {lack_member} 名队员即可获得当前宝箱哦~
</div>
{
!(standby_plan && browser.isWeixin) &&
<div id="to-square">
<a href='javascript:' onClick={this.toSquare}>前往活动会场,享更多福利! >></a>
</div>
!(standby_plan && browser.isWeixin) &&
<button type='button' className={'invite-btn'} onClick={this.showShareTip}>
邀请好友加入队伍
</button>
}
<BoxContainer>
<i className={`snow-deco ${standby_plan && browser.isWeixin ? 'standby' : ''}`}/>
{
standby_plan && browser.isWeixin &&
<>
<WhiteSpace className={'standby-space'}/>
<div className={'standby-tip'}>点击右上角,选择“在浏览器打开”</div>
</>
}
<img className='main-avatar'
src={head_img} alt=""/>
{
status === 1
? <div className="des">邀请好友加入队伍,开宝箱领取丰厚奖品~</div>
: <div className="des">加入我的<span>{team_num}</span>号队伍,一起开宝箱领取丰厚奖品~</div>
}
<Prizes showSystemNotices={false}/>
<BulletScreen/>
<div className="group">
<ul className={'member'}>
{
member && member.length &&
new Array(5).fill('a').map((item, index) => {
const _member = member[index]
return (
_member
? <li key={index}>
<div className='avatar-wrapper'>
{
_member.is_captain ? <sup>队长</sup> : null
}
<img
src={_member.head_img}
alt=""
className="avatar"/>
</div>
</li>
:
<li key={index}>
<div className="avatar-wrapper">
<img
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/default-member-avatar.png"
alt=""
className="avatar"/>
</div>
</li>
)
})
}
</ul>
<ul className={'bars'}>
{
member && member.length &&
new Array(5).fill('a').map((item, index) => {
return (
<li key={index} className={`${index < member.length ? 'active' : ''}`}/>
)
})
}
</ul>
{
status === 1 && !activityEnd &&
<>
<div className="group-des">
还差 {lack_member} 名队员即可获得当前宝箱哦~
</div>
{
!(standby_plan && browser.isWeixin) &&
<button type='button' className={'invite-btn'} onClick={this.showShareTip}>
邀请好友加入队伍
</button>
}
</>
}
{
status === 3 && !activityEnd &&
<>
<div className="group-des">
<p>已加入当前队伍</p>
{
my_team['is_team']
? <p>您的{my_team['team_num']}号队伍还差{my_team['lack_member']}人即可获得宝箱~</p>
: <p>自己当队长,宝箱内有专属奖品哦~</p>
}
</div>
{
!(standby_plan && browser.isWeixin) && (my_team['is_team'] ?
<button type='button' className={'invite-btn'}
onClick={this.inviteMembers.bind(this, my_team['treasure_code'])}>
继续组队
</button>
: <button type='button' className={'invite-btn'} onClick={this.createMyTeam}>
创建我的队伍
</button>)
}
</>
}
{
status === 4 && !activityEnd &&
<>
<div className="group-des">
<p>已加入其他队伍</p>
{
my_team['is_team']
? <p>您的{my_team['team_num']}号队伍还差{my_team['lack_member']}人即可获得宝箱~</p>
: <p>自己当队长,宝箱内有专属奖品哦~</p>
}
</div>
{
!(standby_plan && browser.isWeixin) && (my_team['is_team'] ?
<button type='button' className={'invite-btn'}
onClick={this.inviteMembers.bind(this, my_team['treasure_code'])}>
继续组队
</button>
: <button type='button' className={'invite-btn'} onClick={this.createMyTeam}>
创建我的队伍
</button>)
}
</>
}
{
!(standby_plan && browser.isWeixin) && status === 2 && !activityEnd &&
< button type='button' className={'invite-btn'} onClick={this.joinTeam}>
同意加入队伍
</button>
}
{
prize_info && prize_info.length !== 0 && !activityEnd && !isCaptain && <div className="certainly-prompt">
{prize_info[0].stage_no}个宝箱必中 {prize_info[0].name}
</div>
}
{
activityEnd &&
<div className='activity-end'>
<div>活动已结束</div>
<div>关注【七月在线】服务号了解更多活动信息</div>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/right_weixin.png" alt=""/>
<div>长按识别 / 微信扫码</div>
</div>
}
</div>
</BoxContainer>
</>
}
{
status === 3 && !activityEnd &&
<>
<div className="group-des">
<p>已加入当前队伍</p>
{
my_team['is_team']
? <p>您的{my_team['team_num']}号队伍还差{my_team['lack_member']}人即可获得宝箱~</p>
: <p>自己当队长,宝箱内有专属奖品哦~</p>
}
</div>
{
!isCaptain &&
<>
<WhiteSpace size={'xl'}/>
<YearCourse getSum={() => {
}}/>
</>
!(standby_plan && browser.isWeixin) && (my_team['is_team'] ?
<button type='button' className={'invite-btn'}
onClick={this.inviteMembers.bind(this, my_team['treasure_code'])}>
继续组队
</button>
: <button type='button' className={'invite-btn'} onClick={this.createMyTeam}>
创建我的队伍
</button>)
}
</>
}
{
status === 4 && !activityEnd &&
<>
<div className="group-des">
<p>已加入其他队伍</p>
{
my_team['is_team']
? <p>您的{my_team['team_num']}号队伍还差{my_team['lack_member']}人即可获得宝箱~</p>
: <p>自己当队长,宝箱内有专属奖品哦~</p>
}
</div>
{
isLoading && <div className="loading" style={{width: window.innerWidth + 'px', height: window.innerHeight + 'px'}}>
<FadeLoader
color={'#fff'}
loading={isLoading}
/>
</div>
!(standby_plan && browser.isWeixin) && (my_team['is_team'] ?
<button type='button' className={'invite-btn'}
onClick={this.inviteMembers.bind(this, my_team['treasure_code'])}>
继续组队
</button>
: <button type='button' className={'invite-btn'} onClick={this.createMyTeam}>
创建我的队伍
</button>)
}
</div>
)
}
</>
}
{
!(standby_plan && browser.isWeixin) && status === 2 && !activityEnd &&
< button type='button' className={'invite-btn'} onClick={this.joinTeam}>
同意加入队伍
</button>
}
{
prize_info && prize_info.length !== 0 && !activityEnd && !isCaptain && <div className="certainly-prompt">
{prize_info[0].stage_no}个宝箱必中 {prize_info[0].name}
</div>
}
{
activityEnd &&
<div className='activity-end'>
<div>活动已结束</div>
<div>关注【七月在线】服务号了解更多活动信息</div>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/right_weixin.png" alt=""/>
<div>长按识别 / 微信扫码</div>
</div>
}
</div>
</BoxContainer>
{
!isCaptain &&
<>
<WhiteSpace size={'xl'}/>
<YearCourse getSum={() => {
}}/>
</>
}
{
isLoading && <div className="loading" style={{width: window.innerWidth + 'px', height: window.innerHeight + 'px'}}>
<FadeLoader
color={'#fff'}
loading={isLoading}
/>
</div>
}
</div>
)
}
}
export default connect(
state => ({user: state.user}),
null
state => ({user: state.user}),
null
)(Landing)
......@@ -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>
)
}
......
......@@ -75,7 +75,7 @@ class YearCourse extends Component {
this.getTeamInfo()
this.getLength()
this.fetchBigCourse()
this.fetchFreeCourse()
// this.fetchFreeCourse()
this.fetchGroupCourse()
// AI之路-基础
this.fetchAICourse('one', false)
......@@ -92,7 +92,7 @@ class YearCourse extends Component {
this.getTeamInfo()
this.getLength()
this.fetchBigCourse()
this.fetchFreeCourse()
// this.fetchFreeCourse()
this.fetchGroupCourse()
// AI之路-基础
this.fetchAICourse('one')
......@@ -221,7 +221,7 @@ class YearCourse extends Component {
toQQque = () => {
if (!getParam('version')) {
location.href = 'https://q.url.cn/AB8aue?_type=wpa&qidian=true'
qimoChatClick()
} else {
SendMessageToApp("toQQ", 'https://q.url.cn/AB8aue?_type=wpa&qidian=true')
}
......@@ -610,44 +610,81 @@ class YearCourse extends Component {
<div className={'year-index-course'}>
{/*浮框*/}
{
Number(treasureStage) === 1 &&
<div className='nav-right'>
<span onClick={this.toBoxList} className='nav-right__link'>
未开宝箱
{
removable > 0 &&
<i className="nav-right__number">{removable}</i>
}
</span>
<a onClick={() => this.toYearWish()} className='nav-right__link'>
心愿单
{
sum > 0 &&
<i className="nav-right__number">{sum}</i>
}
</a>
</div>
}
{/*{*/}
{/*Number(treasureStage) === 1 &&*/}
{/*<div className='nav-right'>*/}
{/*<span onClick={this.toBoxList} className='nav-right__link'>*/}
{/*未开宝箱*/}
{/*{*/}
{/*removable > 0 &&*/}
{/*<i className="nav-right__number">{removable}</i>*/}
{/*}*/}
{/*</span>*/}
{/*<a onClick={() => this.toYearWish()} className='nav-right__link'>*/}
{/*心愿单*/}
{/*{*/}
{/*sum > 0 &&*/}
{/*<i className="nav-right__number">{sum}</i>*/}
{/*}*/}
{/*</a>*/}
{/*</div>*/}
{/*}*/}
{
Number(treasureStage) === 0 &&
<div className='nav-right-wish nav-right'>
<a onClick={() => this.toYearWish()} className='nav-right__link'>
心愿单
{
sum > 0 &&
<i className="nav-right__number">{sum}</i>
}
</a>
</div>
}
{/*{*/}
{/*Number(treasureStage) === 0 &&*/}
{/*<div className='nav-right-wish nav-right'>*/}
{/*<a onClick={() => this.toYearWish()} className='nav-right__link'>*/}
{/*心愿单*/}
{/*{*/}
{/*sum > 0 &&*/}
{/*<i className="nav-right__number">{sum}</i>*/}
{/*}*/}
{/*</a>*/}
{/*</div>*/}
{/*}*/}
<div className='nav-right-wish nav-right'>
<span onClick={this.toBoxList} className='nav-right__link'>未开宝箱
{
removable > 0 &&
<i className="nav-right__number">{removable}</i>
}
</span>
</div>
<CommonContainer title='驰援武汉 注册即送' id='year-wuhan'>
<div className="course-box course-conter">
<div className="course-item">
<span>注册即送</span>
<a href="/detail?id=224">
<img
src="http://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/75d1c3cef5.png"
alt=""/>
</a>
</div>
<div className="course-item">
<span>注册即送</span>
<a href="/detail?id=206">
<img
src="https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/b001fac23f.png"
alt=""/>
</a>
</div>
<div className="course-item">
<span>注册即送</span>
<a href="/detail?id=230">
<img
src="https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/d0fb0a479b.png"
alt=""/>
</a>
</div>
</div>
</CommonContainer>
{/* 重磅好课 */}
<CommonContainer title='重磅好课' id='year-course'>
<CommonContainer title='重磅好课限时免费试听' id='year-course'>
{
(bigcourse.course && bigcourse.course.length > 0) &&
<>
......@@ -662,18 +699,30 @@ class YearCourse extends Component {
>
<div className="coupon-course__footer">
{
item.is_buy == 1 &&
<a
onClick={() => this.toCourse(item.course_id)}
className="btn to-study"
>开始学习</a>
item.is_vip == 1 &&
<a className="btn big-course" onClick={() => this.toQQque()}>
首购¥99
</a>
}
{
item.is_buy == 0 &&
<a
onClick={() => this.toQQque()}
className="btn big-course"
>免费试听</a>
item.is_vip == 0 &&
<>
{
item.is_buy == 1 &&
<a
onClick={() => this.toCourse(item.course_id)}
className="btn to-study"
>开始学习</a>
}
{
item.is_buy == 0 &&
<a
onClick={() => this.toQQque()}
className="btn big-course"
>{item.is_audition == 1 ? '免费试听': '抢团报名额'}</a>
}
</>
}
</div>
</CourseItem>
......@@ -692,63 +741,63 @@ class YearCourse extends Component {
</CommonContainer>
{/* 人气好课免费学 */}
{
Number(stage) !== 3 &&
<CommonContainer title='人气好课免费学' id='year-free'>
<a className='boss__add' href="/active/givecourse?activename=shuangdan">更有42本技术书籍免费送</a>
{
(freecourse.course && freecourse.course.length > 0) &&
<>
<div className='freecourse course-box'>
{
freecourse.course.map(item => (
<CourseItem
image={item.image_name}
key={item.course_id}
id={item.course_id}
toCourse={this.toCourse}
>
<div className="coupon-course__footer">
{
item.is_buy == 1 &&
<a
onClick={() => this.toCourse(item.course_id)}
className="btn to-study"
>开始学习</a>
}
{
item.is_buy == 0 &&
<a
onClick={() => this.freeStudy()}
className="btn free-study"
>点击免费学</a>
}
<p className="free-course-num">
已送出<span>{item.act_num}</span>个课
</p>
</div>
</CourseItem>
))
}
</div>
{
(freecourse.courseList && freecourse.courseList.length > 4) &&
<button
className="more-button"
onClick={() => this.handleToMore('freecourse')}
>{freecourse.isMore ? '查看更多' : '收起'}</button>
}
</>
}
</CommonContainer>
}
{/*{*/}
{/*Number(stage) !== 3 &&*/}
{/*<CommonContainer title='人气好课免费学' id='year-free'>*/}
{/*<a className='boss__add' href="/active/givecourse?activename=shuangdan">更有42本技术书籍免费送</a>*/}
{/*{*/}
{/*(freecourse.course && freecourse.course.length > 0) &&*/}
{/*<>*/}
{/*<div className='freecourse course-box'>*/}
{/*{*/}
{/*freecourse.course.map(item => (*/}
{/*<CourseItem*/}
{/*image={item.image_name}*/}
{/*key={item.course_id}*/}
{/*id={item.course_id}*/}
{/*toCourse={this.toCourse}*/}
{/*>*/}
{/*<div className="coupon-course__footer">*/}
{/*{*/}
{/*item.is_buy == 1 &&*/}
{/*<a*/}
{/*onClick={() => this.toCourse(item.course_id)}*/}
{/*className="btn to-study"*/}
{/*>开始学习</a>*/}
{/*}*/}
{/*{*/}
{/*item.is_buy == 0 &&*/}
{/*<a*/}
{/*onClick={() => this.freeStudy()}*/}
{/*className="btn free-study"*/}
{/*>点击免费学</a>*/}
{/*}*/}
{/*<p className="free-course-num">*/}
{/*已送出<span>{item.act_num}</span>个课程*/}
{/*</p>*/}
{/*</div>*/}
{/*</CourseItem>*/}
{/*))*/}
{/*}*/}
{/*</div>*/}
{/*{*/}
{/*(freecourse.courseList && freecourse.courseList.length > 4) &&*/}
{/*<button*/}
{/*className="more-button"*/}
{/*onClick={() => this.handleToMore('freecourse')}*/}
{/*>{freecourse.isMore ? '查看更多' : '收起'}</button>*/}
{/*}*/}
{/*</>*/}
{/*}*/}
{/*</CommonContainer>*/}
{/*}*/}
{/* stage,活动阶段 0-不在活动时间 1-预热 2-正式 3-返场 */}
{
Number(stage) !== 1 &&
<>
{/* 精品好课1分开抢 */}
<CommonContainer title='精品好课1分开抢' id='year-group'>
<CommonContainer title='精品好课1分' id='year-group'>
{
(groupcourse.course && groupcourse.course.length > 0) &&
<>
......@@ -850,7 +899,7 @@ class YearCourse extends Component {
</CommonContainer>
{/* 好课价到,等你抄底 */}
<CommonContainer title='好课价到,等你抄底' id='year-discount'>
<CommonContainer title='“薪”年价到,等你抄底!' id='year-discount'>
{
[1, 2, 3, 4].map(item => {
const keys = this.formatTitleAndKey(item);
......@@ -891,7 +940,7 @@ class YearCourse extends Component {
<span className="price-label">¥</span>
<span className="new-price">{val.original_price}</span>
<span className="old-price">
<s>¥{val.original_price}</s>
<s>¥{val.price1}</s>
</span>
</div>
</>
......
......@@ -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 {
......
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
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;
}
}
......@@ -37,236 +37,266 @@ const Activity = loadable(() => import(/* webpackChunkName: 'Activity' */'@/comp
const Invite = loadable(() => import(/* webpackChunkName: 'Invite' */'@/components/activity/1111/invite'))
const NewVip = loadable(() => import(/* webpackChunkName: 'Invite' */'@/components/vip/index'))
export default [
{
path: '/',
exact: true,
component: Index
},
{
path: '/vip/newvip',
component: NewVip
},
{
path: '/classify',
component: Classify
},
{
path: '/study',
component: Study
},
{
path: '/my',
component: My
},
{
path: '/myedit',
component: MyEdit,
isPrivate: true
},
{
path: '/courselist',
component: CourseList
},
{
path: '/preferential',
component: Preferential
},
{
path: '/search',
exact: true,
component: Search
},
{
path: '/search-result',
component: SearchResult
},
{
path: '/order',
component: Order,
isPrivate: true
},
{
path: '/orderinfo',
component: Orderinfo
},
{
path: '/detail',
component: Detail
},
{
path: '/getDetail',
component: Detail
},
{
path: '/examination',
component: Examination
},
{
path: '/coupons',
component: Coupons,
isPrivate: true
},
{
path: '/shopcart',
component: ShopCart,
isPrivate: true
},
{
path: '/bargain-middle-page',
component: BargainMiddlePage
},
{
path: '/passport',
component: Passport
},
{
path: '/play',
component: Video,
isPrivate: true
},
{
path: '/scholarship',
component: Scholarship
},
{
path: '/document',
component: DrawDocument
},
{
path: '/shareposter',
component: sharePoster,
isPrivate: true
},
{
path: '/myorders',
component: myOrders,
isPrivate: true
},
{
path: '/purchased',
component: Purchased,
isPrivate: true
},
{
path: '/payOrder',
component: PayOrder
},
{
path: '/campTest',
component: CampTest
},
{
path: '/campResolve',
component: CampResolve
},
// 分享领红包课程列表页
{
path: '/ShareCourse',
component: ShareCourse
},
{
path: '/',
exact: true,
component: Index
},
{
path: '/vip/newvip',
component: NewVip
},
{
path: '/classify',
component: Classify
},
{
path: '/study',
component: Study
},
{
path: '/my',
component: My
},
{
path: '/myedit',
component: MyEdit,
isPrivate: true
},
{
path: '/courselist',
component: CourseList
},
{
path: '/preferential',
component: Preferential
},
{
path: '/search',
exact: true,
component: Search
},
{
path: '/search-result',
component: SearchResult
},
{
path: '/order',
component: Order,
isPrivate: true
},
{
path: '/orderinfo',
component: Orderinfo
},
{
path: '/detail',
component: Detail
},
{
path: '/getDetail',
component: Detail
},
{
path: '/examination',
component: Examination
},
{
path: '/coupons',
component: Coupons,
isPrivate: true
},
{
path: '/shopcart',
component: ShopCart,
isPrivate: true
},
{
path: '/bargain-middle-page',
component: BargainMiddlePage
},
{
path: '/passport',
component: Passport
},
{
path: '/play',
component: Video,
isPrivate: true
},
{
path: '/scholarship',
component: Scholarship
},
{
path: '/document',
component: DrawDocument
},
{
path: '/shareposter',
component: sharePoster,
isPrivate: true
},
{
path: '/myorders',
component: myOrders,
isPrivate: true
},
{
path: '/purchased',
component: Purchased,
isPrivate: true
},
{
path: '/payOrder',
component: PayOrder
},
{
path: '/campTest',
component: CampTest
},
{
path: '/campResolve',
component: CampResolve
},
// 分享领红包课程列表页
{
path: '/ShareCourse',
component: ShareCourse
},
// 区号
{
path: '/country',
component: Country
},
{
path: '/togroup',
component: ToGroup
},
{
path: '/aist-share',
component: loadable(() => import(/* webpackChunkName: 'aist-share'*/'@/components/share-page/aist-share'))
},
{
path: '/blessingRank',
component: loadable(() => import(/* webpackChunkName: 'blessing-rank' */'@/components/blessingRank/index'))
},
{
path: '/blessingPreheat',
component: loadable(() => import(/* webpackChunkName: 'blessing-preheat' */'@/components/blessingPreheat/index'))
},
{
path: '/blessingGetPrize',
component: loadable(() => import(/* webpackChunkName: 'blessing-getPrize' */'@/components/blessingGetPrize/index'))
},
{
path: '/prize-winner-list',
component: loadable(() => import(/* webpackChunkName: 'prize-winner-list' */'@/components/activity/1111/prize-winner-list'))
},
//定金订单页面
{
path: '/deposit-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-order' */ '@components/order/deposit/deposit-order'))
},
//定金支付页面
{
path: '/deposit-pay-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-pay-order' */ '@components/order/deposit/deposit-pay-order'))
},
//尾款支付页面
{
path: '/final-deposit-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-pay-order' */ '@components/order/deposit/final-order'))
},
// 定金支付之后
{
path: '/expand/callback',
component: ExpandCallback
},
// 定金-邀请好友助力
{
path: '/expand/index',
component: ExpandShare
},
{
path: '/toAppDemo',
component: loadable(() => import(/* webpackChunkName: 'aist-share'*/'@/components/blessingPreheat/toAppDemo'))
},
{
path: '/activity',
exact: true,
component: Activity,
},
{
path: '/invite',
component: Invite,
},
// python 小课页面
{
path: '/python',
component: loadable(() => import(/* webpackChunkName: 'python-class'*/'@/components/python'))
},
{
path: '/pythonShare',
component: loadable(() => import('@/components/pythonShare'))
},
{
path: '/pythonStudy',
component: loadable(() => import('@/components/python/pythonStudy'))
},
// 区号
{
path: '/country',
component: Country
},
{
path: '/togroup',
component: ToGroup
},
{
path: '/aist-share',
component: loadable(() => import(/* webpackChunkName: 'aist-share'*/'@/components/share-page/aist-share'))
},
{
path: '/blessingRank',
component: loadable(() => import(/* webpackChunkName: 'blessing-rank' */'@/components/blessingRank/index'))
},
{
path: '/blessingPreheat',
component: loadable(() => import(/* webpackChunkName: 'blessing-preheat' */'@/components/blessingPreheat/index'))
},
{
path: '/blessingGetPrize',
component: loadable(() => import(/* webpackChunkName: 'blessing-getPrize' */'@/components/blessingGetPrize/index'))
},
{
path: '/prize-winner-list',
component: loadable(() => import(/* webpackChunkName: 'prize-winner-list' */'@/components/activity/1111/prize-winner-list'))
},
//定金订单页面
{
path: '/deposit-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-order' */ '@components/order/deposit/deposit-order'))
},
//定金支付页面
{
path: '/deposit-pay-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-pay-order' */ '@components/order/deposit/deposit-pay-order'))
},
//尾款支付页面
{
path: '/final-deposit-order',
component: loadable(() => import(/* webpackChunkName: 'deposit-pay-order' */ '@components/order/deposit/final-order'))
},
// 定金支付之后
{
path: '/expand/callback',
component: ExpandCallback
},
// 定金-邀请好友助力
{
path: '/expand/index',
component: ExpandShare
},
{
path: '/toAppDemo',
component: loadable(() => import(/* webpackChunkName: 'aist-share'*/'@/components/blessingPreheat/toAppDemo'))
},
{
path: '/activity',
exact: true,
component: Activity,
},
{
path: '/invite',
component: Invite,
},
//双旦活动
{
path: '/activity/newyear-2019/landing',
component: loadable(() => import(/* webpackChunkName: 'newyear-2019-landing'*/ '@components/activity/newyear-2019/landing/index'))
},
//双旦活动
{
path: '/activity/newyear-2019/landing',
component: loadable(() => import(/* webpackChunkName: 'newyear-2019-landing'*/ '@components/activity/newyear-2019/landing/index'))
},
// 双旦活动预热页面
{
path: '/year/yearIndex',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/preheat/index'))
},
// 双旦活动预热页面
{
path: '/year/yearIndex',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/preheat/index'))
},
// 我的宝箱
{
path: '/year/yearTreasure',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/myTreasure/index'))
}
,
// 双旦心愿单
{
path: '/year/yearWish',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/year-wish/index'))
}
// 我的宝箱
{
path: '/year/yearTreasure',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/myTreasure/index'))
}
,
// 双旦心愿单
{
path: '/year/yearWish',
component: loadable(() => import(/* webpackChunkName: 'newyear-yearIndex' */ '@components/activity/newyear-2019/year-wish/index'))
},
// python 小课页面
{
path: '/python',
component: loadable(() => import(/* webpackChunkName: 'python-class'*/'@/components/python'))
},
{
path: '/pythonShare',
component: loadable(() => import('@/components/pythonShare'))
},
{
path: '/pythonStudy',
component: loadable(() => import('@/components/python/pythonStudy'))
},
// 赠一得一
{
path: '/active/givecourse',
component: loadable(() => import(/* activity-give-courses */'@components/activity/give-courses/index'))
},
{
path: '/active/assistance',
component: loadable(() => import(/* activity-give-courses-assistance */'@components/activity/give-courses/assistance/index'))
},
{
path: '/active/to',
component: loadable(() => import(/* activity-give-courses-share */'@components/activity/give-courses/share-content/index'))
},
{
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'))
},
]
......@@ -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