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> &gt; <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> &gt; <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