diff --git a/.merge_file_a17580 b/.merge_file_a17580 new file mode 100644 index 000000000..47e471767 --- /dev/null +++ b/.merge_file_a17580 @@ -0,0 +1,529 @@ +import React, {Component} from 'react'; +import logo from './logo.svg'; +import './App.css'; +import {LocaleProvider} from 'antd' +import zhCN from 'antd/lib/locale-provider/zh_CN'; +import { + BrowserRouter as Router, + Route, + Switch +} from 'react-router-dom'; +import axios from 'axios'; +import '@icedesign/base/dist/ICEDesignBase.css'; + +import '@icedesign/base/index.scss'; + +import LoginDialog from './modules/login/LoginDialog'; +import Notcompletedysl from './modules/user/Notcompletedysl'; +import Trialapplicationysl from './modules/login/Trialapplicationysl'; +import Trialapplicationreview from './modules/user/Trialapplicationreview'; +import Addcourses from "./modules/courses/coursesPublic/Addcourses"; +import AccountProfile from"./modules/user/AccountProfile"; + + +import Trialapplication from './modules/login/Trialapplication' + +import NotFoundPage from './NotFoundPage' + +import Loading from './Loading' + +import Loadable from 'react-loadable'; + + +import moment from 'moment' + +import {MuiThemeProvider, createMuiTheme} from 'material-ui/styles'; + +// import './AppConfig' + +import history from './history'; + +import {SnackbarHOC} from 'educoder' +import {initAxiosInterceptors} from './AppConfig' + + +// !!!tpi需要这个来加载css +import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC'; + + +const theme = createMuiTheme({ + palette: { + primary: { + main: '#4CACFF', + contrastText: 'rgba(255, 255, 255, 0.87)' + }, + secondary: {main: '#4CACFF'}, // #11cb5f This is just green.A700 as hex. + }, +}); +// +// const Trialapplication= Loadable({ +// loader: () =>import('./modules/login/Trialapplication'), +// loading:Loading, +// }) +//登入 +const EducoderLogin = Loadable({ + loader: () => import('./modules/login/EducoderLogin'), + loading: Loading, +}) +const TestIndex = Loadable({ + loader: () => import('./modules/test'), + loading: Loading, +}) + +const IndexWrapperComponent = Loadable({ + loader: () => import('./modules/page/IndexWrapper'), + loading: Loading, +}) + +const CommentComponent = Loadable({ + loader: () => import('./modules/comment/CommentContainer'), + loading: Loading, +}) + +const TestMaterialDesignComponent = Loadable({ + loader: () => import('./modules/test/md/TestMaterialDesign'), + loading: Loading, +}) +const TestCodeMirrorComponent = Loadable({ + loader: () => import('./modules/test/codemirror/TestCodeMirror'), + loading: Loading, +}) + +const TestComponent = Loadable({ + loader: () => import('./modules/test/TestRC'), + loading: Loading, +}) +const TestUrlQueryComponent = Loadable({ + loader: () => import('./modules/test/urlquery/TestUrlQuery'), + loading: Loading, +}) + +const TPMIndexComponent = Loadable({ + loader: () => import('./modules/tpm/TPMIndex'), + loading: Loading, +}) +const TPMShixunsIndexComponent = Loadable({ + loader: () => import('./modules/tpm/shixuns/ShixunsIndex'), + loading: Loading, +}) + +//实训课程(原实训路径) +const ShixunPaths = Loadable({ + loader: () => import('./modules/paths/Index'), + loading: Loading, +}) + +//在线课堂 +const CoursesIndex = Loadable({ + loader: () => import('./modules/courses/Index'), + loading: Loading, +}) +const SearchPage = Loadable({ + loader: () => import('./search/SearchPage'), + loading: Loading, +}) + +//教学案例 +const MoopCases = Loadable({ + loader: () => import('./modules/moop_cases/index'), + loading: Loading, +}) + +// 课堂讨论 +// const BoardIndex = Loadable({ +// loader: () => import('./modules/courses/boards/BoardIndex'), +// loading:Loading, +// }) + +// //课堂普通作业&分组作业 +// const CoursesWorkIndex = Loadable({ +// loader: () => import('./modules/courses/busyWork/Index'), +// loading:Loading, +// }) +// + +// const TPMShixunchildIndexComponent = Loadable({ +// loader: () => import('./modules/tpm/shixunchild/ShixunChildIndex'), +// loading: Loading, +// }) + + +// const TPMshixunfork_listIndexComponent = Loadable({ +// loader: () => import('./modules/tpm/shixunchild/Shixunfork_list'), +// loading: Loading, +// }) + + +const ForumsIndexComponent = Loadable({ + loader: () => import('./modules/forums/ForumsIndex'), + loading: Loading, +}) + +// trustie plus forum +// const TPForumsIndexComponent = Loadable({ +// loader: () => import('./modules/tp-forums/TPForumsIndex'), +// loading: Loading, +// }) + + +// const TestPageComponent = Loadable({ +// loader: () => import('./modules/page/Index'), +// loading: Loading, +// }) + + +//新建实训 +const Newshixuns = Loadable({ + loader: () => import('./modules/tpm/newshixuns/Newshixuns'), + loading: Loading, +}) + + +//实训首页 +const ShixunsHome = Loadable({ + loader: () => import('./modules/home/shixunsHome'), + loading: Loading, +}) + + +const CompatibilityPageLoadable = Loadable({ + loader: () => import('./modules/common/CompatibilityPage'), + loading: Loading, +}) + +//403页面 +const Shixunauthority = Loadable({ + loader: () => import('./modules/403/Shixunauthority'), + loading: Loading, +}) + + +//404页面 +const Shixunnopage = Loadable({ + loader: () => import('./modules/404/Shixunnopage'), + loading: Loading, +}) + +//500页面 +const http500 = Loadable({ + loader: () => import('./modules/500/http500'), + loading: Loading, +}) + +// 登录注册 +const LoginRegisterPage = Loadable({ + loader: () => import('./modules/user/LoginRegisterPage'), + loading: Loading, +}) +const AccountPage = Loadable({ + loader: () => import('./modules/user/AccountPage'), + loading: Loading, +}) + +// 个人主页 +const UsersInfo = Loadable({ + loader: () => import('./modules/user/usersInfo/Infos'), + loading: Loading, +}) + +// 兴趣页面 +const Interestpage = Loadable({ + loader: () => import('./modules/login/EducoderInteresse'), + loading: Loading, +}) + +//众包创新 +const ProjectPackages=Loadable({ + loader: () => import('./modules/projectPackages/ProjectPackageIndex'), + loading: Loading, +}) + +class App extends Component { + constructor(props) { + super(props) + // this.state = { + // isRenders:false, + // } + + } + + componentDidMount() { + // force an update if the URL changes + history.listen(() => { + this.forceUpdate() + const $ = window.$ + // https://www.trustie.net/issues/21919 可能会有问题 + $("html").animate({ scrollTop: $('html').scrollTop() - 0 }) + }); + + initAxiosInterceptors(this.props) + + // + // axios.interceptors.response.use((response) => { + // // console.log("response"+response); + // if(response!=undefined) + // // console.log("response"+response.data.statu); + // if (response&&response.data.status === 407) { + // this.setState({ + // isRenders: true, + // }) + // } + // return response; + // }, (error) => { + // //TODO 这里如果样式变了会出现css不加载的情况 + // }); + } + //修改登录方法 + Modifyloginvalue=()=>{ + this.setState({ + isRender:false, + }) + } + + render() { + + + return ( + <LocaleProvider locale={zhCN}> + + + <MuiThemeProvider theme={theme}> + <LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog> + <Notcompletedysl {...this.props} {...this.state}></Notcompletedysl> + <Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl> + <Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview> + <Addcourses {...this.props} {...this.state}/> + <AccountProfile {...this.props} {...this.state}/> + {/*{*/} + {/* isRender === true?*/} + {/* <LoginDialog></LoginDialog> : ""*/} + {/*}*/} + + {/*{*/} + {/* isRenders === true?*/} + {/*<Trialapplication></Trialapplication>*/} + {/*:""*/} + {/*}*/} + + <Router> + <Switch> + {/*<Route path="/login" component={LoginRegisterPage}/>*/} + + {/*众包创新*/} + <Route path={"/crowdsourcings"} component={ProjectPackages}/> + {/*认证*/} + <Route path="/account" component={AccountPage}/> + + {/*403*/} + <Route path="/403" component={Shixunauthority}/> + + <Route path="/500" component={http500}/> + + {/*404*/} + <Route path="/nopage" component={Shixunnopage}/> + + <Route path="/compatibility" component={CompatibilityPageLoadable}/> + <Route + path="/login" component={EducoderLogin} + /> + <Route + path="/register" component={EducoderLogin} + /> + <Route path="/users/:username" + render={ + (props) => (<UsersInfo {...this.props} {...props} {...this.state} />) + }></Route> + {/*<Route*/} + {/* path="/trialapplication" component={Trialapplication}*/} + {/*/>*/} + <Route + path="/changepassword" component={EducoderLogin} + /> + <Route + path="/interesse" component={Interestpage} + + /> + + <Route path="/shixuns/new" component={Newshixuns}> + </Route> + + <Route path="/tasks/:stageId" component={IndexWrapperComponent}/> + + <Route path="/shixuns/:shixunId" component={TPMIndexComponent}> + </Route> + + {/*列表页*/} + <Route path="/shixuns" component={TPMShixunsIndexComponent}/> + + {/* <Route path="/shixunchild" component={TPMShixunchildIndexComponent}> + </Route> + <Route path="/fork_list" component={TPMshixunfork_listIndexComponent}> + </Route> */} + + {/*<Route path="/forums" component={ForumsIndexComponent}>*/} + {/*</Route>*/} + + + {/*实训课程(原实训路径)*/} + <Route path="/paths" component={ShixunPaths}></Route> + + <Route path="/search" + render={ + (props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>) + } + ></Route> + + {/*课堂*/} + <Route path="/courses" component={CoursesIndex} {...this.props}></Route> + + {/* 课堂讨论 */} + {/* <Route path="/board" component = {BoardIndex} {...this.props}></Route> */} + + {/* <Route path="/tpforums" component={TPForumsIndexComponent}> + </Route> */} + + {/* <Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/> */} + {/* 兴趣页面*/} + {/*<Route path="/interest" component={Interestpage}/>*/} + + <Route path="/comment" component={CommentComponent}/> + <Route path="/testMaterial" component={TestMaterialDesignComponent}/> + <Route path="/test" component={TestIndex}/> + <Route path="/testCodeMirror" component={TestCodeMirrorComponent}/> + <Route path="/testRCComponent" component={TestComponent}/> + <Route path="/testUrlQuery" component={TestUrlQueryComponent}/> + + {/* 教学案例 */} + <Route path="/moop_cases"render={ + (props) => (<MoopCases {...this.props} {...props} {...this.state} />) + }/> + + {/* <Route component={NotFoundPage}/> */} + {/*列表页*/} + {/*<Route component={TPMShixunsIndexComponent}/>*/} + {/*首页*/} + <Route exact path="/" component={ShixunsHome}/> + <Route component={Shixunnopage}/> + + {/*<Route component={ShixunsHome}/>*/} + + </Switch> + </Router> + </MuiThemeProvider> + </LocaleProvider> + ); + } +} + +// moment国际化,设置为中文 +moment.defineLocale('zh-cn', { + months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), + weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'), + weekdaysMin: '日_一_二_三_四_五_六'.split('_'), + longDateFormat: { + LT: 'Ah点mm分', + LTS: 'Ah点m分s秒', + L: 'YYYY-MM-DD', + LL: 'YYYY年MMMD日', + LLL: 'YYYY年MMMD日Ah点mm分', + LLLL: 'YYYY年MMMD日ddddAh点mm分', + l: 'YYYY-MM-DD', + ll: 'YYYY年MMMD日', + lll: 'YYYY年MMMD日Ah点mm分', + llll: 'YYYY年MMMD日ddddAh点mm分' + }, + meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, + meridiemHour: function (hour, meridiem) { + if (hour === 12) { + hour = 0; + } + if (meridiem === '凌晨' || meridiem === '早上' || + meridiem === '上午') { + return hour; + } else if (meridiem === '下午' || meridiem === '晚上') { + return hour + 12; + } else { + // '中午' + return hour >= 11 ? hour : hour + 12; + } + }, + meridiem: function (hour, minute, isLower) { + var hm = hour * 100 + minute; + if (hm < 600) { + return '凌晨'; + } else if (hm < 900) { + return '早上'; + } else if (hm < 1130) { + return '上午'; + } else if (hm < 1230) { + return '中午'; + } else if (hm < 1800) { + return '下午'; + } else { + return '晚上'; + } + }, + calendar: { + sameDay: function () { + return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT'; + }, + nextDay: function () { + return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT'; + }, + lastDay: function () { + return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT'; + }, + nextWeek: function () { + var startOfWeek, prefix; + startOfWeek = moment().startOf('week'); + prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]'; + return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; + }, + lastWeek: function () { + var startOfWeek, prefix; + startOfWeek = moment().startOf('week'); + prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]'; + return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; + }, + sameElse: 'LL' + }, + ordinalParse: /\d{1,2}(日|月|周)/, + ordinal: function (number, period) { + switch (period) { + case 'd': + case 'D': + case 'DDD': + return number + '日'; + case 'M': + return number + '月'; + case 'w': + case 'W': + return number + '周'; + default: + return number; + } + }, + relativeTime: { + future: '%s内', + past: '%s前', + s: '几秒', + m: '1分钟', + mm: '%d分钟', + h: '1小时', + hh: '%d小时', + d: '1天', + dd: '%d天', + M: '1个月', + MM: '%d个月', + y: '1年', + yy: '%d年' + }, + week: { + // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 + dow: 1, // Monday is the first day of the week. + doy: 4 // The week that contains Jan 4th is the first week of the year. + } +}); +export default SnackbarHOC()(App); \ No newline at end of file diff --git a/public/react/config/webpack.config.dev.js b/public/react/config/webpack.config.dev.js index 12d5e437f..15e7e6bef 100644 --- a/public/react/config/webpack.config.dev.js +++ b/public/react/config/webpack.config.dev.js @@ -29,7 +29,7 @@ const env = getClientEnvironment(publicUrl); module.exports = { // You may want 'eval' instead if you prefer to see the compiled output in DevTools. // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s - devtool: "eval", // 开启调试 + devtool: "source-map", // 开启调试 // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. // The first two entry points enable "hot" CSS and auto-refreshes for JS. diff --git a/public/react/src/App.js b/public/react/src/App.js index 46522d2b9..e71ce4a9a 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -218,6 +218,12 @@ const UsersInfo = Loadable({ loading: Loading, }) +// 教学案例 +const MoopCases = Loadable({ + loader: () => import('./modules/moop_cases/index'), + loading: Loading, +}) + // 兴趣页面 const Interestpage = Loadable({ loader: () => import('./modules/login/EducoderInteresse'), @@ -351,14 +357,19 @@ class App extends Component { {/*课堂*/} <Route path="/courses" component={CoursesIndex} {...this.props}></Route> - + {/* <Route path="/forums" component={ForumsIndexComponent}> + </Route> */} + {/* 教学案例 */} + <Route path="/moop_cases"render={ + (props) => (<MoopCases {...this.props} {...props} {...this.state} />) + }/> + <Route path="/forums" render={ (props)=>(<ForumsIndexComponent {...this.props} {...props} {...this.state}></ForumsIndexComponent>) } > </Route> - <Route path="/comment" component={CommentComponent}/> <Route path="/testMaterial" component={TestMaterialDesignComponent}/> <Route path="/test" component={TestIndex}/> diff --git a/public/react/src/common/course/ActionBtn.js b/public/react/src/common/course/ActionBtn.js index 03da0d3e7..e5eeb11a4 100644 --- a/public/react/src/common/course/ActionBtn.js +++ b/public/react/src/common/course/ActionBtn.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import {Link} from 'react-router-dom' -const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack", +const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack",'greyLine':"greyLine", 'colorBlue': 'colorBlue', // 蓝字白底 } class ActionBtn extends Component { diff --git a/public/react/src/images/moop_cases/success.png b/public/react/src/images/moop_cases/success.png new file mode 100644 index 000000000..9282ef4d0 Binary files /dev/null and b/public/react/src/images/moop_cases/success.png differ diff --git a/public/react/src/images/moop_cases/teach_ex.jpg b/public/react/src/images/moop_cases/teach_ex.jpg new file mode 100644 index 000000000..eef1f7ce6 Binary files /dev/null and b/public/react/src/images/moop_cases/teach_ex.jpg differ diff --git a/public/react/src/modules/courses/css/Courses.css b/public/react/src/modules/courses/css/Courses.css index 2408e95bb..47db2f595 100644 --- a/public/react/src/modules/courses/css/Courses.css +++ b/public/react/src/modules/courses/css/Courses.css @@ -683,8 +683,9 @@ a.white-btn.use_scope-btn:hover{ } .colorBlue { + padding: 0px 7px; background-color: #fff; - color: #4CACFF; + color: #4CACFF!important; border: 1px solid #4CACFF; } .greyBack{ @@ -701,6 +702,12 @@ a.white-btn.use_scope-btn:hover{ .Actionbtn.middle { padding: 0px 18px; } +.greyLine{ + background: #fff; + border:1px solid #eaeaea; + color: #999!important; + padding:0px 10px; +} .colorFF6800{ diff --git a/public/react/src/modules/forums/MemoDetail.js b/public/react/src/modules/forums/MemoDetail.js index f2d50f9bb..8441a8ac9 100644 --- a/public/react/src/modules/forums/MemoDetail.js +++ b/public/react/src/modules/forums/MemoDetail.js @@ -14,7 +14,7 @@ import moment from 'moment' import Comments from '../comment/Comments' import update from 'immutability-helper' -import Tooltip from 'material-ui/Tooltip'; +// import Tooltip from 'material-ui/Tooltip'; import RewardDialog from '../common/RewardDialog'; import {ImageLayerOfCommentHOC} from '../page/layers/ImageLayerOfCommentHOC' @@ -23,6 +23,7 @@ import MemoDetailKEEditor from './MemoDetailKEEditor' import MemoDetailMDEditor from './MemoDetailMDEditor' import { bytesToSize } from 'educoder' +import { Tooltip } from 'antd' const $ = window.$ function urlStringify(params) { let noParams = true; @@ -691,15 +692,23 @@ class MemoDetail extends Component { <div id="forum_list" className="forum_table mh650"> <div className="padding40-30 bor-bottom-greyE"> <div className="font-16 mb5 cdefault clearfix pr pr35" style={{display: 'flex', alignItems: 'center'}}> - <span className="noteDetailTitle">{memo.subject}</span> - { memo.sticky && <span className="btn-cir btn-cir-red ml10 " style={{ height: '20px' }}>置顶</span>} - { !!memo.reward && <span className="color-orange font-14 ml15" - data-tip-down={`获得平台奖励金币:${memo.reward}`} > - <i className="iconfont icon-gift mr5"></i> - <span style={{ 'vertical-align': 'sub' }}>{memo.reward}</span> - </span> } - - <div style={{ flex: 1 }}> + {/* overflowHidden1 */} + <span className="noteDetailTitle " style={{maxWidth: '634px'}}>{memo.subject + memo.subject}</span> + { memo.sticky && <span className="btn-cir btn-cir-red ml10 " + style={{ height: '20px', alignSelf: 'flex-start', marginTop: '10px' }} + >置顶</span>} + { !!memo.reward && + <Tooltip title={`获得平台奖励金币:${memo.reward}`}> + <span className="color-orange font-14 ml15" + style={{ height: '20px', alignSelf: 'flex-start', marginTop: '1px' }} + > + <i className="iconfont icon-gift mr5"></i> + <span style={{ 'vertical-align': 'sub' }}>{memo.reward}</span> + </span> + </Tooltip> + } + + <div style={{ flex: 1, alignSelf: 'flex-start' }}> { _current_user && (_current_user.admin === true || _current_user.user_id === author_info.user_id) && <div className="edu-position-hidebox" style={{position: 'absolute', right: '12px',top:'4px'}}> <a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a> diff --git a/public/react/src/modules/forums/MemoList.js b/public/react/src/modules/forums/MemoList.js index 26cad1448..96af68767 100644 --- a/public/react/src/modules/forums/MemoList.js +++ b/public/react/src/modules/forums/MemoList.js @@ -17,16 +17,22 @@ import PostItem from './PostItem' import ForumsNavTab from './ForumsNavTab' -import { queryString } from 'educoder' +import { queryString, ThemeContext } from 'educoder' class MemoList extends Component { render() { const { match, history, currentPage, memo_count ,memo_list, renderMemoList, onPaginationChange } = this.props + let theme = this.context; return ( <React.Fragment> <div id="forum_list" className="forum_table"> + <style>{` + .forum_table_item .item_name:hover { + color: ${theme.foreground_select} + } + `}</style> <div className="mh650 edu-back-white"> {!memo_list || memo_list.length === 0 ? <div className="edu-tab-con-box clearfix edu-txt-center"> @@ -49,5 +55,6 @@ class MemoList extends Component { ); } } +MemoList.contextType = ThemeContext; export default ( MemoList ); diff --git a/public/react/src/modules/forums/PostItem.js b/public/react/src/modules/forums/PostItem.js index 02d9fa273..43b63c3fa 100644 --- a/public/react/src/modules/forums/PostItem.js +++ b/public/react/src/modules/forums/PostItem.js @@ -7,10 +7,12 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { getImageUrl, toPath } from 'educoder'; +import { getImageUrl, toPath, ThemeContext } from 'educoder'; import moment from 'moment'; +import { Tooltip } from 'antd' + class PostItem extends Component { _toTenThousand(num) { @@ -31,17 +33,19 @@ class PostItem extends Component { <div className="fl pr" style={{flex: 1}}> <p className="font-16 clearfix" style={{ lineHeight: 2 }}> {/* target="_blank" */} - <a href={`/forums/${memo.id}`} target="_blank" title={memo.subject} - className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} > + <a href={`/forums/${memo.id}`} target="_blank" title={memo.subject && memo.subject.length > 46 ? memo.subject : ''} + className="clearfix task-hide item_name fl" style={{maxWidth: '600px'}} > {memo.subject} </a> { memo.sticky && <span className="btn-top btn-cir-orange mt6 ml5 fl">置顶</span> } { memo.reward && - <span className=" ml10 fl color-orange03 fl" data-tip-down={`获得平台奖励金币:${memo.reward}`}> - <i className="iconfont icon-gift font-16 mr5 fl"></i><span className="fl mt3 font-14">{memo.reward}</span> - </span> + <Tooltip title={`获得平台奖励金币:${memo.reward}`}> + <span className=" ml10 fl color-orange03 fl" > + <i className="iconfont icon-gift font-16 mr5 fl"></i><span className="fl mt3 font-14">{memo.reward}</span> + </span> + </Tooltip> } </p> @@ -101,4 +105,6 @@ class PostItem extends Component { ); } } +PostItem.contextType = ThemeContext; + export default PostItem diff --git a/public/react/src/modules/moop_cases/CaseDetail.js b/public/react/src/modules/moop_cases/CaseDetail.js new file mode 100644 index 000000000..193961240 --- /dev/null +++ b/public/react/src/modules/moop_cases/CaseDetail.js @@ -0,0 +1,165 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' + +import { getImageUrl , MarkdownToHtml , ActionBtn } from 'educoder'; + +import Tags from './CaseTags' + +import axios from 'axios'; +import Modals from '../modals/Modals' + +class CaseDetail extends Component{ + constructor(props){ + super(props); + this.state={ + modalsType:"", + modalsTopval:"", + modalsBottomval:"", + modalCancel:"", + } + } + + componentDidMount =()=>{ + let caseID = this.props.match.params.caseID; + this.props.getDetail(caseID); + } + // 是否删除 + delCases=()=>{ + this.setState({ + modalsType:true, + modalsTopval:"是否确认删除?", + modalsBottomval:"" + }) + } + + // 取消删除 + cancelDelClasses=()=>{ + this.setState({ + modalsType:false, + modalsTopval:"", + modalsBottomval:"" + }) + } + // 确定删除 + sureDelClasses=()=>{ + let caseID = this.props.match.params.caseID; + let url =`/libraries/${caseID}.json`; + axios.delete(url).then((result)=>{ + if(result){ + this.props.showNotification("删除成功"); + this.props.history.push("/moop_cases"); + } + }).catch((error)=>{ + console.log(error); + }) + } + + + render(){ + let { CaseDetail , praise_count , creator , operation , user_praised , tags , attachments }=this.props + let { + modalsType, + modalsTopval, + modalsBottomval, + } = this.state; + return( + <div className="educontent mt10 mb50"> + <Modals + modalsType={modalsType} + modalsTopval={modalsTopval} + modalsBottomval={modalsBottomval} + modalCancel={this.cancelDelClasses} + modalSave={this.sureDelClasses} + > + </Modals> + <p className="mt10 mb20 clearfix lineh-20"> + <a href="/moop_cases" className="color-grey-9">教学案例</a> > <span className="color-grey-3">{ CaseDetail && CaseDetail.title}</span> + </p> + <p className="lineh-25 mb20 clearfix"> + <span className="font-22 fl mr10 task-hide" style={{maxWidth:"800px"}}> + { CaseDetail && CaseDetail.title} + </span> + <span className="mt5 fl"> + <Tags tags={tags}></Tags> + { + CaseDetail && CaseDetail.status == "pending" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">草稿</span> + } + </span> + <a href="/moop_cases" className="fr color-grey-9 mt5">返回</a> + </p> + <div className="edu-back-white"> + <div className="padding30"> + <div className="df"> + <a href="/users/moop"><img alt="82274?1563067098" className="radius mr15 mt3" height="50" src={getImageUrl(`images/${creator && creator.image_url}`)} width="50" /></a> + <div className="flex1"> + <li className="clearfix mb5"> + <span className="font-16 fl">{creator && creator.name}</span> + { + operation && operation.can_deletable ? <ActionBtn style="greyLine" onClick={this.delCases} className="fr">删除</ActionBtn>:"" + } + + { + operation && operation.can_editable ? <ActionBtn style="colorBlue" to={`/moop_cases/${this.props.match.params.caseID}/edit`} className="fr mr20">编辑</ActionBtn>:"" + } + </li> + <li className="clearfix"> + <span className="fl color-grey-9 mr20">{creator && creator.school_name}</span> + <span className="fr"> + <span className="fl color-grey-9 mr30">编码:<span className="color-grey-6">{CaseDetail && CaseDetail.uuid}</span></span> + <span className="fl color-grey-9">发布时间:<span className="color-grey-6">{CaseDetail && CaseDetail.published_at}</span></span> + </span> + </li> + </div> + </div> + <div> + <span className="fl color-grey-9">作者:</span>{CaseDetail && CaseDetail.author_name}/{CaseDetail && CaseDetail.author_school_name} + </div> + <style> + {` + .setMDStyle .editormd-html-preview{ + width:100%!important; + } + `} + </style> + <div class="mt20 setMDStyle"> + { CaseDetail && CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} selector="casesDetail" style={{width:"100%!important"}}></MarkdownToHtml>} + </div> + { attachments && + <div className="mt10"> + { + attachments.map((item,key)=>{ + return( + <p> + <a className="color-grey"> + <i className="font-14 color-green iconfont icon-fujian mr8"></i> + </a> + <a href={item.url} className="mr12" length="58">{item.title}</a> + <span className="color-grey mt2 color-grey-6 font-12">{item.filesize}</span> + </p> + ) + }) + } + </div> + } + <div class="mt40"> + { + user_praised ? + <p className="pointsBtn pointedBtn"> + <span>已赞</span> + <span>{praise_count}</span> + </p> + : + <p onClick = {()=>this.props.praisePoint(this.props.match.params.caseID)} className="pointsBtn"> + <span><i class="iconfont icon-dianzan"></i></span> + <span>{praise_count}</span> + </p> + } + </div> + </div> + </div> + </div> + ) + } +} +export default CaseDetail; \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/CaseItem.js b/public/react/src/modules/moop_cases/CaseItem.js new file mode 100644 index 000000000..a5086133b --- /dev/null +++ b/public/react/src/modules/moop_cases/CaseItem.js @@ -0,0 +1,48 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' + +import { getUrl } from 'educoder'; + +import Tags from './CaseTags' + +class CaseItem extends Component{ + constructor(props){ + super(props); + } + + render(){ + let { libraries } = this.props; + console.log(libraries) + return( + <div className="library-list-container"> + { + libraries && libraries.map((item,key)=>{ + return( + <li className="library_list_item"> + <img alt={item.id} className="mr15 mt3 radius4" height="90" src={getUrl(`${item.cover_url}`)} width="120" /> + <div className="flex1"> + <p className="clearfix mb25 lineh-40"> + <a href={`/moop_cases/${item.id}`} className="task-hide font-22 library_l_name">{item.title}</a> + <span className="fl mt10"><Tags tags={item.tags}></Tags></span> + </p> + <p className="clearfix lineh-20"> + <span className="color-grey-3 mr10">{item.author_name}</span> + <span className="color-grey-3 mr20">{item.author_school_name}</span> + <span className="color-grey-c fr"> + <span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-eye"></i></span>{item.visited_count} 浏览</span> + <span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-thumbs-o-up"></i></span>{item.praise_count} 赞</span> + <span className="color-grey-c"><span className=" item-group-icon mr5"><i className="fa fa-download"></i></span>{item.download_count} 下载</span> + </span> + </p> + </div> + </li> + ) + }) + } + + </div> + ) + } +} +export default CaseItem; \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/CaseList.js b/public/react/src/modules/moop_cases/CaseList.js new file mode 100644 index 000000000..b968cc930 --- /dev/null +++ b/public/react/src/modules/moop_cases/CaseList.js @@ -0,0 +1,134 @@ +import React,{ Component } from "react"; +import { Input , Spin , Pagination } from "antd"; +import './css/moopCases.css' +import '../courses/css/Courses.css' + +import { ActionBtn } from 'educoder'; + +import axios from 'axios' + +import NoneData from '../courses/coursesPublic/NoneData' + +import mainImg from '../../images/moop_cases/teach_ex.jpg' +import CaseItem from './CaseItem' + + +const Search = Input.Search; +class CaseList extends Component{ + constructor(props){ + super(props); + this.state={ + type:0, + search:undefined, + page:1, + pageSize:20, + libraries:undefined, + totalCount:undefined + } + } + + componentDidMount = () =>{ + let { type , search , page , pageSize } = this.state; + this.InitList(type,search,page,pageSize); + } + + // 列表 + InitList = (type,search,page,pageSize) =>{ + let url = `/libraries.json`; + axios.get(url,{params:{ + type:type == 0 ? undefined : "mine", + keyword:search, + page, + per_page:pageSize + }}).then((result)=>{ + if(result){ + this.setState({ + libraries:result.data.libraries, + totalCount:result.data.count + }) + } + }).catch((error)=>{ + console.log(error); + }) + } + + // tab切换 + changeType = (type) =>{ + this.setState({ + type, + page:1 + }) + let { search , page , pageSize } = this.state; + this.InitList(type,search,page,pageSize); + } + + // 输入搜索内容 + inputStudent=(e)=>{ + this.setState({ + search:e.target.value + }) + } + + // 搜索 + searchInfo = () =>{ + let { type , search , pageSize } = this.state; + this.InitList( type , search , 1 , pageSize ); + } + + // 切换分页 + onChangePage =(pageNumber)=>{ + this.setState({ + page:pageNumber + }) + let { type , search , pageSize } = this.state; + this.InitList( type , search , pageNumber , pageSize ); + } + + render(){ + let { type , search ,libraries , totalCount ,pageSize ,page } = this.state; + return( + <React.Fragment> + <img src={mainImg} width="100%" /> + <div className="educontent"> + <div className="edu-back-white mb30 mt30"> + <p className="padding20-30 clearfix bor-bottom-greyE"> + <span className="font-18 fl color-grey-3">教学案例</span> + <ActionBtn style="colorBlue" className="fr" onClick={() => this.addQuestion(null, Q_TYPE_SINGLE)}>发布案例</ActionBtn> + </p> + <div className="clearfix pl30 pr30"> + <ul className="fl library_nav mt25"> + <li className={type == 0 ? "active" :""} onClick={()=>this.changeType(0)}> + <a href="javascript:void(0)">全部</a> + </li> + <li className={type == 1 ? "active" :""} onClick={()=>this.changeType(1)}> + <a href="javascript:void(0)">我的</a> + </li> + </ul> + <div className="fr mt16 mb16 searchView"style={{width:"300px"}}> + <Search + value={search} + placeholder="输入教学案例标题、作者、单位进行检索" + onInput={this.inputStudent} + onSearch={this.searchInfo} + ></Search> + </div> + </div> + </div> + { + libraries && libraries.length > 0 && <CaseItem {...this.props} {...this.state} libraries={libraries}></CaseItem> + } + { + libraries && libraries.length == 0 && <div className="mb50"><NoneData></NoneData></div> + } + { + totalCount && totalCount > pageSize && + <div className="mb50 edu-txt-center clearfix"> + <Pagination defaultCurrent={page} current={page} pageSize={pageSize} showQuickJumper onChange={this.onChangePage} total={totalCount}></Pagination> + </div> + } + </div> + </React.Fragment> + ) + } +} +export default CaseList \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/CaseNew.js b/public/react/src/modules/moop_cases/CaseNew.js new file mode 100644 index 000000000..056733283 --- /dev/null +++ b/public/react/src/modules/moop_cases/CaseNew.js @@ -0,0 +1,420 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' +import { Form , Input , Upload , Button , Icon , message , Tooltip } from "antd"; + +import { getImageUrl , MarkdownToHtml , ActionBtn , appendFileSizeToUploadFile , appendFileSizeToUploadFileAll , getUrl , getUploadActionUrl } from 'educoder'; + +import Tags from './CaseTags' + +import axios from 'axios'; +import TPMMDEditor from '../tpm/challengesnew/TPMMDEditor'; +import _ from 'lodash' +const { Dragger } = Upload; +function beforeUpload(file) { + const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; + if (!isJpgOrPng) { + message.error('You can only upload JPG/PNG file!'); + } + const isLt2M = file.size / 1024 / 1024 < 2; + if (!isLt2M) { + message.error('Image must smaller than 2MB!'); + } + return isJpgOrPng && isLt2M; +} +function getBase64(img, callback) { + const reader = new FileReader(); + reader.addEventListener('load', () => callback(reader.result)); + reader.readAsDataURL(img); +} + const $ = window.$; +class CaseNew extends Component{ + constructor(props){ + super(props); + this.DescMdRef = React.createRef(); + + this.state={ + casesTags:[], + contentFileList:[], + filesID:[], + imageUrl:undefined, + loading: false, + checkTag:false, + coverID:undefined + } + } + + // 上传附件-删除确认框 + onAttachmentRemove = (file, stateName) => { + this.props.confirm({ + content: '是否确认删除?', + onOk: () => { + this.deleteAttachment(file, stateName) + }, + onCancel() { + console.log('Cancel'); + }, + }); + return false; + } + + // 上传附件-确认删除 + deleteAttachment = (file, stateName) => { + // 初次上传不能直接取uid + const url = `/attachments/${file.response ? file.response.id : file.uid}.json` + axios.delete(url, { + }).then((response) => { + if (response.data) { + const { status } = response.data; + if (status == 0) { + console.log('--- success') + + this.setState((state) => { + const index = state[stateName].indexOf(file); + const newFileList = state[stateName].slice(); + newFileList.splice(index, 1); + return { + [stateName]: newFileList, + }; + }); + } + } + }) + .catch(function (error) { + console.log(error); + }); + } + // 上传附件-change + handleContentUploadChange = (info) => { + let contentFileList = info.fileList; + this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)}); + let list = appendFileSizeToUploadFileAll(contentFileList); + this.setState({ + filesID:list.map(item=>{ + return ( item.response && item.response.id ) + }) + }) + console.log("fujian"); + console.log(list.map(item=>{ + return ( item.response && item.response.id ) + })); + } + + // 上传封面图-change + handleChange = (info) => { + if (info.file.status === 'uploading') { + this.setState({ loading: true }); + return; + } + if (info.file.status === 'done') { + // Get this url from response in real world. + getBase64(info.file.originFileObj, imageUrl => + this.setState({ + imageUrl, + loading: false + }), + ); + console.log(info.file); + this.setState({ + coverID:info.file.response && info.file.response.id + }) + } + }; + + // 编辑时加载数据 + componentDidMount=()=>{ + if(this.props.match.params.caseID){ + this.InitEditData(); + } + } + + componentDidUpdate=(prevState)=>{ + if(this.props.CaseDetail && prevState.CaseDetail != this.props.CaseDetail){ + this.props.form.setFieldsValue({ + caseTitle:this.props.CaseDetail.title, + userName:this.props.CaseDetail.author_name, + userUnit:this.props.CaseDetail.author_school_name, + }) + this.setState({ + contentFileList:this.props.CaseDetail.attachments.map(item => { + return { + id: item.id, + uid: item.id, + name: appendFileSizeToUploadFile(item), + url: item.url, + filesize: item.filesize, + status: 'done' + } + }), + coverID:this.props.cover && this.props.cover.id, + imageUrl:this.props.CaseDetail.cover && getImageUrl(this.props.CaseDetail.cover.url), + casesTags:this.props.tags.map(item=>{ + return (item.id); + }) + }) + } + } + + InitEditData=()=>{ + let caseID = this.props.match.params.caseID; + this.props.getDetail(caseID); + } + + // 申请提交和保存 + handleSubmit = (type) => { + let caseID = this.props.match.params.caseID; + console.log(type); + this.props.form.validateFieldsAndScroll((err, values) => { + let { casesTags } = this.state; + if(casesTags.length == 0){ + $("html").animate({ scrollTop: $("#tagFormItem").offset().top - 100 }); + this.setState({ + checkTag:true + }) + return; + } + //const mdContnet = this.DescMdRef.current.getValue().trim(); + console.log(values); + let url = caseID ? `/libraries/${caseID}.json`: `/libraries.json`; + if(caseID){ + axios.put((url),{ + title:values.caseTitle, + author_name:values.userName, + author_school_name:values.userUnit, + content:values.description, + attachment_ids:this.state.filesID, + tag_ids:this.state.casesTags, + cover_id:this.state.coverID, + publish:type == 'save' ? false : true + }).then((result)=>{ + if(result){ + this.props.showNotification(type == 'save' ? `案例保存成功!`: `提交成功!`); + this.props.history.push(type == 'save' ? `/moop_cases/${result.data.id}` : `/moop_cases/${result.data.id}/publish_success`); + } + }).catch((error)=>{ + console.log(error); + }) + }else{ + axios.post((url),{ + title:values.caseTitle, + author_name:values.userName, + author_school_name:values.userUnit, + content:values.description, + attachment_ids:this.state.filesID, + tag_ids:this.state.casesTags, + cover_id:this.state.coverID, + publish:type == 'save' ? false : true + }).then((result)=>{ + if(result){ + this.props.showNotification(type == 'save' ? `案例保存成功!`: `提交成功!`); + this.props.history.push(type == 'save' ? `/moop_cases/${result.data.id}` : `/moop_cases/${result.data.id}/publish_success`); + } + }).catch((error)=>{ + console.log(error); + }) + } + + }) + } + + // 选择标签 + changeType=(type)=>{ + let tags = []; + if(this.state.casesTags.indexOf(type) > -1){ + tags = this.state.casesTags.filter(item => item != type); + }else{ + tags = this.state.casesTags.concat(type); + } + const tagUniqed = _.uniq(tags); + this.setState({ + casesTags: tagUniqed, + checkTag:tags.length > 0 ? false : true + }) + } + + render(){ + let { caseID } = this.props.match.params; + let { CaseDetail } = this.props; + let { casesTags , contentFileList , imageUrl , checkTag } = this.state; + const {getFieldDecorator} = this.props.form; + + + // 上传附件点击事件 + const uploadProps = { + width: 600, + multiple: true, + fileList:contentFileList, + action: `${getUploadActionUrl()}`, + onChange: this.handleContentUploadChange, + onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'), + beforeUpload: (file) => { + const isLt150M = file.size / 1024 / 1024 < 150; + if (!isLt150M) { + message.error('文件大小必须小于150MB!'); + } + return isLt150M; + } + }; + // 上传封面图-html + const uploadButton = ( + <div> + <Icon className='font-36 color-grey-c' type={this.state.loading ? 'loading' : 'plus'} /> + </div> + ); + // 上传封面图点击事件 + const uploadCover = { + listType:"picture-card", + className:"avatar-uploader", + showUploadList:false, + action:`${getUploadActionUrl()}`, + onChange:this.handleChange, + } + return( + <div className="educontent mt10 mb50"> + <style> + { + ` + .newCases .ant-col.ant-form-item-label{ + float:left; + margin-right:20px; + height:35px; + line-height:35px; + } + + .newCaseUpload{ + width: 100%; + background: #F2F9FF; + justify-content: center; + align-items: center; + display: -webkit-flex; + text-align: center; + height: 120px; + border-radius: 4px; + border: 1px dashed #4cacff; + } + .newCases .ant-form-item{ + margin-bottom:20px!important ; + } + .newCases .ant-col.ant-form-item-control-wrapper{ + position:relative; + } + .newCases .ant-form-explain{ + position:absolute; + bottom:-18px; + left:76px; + padding-left: 7px; + } + .newCases .resetLeft .ant-form-explain{ + left:0px; + } + .newCases .resetBottom .ant-form-explain{ + bottom:2px; + } + ` + } + </style> + <p className="mt10 mb20 clearfix lineh-20"> + <a href="/moop_cases" className="color-grey-9">教学案例</a> > <span className="color-grey-3">{ caseID ? "编辑" : "新建" }</span> + </p> + <p class="lineh-25 font-22 mb20">上传教学案例</p> + <Form onSubmit={this.handleSubmit} className={"newCases"}> + <div className="padding30 edu-back-white"> + <Form.Item label="标题"> + {getFieldDecorator('caseTitle', { + rules: [{required: true, message: "案例标题不能为空"}], + })( + <Input placeholder="例如:软件工程教学案例" className="greyInput winput-300-35 mr20 fl"/> + )} + <span className="color-grey-c font-12 fl">简明扼要介绍文档/视频所包含的主要的内容</span> + </Form.Item> + <div className="clearfix"> + <Form.Item label="作者" className="fl with22"> + {getFieldDecorator('userName', { + rules: [{required: true, message: "请输入作者姓名"}], + })( + <Input placeholder="请输入姓名" className="greyInput winput-120-35 mr20 fl winput150"/> + )} + </Form.Item> + <Form.Item className="fl resetLeft"> + {getFieldDecorator('userUnit', { + rules: [{required: true, message: "请输入作者单位名称"}], + })( + <Input placeholder="请输入作者单位名称" className="greyInput winput-300-35 mr20 fl"/> + )} + </Form.Item> + </div> + <div className={checkTag==true ? "clearfix mb20 pr has-error" : "clearfix mb20"} id="tagFormItem"> + <span className="upload_Title must">标签</span> + <ul className="fl libraries_tab"> + <li className={ casesTags.indexOf(1) > -1 ? "active" :"" } onClick={()=>this.changeType(1)}>获奖案例</li> + <li className={ casesTags.indexOf(2) > -1 ? "active" :"" } onClick={()=>this.changeType(2)}>入库案例</li> + </ul> + { + checkTag && <div class="ant-form-explain">请选择标签</div> + } + </div> + <Form.Item label="描述" className="resetBottom" style={{marginBottom:"0px"}}> + {getFieldDecorator('description', { + rules: [{ + required: true, message: '请输入描述内容' + }], + })( + <TPMMDEditor ref={this.DescMdRef} placeholder="请添加描述" mdID={'caseContentMD'} refreshTimeout={1500} + watch={true} className="caseMessageMD" initValue={CaseDetail && CaseDetail.content}></TPMMDEditor> + )} + </Form.Item> + <div style={{marginLeft:"76px"}}> + <Dragger {...uploadProps} className="librariesField upload_1"> + <p className="ant-upload-text color-blue font-18 mb20">上传附件</p> + <p className="ant-upload-text color-grey-c">从我的电脑选择要上传的文档:按住CTRL可以上传多份文档。单个文件最大限制:150MB</p> + </Dragger> + </div> + <p className="lineh-25 mt20 mb10 clearfix"> + <span className="upload_Title" style={{marginRight:"12px"}}>封面图</span><span class="color-grey-c fl lineh-35">(上传尺寸:120*90 px)</span> + </p> + <div style={{marginLeft:"76px"}} className="uploadImage"> + <Upload {...uploadCover}> + { imageUrl ? + <Tooltip title="重新上传"> + <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> + </Tooltip> + : + <Tooltip title="上传图片"> + {uploadButton} + </Tooltip> + } + </Upload> + </div> + </div> + <div className="padding30 bor-top-greyE edu-back-white"> + <li className="lineh-25 color-grey-6 font-18 mb20">审核说明</li> + <ul className="font-16"> + <li>平台管理员将对每天新上传的文档进行审核,审核通过的文档将公开显示,否则将私有化或移除</li> + </ul> + </div> + <div className="padding30 bor-top-greyE edu-back-white"> + <li className="lineh-25 color-grey-6 font-18 mb20">温馨提示</li> + <ul className="font-16 lineh-30"> + <li>1.请勿上传已设置加密口令的文档资源;</li> + <li>2.可以上传符合教学案例标准的文档资料,如 + <a className="color-blue" target="_blank" href="https://www.educoder.net/courses/1309/boards/5909/messages/34799">案例入库标准</a>、 + <a target="_blank" className="color-blue" href="https://www.educoder.net/courses/1309/boards/5909/messages/34798">案例使用说明书</a>以及其他资料等,上传支持的文件最大容量:100MB;</li> + <li>3.请确保上传内容无侵权或违反国家关于互联网政策的不良行为;</li> + <li>4.请使用Chrome,Firefox,Safari,IE11(及以上版本)浏览器;</li> + </ul> + </div> + <Form.Item> + <div className="clearfix mt30 mb30"> + { + !caseID && <Button type="primary" onClick={()=>this.handleSubmit("submit")} className="defalutSubmitbtn fl mr20">申请发布</Button> + } + <a className="defalutCancelbtn fl" onClick={()=>this.handleSubmit("save")}>保存</ a> + </div> + </Form.Item> + </Form> + </div> + ) + } +} +const WrappedCoursesNewApp = Form.create({name: 'CaseNew'})(CaseNew); +export default WrappedCoursesNewApp; \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/CaseTags.js b/public/react/src/modules/moop_cases/CaseTags.js new file mode 100644 index 000000000..4b4f0670c --- /dev/null +++ b/public/react/src/modules/moop_cases/CaseTags.js @@ -0,0 +1,26 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' + +class CaseTags extends Component{ + constructor(props){ + super(props); + } + + render(){ + let { tags } = this.props; + return( + <React.Fragment> + { + tags && tags.map((item,key)=>{ + return( + <span key={key} className={item.name == "获奖案例" ? "edu-filter-btn fl cdefault edu-activity-red ml10" : "edu-filter-btn fl cdefault edu-activity-blue ml10"}>{item.name}</span> + ) + }) + } + </React.Fragment> + + ) + } +} +export default CaseTags; \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/Success.js b/public/react/src/modules/moop_cases/Success.js new file mode 100644 index 000000000..d612758c8 --- /dev/null +++ b/public/react/src/modules/moop_cases/Success.js @@ -0,0 +1,28 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' +import { getImageUrl } from 'educoder'; +import success from '../../images/moop_cases/success.png' + +class Success extends Component{ + constructor(props){ + super(props); + } + render(){ + return( + <div className="educontent edu-back-white mt20 successPage"> + <div> + <img src={success} width="100" className="mb30" /> + <p className="lineh-30 ed-txt-center font-24 color-grey-3 font-bd">恭喜!</p> + <p className="lineh-30 ed-txt-center font-24 color-grey-3 font-bd mb20">提交成功</p> + <p className="lineh-30 ed-txt-center font-16 color-grey-9 font-bd mb20">平台正在审核您的申请,审核结果将以平台消息的形式通知您</p> + <li className="inline"> + <a href={`/moop_cases/${this.props.match.params.caseID}`} className="white-btn edu-blueline-btn changebtn mr20 fl">查看已上传文档</a> + <a href="/moop_cases/new" className="white-btn edu-blueback-btn changebtn fl">继续上传</a> + </li> + </div> + </div> + ) + } +} +export default Success; \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/css/moopCases.css b/public/react/src/modules/moop_cases/css/moopCases.css new file mode 100644 index 000000000..01668b637 --- /dev/null +++ b/public/react/src/modules/moop_cases/css/moopCases.css @@ -0,0 +1,155 @@ +.winput-300-35{ + width: 300px; + height: 35px; + padding: 5px; + box-sizing: border-box; +} +.library_nav li { + float: left; + cursor: pointer; + margin-right: 30px; + position: relative; + color: #05101A; + height: 40px; + line-height: 20px; + font-size: 16px; +} +.library_nav li.active a, .library_nav li:hover a{ + color: #4cacff!important; +} +.library_list { + margin-bottom: 30px; +} +.library_list_item:hover { + box-shadow: 0px 4px 8px rgba(158,158,158,0.16); +} +.library_list_item { + background: #fff; + padding: 20px 30px; + margin-bottom: 30px; + display: flex; +} +.library_list_item .library_l_name { + max-width: 600px; + float: left; +} + +.edu-activity-red { + background-color: #FC2B6A; + color: #fff!important; + cursor: pointer; + border: 1px solid #FC2B6A; + line-height: 17px; +} +.edu-activity-green { + background-color: green; + color: #fff!important; + cursor: pointer; + border: 1px solid green; + line-height: 17px; +} +.edu-activity-blue { + background-color: #4CACFF; + color: #fff!important; + cursor: pointer; + border: 1px solid #4CACFF; + line-height: 17px; +} + +.pointsBtn { + width: 70px; + height: 70px; + background-color: #4cacff; + border-radius: 50%; + color: #fff; + text-align: center; + margin: 0 auto; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 2px 0; + cursor: pointer; + line-height: 22px; + padding-top: 12px; +} +.pointedBtn{ + background: #BCD1E3; + cursor: default +} +.pointsBtn span{ + display: block; +} +.upload_Title { + position: relative; + margin-right: 20px; + float: left; + line-height: 35px; + font-size: 16px; + width: 56px; + color:rgba(0, 0, 0, 0.85); + text-align: center +} +.upload_Title.must:before { + display: inline-block; + margin-right: 4px; + color: #f5222d; + font-size: 14px; + font-family: SimSun, sans-serif; + line-height: 1; + content: '*'; +} +.upload_Title:after{ + content: ':'; + position: relative; + top: -0.5px; + margin: 0 8px 0 2px; +} +.libraries_tab li { + width: 120px; + height: 35px; + line-height: 33px; + border-radius: 18px; + border: 1px solid #4C98FF; + color: #4C98FF; + cursor: pointer; + margin-right: 30px; + float: left; + text-align: center; +} +.libraries_tab li.active { + background: #4C98FF; + color: #fff; +} +.librariesField .ant-upload{ + width: 100%; + background: #F2F9FF; + justify-content: center; + align-items: center; + display: -webkit-flex; + text-align: center; + height: 120px!important; + border-radius: 4px; + border: 1px dashed #4cacff!important; + display: block; + cursor: pointer; +} +.uploadImage .ant-upload.ant-upload-select-picture-card{ + width:120px; + height: 90px; +} +.uploadImage .ant-upload.ant-upload-select-picture-card > .ant-upload{ + padding:0px!important; +} +.successPage { + justify-content: center; + align-items: center; + display: -webkit-flex; + height: 570px; + text-align: center; + margin-bottom: 50px; +} +.changebtn { + width:166px; + font-size: 16px; + height: 45px; + line-height: 45px; +} \ No newline at end of file diff --git a/public/react/src/modules/moop_cases/index.js b/public/react/src/modules/moop_cases/index.js new file mode 100644 index 000000000..cc8ae68b9 --- /dev/null +++ b/public/react/src/modules/moop_cases/index.js @@ -0,0 +1,130 @@ +import React,{ Component } from "react"; +import './css/moopCases.css' +import '../courses/css/Courses.css' + +import { SnackbarHOC } from 'educoder'; + +import { TPMIndexHOC } from '../tpm/TPMIndexHOC'; +import { CNotificationHOC } from '../courses/common/CNotificationHOC' + +import {BrowserRouter as Router,Route,Switch} from 'react-router-dom'; +import Loading from '../../Loading'; +import Loadable from 'react-loadable'; + +import axios from 'axios'; + +const CaseList = Loadable({ + loader: () => import('./CaseList'), + loading:Loading, +}) +const CaseDetail = Loadable({ + loader: () => import('./CaseDetail'), + loading:Loading, +}) +const CaseNew = Loadable({ + loader: () => import('./CaseNew'), + loading:Loading, +}) +const CaseSuccess = Loadable({ + loader: () => import('./Success'), + loading:Loading, +}) + + + +class Index extends Component{ + constructor(props){ + super(props); + this.state={ + praise_count:0, + CaseDetail:undefined, + cover:undefined, + creator:undefined, + operation:undefined, + tags:undefined, + attachments:undefined, + user_praised:true, + } + } + // 获取案例详情 + getDetail = (caseID) =>{ + let url=`/libraries/${caseID}.json` + axios.get(url).then((result)=>{ + if(result){ + this.setState({ + CaseDetail:result.data, + praise_count:result.data.praise_count, + cover:result.data.cover, + creator:result.data.creator, + operation:result.data.operation, + user_praised:result.data.operation.user_praised, + tags:result.data.tags, + attachments:result.data.attachments + }) + } + }).catch((error)=>{ + console.log(error); + }) + } + // 点赞 + praisePoint=(caseID)=>{ + let { praise_count }=this.state; + let url =`/praise_tread/like.json`; + axios.post(url,{ + object_id:caseID, + object_type:"library" + }).then((result)=>{ + if(result){ + this.setState({ + praise_count: parseInt(praise_count)+1, + user_praised:true + }) + } + }).catch((error)=>{ + console.log(error); + }) + } + + render(){ + + return( + <div className="newMain"> + <Switch {...this.props}> + + <Route exact path="/moop_cases" + render={ + (props) => (<CaseList {...this.props} {...props} {...this.state} />) + } + ></Route> + + <Route exact path="/moop_cases/new" + render={ + (props) => (<CaseNew {...this.props} {...props} {...this.state} />) + } + ></Route> + + <Route exact path="/moop_cases/:caseID" + render={ + (props) => (<CaseDetail {...this.props} {...props} {...this.state} getDetail={this.getDetail} praisePoint ={this.praisePoint}/>) + } + ></Route> + + + <Route exact path="/moop_cases/:caseID/edit" + render={ + (props) => (<CaseNew {...this.props} {...props} {...this.state} getDetail={this.getDetail} />) + } + ></Route> + + <Route exact path="/moop_cases/:caseID/publish_success" + render={ + (props) => (<CaseSuccess {...this.props} {...props} {...this.state} getDetail={this.getDetail} />) + } + ></Route> + + </Switch> + </div> + ) + } +} +export default CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(Index) )); \ No newline at end of file