Merge branch 'dev_react' of https://git.trustie.net/Hjqreturn/trustieplus into develop
This commit is contained in:
commit
33d9eea3a4
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>Educoder</title><script type="text/javascript">window.__isR=!0</script><link rel="stylesheet" href="/react/build/css/css_min_all.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_653600_euyzqz9ex50cnmi.css"><link href="/react/build/./static/css/main.aeee84e5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="page -layout-v -fit"></div><div id="picture_display" style="display:none"></div><script type="text/javascript" src="/react/build/js/js_min_all_2.js"></script><script type="text/javascript" src="/assets/kindeditor/kindeditor.js"></script><script type="text/javascript" src="/react/build/js/create_kindeditor.js"></script><script type="text/javascript" src="/react/build/./static/js/main.1220ee9a.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>Educoder</title><script type="text/javascript">window.__isR=!0</script><link rel="stylesheet" href="/react/build/css/css_min_all.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_653600_euyzqz9ex50cnmi.css"><link href="/react/build/./static/css/main.d04ce8a5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="page -layout-v -fit"></div><div id="picture_display" style="display:none"></div><script type="text/javascript" src="/react/build/js/js_min_all_2.js"></script><script type="text/javascript" src="/assets/kindeditor/kindeditor.js"></script><script type="text/javascript" src="/react/build/js/create_kindeditor.js"></script><script type="text/javascript" src="/react/build/./static/js/main.92a4278d.js"></script></body></html>
|
||||
|
|
@ -1,20 +1,24 @@
|
|||
{
|
||||
"./static/js/0.8178fab1.chunk.js": "./static/js/0.8178fab1.chunk.js",
|
||||
"./static/js/0.8178fab1.chunk.js.map": "./static/js/0.8178fab1.chunk.js.map",
|
||||
"./static/js/1.0774461e.chunk.js": "./static/js/1.0774461e.chunk.js",
|
||||
"./static/js/1.0774461e.chunk.js.map": "./static/js/1.0774461e.chunk.js.map",
|
||||
"./static/js/2.ff99d076.chunk.js": "./static/js/2.ff99d076.chunk.js",
|
||||
"./static/js/2.ff99d076.chunk.js.map": "./static/js/2.ff99d076.chunk.js.map",
|
||||
"./static/js/3.3c55656b.chunk.js": "./static/js/3.3c55656b.chunk.js",
|
||||
"./static/js/3.3c55656b.chunk.js.map": "./static/js/3.3c55656b.chunk.js.map",
|
||||
"./static/js/4.b4f49c8d.chunk.js": "./static/js/4.b4f49c8d.chunk.js",
|
||||
"./static/js/4.b4f49c8d.chunk.js.map": "./static/js/4.b4f49c8d.chunk.js.map",
|
||||
"./static/js/5.06a0f326.chunk.js": "./static/js/5.06a0f326.chunk.js",
|
||||
"./static/js/5.06a0f326.chunk.js.map": "./static/js/5.06a0f326.chunk.js.map",
|
||||
"main.css": "./static/css/main.aeee84e5.css",
|
||||
"main.css.map": "./static/css/main.aeee84e5.css.map",
|
||||
"main.js": "./static/js/main.1220ee9a.js",
|
||||
"main.js.map": "./static/js/main.1220ee9a.js.map",
|
||||
"./static/js/0.5c3673c2.chunk.js": "./static/js/0.5c3673c2.chunk.js",
|
||||
"./static/js/0.5c3673c2.chunk.js.map": "./static/js/0.5c3673c2.chunk.js.map",
|
||||
"./static/js/1.29e1ced4.chunk.js": "./static/js/1.29e1ced4.chunk.js",
|
||||
"./static/js/1.29e1ced4.chunk.js.map": "./static/js/1.29e1ced4.chunk.js.map",
|
||||
"./static/js/2.d7daf6bd.chunk.js": "./static/js/2.d7daf6bd.chunk.js",
|
||||
"./static/js/2.d7daf6bd.chunk.js.map": "./static/js/2.d7daf6bd.chunk.js.map",
|
||||
"./static/js/3.0db7e213.chunk.js": "./static/js/3.0db7e213.chunk.js",
|
||||
"./static/js/3.0db7e213.chunk.js.map": "./static/js/3.0db7e213.chunk.js.map",
|
||||
"./static/js/4.5ff2c663.chunk.js": "./static/js/4.5ff2c663.chunk.js",
|
||||
"./static/js/4.5ff2c663.chunk.js.map": "./static/js/4.5ff2c663.chunk.js.map",
|
||||
"./static/js/5.9b55d5dc.chunk.js": "./static/js/5.9b55d5dc.chunk.js",
|
||||
"./static/js/5.9b55d5dc.chunk.js.map": "./static/js/5.9b55d5dc.chunk.js.map",
|
||||
"./static/js/6.d70c9fdd.chunk.js": "./static/js/6.d70c9fdd.chunk.js",
|
||||
"./static/js/6.d70c9fdd.chunk.js.map": "./static/js/6.d70c9fdd.chunk.js.map",
|
||||
"./static/js/7.11f39dc9.chunk.js": "./static/js/7.11f39dc9.chunk.js",
|
||||
"./static/js/7.11f39dc9.chunk.js.map": "./static/js/7.11f39dc9.chunk.js.map",
|
||||
"main.css": "./static/css/main.d04ce8a5.css",
|
||||
"main.css.map": "./static/css/main.d04ce8a5.css.map",
|
||||
"main.js": "./static/js/main.92a4278d.js",
|
||||
"main.js.map": "./static/js/main.92a4278d.js.map",
|
||||
"static\\media\\logo.svg": "static/media/logo.5d5d9eef.svg",
|
||||
"static\\media\\match_ad.jpg": "static/media/match_ad.4e957369.jpg",
|
||||
"static\\media\\message.svg": "static/media/message.a7af2a8f.svg",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>Educoder</title><script type="text/javascript">window.__isR=!0</script><link rel="stylesheet" href="/css/css_min_all.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_653600_euyzqz9ex50cnmi.css"><link href="/react/build/./static/css/main.aeee84e5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="page -layout-v -fit"></div><div id="picture_display" style="display:none"></div><script type="text/javascript" src="/js/js_min_all_2.js"></script><script type="text/javascript" src="http://localhost:3000/assets/kindeditor/kindeditor.js"></script><script type="text/javascript" src="/js/create_kindeditor.js"></script><script type="text/javascript" src="/react/build/./static/js/main.1220ee9a.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><title>Educoder</title><script type="text/javascript">window.__isR=!0</script><link rel="stylesheet" href="/css/css_min_all.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_653600_euyzqz9ex50cnmi.css"><link href="/react/build/./static/css/main.d04ce8a5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root" class="page -layout-v -fit"></div><div id="picture_display" style="display:none"></div><script type="text/javascript" src="/js/js_min_all_2.js"></script><script type="text/javascript" src="http://localhost:3000/assets/kindeditor/kindeditor.js"></script><script type="text/javascript" src="/js/create_kindeditor.js"></script><script type="text/javascript" src="/react/build/./static/js/main.92a4278d.js"></script></body></html>
|
||||
|
|
@ -1 +1 @@
|
|||
"use strict";var precacheConfig=[["/react/build/index.html","2bca516e9e1247d675780a6401df53f2"],["/react/build/static/css/main.aeee84e5.css","0c3bca40170c1c2b1c24f98ef5f6048a"],["/react/build/static/js/0.8178fab1.chunk.js","9fee95c3b447aaa6340624ba4a846afa"],["/react/build/static/js/1.0774461e.chunk.js","957738506f07420208d36eaabbb22063"],["/react/build/static/js/2.ff99d076.chunk.js","0c7d2eb1de8a69f0743029ee3e19420a"],["/react/build/static/js/3.3c55656b.chunk.js","320a2e6e1d03c47c954d8757baf619ce"],["/react/build/static/js/4.b4f49c8d.chunk.js","914795939ca56335e711899121817e2d"],["/react/build/static/js/5.06a0f326.chunk.js","6abcd79d1c9d7cb6b40f60f011ef5416"],["/react/build/static/js/main.1220ee9a.js","3353f62ce281edaabac2dd08f1dcb652"],["/react/build/static/media/logo.5d5d9eef.svg","5d5d9eefa31e5e13a6610d9fa7a283bb"],["/react/build/static/media/match_ad.4e957369.jpg","4e957369790f079662eb2dca03ece6be"],["/react/build/static/media/message.a7af2a8f.svg","a7af2a8f2f74540f8d77004b10a10ec3"],["/react/build/static/media/messagegrey.8c1dff55.svg","8c1dff55058b2926819eeda567a3ec9d"],["/react/build/static/media/passall.46817e26.png","46817e2696bbb2ac4bf0e5bc4e8b101e"],["/react/build/static/media/passpart.4aaf3e6b.png","4aaf3e6b4667dc531eee7f4e5f807d70"],["/react/build/static/media/rc-tree.776c7fe6.png","776c7fe67df0637005b1a1372066ed84"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var a=new URL(e);return"/"===a.pathname.slice(-1)&&(a.pathname+=t),a.toString()},cleanResponse=function(t){return t.redirected?("body"in t?Promise.resolve(t.body):t.blob()).then(function(e){return new Response(e,{headers:t.headers,status:t.status,statusText:t.statusText})}):Promise.resolve(t)},createCacheKey=function(e,t,a,n){var r=new URL(e);return n&&r.pathname.match(n)||(r.search+=(r.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(a)),r.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var a=new URL(t).pathname;return e.some(function(e){return a.match(e)})},stripIgnoredUrlParameters=function(e,a){var t=new URL(e);return t.hash="",t.search=t.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(t){return a.every(function(e){return!e.test(t[0])})}).map(function(e){return e.join("=")}).join("&"),t.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],a=e[1],n=new URL(t,self.location),r=createCacheKey(n,hashParamName,a,/\.\w{8}\./);return[n.toString(),r]}));function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(n){return setOfCachedUrls(n).then(function(a){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(t){if(!a.has(t)){var e=new Request(t,{credentials:"same-origin"});return fetch(e).then(function(e){if(!e.ok)throw new Error("Request for "+t+" returned a response with status "+e.status);return cleanResponse(e).then(function(e){return n.put(t,e)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var a=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(t){return t.keys().then(function(e){return Promise.all(e.map(function(e){if(!a.has(e.url))return t.delete(e)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(t){if("GET"===t.request.method){var e,a=stripIgnoredUrlParameters(t.request.url,ignoreUrlParametersMatching),n="index.html";(e=urlsToCacheKeys.has(a))||(a=addDirectoryIndex(a,n),e=urlsToCacheKeys.has(a));var r="/index.html";!e&&"navigate"===t.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],t.request.url)&&(a=new URL(r,self.location).toString(),e=urlsToCacheKeys.has(a)),e&&t.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(a)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(e){return console.warn('Couldn\'t serve response for "%s" from cache: %O',t.request.url,e),fetch(t.request)}))}});
|
||||
"use strict";var precacheConfig=[["/react/build/index.html","e033a9375f02289ebc74a5237203c415"],["/react/build/static/css/main.d04ce8a5.css","d4b9332171b8b346a5b7b42c6a82ea97"],["/react/build/static/js/0.5c3673c2.chunk.js","8a384455c8ac118b921346e7709cf59a"],["/react/build/static/js/1.29e1ced4.chunk.js","0d68566907b14aa19c82673a08553901"],["/react/build/static/js/2.d7daf6bd.chunk.js","22fa29f39e479ad64b1e575208c888d0"],["/react/build/static/js/3.0db7e213.chunk.js","620d01587912c553e60c27371d30ed6f"],["/react/build/static/js/4.5ff2c663.chunk.js","ac6bbe148d67aebe533b15b66ac8d4b5"],["/react/build/static/js/5.9b55d5dc.chunk.js","9341d548562003aa9a6e35abe5e47910"],["/react/build/static/js/6.d70c9fdd.chunk.js","bc4c1ca86abbc719dbc21d8d869ba4cb"],["/react/build/static/js/7.11f39dc9.chunk.js","ab394048b8c9aba9fdf2a0f13dfd1002"],["/react/build/static/js/main.92a4278d.js","77b660ed35d310e9a83ee2a289224e02"],["/react/build/static/media/logo.5d5d9eef.svg","5d5d9eefa31e5e13a6610d9fa7a283bb"],["/react/build/static/media/match_ad.4e957369.jpg","4e957369790f079662eb2dca03ece6be"],["/react/build/static/media/message.a7af2a8f.svg","a7af2a8f2f74540f8d77004b10a10ec3"],["/react/build/static/media/messagegrey.8c1dff55.svg","8c1dff55058b2926819eeda567a3ec9d"],["/react/build/static/media/passall.46817e26.png","46817e2696bbb2ac4bf0e5bc4e8b101e"],["/react/build/static/media/passpart.4aaf3e6b.png","4aaf3e6b4667dc531eee7f4e5f807d70"],["/react/build/static/media/rc-tree.776c7fe6.png","776c7fe67df0637005b1a1372066ed84"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var a=new URL(e);return"/"===a.pathname.slice(-1)&&(a.pathname+=t),a.toString()},cleanResponse=function(t){return t.redirected?("body"in t?Promise.resolve(t.body):t.blob()).then(function(e){return new Response(e,{headers:t.headers,status:t.status,statusText:t.statusText})}):Promise.resolve(t)},createCacheKey=function(e,t,a,n){var r=new URL(e);return n&&r.pathname.match(n)||(r.search+=(r.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(a)),r.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var a=new URL(t).pathname;return e.some(function(e){return a.match(e)})},stripIgnoredUrlParameters=function(e,a){var t=new URL(e);return t.hash="",t.search=t.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(t){return a.every(function(e){return!e.test(t[0])})}).map(function(e){return e.join("=")}).join("&"),t.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],a=e[1],n=new URL(t,self.location),r=createCacheKey(n,hashParamName,a,/\.\w{8}\./);return[n.toString(),r]}));function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(n){return setOfCachedUrls(n).then(function(a){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(t){if(!a.has(t)){var e=new Request(t,{credentials:"same-origin"});return fetch(e).then(function(e){if(!e.ok)throw new Error("Request for "+t+" returned a response with status "+e.status);return cleanResponse(e).then(function(e){return n.put(t,e)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var a=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(t){return t.keys().then(function(e){return Promise.all(e.map(function(e){if(!a.has(e.url))return t.delete(e)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(t){if("GET"===t.request.method){var e,a=stripIgnoredUrlParameters(t.request.url,ignoreUrlParametersMatching),n="index.html";(e=urlsToCacheKeys.has(a))||(a=addDirectoryIndex(a,n),e=urlsToCacheKeys.has(a));var r="/index.html";!e&&"navigate"===t.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],t.request.url)&&(a=new URL(r,self.location).toString(),e=urlsToCacheKeys.has(a)),e&&t.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(a)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(e){return console.warn('Couldn\'t serve response for "%s" from cache: %O',t.request.url,e),fetch(t.request)}))}});
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,2 +0,0 @@
|
|||
webpackJsonp([5],{852:function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==typeof t&&"function"!==typeof t?e:t}function i(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var u=n(0),c=n.n(u),a=(n(164),n(2)),f=(n.n(a),n(853)),l=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),p=function(e){function t(e){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e))}return i(t,e),l(t,[{key:"componentWillReceiveProps",value:function(e,t){}},{key:"render",value:function(){return c.a.createElement(f.a,Object.assign({},this.state,this.props))}}]),t}(u.Component);t.default=p},853:function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!==typeof t&&"function"!==typeof t?e:t}function i(e,t){if("function"!==typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var u=n(0),c=n.n(u),a=(n(164),n(2)),f=(n.n(a),function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}()),l=function(e){function t(e){return r(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e))}return i(t,e),f(t,[{key:"componentWillReceiveProps",value:function(e,t){}},{key:"render",value:function(){return c.a.createElement("div",null,"TPMChallenge")}}]),t}(u.Component);t.a=l}});
|
||||
//# sourceMappingURL=5.06a0f326.chunk.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"antd": "^3.6.5",
|
||||
"autoprefixer": "7.1.6",
|
||||
"axios": "^0.18.0",
|
||||
"babel-core": "6.26.0",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -51,8 +51,8 @@ const TestCodeMirrorComponent = Loadable({
|
|||
loading: Loading,
|
||||
})
|
||||
|
||||
const TaskListContainerComponent = Loadable({
|
||||
loader: () => import('./modules/taskList/TaskListContainer'),
|
||||
const TestComponent = Loadable({
|
||||
loader: () => import('./modules/test/TestRC'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
|
@ -66,6 +66,13 @@ const ForumsIndexComponent = Loadable({
|
|||
loading: Loading,
|
||||
})
|
||||
|
||||
// trustie plus forum
|
||||
const TPForumsIndexComponent = Loadable({
|
||||
loader: () => import('./modules/tp-forums/TPForumsIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
|
||||
// const TestPageComponent = Loadable({
|
||||
// loader: () => import('./modules/page/Index'),
|
||||
|
|
@ -90,7 +97,7 @@ class App extends Component {
|
|||
|
||||
<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>
|
||||
|
||||
<Route path="/taskList" component={TaskListContainerComponent}/>
|
||||
<Route path="/testRCComponent" component={TestComponent}/>
|
||||
|
||||
|
||||
|
||||
|
|
@ -101,6 +108,9 @@ class App extends Component {
|
|||
|
||||
<Route path="/forums" component={ForumsIndexComponent}>
|
||||
</Route>
|
||||
|
||||
<Route path="/tpforums" component={TPForumsIndexComponent}>
|
||||
</Route>
|
||||
|
||||
|
||||
<Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ class NotFoundPage extends Component {
|
|||
|
|
||||
<Link to="/taskList">taskList</Link>
|
||||
|
|
||||
<Link to="/testPage">testPage</Link>
|
||||
<Link to="/testRCComponent">testRCComponent</Link>
|
||||
|
|
||||
<Link to="/tpforums">tpforums</Link>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@
|
|||
width: fit-content;
|
||||
}
|
||||
|
||||
.comment_content img {
|
||||
max-width: 23% !important;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------- CommentInput START */
|
||||
|
||||
/*收起的时候radius变化*/
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class MemoDetail extends Component {
|
|||
// if (this.props.memo && this.props.memo.content
|
||||
// && (!prevProps.memo || prevProps.memo.content != this.props.memo.content) ) {
|
||||
if (this.props.memo && this.props.memo.content && prevState.memoLoading === true && this.state.memoLoading === false) {
|
||||
// md渲染content
|
||||
// md渲染content,等xhr执行完(即memoLoading变化),memo.content更新后初始化md
|
||||
if (this.props.memo.is_md) {
|
||||
setTimeout(()=>{
|
||||
var shixunDescr = window.editormd.markdownToHTML("memo_content_editorMd", {
|
||||
|
|
@ -641,9 +641,9 @@ class MemoDetail extends Component {
|
|||
<div className="clearfix">
|
||||
<div id="forum_list" className="forum_table mh650">
|
||||
<div className="padding40-30 bor-bottom-greyE">
|
||||
<div className="font-16 mb5 cdefault clearfix pr">
|
||||
<span className="noteDetailTitle task-hide fl">{memo.subject}</span>
|
||||
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 mt10 fl">置顶</span>}
|
||||
<div className="font-16 mb5 cdefault clearfix pr pr20">
|
||||
<span className="noteDetailTitle">{memo.subject}</span>
|
||||
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 mt10">置顶</span>}
|
||||
{ !!memo.reward && <span className="color-orange font-14 ml15"
|
||||
data-tip-down={`获得平台奖励金币:${memo.reward}`} >
|
||||
<i className="iconfont icon-gift mr5"></i>{memo.reward}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
.user_default_btn {width: 114px;}
|
||||
.userPrivateName{line-height: 25px;margin-bottom: 9px;}
|
||||
.userPrivatePost{line-height: 20px;}
|
||||
.noteDetailTitle{max-width: 650px;line-height: 38px;font-size: 24px;font-weight: normal}
|
||||
.noteDetailTitle{line-height: 38px;font-size: 24px;font-weight: normal}
|
||||
|
||||
.noteDetailNum{float: left;padding:0px 12px;position: relative;color: #999!important;height: 28px;line-height: 26px;}
|
||||
.noteDetailNum.rightline:after{position: absolute;content: '';right: 0px;width: 1px;background-color: #EAEAEA;height: 8px;top:10px;}
|
||||
|
|
|
|||
|
|
@ -37,19 +37,34 @@
|
|||
|
||||
/* ImageLayer ----------------- Start */
|
||||
.passContent{
|
||||
/*width: 652px;*/
|
||||
|
||||
height:100%;
|
||||
margin:0px auto;
|
||||
margin-top: 4%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.passImg{
|
||||
/*position: absolute;*/
|
||||
z-index: 2;
|
||||
user-select: none;
|
||||
flex: 0;
|
||||
}
|
||||
/* ImageLayer ----------------- End */
|
||||
|
||||
.passTaskContent{
|
||||
width: 652px;
|
||||
height:100%;
|
||||
margin:0px auto;
|
||||
margin-top: 4%;
|
||||
}
|
||||
.passTaskImg{
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.winpPerson{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ class TaskResultLayer extends Component {
|
|||
setTimeout(()=>{
|
||||
// true ||
|
||||
if ( isLastGame) {
|
||||
var dClass = 'zoomInDown' ;$('.passContent').show().addClass(dClass + ' animated');
|
||||
setTimeout( function(){$('.passContent').removeClass(dClass + ' animated')}, 1000)
|
||||
var dClass = 'zoomInDown' ;$('.passTaskContent').show().addClass(dClass + ' animated');
|
||||
setTimeout( function(){$('.passTaskContent').removeClass(dClass + ' animated')}, 1000)
|
||||
} else {
|
||||
var dClass = 'zoomIn' ;$('.passContent').show().addClass(dClass + ' animated');
|
||||
setTimeout( function(){$('.passContent').removeClass(dClass + ' animated')}, 1000)
|
||||
var dClass = 'zoomIn' ;$('.passTaskContent').show().addClass(dClass + ' animated');
|
||||
setTimeout( function(){$('.passTaskContent').removeClass(dClass + ' animated')}, 1000)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
|
@ -120,9 +120,9 @@ class TaskResultLayer extends Component {
|
|||
<div>
|
||||
{currentGamePassed ?
|
||||
<div className="taskResultLayer">
|
||||
<div className="pr passContent" style={{display:'none'}} >
|
||||
<div className="pr passTaskContent" style={{display:'none'}} >
|
||||
<i className="far fa-times-circle closeIcon" onClick={closeTaskResultLayer} title="关闭"></i>
|
||||
<img src={isLastGame? passallImg : passpartImg} width="652px" className="passImg"/>
|
||||
<img src={isLastGame? passallImg : passpartImg} width="652px" className="passTaskImg"/>
|
||||
<div className="winpPerson expGold">
|
||||
<p className="inline"><span className=" goldring"/>
|
||||
{currentPassedGameGainGold >= 0 ? `+${currentPassedGameGainGold}` : '+0'}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import moment from 'moment'
|
||||
import Select, {Option, OptGroup} from 'rc-select';
|
||||
import 'rc-select/assets/index.css';
|
||||
|
||||
import { Upload, Icon, message } from 'antd';
|
||||
|
||||
import 'antd/lib/upload/style/index.css'
|
||||
|
||||
const Dragger = Upload.Dragger;
|
||||
|
||||
const props = {
|
||||
name: 'file',
|
||||
multiple: true,
|
||||
action: '//jsonplaceholder.typicode.com/posts/',
|
||||
onChange(info) {
|
||||
const status = info.file.status;
|
||||
if (status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (status === 'done') {
|
||||
message.success(`${info.file.name} file uploaded successfully.`);
|
||||
} else if (status === 'error') {
|
||||
message.error(`${info.file.name} file upload failed.`);
|
||||
}
|
||||
},
|
||||
};
|
||||
// -------------------------------------------
|
||||
|
||||
function getBase64(img, callback) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => callback(reader.result));
|
||||
reader.readAsDataURL(img);
|
||||
}
|
||||
|
||||
function beforeUpload(file) {
|
||||
const isJPG = file.type === 'image/jpeg';
|
||||
// if (!isJPG) {
|
||||
// message.error('You can only upload JPG file!');
|
||||
// }
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
if (!isLt2M) {
|
||||
message.error('Image must smaller than 2MB!');
|
||||
}
|
||||
// return isJPG && isLt2M;
|
||||
return isLt2M;
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
const children = [];
|
||||
for (let i = 10; i < 36; i++) {
|
||||
// value={i}
|
||||
children.push(
|
||||
<Option key={i.toString(36) + i} disabled={i === 10} title={`中文${i}`} >
|
||||
中文{i}
|
||||
</Option>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
class TestRC extends Component {
|
||||
state = {
|
||||
loading: false,
|
||||
};
|
||||
|
||||
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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const uploadButton = (
|
||||
<div>
|
||||
<Icon type={this.state.loading ? 'loading' : 'plus'} />
|
||||
<div className="ant-upload-text">Upload</div>
|
||||
</div>
|
||||
);
|
||||
const imageUrl = this.state.imageUrl;
|
||||
return (
|
||||
<Upload
|
||||
name="avatar"
|
||||
listType="picture-card"
|
||||
className="avatar-uploader"
|
||||
showUploadList={false}
|
||||
action="//jsonplaceholder.typicode.com/posts/"
|
||||
beforeUpload={beforeUpload}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
{imageUrl ? <img src={imageUrl} alt="avatar" /> : uploadButton}
|
||||
</Upload>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TestRC;
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*Nav START*/
|
||||
._forum_tab {
|
||||
padding-bottom: 26px;
|
||||
}
|
||||
.discuss-tab {
|
||||
height: 90px;
|
||||
}
|
||||
.discuss-tab ._forum_tab a.navItem:hover {
|
||||
color: #4CACFF !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
.discuss-tab ._forum_tab a.navItem.active {
|
||||
border-bottom: none !important;
|
||||
border: 1px solid #4CACFF !important;
|
||||
color: #4CACFF !important;
|
||||
border-radius:24px;
|
||||
}
|
||||
.discuss-tab a.returnBtnA:hover {
|
||||
border-bottom: none!important;
|
||||
}
|
||||
/*Nav END*/
|
||||
|
||||
#forum_list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.rc-pagination {
|
||||
padding: 30px 20px;
|
||||
background: #FAFAFA;
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
|
||||
}
|
||||
|
||||
/*分页*/
|
||||
.ec-pagination .rc-pagination-item{
|
||||
border-radius: 2px;
|
||||
width: 30px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
.ec-pagination a{outline: none;}
|
||||
.ec-pagination .rc-pagination-jump-next{
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
.ec-pagination .rc-pagination-item:hover{
|
||||
border: 1px solid #4cacff;
|
||||
color: #4cacff;
|
||||
}
|
||||
.ec-pagination .rc-pagination-item-active{
|
||||
background-color: #4CACFF;
|
||||
}
|
||||
.ec-pagination .rc-pagination-prev, .ec-pagination .rc-pagination-next{display: none}
|
||||
.ec-pagination .rc-pagination-options-quick-jumper input{
|
||||
height: 32px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ec-pagination .rc-pagination-options-quick-jumper{
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
class ForumsNavTab extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onNavClick(active) {
|
||||
// TODO 为什么事件发不过去
|
||||
// https://github.com/facebook/react/issues/3249#issuecomment-177750141
|
||||
// window.$(window).trigger('setSearchValue', '', true);
|
||||
this.props.setSearchValue('')
|
||||
|
||||
if (!active) {
|
||||
this.props.initForumState({
|
||||
selectedHotLabelIndex: -1,
|
||||
})
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage } = this.props
|
||||
|
||||
const techSharePath = `/tpforums/categories/5`
|
||||
const guidePath = `/tpforums/categories/3`
|
||||
|
||||
const hottestPath = `/tpforums/categories/all?order=hottest` // ?order=hottest
|
||||
const newestPath = `/tpforums/categories/all?order=newest` // ?order=newest
|
||||
|
||||
const shixunDiscussPath = `/tpforums/categories/shixun_discuss`
|
||||
const locationPath = history.location.pathname + history.location.search
|
||||
/*
|
||||
<ul>
|
||||
<li className={classNames({'selected': locationPath.indexOf(techSharePath) === 0 })}>
|
||||
<Link to={`${techSharePath}`} >techShare</Link>
|
||||
</li>
|
||||
<li className={classNames({'selected': locationPath.indexOf(guidePath) === 0 })}>
|
||||
<Link to={`${guidePath}`}>guide</Link>
|
||||
</li>
|
||||
</ul>
|
||||
*/
|
||||
return (
|
||||
<div className="discuss-tab pl20 bor-bottom-greyE clearfix pr edu-back-white">
|
||||
<p className="_forum_tab clearfix">
|
||||
{/*<a href="/forums" className="fl font-16 ptl5-10 block mr20 active">
|
||||
<span className="fl">技术分享</span>
|
||||
<span className="forum_filtrate_span2 forum_filtrate_span2_bg mt10 ml10 fl">219</span>
|
||||
</a>*/}
|
||||
<Link to={`${newestPath}`} className={classNames("fl font-16 padding5-20 block mr30 navItem"
|
||||
, {'active': locationPath.indexOf('order=newest') !== -1 })}
|
||||
onClick={()=>this.onNavClick(locationPath.indexOf('order=newest') !== -1)}
|
||||
>
|
||||
<span className="fl">最新回复</span>
|
||||
</Link>
|
||||
<Link to={`${hottestPath}`} className={classNames("fl font-16 padding5-20 block mr30 navItem"
|
||||
, {'active': locationPath.indexOf('order=hottest') !== -1 })}
|
||||
onClick={()=>this.onNavClick(locationPath.indexOf('order=hottest') !== -1)}
|
||||
>
|
||||
<span className="fl">热门话题</span>
|
||||
</Link>
|
||||
<Link to={`${shixunDiscussPath}`} className={classNames("fl font-16 padding5-20 block mr30 navItem"
|
||||
, {'active': locationPath.indexOf('shixun_discuss') !== -1 })}
|
||||
onClick={()=>this.onNavClick(locationPath.indexOf('shixun_discuss') !== -1)}
|
||||
>
|
||||
<span className="fl">实训回复</span>
|
||||
</Link>
|
||||
|
||||
<Link to={`${techSharePath}`} className={classNames("fl font-16 padding5-20 block mr30 navItem"
|
||||
, {'active': locationPath.indexOf(techSharePath) === 0 })}
|
||||
onClick={()=>this.onNavClick(locationPath.indexOf(techSharePath) === 0)}
|
||||
>
|
||||
<span className="fl">技术分享</span>
|
||||
</Link>
|
||||
<Link to={`${guidePath}`} className={classNames("fl font-16 padding5-20 block mr30 navItem"
|
||||
, {'active': locationPath.indexOf(guidePath) === 0 })}
|
||||
onClick={()=>this.onNavClick(locationPath.indexOf(guidePath) === 0)}
|
||||
>
|
||||
<span className="fl">操作指南</span>
|
||||
</Link>
|
||||
{/*<a href="/forums?type=discuss" className="fl font-16 ptl5-10 block mr20">
|
||||
<span className="fl">实训交流</span>
|
||||
<span className="forum_filtrate_span2 mt10 ml10 fl">1391</span>
|
||||
</a>*/}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ForumsNavTab;
|
||||
|
|
@ -0,0 +1,776 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
import Comments from '../comment/Comments'
|
||||
|
||||
import update from 'immutability-helper'
|
||||
import Tooltip from 'material-ui/Tooltip';
|
||||
import RewardDialog from '../common/RewardDialog';
|
||||
import ImageLayer from '../page/layers/ImageLayer'
|
||||
|
||||
|
||||
import { bytesToSize } from 'educoder'
|
||||
const $ = window.$
|
||||
function urlStringify(params) {
|
||||
let noParams = true;
|
||||
let paramsUrl = '';
|
||||
for (let key in params) {
|
||||
noParams = false;
|
||||
paramsUrl += `${key}=${params[key]}&`
|
||||
}
|
||||
if (noParams) {
|
||||
return '';
|
||||
}
|
||||
paramsUrl = paramsUrl.substring(0, paramsUrl.length - 1);
|
||||
return paramsUrl;
|
||||
}
|
||||
class MemoDetail extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
memoLoading: true,
|
||||
hasMoreComments: false,
|
||||
pageCount: 1,
|
||||
|
||||
goldRewardDialogOpen: false
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
|
||||
const { match } = this.props
|
||||
|
||||
const memoUrl = `/api/v1/memos/${match.params.memoId}`;
|
||||
this.setState({
|
||||
memoLoading: true
|
||||
})
|
||||
axios.get(memoUrl,{
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const memo = response.data.memo
|
||||
|
||||
if (memo) {
|
||||
// this.setState({...response.data})
|
||||
|
||||
const { memo_replies } = response.data;
|
||||
const hasMoreComments = memo_replies ? memo_replies.length === 10 : false
|
||||
this.setState({
|
||||
hasMoreComments,
|
||||
pageCount: 1,
|
||||
comments: memo_replies
|
||||
})
|
||||
delete response.data.memo_replies;
|
||||
this.props.initForumState(response.data)
|
||||
const user = response.data.current_user;
|
||||
user.tidding_count = response.data.tidding_count;
|
||||
this.props.initCommonState(user)
|
||||
}
|
||||
this.setState({
|
||||
memoLoading: false
|
||||
}, ()=>{
|
||||
setTimeout(()=> {
|
||||
// 初始化回复ke
|
||||
this.keEditor = window.sd_create_editor_from_data(memo.id, null, "100%", "Memo");
|
||||
window._kk = this.keEditor
|
||||
|
||||
}, 300)
|
||||
})
|
||||
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
$('body>#root').on('onMemoDelete', (event) => {
|
||||
// const val = $('body>#root').data('onMemoDelete')
|
||||
const val = window.onMemoDelete ;
|
||||
this.onMemoDelete( JSON.parse(decodeURIComponent(val)) )
|
||||
})
|
||||
|
||||
|
||||
// 图片放大
|
||||
setTimeout(()=>{
|
||||
$(".commentsDelegateParent").delegate(".comment_item_cont .J_Comment_Reply img ","click", (event) => {
|
||||
const imageSrc = event.target.src
|
||||
// 非回复里的头像图片; 非emoticons
|
||||
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
|
||||
this.setState({
|
||||
showImage: true,
|
||||
imageSrc,
|
||||
})
|
||||
}
|
||||
});
|
||||
}, 4000)
|
||||
|
||||
}
|
||||
onImageLayerClose = () => {
|
||||
this.setState({
|
||||
showImage: false,
|
||||
imageSrc: '',
|
||||
})
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$('body>#root').off('onMemoDelete')
|
||||
}
|
||||
onMemoDelete(memo) {
|
||||
const deleteUrl = `/api/v1/memos/${memo.id}`;
|
||||
// 获取memo list
|
||||
axios.delete(deleteUrl, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const status = response.data.status
|
||||
if (status === 0) {
|
||||
|
||||
this.props.showSnackbar('删除成功');
|
||||
this.props.history.push(`/forums`)
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
// if (this.props.memo && this.props.memo.content
|
||||
// && (!prevProps.memo || prevProps.memo.content != this.props.memo.content) ) {
|
||||
if (this.props.memo && this.props.memo.content && prevState.memoLoading === true && this.state.memoLoading === false) {
|
||||
// md渲染content,等xhr执行完(即memoLoading变化),memo.content更新后初始化md
|
||||
if (this.props.memo.is_md) {
|
||||
setTimeout(()=>{
|
||||
var shixunDescr = window.editormd.markdownToHTML("memo_content_editorMd", {
|
||||
htmlDecode: "style,script,iframe", // you can filter tags decode
|
||||
taskList: true,
|
||||
tex: true, // 默认不解析
|
||||
flowChart: true, // 默认不解析
|
||||
sequenceDiagram: true // 默认不解析
|
||||
});
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clickPraise(){
|
||||
const { memo } = this.props;
|
||||
const url = `/api/v1/discusses/${memo.id}/plus`;
|
||||
console.log(url)
|
||||
axios.post(url, {
|
||||
container_type: 'Memo',
|
||||
type: 1 // "踩0;赞1"
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
console.log(response);
|
||||
const newMemo = Object.assign({}, this.props.memo)
|
||||
newMemo.praise_count = response.data.praise_count
|
||||
newMemo.user_praise = !newMemo.user_praise
|
||||
this.props.initForumState({memo : newMemo })
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
renderAttachment() {
|
||||
const { memo } = this.props;
|
||||
const attachments = []
|
||||
memo.attachment_url.forEach((item, index) => {
|
||||
const ar = item.url.split('/')
|
||||
const fileName = item.filename || ar[ar.length - 1]
|
||||
let filesize = 0
|
||||
if (item.filesize) {
|
||||
filesize = bytesToSize(item.filesize)
|
||||
}
|
||||
attachments.push(
|
||||
<p className="clearfix" key={index} >
|
||||
<a href={item.url} className="color-green clearfix notefileDownload">
|
||||
<i className="iconfont icon-xiazai color-green ml5 fl"></i><span className="fl">{fileName}{filesize? ` ${filesize}` : ''}</span>
|
||||
</a>
|
||||
</p>
|
||||
)
|
||||
})
|
||||
return attachments;
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------- comments START
|
||||
// ------------------------------------------------------------------------------------------- comments START
|
||||
_getUser() {
|
||||
const { current_user } = this.props;
|
||||
current_user.user_url = `/users/${current_user.login}`;
|
||||
return current_user;
|
||||
}
|
||||
_findById(id, arg_comments) {
|
||||
const comments = arg_comments;
|
||||
for(let i = 0; i < comments.length; i++) {
|
||||
if (id === comments[i].id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
replyComment = (commentContent, id, editor) => {
|
||||
if (this.props.memo.id === id ) { // 回复帖子
|
||||
this.createNewComment(commentContent, id, editor);
|
||||
return;
|
||||
}
|
||||
const url = `/api/v1/memos/${id}/reply`;
|
||||
const { comments } = this.state;
|
||||
const user = this._getUser();
|
||||
axios.post(url, {
|
||||
content: commentContent
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data.memo) {
|
||||
let newDiscuss = response.data.memo;
|
||||
|
||||
var commentIndex = this._findById(id, comments);
|
||||
let comment = comments[commentIndex];
|
||||
if (!comment.children ) {
|
||||
comment.children = []
|
||||
}
|
||||
// TODO userName iamge_url
|
||||
comment.children.push( {
|
||||
"can_delete": true,
|
||||
"content": commentContent,
|
||||
|
||||
"image_url": user.image_url,
|
||||
"username": user.username,
|
||||
|
||||
"id": newDiscuss.id,
|
||||
// "position": newDiscuss.position,
|
||||
"time": "1分钟前",
|
||||
"praise_count": 0,
|
||||
|
||||
"user_id": newDiscuss.author_id,
|
||||
})
|
||||
|
||||
|
||||
comments[commentIndex] = comment
|
||||
|
||||
editor.html('')
|
||||
const $ = window.$
|
||||
var reply_message_el = `#reply_message_${id}`
|
||||
$(reply_message_el).hide();
|
||||
|
||||
this.setState({
|
||||
// runTesting: false,
|
||||
comments: comments
|
||||
}, ()=>{
|
||||
// keditor代码美化
|
||||
window.prettyPrint()
|
||||
})
|
||||
|
||||
const newMemo2 = Object.assign({}, this.props.memo);
|
||||
newMemo2.replies_count = newMemo2.replies_count + 1;
|
||||
this.props.initForumState({
|
||||
memo: newMemo2
|
||||
})
|
||||
}
|
||||
console.log(response)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
deleteComment = (parrentComment, childCommentId) => {
|
||||
let deleteCommentId = parrentComment.id
|
||||
if (childCommentId) {
|
||||
deleteCommentId = childCommentId;
|
||||
}
|
||||
const url = `/api/v1/memos/${deleteCommentId}`
|
||||
let comments = this.state.comments;
|
||||
|
||||
axios.delete(url,
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
// TODO 删除成功或失败
|
||||
if (response.data) {
|
||||
const commentIndex = this._findById(parrentComment.id, comments);
|
||||
|
||||
// https://stackoverflow.com/questions/29527385/removing-element-from-array-in-component-state
|
||||
if (!childCommentId) {
|
||||
this.setState((prevState) => ({
|
||||
comments: update(prevState.comments, {$splice: [[commentIndex, 1]]})
|
||||
}))
|
||||
|
||||
// if (this.state.comments.length <= 5) {
|
||||
// this.fetchComment()
|
||||
// }
|
||||
} else {
|
||||
let childCommentIndex = this._findById(childCommentId, comments[commentIndex].children);
|
||||
comments[commentIndex].children = update(comments[commentIndex].children, {$splice: [[childCommentIndex, 1]]})
|
||||
this.setState({ comments })
|
||||
}
|
||||
|
||||
const newMemo = Object.assign({}, this.props.memo);
|
||||
newMemo.replies_count = newMemo.replies_count - 1;
|
||||
this.props.initForumState({
|
||||
memo: newMemo
|
||||
})
|
||||
}
|
||||
console.log(response)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
// 评论点赞
|
||||
commentPraise = (discussId) => {
|
||||
const { comments } = this.state;
|
||||
const commentIndex = this._findById(discussId, comments);
|
||||
|
||||
const url = `/api/v1/discusses/${discussId}/plus`
|
||||
axios.post(url, {
|
||||
// id: discussId,
|
||||
// container_id: challenge.id,
|
||||
container_type: 'Memo', //Discuss
|
||||
type: comments[commentIndex].user_praise === true ? 0 : 1, // "踩0;赞1"
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data.praise_count === 0 || response.data.praise_count) {
|
||||
|
||||
comments[commentIndex].user_praise = !comments[commentIndex].user_praise;
|
||||
comments[commentIndex].praise_count = response.data.praise_count;
|
||||
|
||||
this.setState({
|
||||
comments
|
||||
})
|
||||
}
|
||||
console.log(response)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
rewardCode = (parrentComment, childComment, amount) => {
|
||||
const { showSnackbar } = this.props;
|
||||
const { comments } = this.state;
|
||||
let handleComment = parrentComment
|
||||
if (childComment) {
|
||||
handleComment = childComment;
|
||||
}
|
||||
let handleCommentId = handleComment.id;
|
||||
const url = `/api/v1/discusses/${handleCommentId}/reward_code`
|
||||
|
||||
axios.post(url, {
|
||||
id: handleCommentId,
|
||||
|
||||
container_type: 'Memo',
|
||||
score: amount,
|
||||
user_id: handleComment.user_id
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data && response.data.code) {
|
||||
const commentIndex = this._findById(handleComment.id, comments);
|
||||
comments[commentIndex].reward = response.data.code;
|
||||
|
||||
this.setState({
|
||||
comments
|
||||
})
|
||||
}
|
||||
console.log(response)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
showSnackbar('奖励失败,请联系系统管理员!')
|
||||
})
|
||||
}
|
||||
hiddenComment = (item, childCommentId) => {
|
||||
const id = item.id
|
||||
const { showSnackbar } = this.props;
|
||||
const user = this._getUser();
|
||||
const url = `/api/v1/memos/${id}/hidden`
|
||||
const { comments } = this.state;
|
||||
|
||||
const commentIndex = this._findById(id, comments);
|
||||
const comment = comments[commentIndex];
|
||||
axios.post(url, {
|
||||
hidden: !comment.hidden ? "1" : "0"
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data.status === -1) {
|
||||
showSnackbar(response.data.message)
|
||||
return;
|
||||
}
|
||||
if (response.data.status === 1) {
|
||||
|
||||
if (!childCommentId) {
|
||||
comment.hidden = !comment.hidden;
|
||||
this.setState({
|
||||
comments: comments
|
||||
})
|
||||
} else { // TODO 目前子回复没hidden字段
|
||||
let childCommentIndex = this._findCommentById(childCommentId, comments[commentIndex].children);
|
||||
const childComment = comments[commentIndex].children[childCommentIndex]
|
||||
childComment.hidden = !childComment.hidden;
|
||||
this.setState({ comments })
|
||||
}
|
||||
|
||||
}
|
||||
// {"message":"Couldn't find Discuss with id=911","status":-1}
|
||||
console.log(response)
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
createNewComment = (commentContent, id, editor) => {
|
||||
const content = commentContent;
|
||||
const { memo } = this.props;
|
||||
if(content != undefined){
|
||||
var beforeImage = content.split("<img");
|
||||
var afterImage = content.split("/>");
|
||||
if(beforeImage[0] == "" && afterImage[1] == ""){
|
||||
window.notice_box('不支持纯图片评论<br/>请在评论中增加文字信息');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const url = `/api/v1/memos/${memo.id}/reply`;
|
||||
let { comments } = this.state;
|
||||
const user = this._getUser();
|
||||
axios.post(url, {
|
||||
content: content
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data.status === -1) {
|
||||
console.error('服务端异常')
|
||||
return;
|
||||
}
|
||||
if (response.data) {
|
||||
const newMemo = response.data.memo;
|
||||
editor.html('');
|
||||
editor.afterBlur()
|
||||
if (!comments) {
|
||||
comments = [];
|
||||
}
|
||||
comments.unshift( {
|
||||
"can_delete": true,
|
||||
"admin": user.admin,
|
||||
"content": content,
|
||||
|
||||
"image_url": user.image_url,
|
||||
"username": user.username,
|
||||
"login": user.login,
|
||||
|
||||
"id": newMemo.id,
|
||||
"reward": null,
|
||||
"hidden": newMemo.hidden,
|
||||
|
||||
"user_praise": false,
|
||||
"time": "1分钟前",
|
||||
"praise_count": 0,
|
||||
|
||||
"user_id": user.user_id,
|
||||
})
|
||||
|
||||
|
||||
this.setState({
|
||||
comments
|
||||
})
|
||||
const newMemo2 = Object.assign({}, this.props.memo);
|
||||
newMemo2.replies_count = newMemo2.replies_count + 1;
|
||||
this.props.initForumState({
|
||||
memo: newMemo2
|
||||
})
|
||||
console.log(response)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
moreMemos = () => {
|
||||
let { comments, pageCount } = this.state;
|
||||
let { memo } = this.props;
|
||||
const user = this._getUser();
|
||||
const url = `/api/v1/memos/${memo.id}/more_reply?page=${pageCount}`;
|
||||
axios.get(url, {
|
||||
|
||||
},
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
).then((response) => {
|
||||
if (response.data.status === -1) {
|
||||
console.error('服务端异常')
|
||||
return;
|
||||
}
|
||||
let { memo_replies } = response.data;
|
||||
if (!memo_replies || memo_replies.length === 0) {
|
||||
this.setState({
|
||||
hasMoreComments: false
|
||||
})
|
||||
return;
|
||||
}
|
||||
if (response.data.memos_count) {
|
||||
const newComments = comments.concat(memo_replies);
|
||||
const hasMoreComments = memo_replies.length === 10
|
||||
this.setState({
|
||||
comments: newComments,
|
||||
hasMoreComments,
|
||||
pageCount: pageCount+1
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------- comments END
|
||||
// ------------------------------------------------------------------------------------------- comments END
|
||||
// 置顶
|
||||
setTop(memo) {
|
||||
const params = {
|
||||
sticky: memo.sticky ? 0 : 1,
|
||||
}
|
||||
if (this.state.p_s_order) {
|
||||
params.order = this.state.p_s_order;
|
||||
}
|
||||
if (this.state.p_forum_id) {
|
||||
params.forum_id = this.state.p_forum_id;
|
||||
}
|
||||
let paramsUrl = urlStringify(params)
|
||||
const set_top_or_down_Url = `/api/v1/memos/${memo.id}/set-top-or-down?${paramsUrl}`;
|
||||
// 获取memo list
|
||||
axios.get(set_top_or_down_Url, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const status = response.data.status
|
||||
if (status === 0) {
|
||||
this.props.showSnackbar( memo.sticky ? '取消置顶成功' : '置顶成功');
|
||||
memo.sticky = memo.sticky ? false : true
|
||||
this.setState({
|
||||
memo: Object.assign({}, memo)
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------帖子獎勵
|
||||
rewardCodeMemo = (inputVal) => {
|
||||
console.log(inputVal)
|
||||
const { memo, author_info } = this.props;
|
||||
const newMemo = Object.assign({}, memo);
|
||||
const _reward = parseInt(inputVal)
|
||||
const newMemoUrl = `/api/v1/memos/${memo.id}/update`
|
||||
|
||||
const url = `/api/v1/discusses/${memo.id}/reward_code`
|
||||
|
||||
axios.post(url, {
|
||||
id: memo.id,
|
||||
|
||||
container_type: 'Memo',
|
||||
score: _reward,
|
||||
user_id: author_info.user_id
|
||||
}, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const { code } = response.data;
|
||||
if (code > 0) {
|
||||
newMemo.reward = code
|
||||
this.props.initForumState({
|
||||
memo: newMemo
|
||||
})
|
||||
this.props.showSnackbar( '奖励成功' );
|
||||
} else {
|
||||
this.props.showSnackbar( '奖励失败,请联系系统管理员!' );
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
setRewardDialogVisible = (visible) => {
|
||||
this.setState({
|
||||
goldRewardDialogOpen: visible
|
||||
})
|
||||
}
|
||||
showRewardDialog = () => {
|
||||
this.setState({
|
||||
goldRewardDialogOpen: true
|
||||
})
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------帖子獎勵 END
|
||||
showCommentInput = () => {
|
||||
$("html, body").animate({ scrollTop: $('#commentInput').offset().top - 100 }, 1000, () => {
|
||||
this.keEditor && this.keEditor.focus();
|
||||
});
|
||||
|
||||
}
|
||||
render() {
|
||||
const { match, history } = this.props
|
||||
const { memo, recommend_shixun, current_user,author_info } = this.props;
|
||||
const { comments, hasMoreComments, goldRewardDialogOpen } = this.state;
|
||||
|
||||
if (this.state.memoLoading) {
|
||||
return <div className="edu-back-white" id="forum_index_list"></div>
|
||||
}
|
||||
current_user.user_url = `/users/${current_user.login}`;
|
||||
memo.isDetailPage = true;
|
||||
// TODO 图片上传地址
|
||||
return (
|
||||
<div className="edu-back-white" id="forum_index_list"> {/* fl with100 */}
|
||||
<RewardDialog goldRewardDialogOpen={goldRewardDialogOpen}
|
||||
setRewardDialogVisible={this.setRewardDialogVisible}
|
||||
rewardCode={this.rewardCodeMemo}
|
||||
></RewardDialog>
|
||||
<div className="clearfix">
|
||||
<div id="forum_list" className="forum_table mh650">
|
||||
<div className="padding40-30 bor-bottom-greyE">
|
||||
<div className="font-16 mb5 cdefault clearfix pr">
|
||||
<span className="noteDetailTitle task-hide fl">{memo.subject}</span>
|
||||
{ memo.sticky && <span className="btn-cir btn-cir-red ml10 mt10 fl">置顶</span>}
|
||||
{ !!memo.reward && <span className="color-orange font-14 ml15"
|
||||
data-tip-down={`获得平台奖励金币:${memo.reward}`} >
|
||||
<i className="iconfont icon-gift mr5"></i>{memo.reward}
|
||||
</span> }
|
||||
{ current_user && (current_user.admin === true || current_user.user_id === author_info.user_id) &&
|
||||
<div className="edu-position-hidebox" style={{position: 'absolute', right: '18px',top:'4px'}}>
|
||||
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
|
||||
<ul className="edu-position-hide undis">
|
||||
{ current_user.admin === true &&
|
||||
( memo.sticky === true ?
|
||||
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>取消置顶</a></li>
|
||||
:
|
||||
<li><a href="javascript:void(0);" onClick={() => this.setTop(memo)}>置 顶</a></li> )
|
||||
}
|
||||
<li><Link to={`/forums/${memo.id}/edit`}>编 辑</Link></li>
|
||||
<li>
|
||||
<a href="javascript:void(0)" onClick={() =>
|
||||
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
|
||||
|
||||
删 除</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className="color-grey-9 clearfix">
|
||||
<span className="fl">{moment(memo.time).fromNow()} 发布</span>
|
||||
<div className="fr">
|
||||
{ current_user.admin && <Tooltip title={ "帖子奖励" }>
|
||||
<span className="noteDetailNum rightline cdefault" style={{padding: '0 4px', cursor: 'pointer'}}>
|
||||
<i className="iconfont icon-jiangli mr5" onClick={this.showRewardDialog}></i>
|
||||
</span>
|
||||
</Tooltip> }
|
||||
<span className={`noteDetailNum ${!!memo.replies_count ? 'rightline' : ''} cdefault`}>
|
||||
<i className="iconfont icon-liulanyan mr5"></i>{memo.viewed_count}
|
||||
</span>
|
||||
{ !!memo.replies_count &&
|
||||
<Tooltip title={ "写评论" }>
|
||||
<a href="javascript:void(0)" className="noteDetailNum">
|
||||
<i className="iconfont icon-huifu1 mr5" onClick={this.showCommentInput}></i>{memo.replies_count}
|
||||
</a>
|
||||
</Tooltip>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="padding40 memoContent new_li">
|
||||
{ !memo.is_md ?
|
||||
<div dangerouslySetInnerHTML={{__html: memo.content}}></div> :
|
||||
<div id="memo_content_editorMd" className="new_li">
|
||||
<textarea style={{'display': 'none'}}>
|
||||
{memo.content}
|
||||
</textarea>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="padding40 bor-bottom-greyE" style={{ paddingTop: '0px'}}>
|
||||
|
||||
|
||||
<div className="mt10 mb20">
|
||||
<p className={`noteDetailPoint ${memo.user_praise ? 'Pointed' : ''} `} onClick={()=>{this.clickPraise()}} >
|
||||
<i className="iconfont icon-dianzan"></i><br/>
|
||||
<span>{memo.praise_count}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{ memo.attachment_url && !!memo.attachment_url.length &&
|
||||
<div>
|
||||
{this.renderAttachment()}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div nhname={`new_message_${memo.id}`} className="" style={{ paddingTop: '20px', paddingBottom: '20px' }}
|
||||
id="commentInput">
|
||||
<form acceptCharset="UTF-8" action="/discusses?challenge_id=118&dis_id=61&dis_type=Shixun"
|
||||
style={{ flexDirection: 'column', width: '94%', marginLeft: '3%'}}
|
||||
className="df" data-remote="true" id="new_comment_form" method="post">
|
||||
<div nhname={`toolbar_container_${memo.id}`}></div>
|
||||
<textarea placeholder="" id={`comment_news_${memo.id}`}
|
||||
nhname={`new_message_textarea_${memo.id}`} name="content" className="none">
|
||||
</textarea>
|
||||
</form>
|
||||
<a id={`new_message_submit_btn_${memo.id}`} href="javascript:void(0)"
|
||||
style={{display: 'none'}} className="commentsbtn task-btn task-btn-blue fr">
|
||||
发送
|
||||
</a>
|
||||
</div>
|
||||
{/* onClick={ this.createNewComment } */}
|
||||
<div className="padding40 bor-bottom-greyE memoReplies commentsDelegateParent"
|
||||
style={{ display: (comments && !!comments.length) ? 'block' : 'none' }}>
|
||||
<div className="replies_count">
|
||||
<span className="labal">全部回复</span>
|
||||
<span className="count">{memo.replies_count}</span>
|
||||
</div>
|
||||
|
||||
<ImageLayer {...this.state} onImageLayerClose={this.onImageLayerClose}></ImageLayer>
|
||||
|
||||
<Comments comments={comments} user={current_user}
|
||||
replyComment={this.replyComment}
|
||||
deleteComment={this.deleteComment}
|
||||
commentPraise={this.commentPraise}
|
||||
rewardCode={this.rewardCode}
|
||||
hiddenComment={this.hiddenComment}
|
||||
|
||||
|
||||
></Comments>
|
||||
{ hasMoreComments ?
|
||||
<div className="memoMore">
|
||||
<a onClick={this.moreMemos}>查看更多评论</a>
|
||||
<div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>
|
||||
</div>
|
||||
:
|
||||
<div className="memoMore">
|
||||
<div className="writeCommentBtn" onClick={this.showCommentInput}>写评论</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoDetail;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
import { queryString } from 'educoder'
|
||||
|
||||
class MemoList extends Component {
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count ,memo_list, renderMemoList, onPaginationChange } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div id="forum_list" className="forum_table">
|
||||
<div className="mh650 edu-back-white">
|
||||
{!memo_list || memo_list.length === 0 ?
|
||||
<div className="edu-tab-con-box clearfix edu-txt-center">
|
||||
<img className="edu-nodata-img mb20" src="/images/educoder/nodata.png"/>
|
||||
<p className="edu-nodata-p mb30">没有数据可以显示!</p>
|
||||
</div>
|
||||
: renderMemoList()
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{ !!memo_count && memo_count > 15 &&
|
||||
<div style={{ width: '100%', background: '#FAFAFA'}}>
|
||||
<Pagination className={'ec-pagination'}
|
||||
onChange={(pageNum, pageSize) => onPaginationChange(pageNum, pageSize)}
|
||||
showQuickJumper current={currentPage} total={memo_count} pageSize={15}/>
|
||||
</div> }
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ( MemoList );
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
// import queryString from 'query-string'
|
||||
import { queryString } from 'educoder'
|
||||
import MemoList from './MemoList'
|
||||
|
||||
|
||||
class MemoMyPublish extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
this.props.onPaginationChange(pageNum, pageSize)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
||||
}
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
|
||||
}
|
||||
renderMemoList() {
|
||||
|
||||
|
||||
return this.props.renderMemoList();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count, memo_list } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="discuss-tab bor-bottom-greyE clearfix pr boxsizing">
|
||||
<p className="_forum_tab pl20 pr20 clearfix boxsizing" style={{fontSize:'18px', color:'rgba(5,16,26,1)'}}>
|
||||
我的发布
|
||||
<Link className="returnBtnA fr mr10" to={`/forums`}><span className="color-grey-9 font-16">返回</span></Link>
|
||||
</p>
|
||||
</div>
|
||||
<MemoList {...this.props} renderMemoList={() => this.renderMemoList()}
|
||||
onPaginationChange={ (pageNum, pageSize) => this.props.onPaginationChange(pageNum, pageSize) }
|
||||
>
|
||||
</MemoList>
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default postPaginationHOC({ isMyPublish: true }) ( MemoMyPublish );
|
||||
|
|
@ -0,0 +1,616 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Select, {Option, OptGroup} from 'rc-select';
|
||||
|
||||
// demo http://react-component.github.io/upload/examples/simple.html
|
||||
import Upload from 'rc-upload';
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
const $ = window.$;
|
||||
const isDevServer = window.location.port === "3007";
|
||||
let origin = window.location.origin;
|
||||
let path = "/editormd/lib/" // origin + '/react/build/js/editormd/lib/'
|
||||
if (isDevServer) {
|
||||
origin = 'http://localhost:3000'
|
||||
path = 'http://localhost:3000/editormd/lib/'
|
||||
}
|
||||
|
||||
// load
|
||||
if (!window.postUpMsg) {
|
||||
$.getScript(
|
||||
`${origin}/javascripts/attachments.js`,
|
||||
(data, textStatus, jqxhr) => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// editorMD to create
|
||||
/**
|
||||
*
|
||||
* @param id 渲染DOM的id
|
||||
* @param width 宽度
|
||||
* @param high 高度
|
||||
* @param placeholder
|
||||
* @param imageUrl 上传图片的url
|
||||
* @returns {*} 返回一个editorMD实例
|
||||
*/
|
||||
function create_editorMD(id, width, high, placeholder, imageUrl, callback){
|
||||
var editorName = window.editormd(id, {
|
||||
width : width,
|
||||
height : high,
|
||||
syncScrolling : "single",
|
||||
//你的lib目录的路径,我这边用JSP做测试的
|
||||
path : path , // "/editormd/lib/"
|
||||
tex : true,
|
||||
tocm : true,
|
||||
emoji : true,
|
||||
taskList : true,
|
||||
codeFold : true,
|
||||
searchReplace : true,
|
||||
htmlDecode : "style,script,iframe",
|
||||
sequenceDiagram : true,
|
||||
autoFocus: false,
|
||||
toolbarIcons : function() {
|
||||
// Or return editormd.toolbarModes[name]; // full, simple, mini
|
||||
// Using "||" set icons align right.
|
||||
return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear" ]
|
||||
},
|
||||
toolbarCustomIcons : {
|
||||
testIcon : "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
|
||||
testIcon1 : "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>"
|
||||
},
|
||||
//这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。
|
||||
saveHTMLToTextarea : true,
|
||||
// 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标
|
||||
dialogMaskOpacity : 0.6,
|
||||
placeholder: placeholder,
|
||||
imageUpload : true,
|
||||
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
|
||||
imageUploadURL : imageUrl,//url
|
||||
onload: function(){
|
||||
// this.previewing();
|
||||
$("#"+ id +" [type=\"latex\"]").bind("click", function(){
|
||||
editorName.cm.replaceSelection("```latex");
|
||||
editorName.cm.replaceSelection("\n");
|
||||
editorName.cm.replaceSelection("\n");
|
||||
editorName.cm.replaceSelection("```");
|
||||
var __Cursor = editorName.cm.getDoc().getCursor();
|
||||
editorName.cm.setCursor(__Cursor.line-1, 0);
|
||||
});
|
||||
|
||||
$("#"+ id +" [type=\"inline\"]").bind("click", function(){
|
||||
editorName.cm.replaceSelection("$$$$");
|
||||
var __Cursor = editorName.cm.getDoc().getCursor();
|
||||
editorName.cm.setCursor(__Cursor.line, __Cursor.ch-2);
|
||||
editorName.cm.focus();
|
||||
});
|
||||
$("[type=\"inline\"]").attr("title", "行内公式");
|
||||
$("[type=\"latex\"]").attr("title", "多行公式");
|
||||
|
||||
callback && callback()
|
||||
}
|
||||
});
|
||||
return editorName;
|
||||
}
|
||||
|
||||
const typeNameMap = {
|
||||
'技术分享': 5,
|
||||
'操作指南': 3,
|
||||
}
|
||||
const typeNameMap2 = {
|
||||
5: '技术分享',
|
||||
3: '操作指南',
|
||||
}
|
||||
const defaultType = '技术分享'
|
||||
class MemoNew extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// https://testbdweb.trustie.net/uploads.js?attachment_id=1&filename=jqui.js
|
||||
// https://ant.design/components/upload-cn/
|
||||
this.uploaderProps = {
|
||||
action: '/uploads.js',
|
||||
data: { attachment_id: 1 }, // , filename: 2
|
||||
headers: {
|
||||
Authorization: 'authorization-text',
|
||||
},
|
||||
multiple: true,
|
||||
beforeUpload(file) {
|
||||
console.log('beforeUpload', file.name);
|
||||
},
|
||||
onStart: (file) => {
|
||||
console.log('onStart', file.name);
|
||||
// this.refs.inner.abort(file);
|
||||
},
|
||||
onSuccess(file) {
|
||||
console.log('onSuccess', file);
|
||||
},
|
||||
onProgress(step, file) {
|
||||
console.log('onProgress', Math.round(step.percent), file.name);
|
||||
},
|
||||
onError(err) {
|
||||
console.log('onError', err);
|
||||
},
|
||||
};
|
||||
|
||||
this.state = {
|
||||
memoSubject: '',
|
||||
memoContent: '',
|
||||
memoType: typeNameMap[defaultType],
|
||||
memoRepertoire: '',
|
||||
memoLanguage: '',
|
||||
|
||||
repertoires: [],
|
||||
currentSelectRepertoiresIndex: -1,
|
||||
repertoiresTagMap: {}
|
||||
}
|
||||
}
|
||||
onCommit() {
|
||||
const { memoSubject, memoRepertoire, memoLanguage, currentMemoId, memoType } = this.state;
|
||||
const { showSnackbar } = this.props;
|
||||
if (!memoSubject) {
|
||||
showSnackbar('请先输入话题名称')
|
||||
return
|
||||
}
|
||||
let mdVal;
|
||||
try {
|
||||
mdVal = this.taskpass_editormd.getValue()
|
||||
|
||||
} catch (e) {
|
||||
showSnackbar('编辑器还未加载完毕,请稍后')
|
||||
return
|
||||
}
|
||||
|
||||
if (!mdVal) {
|
||||
showSnackbar('请先输入话题内容')
|
||||
return
|
||||
}
|
||||
if (memoType === 5 && (!memoRepertoire || !memoLanguage)) {
|
||||
showSnackbar('请先选择技术标签')
|
||||
return
|
||||
}
|
||||
/*
|
||||
<meta content="authenticity_token" name="csrf-param" />
|
||||
<meta content="G7peAyb1T37RvdwxnVUKmTXuL8T7FaBze5mK0j6MCKs=" name="csrf-token" />
|
||||
|
||||
http://localhost:3000/attachments/download/185790/Git-2.17.1.2-32-bit.exe
|
||||
https://www.educoder.net/attachments/205112.js?attachment_id=1
|
||||
*/
|
||||
// collect attachments
|
||||
const $ = window.$;
|
||||
const attachmentsMap = {};
|
||||
$('#attachments_fields .attachment').each(( index, item ) => {
|
||||
const filename = $(item).find('.upload_filename').val();
|
||||
// $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
|
||||
const token = $(item).find('input:nth-child(6)').val()
|
||||
const attachment_id = $(item).find('input:nth-child(7)').val()
|
||||
attachmentsMap[index] = {
|
||||
filename,
|
||||
token,
|
||||
attachment_id
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
if (currentMemoId) {
|
||||
this.updateMemo(attachmentsMap)
|
||||
} else {
|
||||
this.newMemo(attachmentsMap)
|
||||
}
|
||||
}
|
||||
onCancel() {
|
||||
const { currentMemoId, memoType } = this.state;
|
||||
if (currentMemoId) { // 编辑
|
||||
this.props.history.push(`/forums/${currentMemoId}`)
|
||||
} else { // 新建
|
||||
this.props.history.push(`/forums`)
|
||||
}
|
||||
// debugger;this.props.history.goBack()
|
||||
}
|
||||
updateMemo(attachmentsMap) {
|
||||
const { memoSubject, memoRepertoire, memoLanguage, memoType, currentMemoId, content } = this.state;
|
||||
const mdVal = this.taskpass_editormd.getValue()
|
||||
console.log('isContentEdit: ', mdVal === content);
|
||||
const newMemoUrl = `/api/v1/memos/${currentMemoId}/update`
|
||||
axios.post(newMemoUrl, {
|
||||
content_changed: this.contentChanged,
|
||||
memo:{
|
||||
subject: memoSubject ,
|
||||
content: mdVal,
|
||||
forum_id: memoType,
|
||||
repertoire_name: memoRepertoire,
|
||||
language: memoLanguage,
|
||||
//
|
||||
},
|
||||
attachments: attachmentsMap
|
||||
}, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const { status, message, memo_id } = response.data;
|
||||
if (status === 0) {
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
this.props.history.push(`/forums/${currentMemoId}`)
|
||||
} else {
|
||||
this.props.showSnackbar(message)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
newMemo(attachmentsMap) {
|
||||
const { memoSubject, memoRepertoire, memoLanguage, memoType } = this.state;
|
||||
const mdVal = this.taskpass_editormd.getValue()
|
||||
|
||||
const newMemoUrl = `/api/v1/memos/create`
|
||||
axios.post(newMemoUrl, {
|
||||
memo:{
|
||||
subject: memoSubject ,
|
||||
content: mdVal,
|
||||
forum_id: memoType,
|
||||
repertoire_name: memoRepertoire,
|
||||
language: memoLanguage
|
||||
},
|
||||
attachments: attachmentsMap
|
||||
}, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const { status, message, memo_id } = response.data;
|
||||
if (status === 0) {
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
this.props.history.push(`/forums/${memo_id}`)
|
||||
} else {
|
||||
this.props.showSnackbar(message)
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
componentDidMount() {
|
||||
const newMemoUrl = `/api/v1/memos/new`
|
||||
axios.get(newMemoUrl,{
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const data = response.data;
|
||||
const repertoires = [];
|
||||
const repertoiresTagMap = {}
|
||||
if ( data.tag_list ) {
|
||||
data.tag_list.forEach((item, index)=>{
|
||||
const tagArray = [];
|
||||
item.tag.forEach( (tag, index) => {
|
||||
tagArray.push(tag.name)
|
||||
})
|
||||
repertoires.push(item.rep.repertoire.name)
|
||||
repertoiresTagMap[item.rep.repertoire.name] = tagArray
|
||||
})
|
||||
this.setState({
|
||||
repertoires,
|
||||
repertoiresTagMap
|
||||
})
|
||||
|
||||
const user = response.data.current_user;
|
||||
user.tidding_count = response.data.tidding_count;
|
||||
this.props.initCommonState(user)
|
||||
|
||||
// 初始化 csrf meta
|
||||
const $ = window.$
|
||||
$('head').append( $('<meta content="authenticity_token" name="csrf-param" />') )
|
||||
$('head').append( $(`<meta content="${response.data.csrf_token}" name="csrf-token" />`) )
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
// 如果是编辑
|
||||
const { match } = this.props
|
||||
const memoId = match.params.memoId;
|
||||
if (memoId) {
|
||||
const memoUrl = `/api/v1/memos/${match.params.memoId}/edit`;
|
||||
axios.get(memoUrl,{
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const tag_list = response.data.tag_list
|
||||
if (tag_list) {
|
||||
// this.setState({...response.data})
|
||||
const { content, forum_id, id, language, repertoire_name, subject,
|
||||
current_user, tag_list, attachments_url } = response.data;
|
||||
this.initMD(content);
|
||||
this.onRepertoiresChange(repertoire_name)
|
||||
this.setState({
|
||||
currentMemoId: id,
|
||||
memoSubject: subject,
|
||||
memoType: forum_id,
|
||||
memoRepertoire: repertoire_name,
|
||||
memoLanguage: language,
|
||||
attachments_url,
|
||||
content
|
||||
|
||||
// repertoires: [],
|
||||
// currentSelectRepertoiresIndex: -1,
|
||||
})
|
||||
// 加载完后滚动条滚动
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
|
||||
this.props.initForumState({
|
||||
current_user,
|
||||
tag_list
|
||||
})
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
} else {
|
||||
this.initMD();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
initMD(initValue) {
|
||||
this.contentChanged = false;
|
||||
const placeholder = "";
|
||||
// amp;
|
||||
// 编辑时要传memoId
|
||||
const imageUrl = `/upload_with_markdown?container_id=&container_type=Memo`;
|
||||
// 创建editorMd
|
||||
|
||||
const taskpass_editormd = create_editorMD("memoMD", '100%', 400, placeholder, imageUrl, () => {
|
||||
|
||||
if (initValue) {
|
||||
taskpass_editormd.setValue(initValue)
|
||||
}
|
||||
taskpass_editormd.cm.on("change", (_cm, changeObj) =>{
|
||||
console.log('....contentChanged')
|
||||
this.contentChanged = true;
|
||||
})
|
||||
});
|
||||
this.taskpass_editormd = taskpass_editormd;
|
||||
window.taskpass_editormd = taskpass_editormd;
|
||||
|
||||
}
|
||||
|
||||
renderOptions(array) {
|
||||
const elementArray = [];
|
||||
array.forEach(( item, index ) => {
|
||||
elementArray.push(
|
||||
<Option key={index} value={item}>{item}</Option>
|
||||
)
|
||||
})
|
||||
return elementArray
|
||||
}
|
||||
onRepertoiresChange(value) {
|
||||
|
||||
const index = this.state.repertoires.indexOf(value)
|
||||
|
||||
this.setState({
|
||||
currentSelectRepertoiresIndex: index,
|
||||
memoRepertoire: value,
|
||||
memoLanguage: ''
|
||||
});
|
||||
};
|
||||
onTagChange(value) {
|
||||
this.setState({
|
||||
memoLanguage: value
|
||||
})
|
||||
}
|
||||
|
||||
onTypeChange(value) {
|
||||
|
||||
this.setState({
|
||||
memoType: typeNameMap[value]
|
||||
})
|
||||
}
|
||||
onMemoNameChange(e) {
|
||||
this.setState({
|
||||
memoSubject: e.target.value
|
||||
})
|
||||
}
|
||||
renderAttachment() {
|
||||
const { attachments_url } = this.state;
|
||||
const attachments = []
|
||||
attachments_url.forEach((item, index) => {
|
||||
const ar = item.url.split('/')
|
||||
const fileName = ar[ar.length - 1]
|
||||
/*
|
||||
<p className="clearfix" key={index} >
|
||||
<a href={item.url} className="color-green clearfix notefileDownload">
|
||||
<span className="fl">{fileName}</span><i className="iconfont icon-xiazai color-green ml5 fl"></i>
|
||||
</a>
|
||||
</p>
|
||||
*/
|
||||
// ?attachment_id=2
|
||||
/*
|
||||
<span id="attachments_fields" className="attachments_fields" xmlns="http://www.w3.org/1999/html">
|
||||
</span>
|
||||
|
||||
*/
|
||||
attachments.push(
|
||||
<React.Fragment>
|
||||
<span id={`attachments_10${index}`} className="attachment">
|
||||
<label className="panel-form-label fl"> </label>
|
||||
<i className="fa fa-folder mr5 color-light-grey" aria-hidden="true"></i>
|
||||
<input type="text" className="upload_filename readonly hidden" name="attachments[2][filename]" readonly="readonly"
|
||||
style={{border:'none', maxWidth:'980px',whiteSpace: 'nowrap', textOverflow:'ellipsis',fontFamily: 'Consolas'}}
|
||||
size="8" value={item.filename}></input>
|
||||
<a href={`/attachments/${item.id}.js?attachment_id=10${index}`} className="remove-upload"
|
||||
style={{verticalAlign: 'top', display: 'inlineBlock'}} data-remote="true"
|
||||
data-method="delete">
|
||||
<i className="fa fa-trash-o mr5"></i>
|
||||
</a>
|
||||
<div className="div_attachments" name="div_attachments_xx"></div>
|
||||
<input type="hidden" name="attachments[xx][token]"
|
||||
value="185811.24305bb2c4912f715629aa3615cdbabc"></input>
|
||||
<input type="hidden" name="attachments[xx][attachment_id]" value="185811"></input>
|
||||
</span>
|
||||
<div className="cl"></div>
|
||||
</React.Fragment>
|
||||
|
||||
)
|
||||
})
|
||||
return attachments;
|
||||
}
|
||||
render() {
|
||||
const { match, history } = this.props
|
||||
const { repertoires, memoSubject, repertoiresTagMap, currentSelectRepertoiresIndex, memoType, memoRepertoire,
|
||||
memoLanguage, attachments_url } = this.state;
|
||||
|
||||
return (
|
||||
<div >
|
||||
<div className="pt20 pl20 pr20 pb20 bor-bottom-greyE clearfix" style={{background: '#fff'}}>
|
||||
<span className="fl font-16">发布话题</span>
|
||||
{/*<a href="/shixuns/mf98sgi3/settings" className="ring-green mt7 fr" id="edit_setting"
|
||||
data-remote="true" data-tip-down="编辑"><img src="/images/educoder/icon/edit.svg" className="fl ml2 mt3">
|
||||
</a>*/}
|
||||
</div>
|
||||
|
||||
<div className="edu-back-white mb10 clearfix" id="memoSubject">
|
||||
<div className="padding30-20">
|
||||
<p className="color-grey-6 font-16 mb30">话题名称</p>
|
||||
<div className="df">
|
||||
<span className="mr30 color-orange pt10">*</span>
|
||||
<div className="flex1 mr20">
|
||||
<input type="text" className="input-100-45 greyInput" maxlength="50"
|
||||
value={memoSubject} onChange={(val)=>this.onMemoNameChange(val)} placeholder="">
|
||||
</input>
|
||||
</div>
|
||||
<div style={{width: '57px'}}>
|
||||
<span className="color-orange mt8 fl none" >
|
||||
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="edu-back-white mb10 clearfix">
|
||||
<div className="padding30-20">
|
||||
<p className="color-grey-6 font-16 mb30">内容</p>
|
||||
<div className="df">
|
||||
<span className="mr30 color-orange pt10">*</span>
|
||||
<div className="flex1 mr20">
|
||||
<div className="flex1 break_word show_content_grey new_li" id="memoMD">
|
||||
<textarea style={{'display':'none'}}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{width: '57px'}}>
|
||||
<span className="color-orange mt8 fl none" >
|
||||
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form className="newForm">
|
||||
<span id={`attachments_fields`} className="attachments_fields"
|
||||
xmlns="http://www.w3.org/1999/html">
|
||||
{ attachments_url && !!attachments_url.length &&
|
||||
this.renderAttachment()
|
||||
}
|
||||
</span>
|
||||
<span className="add_attachment">
|
||||
<input className="file_selector" data-are-you-sure="您确定要删除吗?"
|
||||
data-delete-all-files="您确定要删除所有文件吗" data-description-placeholder="可选的描述"
|
||||
data-field-is-public="公开" data-file-count="个文件已上传"
|
||||
data-lebel-file-uploding="个文件正在上传" data-max-concurrent-uploads="2"
|
||||
data-max-file-size-message="该文件无法上传。超过文件大小限制 (50 MB),建议上传到百度云等其他共享工具里,然后在txt文档里给出链接以及共享密码并上传"
|
||||
data-max-file-size="52428800" data-upload-path="/uploads.js" id="_file"
|
||||
multiple="multiple" name="attachments[dummy][file]"
|
||||
onChange={()=>{debugger;window.addInputFiles( window.$('.file_selector')[0] ) }}
|
||||
style={{'display':'none'}} type="file">
|
||||
</input>
|
||||
</span>
|
||||
</form>
|
||||
|
||||
{/*<Upload {...this.uploaderProps} ref="inner"><a>开始上传</a></Upload>*/}
|
||||
|
||||
{/* 请求status 422 */}
|
||||
<div className="df uploadBtn">
|
||||
<a href="javascript:void(0);" className="fl" onClick={()=>window.$('#_file').click()}
|
||||
data-tip-down="请选择文件上传">
|
||||
<i className="fa fa-upload mr5"></i>
|
||||
<span>上传附件</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* TODOTODO 这里重复的html代码太多,如果有其他页面有类似需求,需要封装*/}
|
||||
|
||||
<div className="edu-back-white mb10 clearfix">
|
||||
<div className="padding30-20">
|
||||
<p className="color-grey-6 font-16 mb30">话题类型</p>
|
||||
<div className="df">
|
||||
<span className="mr30 color-orange pt10">*</span>
|
||||
<div className="flex1 mr20">
|
||||
<Select className="ecSelect" value={typeNameMap2[memoType]}
|
||||
onChange={(val)=>this.onTypeChange(val)}>
|
||||
<Option value="技术分享">技术分享</Option>
|
||||
<Option value="操作指南">操作指南</Option>
|
||||
</Select>
|
||||
</div>
|
||||
<div style={{width: '57px'}}>
|
||||
<span className="color-orange mt8 fl none" >
|
||||
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ memoType === typeNameMap['技术分享'] &&
|
||||
<div className="edu-back-white mb10 clearfix">
|
||||
<div className="padding30-20">
|
||||
<p className="color-grey-6 font-16 mb30">技术标签</p>
|
||||
<div className="df">
|
||||
<span className="mr30 color-orange pt10">*</span>
|
||||
<div className="mr20">
|
||||
<Select className="ecSelect" placeholder="请选择大标签" onChange={(e) => this.onRepertoiresChange(e)}
|
||||
value={memoRepertoire}
|
||||
>
|
||||
{this.renderOptions(repertoires)}
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex1 mr20">
|
||||
<Select className="ecSelect" placeholder="请选择小标签"
|
||||
onChange={(e) => this.onTagChange(e)} value={memoLanguage}
|
||||
dropdownStyle={{'maxHeight': '300px', 'overflow': 'auto'}} >
|
||||
{ currentSelectRepertoiresIndex >= 0
|
||||
&& this.renderOptions(repertoiresTagMap[repertoires[currentSelectRepertoiresIndex]])}
|
||||
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div style={{width: '57px'}}>
|
||||
<span className="color-orange mt8 fl none" >
|
||||
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> }
|
||||
|
||||
<div className="clearfix mt30">
|
||||
<a href="javascript:void(0)" className="defalutSubmitbtn fl mr20"
|
||||
onClick={()=>{this.onCommit()}}>提交</a>
|
||||
<a onClick={()=>{ this.onCancel() }} className="defalutCancelbtn fl">取消</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoNew;
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
class MemoGuide extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
|
||||
this.props.onPaginationChange(pageNum, pageSize)
|
||||
}
|
||||
|
||||
|
||||
renderMemoList() {
|
||||
// const { memo_list, user } = this.props;
|
||||
// if (!memo_list) {
|
||||
// return ''
|
||||
// }
|
||||
// return memo_list.map( (item, index) => {
|
||||
// return (
|
||||
// <PostItem key={item.id} memo={item} user={user} index={index} {...this.props}></PostItem>
|
||||
// )
|
||||
// })
|
||||
return this.props.renderMemoList();
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab>
|
||||
<div id="forum_list" className="forum_table mh650">
|
||||
|
||||
{this.renderMemoList()}
|
||||
|
||||
{ !!memo_count && <Pagination onChange={(pageNum, pageSize) => this.onPaginationChange(pageNum, pageSize)}
|
||||
showQuickJumper current={currentPage} total={memo_count} pageSize={15}/> }
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default postPaginationHOC( MemoGuide );
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
class MemoHottest extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleLocationChange = this.handleLocationChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
|
||||
this.props.onPaginationChange(pageNum, pageSize)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.handleLocationChange(this.props.history.location);
|
||||
this.unlisten = this.props.history.listen(this.handleLocationChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
handleLocationChange(location) {
|
||||
// your staff here
|
||||
console.log(`- - - location: '${location.pathname}'`);
|
||||
if (location.pathname && location.pathname.indexOf('/forums/categories/all') != -1
|
||||
&& this.locationSearch != location.search) {
|
||||
this.props.fetchMemos();
|
||||
}
|
||||
this.locationSearch = location.search;
|
||||
}
|
||||
|
||||
renderMemoList() {
|
||||
// const { memo_list, user } = this.props;
|
||||
// if (!memo_list) {
|
||||
// return ''
|
||||
// }
|
||||
// return memo_list.map( (item, index) => {
|
||||
// return (
|
||||
// <PostItem key={item.id} memo={item} user={user} index={index} {...this.props}></PostItem>
|
||||
// )
|
||||
// })
|
||||
return this.props.renderMemoList();
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab>
|
||||
<div id="forum_list" className="forum_table mh650">
|
||||
|
||||
{this.renderMemoList()}
|
||||
|
||||
{ !!memo_count && <Pagination onChange={(pageNum, pageSize) => this.onPaginationChange(pageNum, pageSize)}
|
||||
showQuickJumper current={currentPage} total={memo_count} pageSize={15}/> }
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default postPaginationHOC( MemoHottest );
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
class MemoNewest extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
|
||||
this.props.onPaginationChange(pageNum, pageSize)
|
||||
}
|
||||
|
||||
|
||||
renderMemoList() {
|
||||
// const { memo_list, user } = this.props;
|
||||
// if (!memo_list) {
|
||||
// return ''
|
||||
// }
|
||||
// return memo_list.map( (item, index) => {
|
||||
// return (
|
||||
// <PostItem key={item.id} memo={item} user={user} index={index} {...this.props}></PostItem>
|
||||
// )
|
||||
// })
|
||||
return this.props.renderMemoList();
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab>
|
||||
<div id="forum_list" className="forum_table mh650">
|
||||
{this.renderMemoList()}
|
||||
|
||||
{ !!memo_count && <Pagination onChange={(pageNum, pageSize) => this.onPaginationChange(pageNum, pageSize)}
|
||||
showQuickJumper current={currentPage} total={memo_count} pageSize={15}/> }
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default postPaginationHOC( MemoNewest );
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import { postPaginationHOC } from './PostPaginationHOC'
|
||||
|
||||
import PostItem from './PostItem'
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
// import queryString from 'query-string'
|
||||
import { queryString } from 'educoder'
|
||||
|
||||
import MemoList from './MemoList'
|
||||
|
||||
|
||||
class MemoTechShare extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleLocationChange = this.handleLocationChange.bind(this);
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
this.props.onPaginationChange(pageNum, pageSize)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.handleLocationChange(this.props.history.location);
|
||||
// this.unlisten = this.props.history.listen(this.handleLocationChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// this.unlisten();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if(this.props.match.params.memoType !== prevProps.match.params.memoType) {
|
||||
// do something
|
||||
console.log(`memoType changed`)
|
||||
this.props.fetchMemos();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
if (newProps.match.url === this.props.match.url) {
|
||||
const oldParsed = queryString.parse(this.props.location.search);
|
||||
const newParsed = queryString.parse(newProps.location.search);
|
||||
if (!newParsed.page && oldParsed.page ||
|
||||
(oldParsed.order && newParsed.order && oldParsed.order != newParsed.order)) {
|
||||
this.props.fetchMemos();
|
||||
}
|
||||
console.log('componentWillReceiveProps...')
|
||||
}
|
||||
}
|
||||
|
||||
handleLocationChange(location) {
|
||||
console.log(`- - - location: '${location.pathname}'`);
|
||||
if (location.pathname) {
|
||||
if (location.pathname.indexOf('/forums/categories/all') != -1
|
||||
&& this.props.location.search && this.props.location.search.indexOf('order=') != -1
|
||||
&& location.search.indexOf('order=') != -1) {
|
||||
const oldParsed = queryString.parse(this.props.location.search);
|
||||
const newParsed = queryString.parse(location.search);
|
||||
if (oldParsed.order != newParsed.order) { // 只有在热门和最新间跳转时,才需要处理
|
||||
this.props.fetchMemos();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderMemoList() {
|
||||
|
||||
|
||||
// const { memo_list, user } = this.props;
|
||||
// if (!memo_list) {
|
||||
// return ''
|
||||
// }
|
||||
// return memo_list.map( (item, index) => {
|
||||
|
||||
// return (
|
||||
// <PostItem key={item.id} user={user} index={index} {...this.props}
|
||||
// setTop={(memo)=>this.setTop(memo)}
|
||||
// setDown={(memo)=>this.setDown(memo)} memo={item}
|
||||
// ></PostItem>
|
||||
// )
|
||||
// })
|
||||
return this.props.renderMemoList();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage, memo_count ,memo_list } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab>
|
||||
<MemoList {...this.props} renderMemoList={() => this.renderMemoList()}
|
||||
onPaginationChange={ (pageNum, pageSize) => this.props.onPaginationChange(pageNum, pageSize) }
|
||||
>
|
||||
</MemoList>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default postPaginationHOC() ( MemoTechShare );
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*MemoDetail --------------------------------- START */
|
||||
/* 左侧区域最小高度*/
|
||||
#forum_index_list {
|
||||
min-height: 400px;
|
||||
position: relative;
|
||||
}
|
||||
.noMemosTip {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 58px;
|
||||
z-index: 999;
|
||||
}
|
||||
#forum_list {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
div#forum_list>div {
|
||||
background: #fff;
|
||||
}
|
||||
.memoContent img {
|
||||
max-width: 815px;
|
||||
}
|
||||
.memoReplies {
|
||||
position: relative;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.memoReplies .-fit {
|
||||
position: static;
|
||||
}
|
||||
.replies_count {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.replies_count .label {
|
||||
color: #666666;
|
||||
}
|
||||
.replies_count .count {
|
||||
color: #999999;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.memoMore {
|
||||
padding-top: 10px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
color: rgba(69,155,230,1);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
.memoMore .writeCommentBtn{
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
color: #666666;
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
/*帖子回復按鈕*/
|
||||
a.commentsbtn.task-btn-blue {
|
||||
background: #4CACFF !important;
|
||||
margin-right: 26px;
|
||||
}
|
||||
|
||||
.iconfont.icon-xiazai {
|
||||
font-size: 22px!important;
|
||||
margin-right: 6px;
|
||||
}
|
||||
/* MemoDetail --------------------------------- END */
|
||||
|
||||
/* PostItem --------------------------------- START */
|
||||
.forum_table_item {
|
||||
padding-left: 20px;
|
||||
}
|
||||
/* 置顶 */
|
||||
.forum_table_item .btn-top {
|
||||
border-radius: 11px;
|
||||
padding: 0px 6px;
|
||||
background: #FF4343;
|
||||
|
||||
}
|
||||
|
||||
/* 管理员操作 */
|
||||
.edu-position-hide {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: -20px;
|
||||
box-shadow: 0px 2px 8px rgba(146, 153, 169, 0.5);
|
||||
background: #fff;
|
||||
z-index: 1001;
|
||||
padding: 5px 0;
|
||||
z-index: 999999;
|
||||
}
|
||||
.edu-position-hide li a:hover {
|
||||
background: #4CACFF;
|
||||
color: #fff;
|
||||
}
|
||||
.edu-position-hidebox>a:link{
|
||||
color: #4CACFF;
|
||||
}
|
||||
.edu-position-hidebox:hover .edu-position-hide {
|
||||
display: block;
|
||||
}
|
||||
.edu-position-hide li a {
|
||||
display: inline-block;
|
||||
height: 30px;
|
||||
width: 100px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
font-size: 12px!important;
|
||||
}
|
||||
/* PostItem --------------------------------- END */
|
||||
|
||||
|
||||
/* MemoNew --------------------------------- START */
|
||||
/* rc-select样式覆写*/
|
||||
.ecSelect {
|
||||
width: 200px;
|
||||
}
|
||||
.ecSelect .rc-select-selection {
|
||||
height: 40px;
|
||||
}
|
||||
.ecSelect .rc-select-search--inline .rc-select-search__field {
|
||||
padding-top: 6px;
|
||||
}
|
||||
.ecSelect .rc-select-selection--single .rc-select-selection-selected-value
|
||||
, .ecSelect .rc-select-selection__placeholder {
|
||||
top: 6px;
|
||||
|
||||
}
|
||||
.ecSelect .rc-select-arrow {
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.newFooter {
|
||||
z-index: 88 !important;
|
||||
}
|
||||
|
||||
.defalutCancelbtn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#attachments_fields {
|
||||
margin-left: -77px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.uploadBtn {
|
||||
margin-left: 46px;
|
||||
}
|
||||
#memoMD.show_content_grey {
|
||||
padding: 0;
|
||||
}
|
||||
.newForm .attachments_fields {
|
||||
/*margin-left: -39px !important*/
|
||||
}
|
||||
/* MemoNew --------------------------------- END */
|
||||
|
||||
|
||||
/*RightMyPublish*/
|
||||
.publishMemoSection {
|
||||
padding-bottom: 0px !important;
|
||||
}
|
||||
.advertisement {
|
||||
margin-top: 10px;
|
||||
height: 155px;
|
||||
}
|
||||
.advertisement img{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* MyPublish*/
|
||||
.returnBtn {
|
||||
font-size:16px;
|
||||
color:rgba(153,153,153,1);
|
||||
float: right;
|
||||
margin-right: 50px;
|
||||
position: relative;
|
||||
bottom: 12px;
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
|
||||
class PostItem extends Component {
|
||||
_toTenThousand(num) {
|
||||
if (num > 10000) {
|
||||
return ( (num - 500)/ 10000 ).toFixed(1) + '万'
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage, memo, user, setTop, setDown } = this.props
|
||||
return (
|
||||
<div class="forum_table_line pl20">
|
||||
<div className="forum_table_item" id={`memo_detail_${memo.id}`}>
|
||||
<a href={`/users/${memo.login}`} className="fr mr15">
|
||||
<img alt="用户头像" className="bor-radius-all mt3" height="50" src={`/images/${memo.image_url}`} width="50">
|
||||
</img>
|
||||
</a>
|
||||
<div className="fl pr" style={{flex: 1}}>
|
||||
<p className="font-16 clearfix" >
|
||||
{/* target="_blank" */}
|
||||
<Link to={`/forums/${memo.id}`} target="_blank" className="clearfix task-hide item_name fl" style={{maxWidth: '750px'}} >
|
||||
{memo.subject}
|
||||
</Link>
|
||||
|
||||
{ memo.sticky && <span className="btn-top btn-cir-orange mt6 ml5 fl">置顶</span> }
|
||||
|
||||
{ memo.reward &&
|
||||
<span className=" ml10 fl color-orange03" data-tip-down={`获得平台奖励金币:${memo.reward}`}>
|
||||
<i className="fa fa-gift fa-lg mr5"></i>{memo.reward}
|
||||
</span>
|
||||
}
|
||||
</p>
|
||||
|
||||
<div className="clearfix mt5 color-grey-9">
|
||||
<span className="fl">{memo.username}</span>
|
||||
{/*最后回复:todo{memo.username}
|
||||
memo.language && memo.language != 'other' && <span className="fl language-cir-orange mr10 mt3 ml6">{memo.language}</span>
|
||||
*/}
|
||||
<span className="fl ml50">{moment(memo.updated_at).fromNow()}</span>
|
||||
{memo.language && <span className="fl ml50">来自 {memo.language}</span> }
|
||||
|
||||
{/*<span className="fl language-cir-orange mr10 mt3">C++</span>*/}
|
||||
|
||||
<p className="font-12 fr mr8 color-grey-6">
|
||||
{/* data-tip-down="回复数" <i className="fa fa-comments-o mr5"></i>{memo.replies_count}
|
||||
<i className="fa fa-thumbs-o-up mr5"></i>{memo.praise_count}*/}
|
||||
{memo.replies_count ?
|
||||
<span className="mr10 ml10 fl edu-txt-right" style={{cursor: 'default'}} >
|
||||
{memo.replies_count} 回复
|
||||
</span> :''}
|
||||
|
||||
{memo.praise_count ?
|
||||
<span className="mr10 ml10 fl edu-txt-right" style={{cursor: 'default'}} >
|
||||
{memo.praise_count} 赞
|
||||
</span> :''}
|
||||
|
||||
{memo.viewed_count ?
|
||||
<span className="mr10 ml10 fl edu-txt-right" style={{cursor: 'default',minWidth:'55px'}}>
|
||||
{this._toTenThousand(memo.viewed_count)} 浏览
|
||||
</span> :''}
|
||||
|
||||
</p>
|
||||
</div>
|
||||
{ user && (user.admin === true || user.user_id === memo.author_id) &&
|
||||
<div className="edu-position-hidebox" style={{position: 'absolute', right: '18px',top:'0px'}}>
|
||||
<a href="javascript:void(0);"><i className="fa fa-bars font-16"></i></a>
|
||||
<ul className="edu-position-hide undis">
|
||||
{ user.admin === true &&
|
||||
( memo.sticky === true ?
|
||||
<li><a href="javascript:void(0);" onClick={() => setDown(memo)}>取消置顶</a></li>
|
||||
:
|
||||
<li><a href="javascript:void(0);" onClick={() => setTop(memo)}>置 顶</a></li> )
|
||||
}
|
||||
<li><Link to={`/forums/${memo.id}/edit`}>编 辑</Link></li>
|
||||
<li>
|
||||
<a href="javascript:void(0)" onClick={() =>
|
||||
window.delete_confirm_box_2_react(`onMemoDelete`, '您确定要删除吗?' , memo)}>
|
||||
|
||||
删 除</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default PostItem
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import axios from 'axios'
|
||||
import update from 'immutability-helper'
|
||||
import PostItem from './PostItem'
|
||||
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
|
||||
import { queryString } from 'educoder'
|
||||
import { on, off, updatePageParams } from 'educoder'
|
||||
import './Post.css'
|
||||
const $ = window.$
|
||||
|
||||
function urlStringify(params) {
|
||||
let noParams = true;
|
||||
let paramsUrl = '';
|
||||
for (let key in params) {
|
||||
noParams = false;
|
||||
paramsUrl += `${key}=${params[key]}&`
|
||||
}
|
||||
if (noParams) {
|
||||
return '';
|
||||
}
|
||||
paramsUrl = paramsUrl.substring(0, paramsUrl.length - 1);
|
||||
return paramsUrl;
|
||||
}
|
||||
export function postPaginationHOC(options = {}) {
|
||||
// options.isMyPublish
|
||||
return function wrap(WrappedComponent) {
|
||||
|
||||
return class II extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
currentPage: 1,
|
||||
loadingMemos: true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
$('body>#root').on('onMemoDelete', (event) => {
|
||||
// const val = $('body>#root').data('onMemoDelete')
|
||||
const val = window.onMemoDelete ;
|
||||
this.onMemoDelete( JSON.parse(decodeURIComponent(val)) )
|
||||
})
|
||||
window.$('#shixun_search_input').val('')
|
||||
|
||||
this.props.setSearchValue('')
|
||||
|
||||
this.fetchMemos(null, '')
|
||||
|
||||
const that = this;
|
||||
$(window).on('popstate', (e) => {
|
||||
var state = e.originalEvent.state;
|
||||
console.log('popstate', state)
|
||||
if (state !== null) {
|
||||
|
||||
let currentPage = that.state.currentPage;;
|
||||
// // 浏览器地址改变了
|
||||
const search = that.props.history.location.search
|
||||
const parsed = queryString.parse(search);
|
||||
if (parsed.page != currentPage) {
|
||||
currentPage = parseInt( parsed.page || 1)
|
||||
// that.setSearchValue('')
|
||||
that.fetchMemos(currentPage)
|
||||
this.setState({
|
||||
currentPage,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// RightMyPublish组件发过来的消息
|
||||
// $(window).on('setSearchValue', (event, val, noFetch)=>{
|
||||
|
||||
// })
|
||||
|
||||
on('hotTagClick', (event, tagName, index) => {
|
||||
this.props.setHotLabelIndex(tagName.selectedHotLabelIndex, () => {
|
||||
this.fetchMemos(1, undefined)
|
||||
})
|
||||
})
|
||||
}
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
if (newProps.enterKeyFlag !== this.props.enterKeyFlag) {
|
||||
const childPath = this.props.match.path.split('/:')[0]
|
||||
// 加入一个浏览地址
|
||||
const _search = this.props.location.search;
|
||||
if (_search) {
|
||||
const parsed = queryString.parse(_search);
|
||||
if (parsed.page != 1) {
|
||||
parsed.page = 1;
|
||||
|
||||
this.props.history.push(`${this.props.match.url}?${queryString.stringify(parsed)}`)
|
||||
}
|
||||
}
|
||||
this.fetchMemos(1, newProps.searchValue, newProps.selectedHotLabelIndex) // 搜索框模糊搜索,重置为第一页
|
||||
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$(window).off('setSearchValue')
|
||||
$(window).off('popstate')
|
||||
off('hotTagClick')
|
||||
}
|
||||
fetchMemos(arg_currentPage, arg_searchValue, arg_selectedHotLabelIndex) {
|
||||
let { match, history } = this.props
|
||||
|
||||
let searchValue = arg_searchValue != undefined ? arg_searchValue : this.props.searchValue
|
||||
// 根据参数初始化页数
|
||||
|
||||
const memoType = match.params.memoType;
|
||||
const urlArray = match.url.split('/')
|
||||
const lastPath = urlArray[2]
|
||||
|
||||
// 1 问题反馈
|
||||
// 3 操作指南 5 技术分享
|
||||
|
||||
const memoTypeMap = {
|
||||
'guide': 3,
|
||||
'techShare': 5
|
||||
}
|
||||
const orderTypeMap = {
|
||||
'hottest': 'replies_count',
|
||||
'newest': 'created_at',
|
||||
}
|
||||
const _search = this.props.history.location.search;
|
||||
const parsed = queryString.parse(_search);
|
||||
|
||||
let currentPage = parseInt( arg_currentPage ? arg_currentPage : (parsed.page || 1) )
|
||||
|
||||
const params = {
|
||||
// replies_count最热 created_at 最新
|
||||
// s_order: 'replies_count',
|
||||
page: currentPage - 1,
|
||||
|
||||
// forum: // forum_id
|
||||
// user_id
|
||||
}
|
||||
if (searchValue) {
|
||||
params.search = searchValue.trim()
|
||||
}
|
||||
let orderType = ''
|
||||
if (memoType==='all') {
|
||||
orderType = parsed.order || 'hottest';
|
||||
params.order = orderTypeMap[orderType]
|
||||
} else if (options.isMyPublish) {
|
||||
params.user_id = -1
|
||||
} else if (memoType) {
|
||||
params.forum = memoType
|
||||
}
|
||||
let { selectedHotLabelIndex, hot_tags } = this.props;
|
||||
selectedHotLabelIndex = arg_selectedHotLabelIndex ? arg_selectedHotLabelIndex : selectedHotLabelIndex
|
||||
if (selectedHotLabelIndex !== -1 && hot_tags[selectedHotLabelIndex]) {
|
||||
params.language = encodeURIComponent(hot_tags[selectedHotLabelIndex])
|
||||
}
|
||||
|
||||
|
||||
let paramsUrl = queryString.stringify(params)
|
||||
const memosUrl = '/api/v1/memos?' + paramsUrl // /${challenge.identifier}/star
|
||||
|
||||
this.setState({
|
||||
currentPage,
|
||||
loadingMemos: true,
|
||||
orderType: orderType
|
||||
})
|
||||
// 获取memo list
|
||||
axios.get(memosUrl,{
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const memo_count = response.data.memo_count
|
||||
if (memo_count>=0) {
|
||||
|
||||
const maxPage = Math.ceil( memo_count / 15 )
|
||||
// page超出,显示最后一页
|
||||
if (maxPage != 0 && maxPage < currentPage) {
|
||||
this.fetchMemos(maxPage);
|
||||
return;
|
||||
}
|
||||
const user = response.data.current_user;
|
||||
user.tidding_count = response.data.tidding_count;
|
||||
this.props.initCommonState(user)
|
||||
this.props.initForumState(response.data)
|
||||
this.setState({
|
||||
p_forum_id: params.forum,
|
||||
p_s_order: params.s_order,
|
||||
loadingMemos: false
|
||||
})
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onCurrentPageChange(pageNum) {
|
||||
this.setState({
|
||||
currentPage: pageNum
|
||||
})
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
|
||||
updatePageParams(pageNum, this.props)
|
||||
|
||||
// 加入一个浏览地址
|
||||
// const params = {
|
||||
// page: pageNum
|
||||
// }
|
||||
// if (this.state.orderType) {
|
||||
// params.order = this.state.orderType;
|
||||
// }
|
||||
// this.props.history.push(`${url}?${queryString.stringify(parsed)}`)
|
||||
|
||||
this.fetchMemos(pageNum);
|
||||
|
||||
// this.setState({
|
||||
// currentPage: pageNum
|
||||
// })
|
||||
}
|
||||
|
||||
// 置顶
|
||||
setTop(memo) {
|
||||
const params = {
|
||||
sticky: memo.sticky ? 0 : 1,
|
||||
}
|
||||
if (this.state.p_s_order) {
|
||||
params.order = this.state.p_s_order;
|
||||
}
|
||||
if (this.state.p_forum_id) {
|
||||
params.forum_id = this.state.p_forum_id;
|
||||
}
|
||||
let paramsUrl = urlStringify(params)
|
||||
const set_top_or_down_Url = `/api/v1/memos/${memo.id}/set-top-or-down?${paramsUrl}`;
|
||||
// 获取memo list
|
||||
axios.get(set_top_or_down_Url, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const status = response.data.status
|
||||
if (status === 0) {
|
||||
const { memo_list } = response.data;
|
||||
this.props.initForumState({ memo_list })
|
||||
// 刷新列表
|
||||
// TODO 服务端直接返回第一页列表
|
||||
// this.props.history.replace('/')
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
// 取消置顶
|
||||
setDown(memo) {
|
||||
this.setTop(memo);
|
||||
}
|
||||
onMemoDelete(memo) {
|
||||
const deleteUrl = `/api/v1/memos/${memo.id}`;
|
||||
// 获取memo list
|
||||
axios.delete(deleteUrl, {
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
const status = response.data.status
|
||||
if (status === 0) {
|
||||
|
||||
this.props.showSnackbar('删除成功');
|
||||
// 刷新列表
|
||||
this.fetchMemos();
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
// item渲染
|
||||
//
|
||||
renderMemoList() {
|
||||
const { memo_list, user } = this.props;
|
||||
if (!memo_list) {
|
||||
return ''
|
||||
}
|
||||
return memo_list.map( (item, index) => {
|
||||
return (
|
||||
<PostItem key={item.id} user={user} index={index} {...this.props}
|
||||
setTop={(memo)=>this.setTop(memo)}
|
||||
setDown={(memo)=>this.setDown(memo)}
|
||||
memo={item}
|
||||
></PostItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loadingMemos } = this.state;
|
||||
const { memo_list, searchValue, showSearchValue, memo_count, selectedHotLabelIndex, hot_tags} = this.props;
|
||||
|
||||
// 规则: 搜索框输入了值 或者 选择了热门标签的时候显示该提示
|
||||
const _showSearchValue = showSearchValue || selectedHotLabelIndex != -1
|
||||
let _searchValue;
|
||||
if (showSearchValue) {
|
||||
_searchValue = searchValue
|
||||
} else if (selectedHotLabelIndex != -1){
|
||||
_searchValue = hot_tags[selectedHotLabelIndex].name || hot_tags[selectedHotLabelIndex]
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<div className="edu-back-white" id="forum_index_list"> {/* fl with100 */}
|
||||
<div className="clearfix">
|
||||
{ _showSearchValue &&
|
||||
<div className="noMemosTip" style={{display: loadingMemos ? 'none' : 'block'}}>
|
||||
<span className="fr pr20" id="search_result">
|
||||
共找到
|
||||
<span className="color-orange03">{memo_count}</span>个"
|
||||
<span class="color-orange03">{_searchValue}</span>"相关的结果
|
||||
</span>
|
||||
</div> }
|
||||
|
||||
<CircularProgress size={40} thickness={3}
|
||||
style={{ marginLeft: 'auto', marginRight: 'auto', paddingTop: '20%'
|
||||
, display: loadingMemos ? 'block': 'none' }}/>
|
||||
{ !loadingMemos &&
|
||||
<WrappedComponent {...this.props} {...this.state}
|
||||
|
||||
onPaginationChange={(pageNum, pageSize) => this.onPaginationChange(pageNum, pageSize)}
|
||||
onCurrentPageChange={(pageNum, pageSize) => this.onCurrentPageChange(pageNum, pageSize)}
|
||||
|
||||
renderMemoList={() => this.renderMemoList()}
|
||||
fetchMemos={(arg1, arg2) => this.fetchMemos(arg1, arg2)}
|
||||
|
||||
></WrappedComponent>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
class RecommendShixun extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
showRecommandShixun(){
|
||||
const { recommend_shixuns } = this.props;
|
||||
if (!recommend_shixuns) {
|
||||
return '';
|
||||
}
|
||||
const result = [];
|
||||
recommend_shixuns.forEach((shixun, index) => {
|
||||
const _shixun = shixun
|
||||
result.push(
|
||||
<div className="recomments clearfix df" key={index}>
|
||||
<a href={`/shixuns/${_shixun.identifier}/challenges`} style={{height:'76px'}} target="_blank">
|
||||
|
||||
<img alt={`${_shixun.id}`} style={{maxHeight:'76px'}} src={`/images/${_shixun.image_url}`} width="100" >
|
||||
</img>
|
||||
</a>
|
||||
<div className="ml10 flex1">
|
||||
<a href={`/shixuns/${_shixun.identifier}/challenges`} target="_blank" data-tip-down={_shixun.name}
|
||||
className="color-grey-6 task-hide mb12 recomment-name" style={{maxWidth:'147px'}}>
|
||||
{_shixun.name}
|
||||
</a>
|
||||
<p className="color-grey-9">{_shixun.myshixuns_count} 人学习</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage } = this.props
|
||||
|
||||
// 参考 TPMShixunDiscuss.js 推荐实训, 页面路径:http://localhost:3007/shixuns/uznmbg54/shixun_discuss
|
||||
return (
|
||||
<div className="padding10">
|
||||
<p className="mb20 font-16 clearfix">推荐实训</p>
|
||||
<div className="recommend-list">
|
||||
{this.showRecommandShixun()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RecommendShixun;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { trigger } from 'educoder'
|
||||
|
||||
class RightHotLabel extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
renderTags() {
|
||||
const { hot_tags, selectedHotLabelIndex } = this.props;
|
||||
if (!hot_tags) {
|
||||
return ''
|
||||
}
|
||||
const result = hot_tags.map((item, index) => {
|
||||
let params = {}
|
||||
if (typeof item === 'string') {
|
||||
params.name = item;
|
||||
} else {
|
||||
params = item;
|
||||
}
|
||||
params.selectedHotLabelIndex = index;
|
||||
return (
|
||||
<a href="javascript:void(0)" onClick={() => trigger('hotTagClick', params)} key={index}
|
||||
className={classNames({"selected": selectedHotLabelIndex === index})}>
|
||||
{item.name || item}
|
||||
</a> )
|
||||
})
|
||||
return result
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage, selectedHotLabelIndex } = this.props
|
||||
|
||||
return (
|
||||
<div className="clearfix padding40-20 edu-back-white mt10">
|
||||
<p className="font-16">热门标签</p>
|
||||
<div className="mt30 HotLabelList clearfix" id="HotLabelList">
|
||||
{this.renderTags()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RightHotLabel;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
class RightHotQuestion extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
renderHotMemos() {
|
||||
const { hot_memos } = this.props;
|
||||
if (!hot_memos) {
|
||||
return ''
|
||||
}
|
||||
return hot_memos.map((item, index) => {
|
||||
return <div className="hotQuestionItem" key={index}>
|
||||
<Link to={`/forums/${item.id}`} className="color-grey-6 task-hide mb5 questiontName">
|
||||
{item.subject}
|
||||
</Link>
|
||||
<p className="clearfix font-12 color-grey-9">
|
||||
<span className="fl">{item.replies_count} 回答</span>
|
||||
{ !!item.language && <span className="fr">来自 {item.language}</span> }
|
||||
</p>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage } = this.props
|
||||
|
||||
return (
|
||||
<div className="clearfix padding40-20 edu-back-white mt10">
|
||||
<p className="font-16">热门问题</p>
|
||||
<div className="mt10 hotQuestionList clearfix" id="hotQuestionList">
|
||||
{this.renderHotMemos()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RightHotQuestion;
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { trigger } from 'educoder'
|
||||
|
||||
class MemoLabel extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
renderTags() {
|
||||
const { memo } = this.props;
|
||||
if (!memo || memo.tag == null) {
|
||||
return ''
|
||||
}
|
||||
return <a href="javascript:void(0)">{memo.tag}</a>
|
||||
}
|
||||
render() {
|
||||
const { match, history, currentPage } = this.props
|
||||
|
||||
return (
|
||||
<div className="clearfix padding30-20 edu-back-white mt10">
|
||||
<p className="font-16">话题标签</p>
|
||||
<div className="mt30 HotLabelList clearfix" id="HotLabelList">
|
||||
{this.renderTags()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoLabel;
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { getImageUrl, toPath } from 'educoder'
|
||||
|
||||
import match_adImg from '../../images/ad/match_ad.jpg'
|
||||
const $ = window.$
|
||||
|
||||
class RightMyPublish extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyPress = (event) => {
|
||||
if(event.type !== 'keypress' || event.key == 'Enter'){
|
||||
this.props.setSearchValue( this.props.searchValue, true);
|
||||
// $(window).trigger('setSearchValue', $('#shixun_search_input').val())
|
||||
}
|
||||
}
|
||||
handleInput = (event) => {
|
||||
this.props.setSearchValue(event.target.value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history, currentPage, my_memos_count, setSearchValue, searchValue } = this.props
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="clearfix edu-back-white padding40-20 publishMemoSection">
|
||||
{/*<div className="searchFor h40 mt15 mb5 ml20">
|
||||
<div className="searchCon fl">
|
||||
<input type="text" className="searchinput" name="search" value="" placeholder="请输入帖子标题的关键字进行搜索">
|
||||
</input>
|
||||
<span className="search_close" onclick="colse_searchbox();" data-tip-down="清除">×</span>
|
||||
</div>
|
||||
<i className="fa fa-search mr5 fl color-dark-grey search_icon"
|
||||
onClick="$('#search_memos').submit();" style={{margin:'8px'}} data-tip-down="搜索"></i>
|
||||
</div>*/}
|
||||
|
||||
<div className="search-new">
|
||||
<input type="text" className="search-new-input fl" placeholder="搜索您想了解的话题" id="shixun_search_input"
|
||||
onKeyPress={this.handleKeyPress} onChange={ this.handleInput } value={searchValue}
|
||||
>
|
||||
</input>
|
||||
<span className="search-span"></span>
|
||||
<img src={getImageUrl("images/educoder/icon/search.svg")} className="fl mt5"
|
||||
onClick={ this.handleKeyPress }>
|
||||
|
||||
</img>
|
||||
</div>
|
||||
<Link to={'/forums/new'} className="sendMyQuestion edu-default-btn edu-blueback-btn edu-txt-center font-16 mb30">发布话题</Link>
|
||||
{/*<p className="edu-txt-center font-16">
|
||||
<span>我的发布</span><br/>
|
||||
<Link to={`/forums/categories/my_published`} className="color-blue">{my_memos_count}</Link>
|
||||
</p>*/}
|
||||
</div>
|
||||
|
||||
<div className="clearfix edu-back-white advertisement" >
|
||||
<a href="/competitions/home" target="_blank"><img src={match_adImg}></img></a>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RightMyPublish;
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/* 右侧搜索区域*/
|
||||
.searchFor .searchCon {
|
||||
width: 215px;
|
||||
}
|
||||
|
||||
.search-new {
|
||||
width:237px!important;
|
||||
height: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.search-new-input {
|
||||
padding-left: 16px;
|
||||
height: 30px;
|
||||
}
|
||||
.search-span {
|
||||
border-radius: 17px;
|
||||
}
|
||||
|
||||
.search-new img {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* 右侧 热门标签 */
|
||||
.HotLabelList a{display: block;float: left;padding: 0px 9px;height: 28px;line-height: 28px;border-radius: 14px;background-color: #f5f5f5;color: #666;margin-right: 10px;margin-bottom: 9px;}
|
||||
.HotLabelList a.selected {
|
||||
background: #4CACFF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 右侧 热门问题 */
|
||||
.hotQuestionItem{padding:20px 0px;border-bottom: 1px solid #eee;}
|
||||
.questiontName{max-width: 100%;display: block;}
|
||||
|
||||
|
||||
|
||||
/* 用户信息-UserSection*/
|
||||
.user_default_btn {width: 114px;}
|
||||
.userPrivateName{line-height: 25px;margin-bottom: 9px;}
|
||||
.userPrivatePost{line-height: 20px;}
|
||||
.noteDetailTitle{max-width: 650px;line-height: 38px;font-size: 24px;font-weight: normal}
|
||||
|
||||
.noteDetailNum{float: left;padding:0px 12px;position: relative;color: #999!important;height: 28px;line-height: 26px;}
|
||||
.noteDetailNum.rightline:after{position: absolute;content: '';right: 0px;width: 1px;background-color: #EAEAEA;height: 8px;top:10px;}
|
||||
|
||||
/*帖子详情点赞*/
|
||||
.noteDetailPoint{width: 100px;height: 70px;background-color: #4cacff;border-radius: 35px;color: #FFFFff;text-align: center;margin: 0px auto;box-sizing: border-box;padding: 2px 0px;cursor: pointer; line-height: 22px;
|
||||
padding-top: 12px;}
|
||||
.Pointed{background-color:#f0f0f0;color: #b3b3b3; cursor: default}
|
||||
.notefileDownload{height: 25px;line-height: 22px;}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Loading from '../../Loading'
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import MemoTechShare from './MemoTechShare'
|
||||
// import MemoGuide from './MemoGuide'
|
||||
// import MemoNewest from './MemoNewest'
|
||||
// import MemoHottest from './MemoHottest'
|
||||
|
||||
import MemoDetail from './MemoDetail'
|
||||
import MemoNew from './MemoNew'
|
||||
import MemoMyPublish from './MemoMyPublish'
|
||||
import MemoShixun from './shixun/MemoShixun'
|
||||
|
||||
|
||||
import { TPMIndexHOC } from '../tpm/TPMIndexHOC'
|
||||
|
||||
import RightMyPublish from './RightMyPublish'
|
||||
import UserSection from './UserSection'
|
||||
import RightHotLabel from './RightHotLabel'
|
||||
import RightHotQuestion from './RightHotQuestion'
|
||||
import RightMemoLabel from './RightMemoLabel'
|
||||
import RecommendShixun from './RecommendShixun'
|
||||
|
||||
|
||||
import ForumsNavTab from './ForumsNavTab'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
|
||||
import 'rc-select/assets/index.css';
|
||||
import './ForumsIndex.css'
|
||||
import './RightSection.css'
|
||||
|
||||
import { SnackbarHOC } from 'educoder'
|
||||
|
||||
|
||||
const isPort3007 = window.location.port == 3007;
|
||||
let _url_origin = isPort3007 ? 'http://localhost:3000' : ''
|
||||
|
||||
const $ = window.$
|
||||
$('head').append( $('<link rel="stylesheet" type="text/css" />')
|
||||
.attr('href', `${_url_origin}/stylesheets/css/edu-admin.css?1525440977`) );
|
||||
$('head').append( $('<link rel="stylesheet" type="text/css" />')
|
||||
.attr('href', `${_url_origin}/stylesheets/css/edu-forum.css?1525440977`) );
|
||||
$('head').append( $('<link rel="stylesheet" type="text/css" />')
|
||||
.attr('href', `${_url_origin}/stylesheets/educoder/magic-check.css?1525440977`) );
|
||||
|
||||
class ForumsIndex extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
searchValue: '',
|
||||
enterKeyFlag: false,
|
||||
showSearchValue: false,
|
||||
selectedHotLabelIndex: -1,
|
||||
}
|
||||
}
|
||||
|
||||
setSearchValue = (searchValue, enterKeyFlag) => {
|
||||
if (enterKeyFlag === true) {
|
||||
this.setState({
|
||||
selectedHotLabelIndex: -1
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
searchValue,
|
||||
showSearchValue: (enterKeyFlag && searchValue) ? true : false,
|
||||
enterKeyFlag: enterKeyFlag === true ? !this.state.enterKeyFlag : this.state.enterKeyFlag
|
||||
})
|
||||
}
|
||||
|
||||
setHotLabelIndex = (index, callback) => {
|
||||
const newState = {
|
||||
selectedHotLabelIndex: index,
|
||||
}
|
||||
if (index != -1) {
|
||||
newState.searchValue = ''
|
||||
newState.showSearchValue = false
|
||||
}
|
||||
this.setState({
|
||||
...newState
|
||||
}, callback)
|
||||
}
|
||||
|
||||
initForumState(data) {
|
||||
this.setState({...data})
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
window.document.title = '讨论区'
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
const { match, history, resLoading } = this.props
|
||||
const { memo } = this.state;
|
||||
|
||||
const techSharePath = `${match.path}/categories/:memoType`
|
||||
const guidePath = `${match.path}/categories/:memoType`
|
||||
const hottestPath = `${match.path}/categories/:memoType` // ?order=hottest
|
||||
const newestPath = `${match.path}/categories/:memoType` // ?order=newest
|
||||
|
||||
const shixunDiscussPath = `/tpforums/categories/shixun_discuss`
|
||||
const locationPath = history.location.pathname
|
||||
|
||||
|
||||
const isWidth100 = (locationPath.indexOf('tpforums/new') !== -1
|
||||
|| locationPath.indexOf('/edit') !== -1) ? true : false
|
||||
|
||||
const pathArray = locationPath.split('/');
|
||||
const isMemoDetail = (!isWidth100 &&
|
||||
pathArray.length === 3 && !isNaN(parseInt(pathArray[2])) ) ? true : false
|
||||
|
||||
const isGuide = locationPath.indexOf('/tpforums/categories/3') !== -1
|
||||
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<div className="educontent mt30 clearfix">
|
||||
{/* 左边栏 component={TechShare}
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab> */}
|
||||
<div className={classNames('fl', { with75: !isWidth100}, { width100: isWidth100}) }>
|
||||
|
||||
|
||||
<Switch>
|
||||
<Route path={`/tpforums/categories/my_published`} render={
|
||||
(props) => (<MemoMyPublish {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
setSearchValue={this.setSearchValue}
|
||||
setHotLabelIndex={this.setHotLabelIndex}
|
||||
/>)
|
||||
}></Route>
|
||||
<Route path={`${shixunDiscussPath}`} render={
|
||||
(props) => (<MemoShixun {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
setSearchValue={this.setSearchValue}
|
||||
setHotLabelIndex={this.setHotLabelIndex}
|
||||
/>)
|
||||
}></Route>
|
||||
<Route path={`${techSharePath}`} render={
|
||||
(props) => (<MemoTechShare {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
setSearchValue={this.setSearchValue}
|
||||
setHotLabelIndex={this.setHotLabelIndex}
|
||||
/>)
|
||||
}></Route>
|
||||
|
||||
|
||||
{/*
|
||||
<Route path={`${guidePath}`} render={
|
||||
(props) => (<MemoGuide {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)} />)
|
||||
}></Route>
|
||||
<Route path={`${hottestPath}`} render={
|
||||
(props) => (<MemoHottest {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)} />)
|
||||
}></Route>
|
||||
<Route path={`${newestPath}`} render={
|
||||
(props) => (<MemoNewest {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)} />)
|
||||
}></Route> */}
|
||||
|
||||
{/* :forumTypeId/ */}
|
||||
<Route path={`/tpforums/new`} render={
|
||||
(props) => (<MemoNew {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
/>)
|
||||
}></Route>
|
||||
|
||||
<Route path={`/tpforums/:memoId/edit`} render={
|
||||
(props) => (<MemoNew {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
/>)
|
||||
}></Route>
|
||||
|
||||
|
||||
|
||||
<Route path={`${match.path}/:memoId`} render={
|
||||
(props) => (<MemoDetail {...this.props} {...this.state} {...props}
|
||||
initForumState={(data)=>this.initForumState(data)}
|
||||
/>)
|
||||
}></Route>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<Redirect from={`${match.url}`} to={`/tpforums/categories/all?order=newest`} />
|
||||
</Switch>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{/* 右边栏 */}
|
||||
{ !isWidth100 && <div className="with25 fl">
|
||||
<div className="ml20">
|
||||
{isMemoDetail ?
|
||||
<React.Fragment>
|
||||
<UserSection {...this.props} {...this.state} initForumState={(data)=>this.initForumState(data)} ></UserSection>
|
||||
{/*todo 新增RightMemoLabel 和 推荐实训RecommendShixun */}
|
||||
{ memo && memo.tag && <RightMemoLabel {...this.props} {...this.state}></RightMemoLabel> }
|
||||
<RecommendShixun {...this.props} {...this.state}></RecommendShixun>
|
||||
</React.Fragment>
|
||||
:
|
||||
<React.Fragment>
|
||||
<RightMyPublish {...this.props} {...this.state} setSearchValue={this.setSearchValue}></RightMyPublish>
|
||||
{ !isGuide && <RightHotLabel {...this.props} {...this.state} ></RightHotLabel> }
|
||||
<RightHotQuestion {...this.props} {...this.state} ></RightHotQuestion>
|
||||
<RecommendShixun {...this.props} {...this.state}></RecommendShixun>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SnackbarHOC() ( TPMIndexHOC ( ForumsIndex ) );
|
||||
|
||||
/*
|
||||
:
|
||||
列表所有:
|
||||
http://localhost:3000/forums/categories/all
|
||||
:
|
||||
详情:
|
||||
:
|
||||
http://localhost:3000/forums/5
|
||||
:
|
||||
http://localhost:3000/forums/new
|
||||
:
|
||||
http://localhost:3000/forums/categories/my_published
|
||||
*/
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
class UserSection extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
/*点击关注或者取消关注*/
|
||||
AboutFocus(){
|
||||
const { author_info } = this.props
|
||||
/*http://localhost:3000/api/v1/users/155/watch?object_id=156&object_type=user*/
|
||||
|
||||
const focusUrl = `/api/v1/users/${author_info.user_id}/${this.props.author_info.watched ? 'unwatch' : 'watch'}?object_id=${author_info.user_id}&object_type=user`
|
||||
|
||||
axios.get(focusUrl,{
|
||||
})
|
||||
.then((response) => {
|
||||
const status = response.data.status;
|
||||
console.log(status);
|
||||
if(status == 1){
|
||||
const new_author_info = Object.assign({}, this.props.author_info)
|
||||
new_author_info.watched = !new_author_info.watched
|
||||
this.props.initForumState({author_info: new_author_info})
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { match, history, author_info , current_user } = this.props
|
||||
if (!author_info) {
|
||||
return <div className="edu-back-white" id="forum_index_list"></div>
|
||||
}
|
||||
return (
|
||||
<div className="edu-back-white padding40-20 edu-txt-center">
|
||||
<a href={`/users/${author_info.login}`} target="_blank"><img src={`/images/${author_info.image_url}`} width="90" height="90" className="radius mb5"/></a>
|
||||
<p className="font-20 userPrivateName">{author_info.username}</p>
|
||||
<p className="color-grey-9 userPrivatePost">{author_info.identity}</p>
|
||||
{ author_info.user_id !== current_user.user_id &&
|
||||
<p className="clearfix mt30">
|
||||
<a href="javascript:void(0)" className="fl font-16 mr10 user_default_btn edu-blueback-btn" onClick={()=>{this.AboutFocus()}}>{ author_info.watched == true ? "取消关注" : "关注" }</a>
|
||||
<a href={`/users/${current_user.login}/message_detail?user_id=${author_info.user_id}`} className="fr font-16 user_default_btn user_private_btn" target="_blank">私信</a>
|
||||
</p> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UserSection;
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
.sxReturnItem{
|
||||
width: 100%;
|
||||
padding-left:20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ItemLine{
|
||||
border-bottom: 1px solid #EBEBEB;
|
||||
padding:30px 0px;
|
||||
}
|
||||
.ItemLineHeadPhoto{
|
||||
height:48px;
|
||||
width: 48px;
|
||||
float: left;
|
||||
margin-top: 7px;
|
||||
}
|
||||
.shixunReply{
|
||||
max-width: 604px;
|
||||
}
|
||||
.sxReturnItem:last-child .ItemLine{
|
||||
border-bottom: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Pagination from 'rc-pagination';
|
||||
|
||||
import ShiXunPostItem from './ShiXunPostItem'
|
||||
import ForumsNavTab from '../ForumsNavTab'
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
import { on, off } from 'educoder'
|
||||
import './MemoShixun.css'
|
||||
|
||||
// import queryString from 'query-string'
|
||||
import { queryString, updatePageParams } from 'educoder'
|
||||
import MemoList from '../MemoList'
|
||||
import axios from 'axios'
|
||||
|
||||
const $ = window.$;
|
||||
class MemoShixun extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
currentPage: 1,
|
||||
loadingMemos: true
|
||||
}
|
||||
}
|
||||
|
||||
onPaginationChange(pageNum, pageSize) {
|
||||
window.$("html,body").animate({"scrollTop":0})
|
||||
updatePageParams(pageNum, this.props)
|
||||
this.fetchShixunMemos(pageNum);
|
||||
}
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
if (newProps.enterKeyFlag !== this.props.enterKeyFlag) {
|
||||
// const childPath = this.props.match.path.split('/:')[0]
|
||||
// // 加入一个浏览地址
|
||||
// const _search = this.props.location.search;
|
||||
// if (_search) {
|
||||
// const parsed = queryString.parse(_search);
|
||||
// if (parsed.page != 1) {
|
||||
// parsed.page = 1;
|
||||
|
||||
// this.props.history.push(`${this.props.match.url}?${queryString.stringify(parsed)}`)
|
||||
// }
|
||||
// }
|
||||
this.fetchShixunMemos(1, newProps.searchValue, newProps.selectedHotLabelIndex) // 搜索框模糊搜索,重置为第一页
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fetchShixunMemos(arg_currentPage, arg_searchValue, arg_selectedHotLabelIndex) {
|
||||
/*
|
||||
page = params[:page].to_i
|
||||
offset = page * 15
|
||||
search = params[:search]
|
||||
tag = params[:tag_repertoire_id]
|
||||
*/
|
||||
|
||||
const _search = this.props.history.location.search;
|
||||
const parsed = queryString.parse(_search);
|
||||
|
||||
let currentPage = parseInt( arg_currentPage ? arg_currentPage : (parsed.page || 1) )
|
||||
|
||||
const paramsObject = {
|
||||
page: currentPage - 1
|
||||
}
|
||||
|
||||
let searchValue = arg_searchValue != undefined ? arg_searchValue : this.props.searchValue
|
||||
|
||||
if (searchValue) {
|
||||
paramsObject.search = searchValue
|
||||
}
|
||||
let { selectedHotLabelIndex, hot_tags } = this.props;
|
||||
selectedHotLabelIndex = arg_selectedHotLabelIndex ? arg_selectedHotLabelIndex : selectedHotLabelIndex
|
||||
if (selectedHotLabelIndex !== -1 && hot_tags[selectedHotLabelIndex]) {
|
||||
paramsObject.tag_repertoire_id = hot_tags[selectedHotLabelIndex].id
|
||||
}
|
||||
|
||||
const stringifid = queryString.stringify(paramsObject);
|
||||
const url = `/api/v1/discusses?${stringifid}` // /${challenge.identifier}/star
|
||||
|
||||
// 获取memo list
|
||||
this.setState({
|
||||
currentPage: currentPage ,
|
||||
loadingMemos: true
|
||||
})
|
||||
axios.get(url,{
|
||||
withCredentials: true,
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data) {
|
||||
const user = response.data.current_user;
|
||||
user.tidding_count = response.data.tidding_count;
|
||||
this.props.initCommonState(user)
|
||||
this.props.initForumState(response.data)
|
||||
|
||||
// const { hot_tags } = response.data;
|
||||
// if (hot_tags && hot_tags.length) {
|
||||
// this.tagNameIdMap = {}
|
||||
// hot_tags.forEach( (item, index) => {
|
||||
// this.tagNameIdMap[item.name] = item.id
|
||||
// })
|
||||
// }
|
||||
|
||||
this.setState({
|
||||
|
||||
// p_forum_id: params.forum,
|
||||
// p_s_order: params.s_order,
|
||||
loadingMemos: false
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
componentDidMount() {
|
||||
this.fetchShixunMemos();
|
||||
|
||||
on('hotTagClick', (event, tagName) => {
|
||||
this.props.setHotLabelIndex(tagName.selectedHotLabelIndex, () => {
|
||||
this.fetchShixunMemos(1, undefined)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
$(window).on('popstate', (e) => {
|
||||
var state = e.originalEvent.state;
|
||||
console.log('popstate', state)
|
||||
if (state !== null) {
|
||||
|
||||
let currentPage = this.state.currentPage;;
|
||||
// // 浏览器地址改变了
|
||||
const search = this.props.history.location.search
|
||||
const parsed = queryString.parse(search);
|
||||
if (parsed.page != currentPage) {
|
||||
currentPage = parseInt( parsed.page || 1)
|
||||
// this.setSearchValue('')
|
||||
this.fetchShixunMemos(currentPage)
|
||||
this.setState({
|
||||
currentPage,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
componentWillUnmount() {
|
||||
off('hotTagClick')
|
||||
$(window).off('popstate')
|
||||
}
|
||||
|
||||
renderMemoList() {
|
||||
|
||||
const { memo_list, user } = this.props;
|
||||
if (!memo_list) {
|
||||
return ''
|
||||
}
|
||||
/*
|
||||
<PostItem key={item.id} user={user} index={index} {...this.props}
|
||||
setTop={(memo)=>this.setTop(memo)}
|
||||
setDown={(memo)=>this.setDown(memo)}
|
||||
memo={item}
|
||||
></PostItem>
|
||||
*/
|
||||
return memo_list.map( (item, index) => {
|
||||
return (
|
||||
<ShiXunPostItem key={item.id} user={user} index={index} {...this.props} memo={item}></ShiXunPostItem>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, history , memo_count ,memo_list, showSearchValue, searchValue
|
||||
, selectedHotLabelIndex, hot_tags } = this.props;
|
||||
const { currentPage, loadingMemos } = this.state;
|
||||
|
||||
// 规则: 搜索框输入了值 或者 选择了热门标签的时候显示该提示
|
||||
const _showSearchValue = showSearchValue || selectedHotLabelIndex != -1
|
||||
let _searchValue;
|
||||
if (showSearchValue) {
|
||||
_searchValue = searchValue
|
||||
} else if (selectedHotLabelIndex != -1){
|
||||
_searchValue = hot_tags[selectedHotLabelIndex].name || hot_tags[selectedHotLabelIndex]
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="edu-back-white" id="forum_index_list"> {/* fl with100 */}
|
||||
<div className="clearfix">
|
||||
{ _showSearchValue &&
|
||||
<div className="noMemosTip" style={{display: loadingMemos ? 'none' : 'block'}}>
|
||||
<span className="fr pr20" id="search_result">
|
||||
共找到
|
||||
<span className="color-orange03">{memo_count}</span>个"
|
||||
<span class="color-orange03">{_searchValue}</span>"相关的结果
|
||||
</span>
|
||||
</div> }
|
||||
|
||||
<CircularProgress size={40} thickness={3}
|
||||
style={{ marginLeft: 'auto', marginRight: 'auto', paddingTop: '20%'
|
||||
, display: loadingMemos ? 'block': 'none' }}/>
|
||||
{ !loadingMemos &&
|
||||
<React.Fragment>
|
||||
<ForumsNavTab {...this.props}></ForumsNavTab>
|
||||
<MemoList {...this.props}
|
||||
renderMemoList={() => this.renderMemoList()}
|
||||
onPaginationChange={ (pageNum, pageSize) => this.onPaginationChange(pageNum, pageSize) }
|
||||
{...this.state}
|
||||
>
|
||||
</MemoList>
|
||||
</React.Fragment>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MemoShixun;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import moment from 'moment'
|
||||
import './MemoShixun.css'
|
||||
|
||||
class ShiXunPostItem extends Component
|
||||
{
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { memo } = this.props;
|
||||
let tagStr = ''
|
||||
if (memo.shixun_tag && memo.shixun_tag.length) {
|
||||
|
||||
memo.shixun_tag.forEach( tag => {
|
||||
tagStr += ` ${tag}`
|
||||
})
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="sxReturnItem">
|
||||
<div className="ItemLine clearfix df">
|
||||
<a href={`/users/${memo.login}`} className="ItemLineHeadPhoto">
|
||||
<img src={`/images/${memo.image_url}`}
|
||||
width="48px" height="48px" className="radius"/>
|
||||
</a>
|
||||
<div className="flex1 ml10 pr20">
|
||||
<div className="clearfix">
|
||||
<p className="shixunReply task-hide font-16 fl">
|
||||
<Link to={`${memo.tpm_url}`} target="_blank">{memo.subject}</Link>
|
||||
</p>
|
||||
{ memo.reward &&
|
||||
<span className="color-orange ml20" data-tip-down={`获得平台奖励金币:${memo.reward}`}>
|
||||
<i className="iconfont icon-gift font-16 mr5"></i>{memo.reward}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<p className="clearfix mt5">
|
||||
<span className="fl color-grey-9">{memo.username}</span>
|
||||
<span className="fl color-grey-9 ml40">{moment(memo.updated_at).fromNow()}</span>
|
||||
{ !!tagStr && <span className="fl color-grey-9 ml40">来自 {tagStr}</span>}
|
||||
{ !!memo.praise_count && <span className="fr color-grey-6 ml20 font-12">{memo.praise_count} 赞</span>}
|
||||
{ !!memo.replies_count && <span className="fr color-grey-6 font-12">{memo.replies_count} 回复</span>}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
export default ShiXunPostItem
|
||||
|
|
@ -53,7 +53,7 @@ class TPMBanner extends Component {
|
|||
<div className="shixunDetail_top">
|
||||
<div className="educontent clearfix">
|
||||
<p className="clearfix">
|
||||
<span className="font-28 color-white mr10">{shixun.name}</span>
|
||||
<a href={`/shixuns/${shixun.identifier}/challenges`} className="font-28 color-white mr10">{shixun.name}</a>
|
||||
{/*TODO folk_from*/}
|
||||
</p>
|
||||
<div className="clearfix mt30">
|
||||
|
|
|
|||
Loading…
Reference in New Issue