Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun
commit
c449018d85
@ -0,0 +1,44 @@
|
|||||||
|
class HelpsController < ApplicationController
|
||||||
|
before_action :require_login, only: [:feedback]
|
||||||
|
|
||||||
|
helper_method :current_help
|
||||||
|
|
||||||
|
def about
|
||||||
|
render_ok(content: current_help&.about_us)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact
|
||||||
|
@cooperations = Cooperation.all.group(:user_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cooperatives
|
||||||
|
@data = { 'alliance_coop' => [], 'com_coop' => [], 'edu_coop' => [] }
|
||||||
|
@data = @data.merge CooImg.all.group_by(&:img_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def agreement
|
||||||
|
render_ok(content: current_help&.agreement)
|
||||||
|
end
|
||||||
|
|
||||||
|
def help_center
|
||||||
|
render_ok(content: current_help&.help_center)
|
||||||
|
end
|
||||||
|
|
||||||
|
def feedback
|
||||||
|
content = "<p>[#{params[:question_kind]}]</p><p>问题页面网址:#{params[:url]}</p>#{params[:description]}"
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
attr = { sender_id: User.current.id, receiver_id: 1, content: content, send_time: Time.now }
|
||||||
|
PrivateMessage.create!(attr.merge(user_id: User.current.id, target_id: 1, status: 1))
|
||||||
|
PrivateMessage.create!(attr.merge(user_id: 1, target_id: User.current.id, status: 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
render_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_help
|
||||||
|
@_current_help ||= Help.first
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,8 @@
|
|||||||
|
json.contacts do
|
||||||
|
json.array! @cooperations.each do |item|
|
||||||
|
json.extract! item, :name, :qq, :mail
|
||||||
|
json.type item.user_type_text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
json.address current_help.status
|
@ -0,0 +1,11 @@
|
|||||||
|
json.data do
|
||||||
|
json.array! @data.each do |type, objs|
|
||||||
|
json.name I18n.t("enumerize.coo_img.img_type.#{type}")
|
||||||
|
json.values do
|
||||||
|
json.array! objs.sort_by(&:position).each do |obj|
|
||||||
|
json.img obj.url_states || (Util::FileManage.exist?('CooImg', obj.id) ? Util::FileManage.disk_file_url('CooImg', obj.id) : '')
|
||||||
|
json.url obj.src_states
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,5 @@
|
|||||||
|
class AddVncEvaluateForShixuns < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :shixuns, :vnc_evaluate, :boolean, default: false
|
||||||
|
end
|
||||||
|
end
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 155 KiB |
File diff suppressed because one or more lines are too long
@ -0,0 +1,12 @@
|
|||||||
|
import Loadable from 'react-loadable';
|
||||||
|
|
||||||
|
import Loading from "./Loading";
|
||||||
|
|
||||||
|
const CustomLoadable = (loader, loading = Loading) => {
|
||||||
|
return Loadable({
|
||||||
|
loader,
|
||||||
|
loading
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomLoadable
|
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Card } from "antd";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { MarkdownToHtml } from 'educoder';
|
||||||
|
|
||||||
|
class AboutUs extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
content: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent(){
|
||||||
|
axios.get("/helps/about.json").then((result) => {
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
content: result.data.content,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
this.setState({ loading: false });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { loading, content } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="about-us-container">
|
||||||
|
<Card title="关于我们" bordered={false} loading={loading} style={{ minHeight: 600 }}>
|
||||||
|
<div className="about-us-content">
|
||||||
|
{ content && <MarkdownToHtml content={content} selector="work_content" className=""></MarkdownToHtml> }
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AboutUs;
|
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Card } from "antd";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { MarkdownToHtml } from 'educoder';
|
||||||
|
|
||||||
|
class Agreement extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
content: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent(){
|
||||||
|
axios.get("/helps/agreement.json").then((result) => {
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
content: result.data.content,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
this.setState({ loading: false });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { loading, content } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="agreement-container">
|
||||||
|
<Card title="服务协议" bordered={false} loading={loading} style={{ minHeight: 600 }}>
|
||||||
|
<div className="agreement-content">
|
||||||
|
{ content && <MarkdownToHtml content={content} selector="work_content" className=""></MarkdownToHtml> }
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Agreement;
|
@ -0,0 +1,24 @@
|
|||||||
|
.contact-us-container {
|
||||||
|
|
||||||
|
}
|
||||||
|
.contact-us-container .contact-item {
|
||||||
|
padding: 20px 15px;
|
||||||
|
border-bottom: 1px solid #EEEEEE;
|
||||||
|
}
|
||||||
|
.contact-us-container .contact-item:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.contact-us-container .contact-item:last-child {
|
||||||
|
border-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-us-container .contact-item-label {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.contact-us-container .contact-item-content {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.contact-us-container .contact-item-content .ant-row {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
.cooperatives-container {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.cooperatives-container .cooperative-item-title {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.cooperatives-container .cooperative-item-list-item {
|
||||||
|
padding: 10px 0;
|
||||||
|
height: 60px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
.cooperatives-container .cooperative-item-list-item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { List, Card } from "antd";
|
||||||
|
import axios from 'axios';
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
import './Cooperatives.css';
|
||||||
|
|
||||||
|
class Cooperatives extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
data: [
|
||||||
|
{ name: "产学联盟" },
|
||||||
|
{ name: "知名企业" },
|
||||||
|
{ name: "各类院校" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.getCooperatives();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCooperatives(){
|
||||||
|
axios.get("/helps/cooperatives.json").then((result) => {
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data: result.data.data,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
this.setState({ loading: false });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { loading, data } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="cooperatives-container">
|
||||||
|
<Card title="合作伙伴" bordered={false} loading={loading} style={{ minHeight: 600 }}>
|
||||||
|
<div className="cooperatives-content">
|
||||||
|
{
|
||||||
|
data && data.length > 0 && data.map((item, _key) => {
|
||||||
|
return (
|
||||||
|
<div className="cooperative-item">
|
||||||
|
<div className="cooperative-item-title">{ item.name }</div>
|
||||||
|
<div className="cooperative-item-list">
|
||||||
|
<List
|
||||||
|
grid={{ gutter: 16, column: 4 }}
|
||||||
|
dataSource={item.values}
|
||||||
|
renderItem={obj => (
|
||||||
|
<List.Item>
|
||||||
|
<div className="cooperative-item-list-item">
|
||||||
|
<a href={obj.url || 'javascript:void(0)'} target={obj.url && '_blank'}>
|
||||||
|
<img className="" height="90" src={getImageUrl(obj.img.substr(1))} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</List.Item>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Cooperatives;
|
@ -0,0 +1,7 @@
|
|||||||
|
.feedback-container {
|
||||||
|
|
||||||
|
}
|
||||||
|
.feedback-container .feedback-message {
|
||||||
|
line-height: 26px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Form, Input, Radio, Button } from "antd";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
class FeedbackForm extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.props.form.validateFields((err, fieldsValue) => {
|
||||||
|
if(err){ return }
|
||||||
|
|
||||||
|
axios.post("/helps/feedback.json", fieldsValue)
|
||||||
|
.then((result) => {
|
||||||
|
if (result.status === 200 && result.data.status === 0) {
|
||||||
|
this.props.history.push(`/messages/${this.props.current_user.login}/message_detail?target_ids=1`);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="feedback-form">
|
||||||
|
<Form onSubmit={this.handleSubmit}>
|
||||||
|
<Form.Item label="问题分类">
|
||||||
|
{getFieldDecorator('question_kind', {
|
||||||
|
initialValue: "登录注册",
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '不能为空',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="登录注册">登录注册</Radio>
|
||||||
|
<Radio value="信息认证">信息认证</Radio>
|
||||||
|
<Radio value="实训编程">实训编程</Radio>
|
||||||
|
<Radio value="实训课程">实训课程</Radio>
|
||||||
|
<Radio value="课堂">课堂</Radio>
|
||||||
|
<Radio value="其它">其它</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="问题页面网址">
|
||||||
|
{getFieldDecorator('url', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '不能为空',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<Input placeholder="反馈平台问题,请同时填写对应的问题页面链接,以便平台能够及时跟踪解决,谢谢" />)}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="问题描述">
|
||||||
|
{getFieldDecorator('description', {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '不能为空',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})(<TextArea rows={4} placeholder="反馈平台问题,请同时填写对应的问题页面链接,以便平台能够及时跟踪解决,谢谢" />)}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" htmlType="submit">提交</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FeedbackForm;
|
@ -0,0 +1,9 @@
|
|||||||
|
.help-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
.help-container .help-menu {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.help-container .help-content {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Switch, Route, Link } from 'react-router-dom';
|
||||||
|
import { Affix, Menu, Row, Col } from "antd";
|
||||||
|
import { SnackbarHOC } from 'educoder';
|
||||||
|
|
||||||
|
import './Help.css';
|
||||||
|
|
||||||
|
import CustomLoadable from "../../CustomLoadable";
|
||||||
|
import {TPMIndexHOC} from "../tpm/TPMIndexHOC";
|
||||||
|
|
||||||
|
const AboutUs = CustomLoadable(() => import('./AboutUs'));
|
||||||
|
const ContactUs = CustomLoadable(() => import('./ContactUs'));
|
||||||
|
const Cooperatives = CustomLoadable(() => import('./Cooperatives'));
|
||||||
|
const Agreement = CustomLoadable(() => import('./Agreement'));
|
||||||
|
const HelpCenter = CustomLoadable(() => import('./HelpCenter'));
|
||||||
|
const Feedback = CustomLoadable(() => import('./Feedback'));
|
||||||
|
|
||||||
|
class Help extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
type: props.match.params.type || 'about_us'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
console.log('update', prevProps, this.props);
|
||||||
|
if(prevProps.match.params.type !== this.props.match.params.type){
|
||||||
|
this.setState({ type: this.props.match.params.type });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="newMain clearfix">
|
||||||
|
<div className="educontent help-container">
|
||||||
|
<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
|
||||||
|
<Col span={4}>
|
||||||
|
<Affix offsetTop={20}>
|
||||||
|
<div className="help-menu">
|
||||||
|
<Menu
|
||||||
|
mode="inline"
|
||||||
|
selectedKeys={[this.state.type]}>
|
||||||
|
|
||||||
|
<Menu.Item key="about_us"><Link to="/help/about_us">关于我们</Link></Menu.Item>
|
||||||
|
<Menu.Item key="contact_us"><Link to="/help/contact_us">联系我们</Link></Menu.Item>
|
||||||
|
<Menu.Item key="cooperatives"><Link to="/help/cooperatives">合作伙伴</Link></Menu.Item>
|
||||||
|
<Menu.Item key="agreement"><Link to="/help/agreement">服务协议</Link></Menu.Item>
|
||||||
|
<Menu.Item key="help_center"><Link to="/help/help_center">帮助中心</Link></Menu.Item>
|
||||||
|
<Menu.Item key="feedback"><Link to="/help/feedback">意见反馈</Link></Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
</Affix>
|
||||||
|
</Col>
|
||||||
|
<Col span={20}>
|
||||||
|
<div className="help-content">
|
||||||
|
<Switch>
|
||||||
|
<Route path='/help/about_us' component={AboutUs}></Route>
|
||||||
|
<Route path='/help/contact_us' component={ContactUs}></Route>
|
||||||
|
<Route path='/help/cooperatives' component={Cooperatives}></Route>
|
||||||
|
<Route path='/help/agreement' component={Agreement}></Route>
|
||||||
|
<Route path='/help/help_center' component={HelpCenter}></Route>
|
||||||
|
<Route path='/help/feedback' render={ (props)=>(<Feedback {...this.props} {...props} {...this.state}></Feedback>) }></Route>
|
||||||
|
<Route component={AboutUs}/>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SnackbarHOC() (TPMIndexHOC ( Help ));
|
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Card } from "antd";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { MarkdownToHtml } from 'educoder';
|
||||||
|
|
||||||
|
class HelpCenter extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loading: true,
|
||||||
|
content: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
getContent(){
|
||||||
|
axios.get("/helps/help_center.json").then((result) => {
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
content: result.data.content,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
this.setState({ loading: false });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { loading, content } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="help-center-container">
|
||||||
|
<Card title="帮助中心" bordered={false} loading={loading} style={{ minHeight: 600 }}>
|
||||||
|
<div className="help-center-content">
|
||||||
|
{ content && <MarkdownToHtml content={content} selector="work_content" className=""></MarkdownToHtml> }
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HelpCenter;
|
Loading…
Reference in new issue