Commit cde47848 by zhanghaozhe

Merge branch 'intelligent-recommend' into dev

parents f82e3c81 b8089100
......@@ -17,7 +17,7 @@ import AllCourseNavigation from "./all-course"
// const animateTypes = Swiper.animateTypes
@connect(state => ({
user: state.user
user: state.user,
}))
class Index extends Component {
constructor(props) {
......@@ -33,28 +33,28 @@ class Index extends Component {
{
'src': require('./image/freeclass_icon.png'),
'name': '公开课',
'href': '/study/free-course'
'href': '/study/free-course',
},
{
'src': require('./image/jingpin_icon.png'),
'name': '精品特惠',
'href': '/preferential'
'href': '/preferential',
},
{
'src': require('./image/zjxj_icon.png'),
'name': '赚奖学金',
'href': '/scholarship'
'href': '/scholarship',
},
{
'src': require('./image/mryt_icon.png'),
'name': '每日一题',
'href': '/examination'
'href': '/examination',
},
{
'src': require('./image/shequ_icon.png'),
'name': '社区',
'href': 'https://ask.julyedu.com'
}
'href': 'https://ask.julyedu.com',
},
],
}
}
......@@ -72,7 +72,7 @@ class Index extends Component {
this.setState({
banner: data.banner,
lives: data.lives,
modules
modules,
})
} else {
Toast.info(res.data.msg, 2)
......@@ -93,7 +93,7 @@ class Index extends Component {
this.setState({
isShow: true,
islive: true,
roomMess: item
roomMess: item,
})
} else {
window.location.href = `${window.location.href.includes('pre') ? 'http://www-pre.julyedu.com' : 'http://www.julyedu.com'}/live/m_room/${item.room_id}`
......@@ -119,15 +119,23 @@ class Index extends Component {
src="http://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/img/index/logo.png"
alt=""
/>
<CallApp
{/* <CallApp
className='to-app'
text='在APP打开'
/>
/>*/}
<div className="right">
<Link to={'/intelligent-recommend'}>
<span className={'intelligent-recommend-entry'}>
<i className={'mind-icon'}></i>
智能选课
</span>
</Link>
<i
className='iconfont iconiconfront- search'
onClick={this.toSearch.bind(this)}
/>
</div>
</div>
<div className='zw_height'></div>
......
......@@ -34,6 +34,29 @@
vertical-align: top;
}
.right {
float: right;
height: 100%;
}
.intelligent-recommend-entry {
float: left;
margin-right: 20px;
font-size: 14px;
color: #09f;
line-height: 24px;
.mind-icon {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 4px;
background: url("./image/mind-icon.png") no-repeat;
background-size: contain;
vertical-align: middle;
}
}
.search {
font-size: 22px !important;
float: right;
......@@ -88,6 +111,7 @@
.category {
width: 100%;
padding: 20px 15px 15px 15px;
.swiper-container {
height: 106px !important;
margin-top: 15px;
......@@ -263,7 +287,6 @@
}
/*
横向滚动
*/
......
import React, { Component } from 'react';
import './index.scss'
import { Link } from "react-router-dom";
import { http } from "@/utils"
import { Toast } from "antd-mobile";
import { connect } from "react-redux";
import { WithFullSize } from "@/HOCs"
import { isEmpty } from "lodash";
import { addMessage, addResult, reselect } from './store'
const messageType = {
SYSTEM_MESSAGE: 1,
USER_MESSAGE: 2,
OPTIONS: 3,
}
class IntelligentRecommend extends Component {
state = {
showAnalyzing: false,
systemAvatar: 'https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/home/avatar_20191104.png',
options: {
data: [],
messageId: 0,
},
next: {
oid: 0,
rid: 0,
},
}
componentDidMount() {
if (!this.props.intelligentRecommend.processing.length) {
this.getMessage()
}
}
handleSelect = (item) => {
this.props.addMessage({type: messageType.USER_MESSAGE, content: item.describe, id: item.id})
this.setState(state => ({
options: {data: [], messageId: 0},
next: {
...state.next,
oid: item.id,
},
}), () => {
this.getMessage()
})
}
getMessage = () => {
const {oid, rid} = this.state.next
http.get(`${API.home}/sys/icc/communicate/${oid}/${rid}`)
.then(res => {
const {data, code, msg} = res.data
if (code === 200) {
if (data.answer_type === 1) {
this.setState({
showAnalyzing: true,
}, () => {
setTimeout(() => {
this.setState({showAnalyzing: false})
this.props.addResult({...data})
}, Math.random() * 1000 + 2000)
})
} else {
const message = {
type: messageType.SYSTEM_MESSAGE,
content: data.contents.find(item => item.is_question),
}
const optionsData = data.contents.filter(item => !item.is_question)
this.props.addMessage(message)
this.setState({
options: {
messageId: message.content.id,
data: optionsData,
},
next: {rid: data.rid},
})
}
} else {
Toast.fail(msg)
}
})
}
consult = (e) => {
e.preventDefault()
http.post(`${API.home}/sys/icc/consult`, {
rid: this.state.result.rid,
}).finally(() => {
window.location.href = e.target.getAttribute('href')
})
}
render() {
const {showAnalyzing, systemAvatar, options} = this.state
const {user, intelligentRecommend: {result, processing}, reselect} = this.props
const recommends = result.contents
return (
<div id={'intelligent-recommend'}>
<div className="head">
<div>
<div className="go-back">
<i className='iconfont iconiconfront-68' onClick={this.goBack}></i>
</div>
<div className={'title'}>七月在线智能选课</div>
</div>
<button onClick={() => {
this.setState({
next: {oid: 0, rid: 0},
options: {messageId: 0, data: []},
}, () => {
reselect()
this.getMessage()
});
}}>重新选课
</button>
</div>
<div className="dialog-box">
{
!!processing.length && processing.map((item, index) => {
switch (item.type) {
case messageType.SYSTEM_MESSAGE:
return (
<React.Fragment key={index}>
<Message text={item.content.describe} identity={'system'} avatar={systemAvatar}/>
{
options.messageId === item.content.id &&
<Options options={options.data} handleSelect={this.handleSelect}/>
}
</React.Fragment>
)
case messageType.USER_MESSAGE:
return <Message text={item.content} identity={'user'} avatar={user.data.avatar} key={index + 2}/>
}
})
}
{
showAnalyzing && <div className="analyzing">努力分析中...</div>
}
</div>
{
!isEmpty(recommends) && <div className="result">
<div className="title">根据您目前的情况,推荐结果如下</div>
<div className="obtained">
<div className={'subtitle'} dangerouslySetInnerHTML={{__html: recommends.skill_title}}></div>
<div className={'skill'} dangerouslySetInnerHTML={{__html: recommends.skill_desc}}></div>
</div>
<div className="obtained">
<div className={'subtitle'} dangerouslySetInnerHTML={{__html: recommends.project_title}}></div>
<div className={'skill project'} dangerouslySetInnerHTML={{__html: recommends.project_desc}}></div>
{/*<ul>
<li className={'skill project'}>·技能描述、技能描述</li>
<li className={'skill project'}>·工作及项目描述、工作及项目描述、项目描述</li>
</ul>*/}
</div>
<div className={'salary-section'}>
<div className={'subtitle'} dangerouslySetInnerHTML={{__html: recommends.salary_title}}></div>
<div className={'salary'} dangerouslySetInnerHTML={{__html: recommends.salary_desc}}></div>
</div>
<div className="recommends">
<div>想获得以上技能,向您推荐:</div>
<ul className={'courses'}>
{
!!recommends.courses.length && recommends.courses.map(item => {
return <li className={'course'} key={item.course_id} onClick={(e) => {
e.target.nodeName.toLowerCase() !== 'a' && this.props.history.push(`/detail?id=${item.course_id}`)
}}>
<div className="cover">
<img src={item.img_url}
alt=""/>
</div>
<div className="info">
<div className={'title'}>{item.recmd_title}</div>
<div className={'des'}>{item.recmd_desc}</div>
<div className="bar">
{
item.c_type === 1 && <React.Fragment key={item.course_id}>
<div className="prices">
<span className={'price'}>{item.price_sale}</span>
<span className={'old-price'}>{item.price_original}</span>
</div>
<Link className={'register'} to={`/detail?id=${item.course_id}`}>{item.second_btn}</Link>
</React.Fragment>
}
{
item.c_type === 2 && <React.Fragment key={item.course_id}>
<a href="http://q.url.cn/s/Vbkup6m?_type=wpa" className={'contact'}
onClick={this.consult.bind(this, item.rid)}>{item.consult}</a>
<Link className={'register'} to={`/detail?id=${item.course_id}`}>{item.second_btn}</Link>
</React.Fragment>
}
{
item.c_type === 0 &&
<Link className={'study'} to={`/play?id=${item.v_course_id}`}>{item.second_btn}</Link>
}
</div>
</div>
</li>
})
}
</ul>
</div>
</div>
}
</div>
);
}
}
function Message({text, avatar, identity}) {
return <div className={`message clearfix ${identity}`}>
<div className="avatar">
<img src={avatar} alt=""/>
</div>
<div className={`text`}>{text}</div>
</div>
}
function Options({options, handleSelect}) {
return <ul className={'options'}>
{
!!options.length && options.map(item => {
return <li onClick={() => handleSelect(item)} key={item.id}>
{item.describe}
</li>
})
}
</ul>
}
export default connect(
({user, intelligentRecommend}) => ({user, intelligentRecommend}),
{addMessage, addResult, reselect},
)(WithFullSize(IntelligentRecommend));
\ No newline at end of file
#intelligent-recommend {
background: linear-gradient(to right, #F1F0F6, #EBF4F9);
padding: 59px 10px 18px;
height: 100%;
.clearfix {
&::after {
content: ".";
visibility: hidden;
display: block;
height: 0;
clear: both;
}
}
.head {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 44px;
padding: 0 15px;
background: #fff;
z-index: 100;
& > div {
display: flex;
align-items: center;
}
.iconfont {
font-size: 16px;
color: #222;
font-weight: 600;
}
.title {
margin-left: 10px;
font-size: 15px;
color: #2b2b2b;
}
button {
width: 84px;
height: 30px;
border: 1px solid #09f;
border-radius: 15px;
color: #09f;
font-size: 13px;
outline: 0;
background-color: transparent;
-webkit-appearance: none;
}
}
.dialog-box {
padding: 20px 15px 20px;
margin-bottom: 20px;
background: rgba(242, 247, 250, 1);
border: 1px solid #5CF9FF;
border-radius: 8px;
.analyzing {
color: #4f5c66;
font-size: 12px;
line-height: 48px;
text-align: center;
}
}
.message {
.avatar {
display: inline-block;
margin-right: 14px;
img {
width: 30px;
height: 30px;
border: 1px solid rgba(0, 153, 255, 1);
border-radius: 50%;
}
}
.text {
position: relative;
padding: 0 16px;
height: 40px;
display: inline-block;
background: rgba(255, 255, 255, 1);
border-radius: 4px;
font-size: 16px;
color: #333;
text-align: center;
line-height: 40px;
}
$angleSize: 8px;
@mixin pseudo {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
display: block;
border-color: transparent;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
}
&.system {
.text::before {
@include pseudo;
left: -$angleSize;
border-right: $angleSize solid #fff;
}
}
&.user {
.avatar {
float: right;
}
.text {
float: right;
margin-right: 15px;
background-color: #0036FF;
color: #fff;
&::after {
@include pseudo;
right: -$angleSize;
border-left: $angleSize solid #0036FF;
}
}
}
}
.message + .message {
margin-top: 30px;
}
.options {
position: relative;
padding-top: 18px;
margin-top: 15px;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 8px;
background: url("./images/options-divide-line.png") no-repeat;
background-size: contain;
}
li {
width: 126px;
height: 33px;
margin: 0 auto 15px;
background: linear-gradient(90deg, rgba(0, 153, 255, 1) 0%, rgba(77, 184, 255, 1) 100%);
border-radius: 17px;
font-size: 16px;
color: #fff;
text-align: center;
line-height: 33px;
}
}
.result {
padding: 0 15px 30px;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(0, 153, 255, 1);
border-radius: 8px;
color: #333;
& > .title {
position: relative;
color: #09f;
font-size: 16px;
text-align: center;
line-height: 62px;
@mixin pseudo {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
display: block;
width: 11px;
height: 11px;
background: url("./images/title-decorator.png") no-repeat;
background-size: contain;
}
&::before {
@include pseudo;
left: 5%;
}
&::after {
@include pseudo;
right: 5%;
transform: translateY(-50%) scale(-1);
}
}
.subtitle {
font-size: 12px;
}
.skill {
font-size: 18px;
line-height: 36px;
font-weight: 600;
&.project {
font-size: 14px;
}
}
.salary {
font-size: 18px;
line-height: 2em;
font-weight: 500;
color: #FF2A00;
}
.obtained {
margin-bottom: 20px;
}
.salary-section {
margin-bottom: 20px;
}
.recommends {
& > div:nth-child(1) {
margin-bottom: 12px;
font-size: 12px;
color: #09f;
}
.cover {
flex: 0 0 auto;
width: 125px;
height: 90px;
margin-right: 10px;
img {
width: 100%;
height: 100%;
}
}
.course {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
.info {
flex: 1 1 auto;
position: relative;
}
.title {
margin-bottom: 8px;
font-size: 16px;
line-height: 16px;
font-weight: 500;
color: #333;
}
.des {
display: -webkit-box;
/* autoprefixer: ignore next */
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
margin-bottom: 5px;
overflow: hidden;
font-size: 11px;
color: #4F5C66;
}
.bar {
position: absolute;
left: 0;
bottom: 0;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 24px;
font-size: 0;
a {
display: inline-block;
box-sizing: border-box;
padding: 6px 14px;
text-align: center;
font-size: 12px;
line-height: 12px;
border-radius: 12px;
}
.contact {
color: #fff;
background-color: #09f;
margin-right: 6px;
}
.register {
color: #FF0000;
border: 1px solid #FF0000;
}
.price {
font-size: 15px;
color: #FF2121;
margin-right: 5px;
}
.old-price {
font-size: 11px;
color: #999;
text-decoration: line-through;
}
.study {
background-color: #09f;
color: #fff;
font-size: 12px;
}
}
}
}
}
}
\ No newline at end of file
export const ADD_MESSAGE = 'ADD_MESSAGE'
export const ADD_RESULT = 'ADD_RESULT'
export const RESELECT = 'RESELECT'
export const addMessage = payload => {
return {
type: ADD_MESSAGE,
payload,
}
}
export const addResult = payload => {
return {
type: ADD_RESULT,
payload,
}
}
export const reselect = () => {
return {
type: RESELECT
}
}
const initialState = {
processing: [],
result: {},
}
export default function intelligentRecommend(state = initialState, action) {
switch (action.type) {
case ADD_MESSAGE:
return {
processing: [...state.processing, action.payload],
result: state.result,
}
case ADD_RESULT:
return {
processing: state.processing,
result: action.payload,
}
case RESELECT:
return initialState
default:
return state
}
}
\ No newline at end of file
......@@ -324,15 +324,20 @@ export default [
{
path: '/ml',
exact: true,
component: loadable(() => import(/* ml */'@/components/ml')),
component: loadable(() => import(/* webpackChunkName: 'ml' */'@/components/ml')),
},
{
path: '/mlShare',
component: loadable(() => import('@/components/mlShare')),
component: loadable(() => import(/* webpackChunkName: 'mlShare' */'@/components/mlShare')),
},
//ai水平测试
{
path: '/ai-test',
component: loadable(() => import('@/components/ai-test')),
component: loadable(() => import(/* webpackChunkName: 'ai-test' */'@/components/ai-test')),
},
//智能选课
{
path: '/intelligent-recommend',
component: loadable(() => import(/* webpackChunkName: 'intelligent-recommend' */'@/components/intelligent-recommend')),
},
]
......@@ -3,13 +3,15 @@ import myCourses from '@/components/study/myCourses/reducers'
import courseInfo from '@/components/detail/reducers'
import user from './userReducer'
import country from '@/components/country/countryRedux'
import intelligentRecommend from "@components/intelligent-recommend/store"
const reducer = combineReducers({
myCourses,
courseInfo,
user,
country
country,
intelligentRecommend
});
export default reducer;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment