Commit 75644ea3 by xuzhenghua

春节活动

parent 46ec3244
......@@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
importScripts(
"/precache-manifest.0f0720c31d8e92a4b165c6f098a1b6f1.js"
"/precache-manifest.ad68e7bc932a1975e677804a8e6f7f83.js"
);
workbox.clientsClaim();
......
......@@ -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"
},
......@@ -3099,9 +3099,9 @@
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"base64id": {
"version": "1.0.0",
......
......@@ -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",
......
......@@ -9,6 +9,7 @@ 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数据
......@@ -69,7 +70,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')) {
......@@ -306,10 +307,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')
......@@ -324,7 +325,12 @@ class App extends Component {
render() {
return <Routes/>
return <>
<Routes/>
<Link className={'year19-index'} to="/year/yearindex">
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/gift-package.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
#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;
}
}
}
......@@ -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 && (
......
......@@ -39,7 +39,7 @@ export default class index extends Component {
_this.loginInfo(result)
}
_this.getStage()
document.title = '七月在线年终大回馈,幸运宝箱随你开,100%有奖!人气好课免费学,精品课程1分抢!';
document.title = '七月在线新春献礼,幸运宝箱随你开,100%有奖!“薪”年好课免费学,精品课程1分秒!';
}
// 获取app登录数据
......
......@@ -13,7 +13,7 @@ class TreasureNav extends Component {
navs: [
{
id: 'year-live',
name: '大咖直播'
name: '好课免费学'
},
{
id: 'year-treasure',
......@@ -23,10 +23,10 @@ class TreasureNav extends Component {
id: 'year-course',
name: '重磅好课'
},
{
id: 'year-free',
name: '免费学'
},
// {
// id: 'year-free',
// name: '免费学'
// },
{
id: 'year-group',
name: '1分拼团'
......
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)
......@@ -48,13 +48,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'
}
]
......
......@@ -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)
......
import React, {Component} from 'react'
import {http, getParam, browser} from '@/utils'
import {http, getParam, browser, SendMessageToApp} from '@/utils'
import PythonDes from './pythomDes'
import PythonStudy from './pythonStudy'
import {connect} from "react-redux"
......@@ -61,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
......@@ -98,18 +100,16 @@ class Python extends Component {
backwardVersion: true,
isPay: 0,
})
}else{
this.setState({
backwardVersion: false,
isPay: data.course_info.is_pay
})
}
if(browser.isIOSApp && version < 380) { // ISO的低版本
}else if(browser.isIOSApp && version < 380) { // ISO的低版本
this.setState({
backwardVersion: true,
isPay: 0,
})
}else{
}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
......@@ -131,7 +131,14 @@ class Python extends Component {
return (
<div>
{
isPay === 0 && <PythonDes history={this.props.history} isAppUpdate={isAppUpdate} backwardVersion={backwardVersion} isPay={isPay}></PythonDes>
isPay === 0 && (
<PythonDes
backwardVersion={backwardVersion}
history={this.props.history}
isAppUpdate={isAppUpdate}
isPay={isPay}
/>
)
}
{
(isPay === 1 && !getParam('version')) && <PythonStudy isAppUpdate={isAppUpdate}/>
......
......@@ -35,7 +35,7 @@ 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)=>{
......
......@@ -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;
......
......@@ -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',
......@@ -96,7 +96,7 @@ class PythonDes extends Component {
},
],
payMoney: 0,
isOnline: false, //课程是否上架
isOnline: true, //课程是否上架
}
}
......@@ -121,7 +121,7 @@ class PythonDes extends Component {
}
toLearn = () => {
const {backwardVersion, isPay} = this.props;
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) {
......@@ -133,7 +133,7 @@ class PythonDes extends Component {
if(backwardVersion) {
Toast.info('当前版本不支持该课程模式,请升级到最新版本或前往PC端体验', 2)
} else {
SendMessageToApp("toLearn")
SendMessageToApp("toLearn", getParam('id'))
}
}
} else {
......@@ -149,7 +149,7 @@ class PythonDes extends Component {
}
toDetail = () => {
const {backwardVersion, isPay} = this.props;
const {backwardVersion} = this.props;
const id = getParam('id')
if (!getParam('version')) { // H5
http.get(`${API['base-api']}/m/cart/addtopreorder/[${id}]`).then((res) => {
......@@ -304,10 +304,18 @@ class PythonDes extends Component {
<Description list={desList} />
<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/>
{/* 试学体验 */}
......
......@@ -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,14 +83,14 @@ 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
});
wxShare({
title: isShare? `我已在【${params.course_name}】上运行了行代码了${params.code_lines}` : `我在${params.course_name}${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,
......
import React from 'react'
import WxErrImage from '@assets/image/wx_error.png'
import './wxerr.scss'
const WxErr = () => {
return (
<div className={'wxerr'}>
<img src={WxErrImage} alt=''/>
<p className="openLink" style={{
fontSize: `16px`,
marginTop: `40px`
}}>请在微信客户端打开链接</p>
</div>
)
}
export default WxErr
.wxerr{
margin-top: 40px;
display: flex;
flex-flow: column;
align-items: center;
.openLink{
font-size: 16px;
margin-top:40px;
}
}
......@@ -4,7 +4,7 @@ import jsCookie from 'js-cookie'
const accountLogin = user => dispatch => {
return http.post(`${API['passport-api']}/user_login`, {
return http.post(`${API['passport-api']}/m/login/accountLogin`, {
user_name: user.username,
password: encrypt(user.password),
is_encrypt: 1,
......@@ -15,7 +15,7 @@ const accountLogin = user => 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 +26,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,
......
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