Compare commits
61 Commits
Author | SHA1 | Date |
---|---|---|
黄心宇 | e414343db3 | |
黄心宇 | 06374112d8 | |
黄心宇 | f45d31b489 | |
黄心宇 | 0580fcb2c0 | |
黄心宇 | baed9df260 | |
caishi | db9bfa7576 | |
caishi | c8a08bbd7f | |
caishi | 82b979894b | |
caishi | 7b430d8257 | |
caishi | 0accc419de | |
caishi | 7423036bb0 | |
caishi | 5e70812ef1 | |
“xxq250” | 58b8fb4ca8 | |
caishi | b56f0b27c2 | |
caishi | ae1f4246e4 | |
caishi | 75f26ac136 | |
caishi | 765840f2c8 | |
caishi | 5b4c103ad5 | |
caishi | 0ce87362d5 | |
xxq250 | 77730321ee | |
caishi | 703c876c18 | |
caishi | 471b8dff3b | |
caishi | 68501898bd | |
caishi | d96611893f | |
caishi | cf1905bc4b | |
caishi | fc5c957bf1 | |
caishi | 8401a55e49 | |
caishi | ee6961d02e | |
caishi | 57ab5d1199 | |
caishi | 6d9af5c906 | |
caishi | e38508ea59 | |
caishi | 68d1b69a09 | |
caishi | aef1451455 | |
caishi | 191e79e05a | |
caishi | ea8b5ece1a | |
caishi | 41e9326174 | |
caishi | 8dad3b231c | |
caishi | 0703fb1bb6 | |
caishi | 7a96f7021a | |
caishi | 54fc16ddad | |
caishi | c04b2ed5f6 | |
sylor_huang@126.com | 107b6ed95e | |
sylor_huang@126.com | 67ccc2ebe5 | |
sylor_huang@126.com | c00933b0ca | |
sylor_huang@126.com | 4f67719dac | |
sylor_huang@126.com | 5be45528cf | |
sylor_huang@126.com | c3b2f8f6ad | |
sylor_huang@126.com | 6950c1a820 | |
sylor_huang@126.com | b5881d5162 | |
sylor_huang@126.com | 4de25d36a8 | |
sylor_huang@126.com | 5992e83c60 | |
caishi | c740dcc6f8 | |
caishi | 9922b109f8 | |
sylor_huang@126.com | 7b097149eb | |
caishi | f7fa5291c6 | |
caishi | 8ebac42d42 | |
caishi | 10db5271d7 | |
sylor_huang@126.com | 4596eac895 | |
caishi | d1232be557 | |
sylor_huang@126.com | 79c943d31b | |
caishi | 6d8834403a |
|
@ -7,6 +7,7 @@
|
||||||
"@novnc/novnc": "^1.1.0",
|
"@novnc/novnc": "^1.1.0",
|
||||||
"actioncable": "^5.2.4-3",
|
"actioncable": "^5.2.4-3",
|
||||||
"antd": "^3.26.15",
|
"antd": "^3.26.15",
|
||||||
|
"antd-img-crop": "^3.14.1",
|
||||||
"array-flatten": "^2.1.2",
|
"array-flatten": "^2.1.2",
|
||||||
"autoprefixer": "7.1.6",
|
"autoprefixer": "7.1.6",
|
||||||
"axios": "^0.18.1",
|
"axios": "^0.18.1",
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
"codemirror": "^5.53.0",
|
"codemirror": "^5.53.0",
|
||||||
"connected-react-router": "4.4.1",
|
"connected-react-router": "4.4.1",
|
||||||
"css-loader": "^3.5.2",
|
"css-loader": "^3.5.2",
|
||||||
|
"dompurify": "^3.0.2",
|
||||||
"dotenv": "4.0.0",
|
"dotenv": "4.0.0",
|
||||||
"dotenv-expand": "4.2.0",
|
"dotenv-expand": "4.2.0",
|
||||||
"echarts": "^4.7.0",
|
"echarts": "^4.7.0",
|
||||||
|
@ -111,7 +113,8 @@
|
||||||
"webpack-dev-server": "^3.10.3",
|
"webpack-dev-server": "^3.10.3",
|
||||||
"webpack-manifest-plugin": "^2.2.0",
|
"webpack-manifest-plugin": "^2.2.0",
|
||||||
"whatwg-fetch": "2.0.3",
|
"whatwg-fetch": "2.0.3",
|
||||||
"wrap-md-editor": "^0.2.20"
|
"wrap-md-editor": "^0.2.20",
|
||||||
|
"xss": "^1.0.14"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --max_old_space_size=15360 scripts/start.js",
|
"start": "node --max_old_space_size=15360 scripts/start.js",
|
||||||
|
|
|
@ -2343,8 +2343,11 @@ input::-ms-clear {
|
||||||
/*中间部分宽度固定为1200*/
|
/*中间部分宽度固定为1200*/
|
||||||
.newMain {
|
.newMain {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding-bottom: 110px;
|
|
||||||
min-width: 1200px;
|
min-width: 1200px;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
padding-top: 70px;
|
||||||
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3942,9 +3945,42 @@ html>body #ajax-indicator {
|
||||||
max-height: 340px;
|
max-height: 340px;
|
||||||
}/*头部导航条样式---2018-03-19--by-cs*/
|
}/*头部导航条样式---2018-03-19--by-cs*/
|
||||||
|
|
||||||
|
.ant-dropdown.imgDropdown,.addDropdown{
|
||||||
|
z-index:10000!important;
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.addDropdown ul{
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
.addDropdown ul a{
|
||||||
|
padding:0px;
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
.imgDropdown li:first-child {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.imgDropdown li:first-child:hover,.imgDropdown li:last-child:hover{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.imgDropdown li:last-child:hover a{
|
||||||
|
color: #4CACFF;
|
||||||
|
}
|
||||||
|
.imgDropdown li:last-child {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
.imgDropdown li,.addDropdown li {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
padding: 0!important;
|
||||||
|
cursor: default;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
.head-nav {
|
.head-nav {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 70px;
|
height: 58px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 780px;
|
min-width: 780px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -3957,14 +3993,14 @@ html>body #ajax-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
height: 70px;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.head-nav ul#header-nav li {
|
.head-nav ul#header-nav li {
|
||||||
float: left;
|
float: left;
|
||||||
height: 70px;
|
height: 58px;
|
||||||
line-height: 70px;
|
line-height: 58px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -3975,7 +4011,7 @@ html>body #ajax-indicator {
|
||||||
display: block;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #333;
|
color: #fff;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4106,20 +4142,7 @@ em.vertical-line {
|
||||||
/* 右侧内容宽度变化的话,需要调整posi-search right的值*/
|
/* 右侧内容宽度变化的话,需要调整posi-search right的值*/
|
||||||
|
|
||||||
/*底部*/
|
/*底部*/
|
||||||
.newFooter {
|
|
||||||
max-height: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.newFooter {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: #323232;
|
|
||||||
clear: both;
|
|
||||||
min-width: 1200px;
|
|
||||||
z-index: 8;
|
|
||||||
left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footercon {
|
.footercon {
|
||||||
border-bottom: 1px solid #47494d;
|
border-bottom: 1px solid #47494d;
|
||||||
|
@ -6716,3 +6739,75 @@ ul.count_ul li:not(:last-child):after {
|
||||||
input.ant-input-lg::placeholder{
|
input.ant-input-lg::placeholder{
|
||||||
font-size: 14px !important;
|
font-size: 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.newFooter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: #323232;
|
||||||
|
clear: both;
|
||||||
|
min-width: 1200px;
|
||||||
|
z-index: 8;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.footEdition {
|
||||||
|
background-color: #171b23;
|
||||||
|
}
|
||||||
|
.footEdition .footContent {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 86px 0;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul {
|
||||||
|
min-width: 120px;
|
||||||
|
text-align: left;
|
||||||
|
margin-right: 80px
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul>img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
margin-top: 25px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul li {
|
||||||
|
height: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #bdc2d1;
|
||||||
|
margin-bottom: 15px!important;
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul li.thehead {
|
||||||
|
height: 25px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 25px;
|
||||||
|
margin-bottom: 20px!important;
|
||||||
|
}
|
||||||
|
.footEdition .footContent ul li a {
|
||||||
|
color: #bdc2d1!important;
|
||||||
|
}
|
||||||
|
.copyrightDesc {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #bdc2d1;
|
||||||
|
line-height: 28px;
|
||||||
|
padding: 15px 0;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #1b212c;
|
||||||
|
}
|
||||||
|
.newFooter p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0!important;
|
||||||
|
}
|
||||||
|
.copyrightDesc a {
|
||||||
|
color: #bdc2d1!important;
|
||||||
|
}
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 8.8 KiB |
|
@ -1,23 +1,14 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
|
<meta name="google" content="notranslate" />
|
||||||
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
|
<meta name="Keywords" Content="trustie,trustieforge,forge,确实让创建更美好,协同开发平台">
|
||||||
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
|
<meta name="Keywords" Content="TrustieOpenSourceProject">
|
||||||
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
|
<meta name="Keywords" Content="issue,bug,tracker,软件工程,课程实践">
|
||||||
|
<meta name="Description" Content="持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动">
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
<!-- <script type="text/javascript">
|
|
||||||
window.__isR = true;
|
|
||||||
if (
|
|
||||||
(navigator.userAgent.indexOf('MSIE 9') != -1
|
|
||||||
|| navigator.userAgent.indexOf('MSIE 10') != -1)
|
|
||||||
&&
|
|
||||||
location.pathname.indexOf("/compatibility") == -1) {
|
|
||||||
location.href = '/compatibility.html'
|
|
||||||
}
|
|
||||||
</script> -->
|
|
||||||
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
||||||
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
|
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
|
||||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
|
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
|
||||||
|
|
|
@ -97,6 +97,9 @@ body {
|
||||||
.editormd-image-click-expand .editormd-image-dialog .image-link {
|
.editormd-image-click-expand .editormd-image-dialog .image-link {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.editormd-fullscreen{
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
/* 解决鼠标框选时,左边第一列没高亮的问题 */
|
/* 解决鼠标框选时,左边第一列没高亮的问题 */
|
||||||
.CodeMirror .CodeMirror-lines pre.CodeMirror-line,
|
.CodeMirror .CodeMirror-lines pre.CodeMirror-line,
|
||||||
|
|
98
src/App.js
|
@ -5,7 +5,8 @@ import zhCN from 'antd/lib/locale-provider/zh_CN';
|
||||||
import {
|
import {
|
||||||
BrowserRouter as Router,
|
BrowserRouter as Router,
|
||||||
Route,
|
Route,
|
||||||
Switch
|
Switch,
|
||||||
|
Redirect
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import LoginDialog from './modules/login/LoginDialog';
|
import LoginDialog from './modules/login/LoginDialog';
|
||||||
|
@ -42,10 +43,10 @@ const theme = createMuiTheme({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
//forge项目
|
//forge项目
|
||||||
const Projects = Loadable({
|
// const Projects = Loadable({
|
||||||
loader: () => import('./forge/Index'),
|
// loader: () => import('./forge/Index'),
|
||||||
loading: Loading,
|
// loading: Loading,
|
||||||
})
|
// })
|
||||||
//forge项目-devOps详情
|
//forge项目-devOps详情
|
||||||
const OpsDetail = Loadable({
|
const OpsDetail = Loadable({
|
||||||
loader: () => import('./forge/DevOps/opsDetail'),
|
loader: () => import('./forge/DevOps/opsDetail'),
|
||||||
|
@ -61,22 +62,24 @@ const Shixunnopage = Loadable({
|
||||||
loader: () => import('./modules/404/Shixunnopage'),
|
loader: () => import('./modules/404/Shixunnopage'),
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
})
|
})
|
||||||
|
const ExchangeForums = Loadable({
|
||||||
|
loader: () => import("./exchange/index"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
// 新版论坛交流
|
||||||
|
const Forums = Loadable({
|
||||||
|
loader: () => import("./forums/Index"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
|
const Account = Loadable({
|
||||||
|
loader: () => import("./user_info/Index"),
|
||||||
|
loading: Loading,
|
||||||
|
});
|
||||||
//500页面
|
//500页面
|
||||||
const http500 = Loadable({
|
const http500 = Loadable({
|
||||||
loader: () => import('./modules/500/http500'),
|
loader: () => import('./modules/500/http500'),
|
||||||
loading: Loading,
|
loading: Loading,
|
||||||
})
|
})
|
||||||
const InfosIndex = Loadable({
|
|
||||||
loader: () => import('./forge/users/Index'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
// 组织
|
|
||||||
const OrganizeIndex = Loadable({
|
|
||||||
loader: () => import('./forge/Team/Index'),
|
|
||||||
loading: Loading,
|
|
||||||
})
|
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -127,7 +130,7 @@ class App extends Component {
|
||||||
document.title = "loading...";
|
document.title = "loading...";
|
||||||
this.disableVideoContextMenu();
|
this.disableVideoContextMenu();
|
||||||
history.listen(() => {
|
history.listen(() => {
|
||||||
this.forceUpdate()
|
this.forceUpdate();
|
||||||
const $ = window.$
|
const $ = window.$
|
||||||
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
|
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
|
||||||
});
|
});
|
||||||
|
@ -213,62 +216,43 @@ class App extends Component {
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConfigProvider locale={zhCN}>
|
<ConfigProvider locale={zhCN}>
|
||||||
<MuiThemeProvider theme={theme}>
|
<MuiThemeProvider theme={theme}>
|
||||||
{/* <Accountnewprofile {...this.props}{...this.state} /> */}
|
|
||||||
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
||||||
{/* <Notcompletedysl {...this.props} {...this.state}></Notcompletedysl> */}
|
<Router>
|
||||||
{/* <Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl> */}
|
|
||||||
{/* <Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview> */}
|
|
||||||
{/* <AccountProfile {...this.props} {...this.state} /> */}
|
|
||||||
{/* <Certifiedprofessional {...this.props} {...this.state} ModalCancelsy={this.ModalCancelsy} ModalshowCancelsy={this.ModalshowCancelsy} /> */}
|
|
||||||
<Router>
|
|
||||||
<Switch>
|
<Switch>
|
||||||
{/*项目*/}
|
|
||||||
<Route
|
<Route
|
||||||
path={"/projects/:projectId/ops/:opsId/detail"}
|
path="/accounts/:login"
|
||||||
render={
|
render={(props) => (
|
||||||
(props) => {
|
<Account {...this.props} {...this.state} {...props} />
|
||||||
return (<OpsDetail {...this.props} {...props} {...this.state} />)
|
)}
|
||||||
}
|
></Route>
|
||||||
}>
|
{/* 论坛交流板块管理 */}
|
||||||
</Route>
|
|
||||||
{/*项目*/}
|
|
||||||
<Route
|
<Route
|
||||||
path={"/projects"}
|
path="/forums/manage"
|
||||||
render={
|
render={(props) => (
|
||||||
(props) => {
|
<ExchangeForums {...this.props} {...this.state} {...props} />
|
||||||
return (<Projects {...this.props} {...props} {...this.state} />)
|
)}
|
||||||
}
|
></Route>
|
||||||
}>
|
{/* 问吧、论坛交流 */}
|
||||||
</Route>
|
<Route
|
||||||
|
path="/forums"
|
||||||
|
render={(props) => (
|
||||||
|
<Forums {...this.props} {...this.state} {...props} />
|
||||||
|
)}
|
||||||
|
></Route>
|
||||||
{/*403*/}
|
{/*403*/}
|
||||||
<Route path="/403" component={Shixunauthority} />
|
<Route path="/403" component={Shixunauthority} />
|
||||||
|
|
||||||
<Route path="/500" component={http500} />
|
<Route path="/500" component={http500} />
|
||||||
<Route path={"/organize"}
|
|
||||||
render={
|
|
||||||
(props) => {
|
|
||||||
return (<OrganizeIndex {...this.props} {...props} {...this.state} />)
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
</Route>
|
|
||||||
{/*404*/}
|
{/*404*/}
|
||||||
<Route path="/nopage" component={Shixunnopage} />
|
<Route path="/nopage" component={Shixunnopage} />
|
||||||
|
<Redirect from="/projects" to="/nopage"/>
|
||||||
{/* 个人主页 */}
|
{/* 个人主页 */}
|
||||||
<Route path="/users/:username"
|
|
||||||
render={
|
|
||||||
(props) => {
|
|
||||||
return (<InfosIndex {...this.props} {...this.state} />)
|
|
||||||
}
|
|
||||||
}></Route>
|
|
||||||
<Route exact path="/"
|
<Route exact path="/"
|
||||||
render={
|
render={
|
||||||
(props) => (
|
(props) => (
|
||||||
<Projects {...this.props} {...props} {...this.state}></Projects>
|
<Forums {...this.props} {...this.state} {...props} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route component={Shixunnopage} />
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
</MuiThemeProvider>
|
</MuiThemeProvider>
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function initAxiosInterceptors(props) {
|
||||||
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
|
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
|
||||||
var
|
var
|
||||||
proxy = "http://localhost:3000"
|
proxy = "http://localhost:3000"
|
||||||
proxy = "https://testforgeplus.trustie.net"
|
proxy = "https://testforum.trustie.net"
|
||||||
|
|
||||||
const requestMap = {};
|
const requestMap = {};
|
||||||
window.setfalseInRequestMap = function (keyName) {
|
window.setfalseInRequestMap = function (keyName) {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import React, { useEffect , useState } from 'react';
|
||||||
|
import './Index.scss';
|
||||||
|
|
||||||
|
function Footer(){
|
||||||
|
const [ value , setValue ] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
try {
|
||||||
|
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
||||||
|
setValue(chromesettingArray.footer);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
},[])
|
||||||
|
|
||||||
|
function showhtml(htmlString){
|
||||||
|
var html = {__html:htmlString};
|
||||||
|
return <div dangerouslySetInnerHTML={html}></div> ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
{value && showhtml(value)}
|
||||||
|
{/* <div className="footerInfos">
|
||||||
|
<ul>
|
||||||
|
<li>社区</li>
|
||||||
|
<li><a href={`/`} target="_blank">网站首页</a></li>
|
||||||
|
<li><a href={`https://www.trustie.net/agreement`} target="_blank">服务协议</a></li>
|
||||||
|
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
||||||
|
<li><a href={`https://forum.trustie.net/`} target="_blank">问吧交流</a></li>
|
||||||
|
<li><a href={`https://www.trustie.net/cooperation`} target="_blank">合作伙伴</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>支持与服务</li>
|
||||||
|
<li><a href={`https://forgeplus.trustie.net/docs/api`} target="_blank">API文档</a></li>
|
||||||
|
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
||||||
|
<li><a href={`https://git-scm.com`} target="_blank">Git常用命令</a></li>
|
||||||
|
<li><a href={`https://forum.trustie.net/forums/3080/detail`} target="_blank">DevOps使用文档</a></li>
|
||||||
|
<li><a href={`https://forgeplus.trustie.net/projects/jasder/forgeplus/tree/master/CHANGELOG.md`} target="_blank">日志更新</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>合作伙伴</li>
|
||||||
|
<li><a href={`http://www.sei.pku.edu.cn`} target="_blank">北京大学</a></li>
|
||||||
|
<li><a href={`http://scse.buaa.edu.cn`} target="_blank">北京航空航天大学</a></li>
|
||||||
|
<li><a href={`https://www.nju.edu.cn`} target="_blank">南京大学</a></li>
|
||||||
|
<li><a href={`https://www.xtu.edu.cn`} target="_blank">湘潭大学</a></li>
|
||||||
|
<li><a href={`http://www.iscas.ac.cn`} target="_blank">ISCAS</a></li>
|
||||||
|
<li><a href={`https://www.ucloud.cn`} target="_blank">UCloud优刻得</a></li>
|
||||||
|
<li><a href={`http://www.inforbus.com`} target="_blank">中创软件</a></li>
|
||||||
|
<li><a href={`https://www.inspur.com`} target="_blank">浪潮集团</a></li>
|
||||||
|
<li><a href={`http://www.copu.org.cn`} target="_blank">中国开源软件推进联盟</a></li>
|
||||||
|
<li><a href={`https://www.sjtu.edu.cn`} target="_blank">上海交通大学</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li>合作伙伴</li>
|
||||||
|
<li><span>热线:</span></li>
|
||||||
|
<li><span>QQ群:1071514693</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p className="footerCopy">© Copyright 2007~2021 国防科技大学Trustie团队 & IntelliDE <a href="https://beian.miit.gov.cn">湘ICP备 17009477号</a></p> */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default Footer;
|
|
@ -0,0 +1,150 @@
|
||||||
|
|
||||||
|
.dropdownFlex{
|
||||||
|
display:flex;
|
||||||
|
padding:5px;
|
||||||
|
background:#fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
.ant-menu-vertical > .ant-menu-item{
|
||||||
|
border:none;
|
||||||
|
height: 35px;
|
||||||
|
line-height: 35px;
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
.ant-menu-vertical{
|
||||||
|
border:none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.newFooter {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: #323232;
|
||||||
|
clear: both;
|
||||||
|
min-width: 1200px;
|
||||||
|
z-index: 8;
|
||||||
|
left: 0px;
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom:0px !important;
|
||||||
|
}
|
||||||
|
.footerInfos{
|
||||||
|
display: flex;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin:0px auto;
|
||||||
|
justify-content: space-around;
|
||||||
|
padding:60px 0px;
|
||||||
|
& >ul{
|
||||||
|
padding:0px 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 25%;
|
||||||
|
text-align: left;
|
||||||
|
li{
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 300;
|
||||||
|
&:first-child{
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
&>a,&>span{
|
||||||
|
color: #bbb;
|
||||||
|
}
|
||||||
|
&>a:hover{
|
||||||
|
color: #4cacff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footerCopy{
|
||||||
|
color: #bbb;
|
||||||
|
border-top: 1px solid #4e4e4e;
|
||||||
|
padding:10px 0px;
|
||||||
|
a{
|
||||||
|
color: #bbb;
|
||||||
|
&:hover{
|
||||||
|
color: #4cacff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.footEdition{
|
||||||
|
background-color: #171B23;
|
||||||
|
.footContent{
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding:86px 0px;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 1200px;
|
||||||
|
margin:0px auto;
|
||||||
|
ul{
|
||||||
|
min-width: 120px;
|
||||||
|
text-align: left;
|
||||||
|
margin-right: 80px;
|
||||||
|
&.center{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&>p{
|
||||||
|
height: 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
&>img{
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
margin-top: 25px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
li{
|
||||||
|
height: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #BDC2D1;
|
||||||
|
margin-bottom: 15px!important;
|
||||||
|
a{
|
||||||
|
color: #BDC2D1!important;
|
||||||
|
&:hover{
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.thehead{
|
||||||
|
height: 25px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FFFFFF;
|
||||||
|
line-height: 25px;
|
||||||
|
margin-bottom: 20px!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.theline{
|
||||||
|
.imgCon{
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
padding:5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
img{
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.copyrightDesc{
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #BDC2D1;
|
||||||
|
line-height: 28px;
|
||||||
|
padding:15px 0px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #1B212C;
|
||||||
|
a{
|
||||||
|
color: #BDC2D1!important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
function bytesToSize(bytes) {
|
||||||
|
if (bytes === 0) return '0 B';
|
||||||
|
let k = 1024,
|
||||||
|
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
|
||||||
|
i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return (bytes / Math.pow(k, i)). toFixed(2) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
class FileList extends PureComponent{
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let { list , className } = this.props;
|
||||||
|
|
||||||
|
const listMap = list && list.map((item)=>{
|
||||||
|
return(
|
||||||
|
<li>
|
||||||
|
<i className="iconfont icon-fujian color-blue font-16 mr8"></i>
|
||||||
|
<a href={item.url} className="color-blue" target="_self">{item.filename}</a>
|
||||||
|
<span className="color-grey-9 ml10">({bytesToSize(`${item.filesize}`)})</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return(
|
||||||
|
<ul className={className}>
|
||||||
|
{ listMap }
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileList;
|
|
@ -0,0 +1,343 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// import "antd/dist/antd.css";
|
||||||
|
|
||||||
|
import { getUrl } from 'educoder';
|
||||||
|
|
||||||
|
|
||||||
|
import './mdEditor.css';
|
||||||
|
require('codemirror/lib/codemirror.css');
|
||||||
|
|
||||||
|
|
||||||
|
let path = '/editormd/lib/'
|
||||||
|
path = getUrl("/editormd/lib/")
|
||||||
|
const $ = window.$;
|
||||||
|
|
||||||
|
// 保存数据
|
||||||
|
function md_add_data(k,mdu,d){
|
||||||
|
window.sessionStorage.setItem(k+mdu,d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空保存的数据
|
||||||
|
function md_clear_data(k,mdu,id){
|
||||||
|
window.sessionStorage.removeItem(k+mdu);
|
||||||
|
var id1 = "#e_tip_"+id;
|
||||||
|
var id2 = "#e_tips_"+id;
|
||||||
|
if(k == 'content'){
|
||||||
|
$(id2).html(" ");
|
||||||
|
}else{
|
||||||
|
$(id1).html(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.md_clear_data = md_clear_data
|
||||||
|
// editor 存在了jquery对象上,应用不需要自己写md_rec_data方法了
|
||||||
|
function md_rec_data(k, mdu, id) {
|
||||||
|
if (window.sessionStorage.getItem(k + mdu) !== null) {
|
||||||
|
var editor = $("#e_tips_" + id).data('editor');
|
||||||
|
editor.setValue(window.sessionStorage.getItem(k + mdu));
|
||||||
|
// debugger;
|
||||||
|
// /shixuns/b5hjq9zm/challenges/3977/tab=3 setValue可能导致editor样式问题
|
||||||
|
md_clear_data(k, mdu, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.md_rec_data = md_rec_data;
|
||||||
|
|
||||||
|
function md_elocalStorage(editor,mdu,id){
|
||||||
|
if (window.sessionStorage){
|
||||||
|
var oc = window.sessionStorage.getItem('content'+mdu);
|
||||||
|
if(oc !== null && oc != editor.getValue()){
|
||||||
|
console.log("#e_tips_"+id)
|
||||||
|
$("#e_tips_"+id).data('editor', editor);
|
||||||
|
var h = '您上次有已保存的数据,是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\''+ mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\''+ mdu + '\',\'' + id + '\')">不恢复</a>';
|
||||||
|
$("#e_tips_"+id).html(h);
|
||||||
|
}
|
||||||
|
setInterval(function() {
|
||||||
|
var d = new Date();
|
||||||
|
var h = d.getHours();
|
||||||
|
var m = d.getMinutes();
|
||||||
|
var s = d.getSeconds();
|
||||||
|
h = h < 10 ? '0' + h : h;
|
||||||
|
m = m < 10 ? '0' + m : m;
|
||||||
|
s = s < 10 ? '0' + s : s;
|
||||||
|
if(editor.getValue().trim() != ""){
|
||||||
|
md_add_data("content",mdu,editor.getValue());
|
||||||
|
var id1 = "#e_tip_"+id;
|
||||||
|
var id2 = "#e_tips_"+id;
|
||||||
|
|
||||||
|
var textStart = " 数据已于 "
|
||||||
|
var text = textStart + h + ':' + m + ':' + s +" 保存 ";
|
||||||
|
// 占位符
|
||||||
|
var oldHtml = $(id2).html();
|
||||||
|
if (oldHtml && oldHtml != ' ' && oldHtml.startsWith(textStart) == false) {
|
||||||
|
$(id2).html( oldHtml.split(' (')[0] + ` (${text})`);
|
||||||
|
} else {
|
||||||
|
$(id2).html(text);
|
||||||
|
}
|
||||||
|
// $(id2).html("");
|
||||||
|
}
|
||||||
|
},10000);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
$("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue,
|
||||||
|
onchange, watch, { noStorage, showNullButton, emoji }, that) {
|
||||||
|
// 还是出现了setting只有一份,被共用的问题
|
||||||
|
|
||||||
|
var editorName = window.editormd(id, {
|
||||||
|
width: width,
|
||||||
|
height: high===undefined?400:high,
|
||||||
|
path: path, // "/editormd/lib/"
|
||||||
|
markdown : initValue,
|
||||||
|
|
||||||
|
dialogLockScreen: false,
|
||||||
|
watch:watch===undefined?true:watch,
|
||||||
|
syncScrolling: "single",
|
||||||
|
tex: true,
|
||||||
|
tocm: true,
|
||||||
|
emoji: !!emoji ,
|
||||||
|
taskList: true,
|
||||||
|
codeFold: true,
|
||||||
|
searchReplace: true,
|
||||||
|
htmlDecode: "style,script,iframe",
|
||||||
|
sequenceDiagram: true,
|
||||||
|
autoFocus: false,
|
||||||
|
|
||||||
|
// mine
|
||||||
|
|
||||||
|
toolbarIcons: function (mdEditor) {
|
||||||
|
let react_id = `react_${mdEditor.id}`;
|
||||||
|
const __that = window[react_id]
|
||||||
|
|
||||||
|
// Or return editormd.toolbarModes[name]; // full, simple, mini
|
||||||
|
// Using "||" set icons align right.
|
||||||
|
const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"];
|
||||||
|
// 试卷处用到的填空题新增按钮
|
||||||
|
if (__that.props.showNullButton) {
|
||||||
|
icons.push('nullBtton')
|
||||||
|
}
|
||||||
|
return icons
|
||||||
|
},
|
||||||
|
toolbarCustomIcons: {
|
||||||
|
testIcon: "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
|
||||||
|
testIcon1: "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>",
|
||||||
|
nullBtton: "<a type=\"nullBtton\" class='pr' title='增加填空'><div class='border-left'><span></span></div><span class='fillTip'>点击插入填空项</span><i class=\"iconfont icon-edit font-16\"></i></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
|
||||||
|
onchange: onchange,
|
||||||
|
onload: function() {
|
||||||
|
let _id = this.id // 如果要使用this,这里不能使用箭头函数
|
||||||
|
let _editorName = this;
|
||||||
|
let react_id = `react_${_editorName.id}`;
|
||||||
|
const __that = window[react_id]
|
||||||
|
|
||||||
|
// this.previewing();
|
||||||
|
// let _id = id;
|
||||||
|
$("#" + _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 - 3);
|
||||||
|
_editorName.cm.focus();
|
||||||
|
});
|
||||||
|
$("[type=\"inline\"]").attr("title", "行内公式");
|
||||||
|
$("[type=\"latex\"]").attr("title", "多行公式");
|
||||||
|
|
||||||
|
if (__that.props.showNullButton) {
|
||||||
|
const NULL_CH = '▁'
|
||||||
|
// const NULL_CH = '〇'
|
||||||
|
// const NULL_CH = '🈳'
|
||||||
|
|
||||||
|
$("#" + _id + " [type=\"nullBtton\"]").bind("click", function () {
|
||||||
|
_editorName.cm.replaceSelection(NULL_CH);
|
||||||
|
// var __Cursor = _editorName.cm.getDoc().getCursor();
|
||||||
|
// _editorName.cm.setCursor(__Cursor.line - 1, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noStorage == true) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
md_elocalStorage(_editorName, `MDEditor__${_id}`, _id);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback && callback(_editorName)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return editorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default class MDEditor extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
initValue: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
// 不能加,影响了试卷填空题
|
||||||
|
// if (this.props.initValue != prevProps.initValue) {
|
||||||
|
// this.answers_editormd.setValue(this.props.initValue)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// react_mdEditor_
|
||||||
|
componentDidMount = () => {
|
||||||
|
const { mdID, initValue, placeholder, showNullButton} = this.props;
|
||||||
|
|
||||||
|
let _id = `mdEditor_${mdID}`
|
||||||
|
this.contentChanged = false;
|
||||||
|
const _placeholder = placeholder || "";
|
||||||
|
// amp;
|
||||||
|
// 编辑时要传memoId
|
||||||
|
const imageUrl = `/upload_with_markdown?container_id=${mdID || ''}&container_type=Memo`;
|
||||||
|
// 创建editorMd
|
||||||
|
let react_id = `react_${_id}`;
|
||||||
|
// 将实例存到了window
|
||||||
|
window[react_id] = this
|
||||||
|
const answers_editormd = create_editorMD(_id, '100%', this.props.height, _placeholder, imageUrl, (_editorName) => {
|
||||||
|
const __editorName = _editorName;
|
||||||
|
react_id = `react_${__editorName.id}`;
|
||||||
|
const that = window[react_id]
|
||||||
|
|
||||||
|
// 一个延迟的recreate或resize,不加这段代码,md初始化可能会出现样式问题
|
||||||
|
setTimeout(() => {
|
||||||
|
if (that.props.needRecreate == true) {
|
||||||
|
__editorName.recreate() // 注意 必须在setValue之前触发,不然会清空
|
||||||
|
} else {
|
||||||
|
__editorName.resize()
|
||||||
|
}
|
||||||
|
console.log('timeout', __editorName.id)
|
||||||
|
__editorName.cm && __editorName.cm.refresh()
|
||||||
|
}, that.props.refreshTimeout || 500)
|
||||||
|
if (this.props.noSetValueOnInit) {
|
||||||
|
that.onEditorChange()
|
||||||
|
} else {
|
||||||
|
if (that.props.initValue != undefined && that.props.initValue != '') {
|
||||||
|
__editorName.setValue(that.props.initValue)
|
||||||
|
}
|
||||||
|
if (that.state.initValue) {
|
||||||
|
__editorName.setValue(that.state.initValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__editorName.cm.on("change", (_cm, changeObj) => {
|
||||||
|
that.contentChanged = true;
|
||||||
|
if (that.state.showError) {
|
||||||
|
that.setState({showError: false})
|
||||||
|
}
|
||||||
|
that.onEditorChange()
|
||||||
|
})
|
||||||
|
that.props.onCMBlur && __editorName.cm.on('blur', () => {
|
||||||
|
that.props.onCMBlur()
|
||||||
|
})
|
||||||
|
that.props.onCMBeforeChange && __editorName.cm.on('beforeChange', (cm,change) => {
|
||||||
|
that.props.onCMBeforeChange(cm,change)
|
||||||
|
})
|
||||||
|
that.answers_editormd = __editorName;
|
||||||
|
// 这里应该可以去掉了,方便调试加的
|
||||||
|
window[__editorName.id+'_'] = __editorName;
|
||||||
|
}, initValue, this.onEditorChange,this.props.watch, {
|
||||||
|
noStorage: this.props.noStorage,
|
||||||
|
showNullButton: this.props.showNullButton,
|
||||||
|
emoji: this.props.emoji
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
// 用在form里时,validate失败时出现一个红色边框
|
||||||
|
showError = () => {
|
||||||
|
this.setState({showError: true})
|
||||||
|
}
|
||||||
|
onEditorChange = () => {
|
||||||
|
if (!this.answers_editormd) return;
|
||||||
|
const val = this.answers_editormd.getValue();
|
||||||
|
//console.log('onEditorChange', this.props.id, val)
|
||||||
|
try {
|
||||||
|
this.props.onChange && this.props.onChange(val)
|
||||||
|
} catch(e) {
|
||||||
|
// http://localhost:3007/courses/1309/common_homeworks/6566/setting
|
||||||
|
// 从这个页面,跳转到编辑页面,再在编辑页面点击返回的时候,这里会报错
|
||||||
|
console.error('出错')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resize = () => {
|
||||||
|
if (!this.answers_editormd) { // 还未初始化
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.answers_editormd.resize()
|
||||||
|
this.answers_editormd.cm && this.answers_editormd.cm.refresh()
|
||||||
|
this.answers_editormd.cm.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue = () => {
|
||||||
|
try {
|
||||||
|
return this.answers_editormd.getValue()
|
||||||
|
} catch (e) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setValue = (val) => {
|
||||||
|
try {
|
||||||
|
this.answers_editormd.setValue(val)
|
||||||
|
} catch (e) {
|
||||||
|
// TODO 这里多实例的时候,前一个实例的state会被后面这个覆盖 参考NewWork.js http://localhost:3007/courses/1309/homework/9300/edit/1
|
||||||
|
// 未初始化
|
||||||
|
this.setState({ initValue: val })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
let {
|
||||||
|
showError
|
||||||
|
} = this.state;
|
||||||
|
let { mdID, className, noStorage, imageExpand } = this.props;
|
||||||
|
let _style = {}
|
||||||
|
if (showError) {
|
||||||
|
_style.border = '1px solid red'
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className={`df ${className} ${imageExpand && 'editormd-image-click-expand' }`} >
|
||||||
|
{/* padding10-20 */}
|
||||||
|
<div className="edu-back-greyf5 radius4" id={`mdEditor_${mdID}`} style={{..._style}}>
|
||||||
|
<textarea style={{display: 'none'}} id={`mdEditors_${mdID}`} name="content"></textarea>
|
||||||
|
<div className="CodeMirror cm-s-defualt">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
noStorage == true ? '' :
|
||||||
|
<div className={"fr rememberTip"}>
|
||||||
|
<p id={`e_tips_mdEditor_${mdID}`} className="edu-txt-right color-grey-cd font-12"> </p>
|
||||||
|
{/* {noStorage == true ? ' ' : <p id={`e_tips_mdEditor_${mdID}`} className="edu-txt-right color-grey-cd font-12"> </p>} */}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,11 @@ const isDev = window.location.port == 3007;
|
||||||
const isdev2= window.location.hostname ==='www.educoder.net'
|
const isdev2= window.location.hostname ==='www.educoder.net'
|
||||||
export const TEST_HOST = "https://testforgeplus.trustie.net/"
|
export const TEST_HOST = "https://testforgeplus.trustie.net/"
|
||||||
export function getImageUrl(path) {
|
export function getImageUrl(path) {
|
||||||
// https://www.educoder.net
|
const reg = /(http|https):\/\/([\w.]+\/?)\S*/;
|
||||||
// https://testbdweb.trustie.net
|
if(reg.test(path)){
|
||||||
// const local = 'http://localhost:3000'
|
return path;
|
||||||
const local = 'https://testforgeplus.trustie.net/'
|
}
|
||||||
|
const local = 'https://testforgeplus.trustie.net'
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
return `${local}/${path}`
|
return `${local}/${path}`
|
||||||
}
|
}
|
||||||
|
@ -218,3 +219,15 @@ export function publicSearchs(Placeholder,onSearch,onInputs,onChanges,loadings)
|
||||||
allowClear={true}
|
allowClear={true}
|
||||||
></Search>)
|
></Search>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手动添加/修改mate标签
|
||||||
|
export function addMeta(name, content){
|
||||||
|
if(document.querySelector(`meta[name='${name}']`)){
|
||||||
|
document.querySelector(`meta[name='${name}']`).content=content;
|
||||||
|
}else{
|
||||||
|
const meta = document.createElement('meta');
|
||||||
|
meta.content = content;
|
||||||
|
meta.name = name;
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(meta);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ export {
|
||||||
getUploadLogoActionUrl as getUploadLogoActionUrl,
|
getUploadLogoActionUrl as getUploadLogoActionUrl,
|
||||||
getImageUrl as getImageUrl, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
getImageUrl as getImageUrl, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
||||||
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
||||||
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl
|
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl, addMeta as addMeta
|
||||||
} from './UrlTool';
|
} from './UrlTool';
|
||||||
|
|
||||||
export { setmiyah as setmiyah } from './Component';
|
export { setmiyah as setmiyah } from './Component';
|
||||||
|
|
|
@ -23,6 +23,7 @@ function indentCodeCompensation(raw, text) {
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//兼容之前的 ##标题式写法
|
//兼容之前的 ##标题式写法
|
||||||
let toc = []
|
let toc = []
|
||||||
let ctx = ["<ul>"]
|
let ctx = ["<ul>"]
|
||||||
|
@ -48,15 +49,15 @@ function buildToc(coll, k, level, ctx) {
|
||||||
});
|
});
|
||||||
ctx.push("</ul>")
|
ctx.push("</ul>")
|
||||||
}
|
}
|
||||||
ctx.push("</li>")
|
ctx.push("</li>");
|
||||||
k = buildToc(coll, k, level, ctx)
|
k = buildToc(coll, k, level, ctx)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTocContent() {
|
export function getTocContent() {
|
||||||
buildToc(toc, 0, 0, ctx)
|
buildToc(toc, 0, 0, ctx);
|
||||||
ctx.push("</ul>")
|
ctx.push("</ul>");
|
||||||
return ctx.join("")
|
return ctx.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenizer = {
|
const tokenizer = {
|
||||||
|
@ -117,6 +118,7 @@ function replace_math_with_ids(text) {
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const original_listitem = renderer.listitem
|
const original_listitem = renderer.listitem
|
||||||
renderer.listitem = function (text, task, checked) {
|
renderer.listitem = function (text, task, checked) {
|
||||||
return original_listitem(replace_math_with_ids(text), task, checked)
|
return original_listitem(replace_math_with_ids(text), task, checked)
|
||||||
|
@ -155,9 +157,9 @@ renderer.heading = function (text, level, raw) {
|
||||||
level: level,
|
level: level,
|
||||||
text: text
|
text: text
|
||||||
})
|
})
|
||||||
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
|
let id = anchor.replace(/[.,/#!$%^&*;:{}=\-_`~():,。¥;「」|?》《~·【】‘、!]/g,"");
|
||||||
|
return '<h' + level + ' id="' + id + '" class="markdown_anchors"><a name="#'+id+'" class="anchors"><i class="iconfont icon-lianjieicon font-14"></i></a>' + text + '</h' + level + '>'
|
||||||
}
|
}
|
||||||
|
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
silent: true,
|
silent: true,
|
||||||
smartypants: true,
|
smartypants: true,
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
import marked from 'marked'
|
||||||
|
import { escape } from 'marked/src/helpers'
|
||||||
|
|
||||||
|
function indentCodeCompensation(raw, text) {
|
||||||
|
const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
|
||||||
|
if (matchIndentToCode === null) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
const indentToCode = matchIndentToCode[1];
|
||||||
|
return text
|
||||||
|
.split('\n')
|
||||||
|
.map(node => {
|
||||||
|
const matchIndentInNode = node.match(/^\s+/);
|
||||||
|
if (matchIndentInNode === null) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
const [indentInNode] = matchIndentInNode;
|
||||||
|
if (indentInNode.length >= indentToCode.length) {
|
||||||
|
return node.slice(indentToCode.length);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
//兼容之前的 ##标题式写法
|
||||||
|
let toc = []
|
||||||
|
let ctx = ["<ul>"]
|
||||||
|
const renderer = new marked.Renderer()
|
||||||
|
const headingRegex = /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/
|
||||||
|
|
||||||
|
export function cleanToc() {
|
||||||
|
toc = []
|
||||||
|
ctx = ["<ul>"]
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildToc(coll, k, level, ctx) {
|
||||||
|
if (k >= coll.length || coll[k].level <= level) { return k }
|
||||||
|
var node = coll[k]
|
||||||
|
ctx.push("<li><a href='#" + node.anchor + "'>" + node.text + "</a>")
|
||||||
|
k++
|
||||||
|
var childCtx = []
|
||||||
|
k = buildToc(coll, k, node.level, childCtx)
|
||||||
|
if (childCtx.length > 0) {
|
||||||
|
ctx.push("<ul>")
|
||||||
|
childCtx.forEach(function (idm) {
|
||||||
|
ctx.push(idm)
|
||||||
|
});
|
||||||
|
ctx.push("</ul>")
|
||||||
|
}
|
||||||
|
ctx.push("</li>")
|
||||||
|
k = buildToc(coll, k, level, ctx)
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTocContent() {
|
||||||
|
buildToc(toc, 0, 0, ctx)
|
||||||
|
ctx.push("</ul>")
|
||||||
|
return ctx.join("")
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenizer = {
|
||||||
|
heading(src) {
|
||||||
|
const cap = headingRegex.exec(src)
|
||||||
|
if (cap) {
|
||||||
|
return {
|
||||||
|
type: 'heading',
|
||||||
|
raw: cap[0],
|
||||||
|
depth: cap[1].length,
|
||||||
|
text: cap[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fences(src) {
|
||||||
|
const cap = this.rules.block.fences.exec(src)
|
||||||
|
if (cap) {
|
||||||
|
const raw = cap[0]
|
||||||
|
let text = indentCodeCompensation(raw, cap[3] || '')
|
||||||
|
const lang = cap[2] ? cap[2].trim() : cap[2]
|
||||||
|
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
||||||
|
const id = next_id()
|
||||||
|
const expression = text
|
||||||
|
text = id
|
||||||
|
math_expressions[id] = { type: 'block', expression }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'code',
|
||||||
|
raw,
|
||||||
|
lang,
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const latexRegex = /(?:\${2})([^\n`]+?)(?:\${2})/gi
|
||||||
|
let katex_count = 0
|
||||||
|
const next_id = () => `__special_katext_id_${katex_count++}__`
|
||||||
|
let math_expressions = {}
|
||||||
|
|
||||||
|
export function getMathExpressions() {
|
||||||
|
return math_expressions
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetMathExpressions() {
|
||||||
|
katex_count = 0
|
||||||
|
math_expressions = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace_math_with_ids(text) {
|
||||||
|
let rs = text.replace(latexRegex, (_match, expression) => {
|
||||||
|
const id = next_id()
|
||||||
|
math_expressions[id] = { type: 'inline', expression }
|
||||||
|
return id
|
||||||
|
})
|
||||||
|
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
const original_listitem = renderer.listitem
|
||||||
|
renderer.listitem = function (text, task, checked) {
|
||||||
|
return original_listitem(replace_math_with_ids(text), task, checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
const original_paragraph = renderer.paragraph
|
||||||
|
renderer.paragraph = function (text) {
|
||||||
|
return original_paragraph(replace_math_with_ids(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
const original_tablecell = renderer.tablecell
|
||||||
|
renderer.tablecell = function (content, flags) {
|
||||||
|
return original_tablecell(replace_math_with_ids(content), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.code = function (code, infostring, escaped) {
|
||||||
|
const lang = (infostring || '').match(/\S*/)[0];
|
||||||
|
if (!lang) {
|
||||||
|
return '<pre class="prettyprint linenums"><code>'
|
||||||
|
+ (escaped ? code : escape(code, true))
|
||||||
|
+ '</code></pre>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['latex', 'katex', 'math'].indexOf(lang) >= 0) {
|
||||||
|
return `<p class='editormd-tex'>${code}</p>`
|
||||||
|
} else {
|
||||||
|
return `<pre class="prettyprint linenums"><code class="language-${infostring}">${escaped ? code : escape(code, true)}</code></pre>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.heading = function (text, level, raw) {
|
||||||
|
let anchor = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w\\u4e00-\\u9fa5]]+/g, '-');
|
||||||
|
toc.push({
|
||||||
|
anchor: anchor,
|
||||||
|
level: level,
|
||||||
|
text: text
|
||||||
|
})
|
||||||
|
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
|
||||||
|
}
|
||||||
|
|
||||||
|
marked.setOptions({
|
||||||
|
silent: true,
|
||||||
|
smartypants: true,
|
||||||
|
gfm: true,
|
||||||
|
pedantic: false
|
||||||
|
})
|
||||||
|
|
||||||
|
marked.use({ tokenizer, renderer });
|
||||||
|
|
||||||
|
export default marked
|
|
@ -0,0 +1,269 @@
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
overflow: auto !important;
|
||||||
|
margin-bottom: -30px;
|
||||||
|
margin-right: -30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
a.white-btn.orange-btn:hover {
|
||||||
|
border: 1px solid #F06200;
|
||||||
|
color: #FFF !important;
|
||||||
|
}
|
||||||
|
.flex1 a.white-btn.orange-btn:hover {
|
||||||
|
border: 1px solid #F06200;
|
||||||
|
color: #FFF !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*.challenge_nav li a{*/
|
||||||
|
/*color:#000 !important;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
.questionli{
|
||||||
|
width: 95%;
|
||||||
|
margin-left: 37px;
|
||||||
|
}
|
||||||
|
#directory_file{
|
||||||
|
height:200px;
|
||||||
|
overflow-y:auto;
|
||||||
|
background:#f5f5f5;
|
||||||
|
padding:10px;
|
||||||
|
}
|
||||||
|
.directory_filepath{
|
||||||
|
width:120px;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
text-decoration: none;
|
||||||
|
color: #05101a;
|
||||||
|
}
|
||||||
|
.repository_url_tippostion{
|
||||||
|
position: absolute;
|
||||||
|
left: 22%;
|
||||||
|
width: 500px;
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-black-trangleft {
|
||||||
|
display: block;
|
||||||
|
border-width: 8px;
|
||||||
|
position: absolute;
|
||||||
|
top: -16px;
|
||||||
|
/* right: 4px; */
|
||||||
|
border-style: dashed solid dashed dashed;
|
||||||
|
border-color: transparent transparent rgba(5,16,26,0.6) transparent;
|
||||||
|
font-size: 0;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#exercisememoMD .CodeMirror {
|
||||||
|
margin-top: 31px !important;
|
||||||
|
height: 370px !important;
|
||||||
|
/*width: 579px !important;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#exercisememoMD .editormd-preview {
|
||||||
|
top: 40px !important;
|
||||||
|
height: 370px !important;
|
||||||
|
width: 578px !important;
|
||||||
|
}
|
||||||
|
#exercisememoMD{
|
||||||
|
/*height: 700px !important;*/
|
||||||
|
}
|
||||||
|
#questioMD{
|
||||||
|
/*width: 95% !important;*/
|
||||||
|
height: 417px !important;
|
||||||
|
margin-left: 0% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#questioMD .CodeMirror {
|
||||||
|
/*width: 550.5px !important;*/
|
||||||
|
margin-top: 31px !important;
|
||||||
|
height: 374px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#questioMD .editormd-preview {
|
||||||
|
top: 40px !important;
|
||||||
|
height: 375px !important;
|
||||||
|
width: 550px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newquestioMD .CodeMirror {
|
||||||
|
/*width: 549px !important;*/
|
||||||
|
margin-top: 31px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newquestioMD .editormd-preview {
|
||||||
|
top: 40px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
width: 578px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#challenge_choose_answer .CodeMirror {
|
||||||
|
margin-top: 31px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
/*width: 578px !important;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#challenge_choose_answer .editormd-preview {
|
||||||
|
top: 40px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
width: 578px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#neweditanswer .CodeMirror {
|
||||||
|
margin-top: 31px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
/*width: 549.5px !important;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#neweditanswer .editormd-preview {
|
||||||
|
top: 40px !important;
|
||||||
|
height: 364px !important;
|
||||||
|
width: 551px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#repository_url_tip {
|
||||||
|
top: 30px !important;
|
||||||
|
left: 249px !important;
|
||||||
|
width: 292px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editanswers .CodeMirror{
|
||||||
|
/*width: 548px !important;*/
|
||||||
|
height: 358px !important;
|
||||||
|
margin-top: 30px !important;
|
||||||
|
}
|
||||||
|
#editanswers .editormd-preview{
|
||||||
|
width: 578px !important;
|
||||||
|
height: 358px !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
#newquestioMDs .CodeMirror{
|
||||||
|
/*width: 510px !important;*/
|
||||||
|
height: 358px !important;
|
||||||
|
margin-top: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newquestioMDs .editormd-preview{
|
||||||
|
width: 578px !important;
|
||||||
|
height: 358px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choose_names{
|
||||||
|
width: 80px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#answerMD .CodeMirror{
|
||||||
|
/*width: 569px !important;*/
|
||||||
|
height: 600px !important;
|
||||||
|
margin-top: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#answerMD .editormd-preview{
|
||||||
|
width: 578px !important;
|
||||||
|
height: 600px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#answerMD {
|
||||||
|
height: 600px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textareavalue{
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.greyInput{
|
||||||
|
width: 107%;
|
||||||
|
}
|
||||||
|
.greyInpus{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdr20{
|
||||||
|
padding-right:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.winput-240-40s {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.winput-240-40s:focus{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.input-100-45{
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
.input-100-45:focus{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind100{
|
||||||
|
width:100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-bule-tip {
|
||||||
|
color: #5485f7 !important;
|
||||||
|
}
|
||||||
|
.martopf4{
|
||||||
|
margin-top:-4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headdfgf{
|
||||||
|
display: block;
|
||||||
|
width: 100px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color979797{
|
||||||
|
color: #979797 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-left{
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-bottom: 6px solid transparent;
|
||||||
|
border-right: 6px solid #cccbcb;
|
||||||
|
border-top: 6px solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
left: 30px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
|
.border-left span{
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-bottom: 6px solid transparent;
|
||||||
|
border-right: 6px solid #fff;
|
||||||
|
border-top: 6px solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
left: 1px;
|
||||||
|
top: -6px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.fillTip{
|
||||||
|
position: absolute;
|
||||||
|
left: 36px;
|
||||||
|
top: 2px;
|
||||||
|
width: 125px;
|
||||||
|
font-size: 12px;
|
||||||
|
display: block;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #eaeaea;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-family: "微软雅黑","宋体";
|
||||||
|
}
|
|
@ -1,46 +1,120 @@
|
||||||
import React, { useEffect, useRef, useMemo } from "react";
|
import React, { useEffect, useRef, useMemo , useState } from 'react'
|
||||||
import "katex/dist/katex.min.css";
|
import 'katex/dist/katex.min.css';
|
||||||
import { renderToString } from 'katex';
|
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
|
||||||
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from "../common/marked";
|
import 'code-prettify';
|
||||||
import 'code-prettify'
|
import dompurify from 'dompurify';
|
||||||
|
// import { getEmoji } from '../forge/Main/emoji';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
const preRegex = /<pre[^>]*>/g
|
import { renderToString } from 'katex'
|
||||||
|
|
||||||
|
const preRegex = /<pre[^>]*>/g;
|
||||||
|
const strRegexSub = /:([a-zA-Z_]+):/g;
|
||||||
|
const quoteRegex = /\[[#][0-9]{0,}\]\(\/(.*?)\/(.*?)\/issues\/[0-9]{0,}\)/g;
|
||||||
function _unescape(str) {
|
function _unescape(str) {
|
||||||
let div = document.createElement('div')
|
let div = document.createElement('div')
|
||||||
div.innerHTML = str
|
div.innerHTML = str
|
||||||
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue;
|
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ value = '', className, style = {} }) => {
|
|
||||||
let str = String(value)
|
|
||||||
|
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
value = '',
|
||||||
|
className,
|
||||||
|
style = {},
|
||||||
|
url,
|
||||||
|
owner=undefined,
|
||||||
|
projectsId=undefined
|
||||||
|
}) => {
|
||||||
|
const [ issues , setIssues ] = useState([]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if(owner&&projectsId){
|
||||||
|
getIssueList();
|
||||||
|
}
|
||||||
|
},[owner,projectsId])
|
||||||
|
|
||||||
|
function getIssueList(){
|
||||||
|
axios.get(`/v1/${owner}/${projectsId}/issues`,{params:{
|
||||||
|
only_name:true,sort_direction:"desc",sort_by:"issues.created_on"
|
||||||
|
}}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
let data = result.data.issues;
|
||||||
|
setIssues(data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = String(value);
|
||||||
const html = useMemo(() => {
|
const html = useMemo(() => {
|
||||||
let rs = marked(str)
|
let rs = marked(str);
|
||||||
const math_expressions = getMathExpressions()
|
const math_expressions = getMathExpressions();
|
||||||
if (str.match(/\[TOC\]/)) {
|
if (str.match(/\[TOC\]/)) {
|
||||||
rs = rs.replace("<p>[TOC]</p>", getTocContent())
|
rs = rs.replace("<p>[TOC]</p>", getTocContent())
|
||||||
cleanToc()
|
cleanToc()
|
||||||
}
|
}
|
||||||
|
// 循环匹配所有emoji
|
||||||
|
// let matchStr = str.match(strRegexSub);
|
||||||
|
// if(matchStr && matchStr.length>0){
|
||||||
|
// for(var i=0;i < matchStr.length;i++){
|
||||||
|
// rs = rs.replace(matchStr[i],getEmoji(matchStr[i]));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if(owner && projectsId && issues && issues.length>0){
|
||||||
|
let matchQuote = str.match(quoteRegex);
|
||||||
|
if(matchQuote && matchQuote.length>0){
|
||||||
|
let getIndexReg = /(?<=#)(.+?)(?=\])/g;
|
||||||
|
for(var x=0;x<matchQuote.length;x++){
|
||||||
|
let getIndex = matchQuote[x].match(getIndexReg);
|
||||||
|
if(getIndex && getIndex.length>0 && getIndex[0]){
|
||||||
|
let index = getIndex[0];
|
||||||
|
let filter = issues.filter(f=>f.project_issues_index.toString() === index);
|
||||||
|
if(filter && filter.length === 1){
|
||||||
|
let content = `#${index}:${filter[0].subject}`;
|
||||||
|
rs = rs.replace(`#${index}`,content);
|
||||||
|
}else{
|
||||||
|
let content = `<span>#${index}(已删除)</span>`;
|
||||||
|
rs = rs.replace(`<a href="`+`/${owner}/${projectsId}/issues/${index}`+`">#${index}</a>`,content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
|
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
|
||||||
const { type, expression } = math_expressions[capture]
|
const { type, expression } = math_expressions[capture];
|
||||||
return renderToString(_unescape(expression), { displayMode: type === 'block', throwOnError: false, output: 'html' })
|
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
|
||||||
})
|
})
|
||||||
rs = rs.replace(/▁/g, "▁▁▁")
|
rs = rs.replace(/▁/g, "▁▁▁")
|
||||||
resetMathExpressions()
|
resetMathExpressions()
|
||||||
return rs
|
return dompurify.sanitize(rs)
|
||||||
}, [str])
|
}, [str,issues]);
|
||||||
|
|
||||||
const el = useRef()
|
// 锚点跳转,链接地址里含#对应的id
|
||||||
|
useEffect(()=>{
|
||||||
|
if(url && url.hash && html){
|
||||||
|
let u = url.hash;
|
||||||
|
if(u){
|
||||||
|
let id = decodeURIComponent(u.split("#")[1]);
|
||||||
|
let ele = document.getElementById(id);
|
||||||
|
if(ele){
|
||||||
|
window.scrollTo(0, ele.offsetTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},[url,html])
|
||||||
|
|
||||||
|
const el = useRef();
|
||||||
function onAncherHandler(e) {
|
function onAncherHandler(e) {
|
||||||
let target = e.target
|
let target = e.target;
|
||||||
if (target.tagName.toUpperCase() === 'A') {
|
if (target.tagName.toUpperCase() === 'A') {
|
||||||
let ancher = target.getAttribute('href')
|
let ancher = target.getAttribute('href');
|
||||||
if (ancher.startsWith('#')) {
|
if (ancher && ancher.startsWith('#')) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
let viewEl = document.getElementById(ancher.replace('#', ''))
|
let viewEl = document.getElementById(ancher.replace('#', ''))
|
||||||
if (viewEl) {
|
if (viewEl) {
|
||||||
viewEl.parentNode.scrollTop = viewEl.offsetTop
|
viewEl.scrollIntoView(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +135,12 @@ export default ({ value = '', className, style = {} }) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [html, el.current, onAncherHandler])
|
}, [html, el.current, onAncherHandler])
|
||||||
|
return (
|
||||||
return (<div ref={el} style={style} className={`${className ? className : ''} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
|
<div
|
||||||
|
ref={el}
|
||||||
|
style={style}
|
||||||
|
className={`${className ? className : ''} markdown-body`}
|
||||||
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { Popconfirm } from 'antd';
|
||||||
|
import './comment.css';
|
||||||
|
import RenderHtml from "../../components/render-html";
|
||||||
|
|
||||||
|
class CommentsItem extends PureComponent{
|
||||||
|
|
||||||
|
cancelEvent=()=>{}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { username , time , content , id , admin , deleteReplyEvent } = this.props
|
||||||
|
|
||||||
|
// 当前用户是否是管理员或者版主,true为有权限删除评论
|
||||||
|
const adminDelete = (
|
||||||
|
admin &&
|
||||||
|
<Popconfirm
|
||||||
|
title="确定删除这条评论?"
|
||||||
|
onConfirm={()=>deleteReplyEvent(id)}
|
||||||
|
onCancel={this.cancelEvent}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<a className="fr c_point color-grey-9">删除</a>
|
||||||
|
</Popconfirm>
|
||||||
|
)
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
<p className="mt3 mb10 clearfix">
|
||||||
|
<span className="color-grey3 font-16 mr20">{username}</span>
|
||||||
|
<span className="color-grey9">{time}</span>
|
||||||
|
|
||||||
|
{ adminDelete }
|
||||||
|
</p>
|
||||||
|
<RenderHtml className="forumsDetailHtml" value={content} />
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommentsItem;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
import './comment.css';
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsItemImg extends PureComponent{
|
||||||
|
render(){
|
||||||
|
const { image_url } = this.props
|
||||||
|
|
||||||
|
return(
|
||||||
|
<img alt="用户头像" src={getImageUrl(`images/${image_url}`)} width="64" height="64" className="radius mr20"></img>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommentsItemImg;
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import MDEditor from '../../common/MDEditor';
|
||||||
|
import { Form , Button } from "antd";
|
||||||
|
import ItemImg from './CommentsItemImg'
|
||||||
|
|
||||||
|
import '../exchange.css'
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsSend extends Component{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.contentMdRef = React.createRef();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送评论
|
||||||
|
handleSubmit=()=>{
|
||||||
|
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||||
|
if(!err){
|
||||||
|
let { id , refresh } = this.props;
|
||||||
|
const url = `/memos/${id}/reply`;
|
||||||
|
axios.post(url,{
|
||||||
|
content:values.replyContent,
|
||||||
|
parent_id:id
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.contentMdRef.current.setValue(' ');
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
|
||||||
|
// 唯一键
|
||||||
|
const { unique , image_url } = this.props;
|
||||||
|
|
||||||
|
const imgWrap = (
|
||||||
|
image_url && <ItemImg image_url={image_url} ></ItemImg>
|
||||||
|
)
|
||||||
|
return(
|
||||||
|
<div className="df mt15">
|
||||||
|
{ imgWrap }
|
||||||
|
<div className="flex1">
|
||||||
|
<Form.Item label="" className="editorFromItem">
|
||||||
|
{getFieldDecorator('replyContent', {
|
||||||
|
rules: [{
|
||||||
|
required: true, message: '请输入评论内容',
|
||||||
|
},{
|
||||||
|
max: 5000 , message:'最大限制5000个字符'
|
||||||
|
}],
|
||||||
|
})(
|
||||||
|
<MDEditor ref={this.contentMdRef} placeholder="请输入评论内容,最大限制5000字符" mdID={`replyContent${unique}`} refreshTimeout={1500}
|
||||||
|
className="CommentSendMD" height={150} watch={false} noStorage={true}></MDEditor>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<p className="clearfix pb10 pt10">
|
||||||
|
<Button type="primary" onClick={this.handleSubmit} className="small-default-btn small-blue-btn fr">发送</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const WrappedCommentsSendForm = Form.create({ name: 'CommentsSend' })(CommentsSend);
|
||||||
|
export default WrappedCommentsSendForm;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import './custom.css'
|
||||||
|
class Index extends PureComponent {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
let { img_url , name ,hrefUrl} = this.props;
|
||||||
|
return (
|
||||||
|
<span {...this.props}>
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href={hrefUrl} ><img alt="用户头像" src={img_url} className="radius custom-img"></img></a>
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href={hrefUrl} ><span className="wrap-name">{name}</span></a>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* 论坛主页 */
|
||||||
|
.custom-wrap{
|
||||||
|
display: flex;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
.custom-wrap .wrap-name{
|
||||||
|
color:#333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.custom-wrap .custom-img{
|
||||||
|
width:36px;
|
||||||
|
height:36px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
/* 版块主页 */
|
||||||
|
.moderatorInfo{
|
||||||
|
width: 82px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction:column;
|
||||||
|
align-items: center;
|
||||||
|
margin:10px 2px 20px 2px;
|
||||||
|
}
|
||||||
|
.moderatorInfo .custom-img{
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
.moderatorInfo .wrap-name{
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 80px;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import nodata from './images/nodata.png';
|
||||||
|
class Empty extends PureComponent{
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div style={{textAlign:'center',fontSize:"20px"}}>
|
||||||
|
<img alt="暂无数据" src={nodata} style={{marginBottom:"20px"}}></img>
|
||||||
|
<p>暂无数据</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Empty;
|
|
@ -0,0 +1,164 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Menu, Dropdown , Pagination ,Spin} from 'antd';
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import ExchangeItem from './ExchangeItem';
|
||||||
|
|
||||||
|
import ExchangeRight from './ExchangeRight'
|
||||||
|
|
||||||
|
import './exchange.css';
|
||||||
|
|
||||||
|
import Empty from './Empty'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
class ExchangeIndex extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
// 热门话题
|
||||||
|
hottest_memos:undefined,
|
||||||
|
// 版主推荐
|
||||||
|
recommend_memos:undefined,
|
||||||
|
// 列表数据
|
||||||
|
memos:undefined,
|
||||||
|
// 列表总数量
|
||||||
|
memos_count:0,
|
||||||
|
// 板块导航
|
||||||
|
forum_sections:undefined,
|
||||||
|
page:1,
|
||||||
|
current_user:undefined,
|
||||||
|
// 默认一页数据
|
||||||
|
pageSize:15,
|
||||||
|
search:undefined,
|
||||||
|
//是否出现加载中的样式
|
||||||
|
loading:false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
this.InitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据加载
|
||||||
|
InitData=(page,search)=>{
|
||||||
|
let url = `/memos`;
|
||||||
|
axios.get((url),{params:{
|
||||||
|
page,search
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
loading:false,
|
||||||
|
hottest_memos:result.data.hottest_memos,
|
||||||
|
recommend_memos:result.data.recommend_memos,
|
||||||
|
forum_sections:result.data.forum_sections,
|
||||||
|
memos:result.data.memos,
|
||||||
|
memos_count:result.data.memos_count,
|
||||||
|
current_user:result.data.current_user
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
searchEvent=(search)=>{
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
loading:true,
|
||||||
|
search,
|
||||||
|
page:1
|
||||||
|
})
|
||||||
|
this.InitData(0,search);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
changePageEvent = (pageNumber) =>{
|
||||||
|
this.setState({
|
||||||
|
page:pageNumber
|
||||||
|
})
|
||||||
|
const { search } = this.state;
|
||||||
|
this.InitData(pageNumber,search);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render(){
|
||||||
|
|
||||||
|
let { hottest_memos , recommend_memos , memos , memos_count , forum_sections , page , pageSize , current_user } = this.state;
|
||||||
|
// 板块导航dropdown显示内容
|
||||||
|
const menu = (
|
||||||
|
<div className="platePanel">
|
||||||
|
{
|
||||||
|
forum_sections && forum_sections.map(item => {
|
||||||
|
return(
|
||||||
|
<div className="plateItem">
|
||||||
|
<span className="plateItem_h"><Link to={`/forums/theme/${item.id}`}>{item.name}</Link></span>
|
||||||
|
<ul className="plateUl">
|
||||||
|
{
|
||||||
|
item.children_tags && item.children_tags.map(i=>{
|
||||||
|
return(<li><Link to={`/forums/theme/${i.id}`}>{i.title}</Link></li>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 数据列表和暂无数据
|
||||||
|
const dataList = memos && memos.length > 0 ?
|
||||||
|
<ExchangeItem memos = {memos} {...this.props} {...this.state} refresh={this.InitData} current_user={current_user} page={page}/>
|
||||||
|
:
|
||||||
|
<div className="pt50 pb50"><Empty /></div>
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="clearfix F_panel">
|
||||||
|
<div className="fl with76 pr20">
|
||||||
|
<div className="back-color-white">
|
||||||
|
<div className="f_left_head">
|
||||||
|
<ul>
|
||||||
|
<li className="active">
|
||||||
|
<a>论坛首页</a>
|
||||||
|
</li>
|
||||||
|
{ forum_sections &&
|
||||||
|
<li>
|
||||||
|
<Dropdown overlay={menu}>
|
||||||
|
<a>板块导航<i className="iconfont icon-xiajiantou font-16 ml10 color-dark-grey"></i></a>
|
||||||
|
</Dropdown>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><Link to='/forums/MyTopic'>我的话题</Link></li>
|
||||||
|
<li><Link to='/forums/MyEnshrine'>我的收藏</Link></li>
|
||||||
|
<li><Link to='/forums/MyInteresting'>我感兴趣的论坛</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Spin spinning={this.state.loading} >
|
||||||
|
{ dataList }
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
memos_count > pageSize &&
|
||||||
|
<div className="pt30 pb50 edu-txt-center"><Pagination showQuickJumper current={page} total={memos_count} onChange={this.changePageEvent} pageSize={pageSize} /></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<ExchangeRight
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
searchEvent={this.searchEvent}
|
||||||
|
hottest_memos={hottest_memos}
|
||||||
|
recommend_memos={recommend_memos}
|
||||||
|
loading={this.setState.loading}
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ExchangeIndex;
|
|
@ -0,0 +1,198 @@
|
||||||
|
import React, { Component, memo } from 'react';
|
||||||
|
import { Dropdown , Menu } from 'antd';
|
||||||
|
import {Link} from 'react-router-dom'
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
import Tags from './TagComponent/Index';
|
||||||
|
import Infos from './InfoComponent/Index';
|
||||||
|
import Custom from './CustomComponent/Index';
|
||||||
|
import "./exchange.css"
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
class ExchangeItem extends Component {
|
||||||
|
|
||||||
|
// 右侧dropdown
|
||||||
|
InitItemMenu = (id,sticky,is_fine,memo_watched) => {
|
||||||
|
const { current_user , detail }= this.props;
|
||||||
|
if(current_user){
|
||||||
|
if(current_user.admin || current_user.banned_permission){
|
||||||
|
return(
|
||||||
|
<Dropdown className="fr c_point" overlay={ this.InitlistMenu(id,sticky,is_fine,memo_watched) } placement="bottomCenter">
|
||||||
|
<span className="addheight"></span>
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}else if(detail){
|
||||||
|
return(
|
||||||
|
<span className="fr c_point color-blue" style={{lineHeight:'16px'}} onClick={()=>this.enShrineEvent(id,memo_watched)}>
|
||||||
|
{ memo_watched ? '取消收藏' : '收藏' }
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧dropdown的选项
|
||||||
|
InitlistMenu = (id,sticky,is_fine,memo_watched) => (
|
||||||
|
<Menu className="edu-txt-center" style={{minWidth:"100px"}}>
|
||||||
|
<Menu.Item onClick={()=>this.topEvent(sticky,id)} > { sticky ? '取消置顶' : '置顶' } </Menu.Item>
|
||||||
|
<Menu.Item onClick={()=>this.bestEvent(is_fine,id)}> { is_fine ? '取消推荐' : '推荐' } </Menu.Item>
|
||||||
|
<Menu.Item onClick={()=>this.enShrineEvent(id,memo_watched)}> { memo_watched ? '取消收藏' : '收藏' } </Menu.Item>
|
||||||
|
<Menu.Item onClick={()=>this.editEvent(id)}> 编辑 </Menu.Item>
|
||||||
|
<Menu.Item onClick={()=>this.deleteEvent(id)}> 删除 </Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// 取消收藏+收藏
|
||||||
|
enShrineEvent=(id,memo_watched)=>{
|
||||||
|
const { refresh } = this.props;
|
||||||
|
const url = `/memos/${id}/watch_memo`;
|
||||||
|
// is_watch:1为添加关注
|
||||||
|
axios.post(url,{
|
||||||
|
is_watch:memo_watched ? 0 : 1
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 取消置顶+置顶
|
||||||
|
topEvent = (sticky,id) =>{
|
||||||
|
const { refresh } = this.props;
|
||||||
|
const url =`/memos/${id}/set_top_or_down.json`;
|
||||||
|
|
||||||
|
axios.get(url,{params:{
|
||||||
|
sticky:sticky ? 0 : 1
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
// 调用父级方法刷新
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消推荐+推荐
|
||||||
|
bestEvent = (is_fine,id) =>{
|
||||||
|
const { refresh } = this.props;
|
||||||
|
const url =`/memos/${id}/is_fine.json`;
|
||||||
|
|
||||||
|
axios.post(url,{
|
||||||
|
is_fine:is_fine ? 0 : 1
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
// 调用父级方法刷新
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 编辑
|
||||||
|
editEvent=(id)=>{
|
||||||
|
this.props.history.push(`/forums/${id}/edit`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
deleteEvent = (id) =>{
|
||||||
|
this.props.confirm({
|
||||||
|
content: '确认删除帖子?',
|
||||||
|
|
||||||
|
onOk: () => {
|
||||||
|
const url =`/memos/${id}/destroy.json`;
|
||||||
|
axios.post(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
if(result.data.status === 0){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
const { page , refresh } = this.props;
|
||||||
|
if(page){
|
||||||
|
refresh(page);
|
||||||
|
}else{
|
||||||
|
this.props.history.push("/forums");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转页面
|
||||||
|
turnToEvent=(id,detail)=>{
|
||||||
|
{/*传了enshine就代表底部右侧操作按钮只有收藏或者取消收藏,否则就是置顶、推荐等 */}
|
||||||
|
const { enShrine} = this.props;
|
||||||
|
if(enShrine){return}
|
||||||
|
if(detail!=true){
|
||||||
|
const w= window.open('about:blank');
|
||||||
|
w.location.href=`/forums/${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
/**detail:true为详情,否则为列表 */
|
||||||
|
const { memos , current_user , detail} = this.props;
|
||||||
|
console.log(memos)
|
||||||
|
const ListItem = memos && memos.map(item=>{
|
||||||
|
return(
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<div className="tabulation_infos flex-align-top mb15 ">
|
||||||
|
<Tags bestClass={item.is_fine ? "mr15" : undefined } topClass={item.sticky ? "mr15" : undefined}></Tags>
|
||||||
|
<p onClick={()=>this.turnToEvent(item.id,detail)} className={ detail ? "exchangeItem-subject task-hide justify color_black" : "exchangeItem-subject task-hide justify c_point is_onclick" }>
|
||||||
|
{item.subject}
|
||||||
|
</p>
|
||||||
|
{/* 右侧的下拉操作菜单项:登录后才能有右侧信息 */}
|
||||||
|
{ current_user && this.InitItemMenu(item.id,item.sticky,item.is_fine,item.memo_watched) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-align-center">
|
||||||
|
{/* 判断用户是否登录 */}
|
||||||
|
<Custom className="custom-wrap fl" hrefUrl={`/users/${item.user_login}`} img_url = {getImageUrl(`images/${item.image_url}`)} name={item.username}></Custom>
|
||||||
|
{ item.forum_section_title && <span className="sendPoint">发表在<Link className="color-blue c_point" to={`/forums/theme/${item.forum_section_id}`}>{item.forum_section_title}</Link></span> }
|
||||||
|
{ item.time && <span className="sendPoint">{item.time}</span> }
|
||||||
|
|
||||||
|
{
|
||||||
|
item.new_reply && item.new_reply.username &&
|
||||||
|
<span className="fl ml30 pl10 sendPoint">
|
||||||
|
<span className="color-grey-9 font-14">最新回复:
|
||||||
|
<span className="color-grey-3">{item.new_reply.username}</span>
|
||||||
|
<span className="ml10 font-12">{item.new_reply.time}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
<span className="flex1"></span>
|
||||||
|
{/* 论坛首页和论坛详情公用,不同情况下左浮动或者有浮动 */}
|
||||||
|
<span className="fr">
|
||||||
|
<Infos className="icon-wrap" icon={"iconfont icon-liulan font-16"} count={item.viewed_count} />
|
||||||
|
<Infos className="icon-wrap" icon={"iconfont icon-dianzan font-16"} count={item.praises_count} />
|
||||||
|
<Infos className="icon-wrap" icon={"iconfont icon-pinglun font-16"} count={item.replies_count} />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return(
|
||||||
|
<div className="plateTabulation">
|
||||||
|
{ ListItem }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ExchangeItem;
|
|
@ -0,0 +1,68 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
import ExchangeRightSearch from './ExchangeRightSearch'
|
||||||
|
import './exchange.css'
|
||||||
|
class ExchangeRight extends PureComponent {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
|
||||||
|
const { searchEvent , hottest_memos , recommend_memos , hideSearchPanel} = this.props;
|
||||||
|
|
||||||
|
// 热门话题
|
||||||
|
const hotList = hottest_memos && hottest_memos.length > 0 && (
|
||||||
|
<div className="bc-white mb20">
|
||||||
|
<p className="clearfix r_part_title">
|
||||||
|
<img src={getImageUrl("images/plate/hot.png")} width="13px" className="mr10 fl mt7" alt=""/>
|
||||||
|
<span className="color-grey3 font-16 fl">热门话题</span>
|
||||||
|
</p>
|
||||||
|
<ul className="r_part_list">
|
||||||
|
{
|
||||||
|
hottest_memos.map((apply)=>{
|
||||||
|
return(
|
||||||
|
<li><a target="_blank" href={`/forums/${apply.id}`}>{apply.subject}</a></li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// 版主推荐
|
||||||
|
const moderatorList = recommend_memos && recommend_memos.length > 0 && (
|
||||||
|
<div className="bc-white">
|
||||||
|
<p className="clearfix r_part_title">
|
||||||
|
<img src={getImageUrl("images/plate/point.png")} width="16px" className="mr10 fl mt7" alt=""/>
|
||||||
|
<span className="color-grey3 font-16 fl">版主推荐</span>
|
||||||
|
</p>
|
||||||
|
<ul className="r_part_list">
|
||||||
|
{
|
||||||
|
recommend_memos.map((apply)=>{
|
||||||
|
return(
|
||||||
|
<li><a target="_blank" href={`/forums/${apply.id}`}>{apply.subject}</a></li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="fl with24">
|
||||||
|
{
|
||||||
|
!hideSearchPanel && <ExchangeRightSearch {...this.props} {...this.state} searchEvent={searchEvent}></ExchangeRightSearch>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{/* 热门话题 */}
|
||||||
|
{hotList}
|
||||||
|
|
||||||
|
{/* 版主推荐 */}
|
||||||
|
{ moderatorList }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ExchangeRight;
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Input } from 'antd'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
const Search = Input.Search;
|
||||||
|
class ExchangeRightSearch extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state={
|
||||||
|
searchDefault:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有关搜索部分
|
||||||
|
activeSearch =(e)=>{
|
||||||
|
this.props.searchEvent(e);
|
||||||
|
if(!e){
|
||||||
|
this.setState({
|
||||||
|
searchDefault:true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showSearchPanel = () =>{
|
||||||
|
this.setState({
|
||||||
|
searchDefault:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let { searchDefault } = this.state;
|
||||||
|
const { current_user } = this.props;
|
||||||
|
|
||||||
|
const sendBtn =()=> {
|
||||||
|
if(current_user){
|
||||||
|
return(<Link to={'/forums/new'} className="send_btn">发布话题</Link>)
|
||||||
|
}else{
|
||||||
|
return(<a href="/login" className="send_btn">发布话题</a>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div className="top_Operate">
|
||||||
|
{sendBtn()}
|
||||||
|
{
|
||||||
|
searchDefault ?
|
||||||
|
<Search
|
||||||
|
className="searchfrom"
|
||||||
|
placeholder="请输入您想搜索的内容"
|
||||||
|
size="large"
|
||||||
|
onSearch={this.showSearchPanel}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<Search
|
||||||
|
className="searchfrom active"
|
||||||
|
placeholder="请输入您想搜索的内容"
|
||||||
|
size="large"
|
||||||
|
onSearch={this.activeSearch}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ExchangeRightSearch;
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
class Index extends PureComponent {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { icon , count = 0 , ...props } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span {...props}>
|
||||||
|
<i className={icon}></i>
|
||||||
|
<span className={'span-text'}>
|
||||||
|
{count}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,171 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Modal , Input , Table ,Pagination } from 'antd'
|
||||||
|
import './manage.css'
|
||||||
|
import '../exchange.css';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const Search = Input.Search;
|
||||||
|
class AddModeratorModal extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
data:undefined,
|
||||||
|
page:1,
|
||||||
|
user_name:undefined,
|
||||||
|
limit:10,
|
||||||
|
total:undefined,
|
||||||
|
selectedRowKeys:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
componentDidUpdate=(prevState)=>{
|
||||||
|
if(prevState.operationPlateId !== this.props.operationPlateId && this.props.visible){
|
||||||
|
this.setState({
|
||||||
|
user_name:undefined,
|
||||||
|
page:1
|
||||||
|
})
|
||||||
|
this.getTabData(undefined , 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTabData=(user_name,page)=>{
|
||||||
|
const { operationPlateId } = this.props;
|
||||||
|
|
||||||
|
const url =`/forum_sections/${operationPlateId}/search_users.json`;
|
||||||
|
axios.get(url,{
|
||||||
|
params:{
|
||||||
|
user_name,
|
||||||
|
page
|
||||||
|
}
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
const user_lists = result.data.user_lists;
|
||||||
|
let array = [];
|
||||||
|
for(var i = 0;i<user_lists.length;i++){
|
||||||
|
array.push({
|
||||||
|
key:user_lists[i].id,
|
||||||
|
username:user_lists[i].username,
|
||||||
|
nickname:user_lists[i].nickname,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
data:array,
|
||||||
|
limit:result.data.limit,
|
||||||
|
total:result.data.users_count
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
getSelectKeys=(selectedRowKeys,selectedRows)=>{
|
||||||
|
this.setState({
|
||||||
|
selectedRowKeys
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
searchEvent=(value)=>{
|
||||||
|
this.setState({
|
||||||
|
user_name:value,
|
||||||
|
page:1
|
||||||
|
})
|
||||||
|
this.getTabData(value,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻页
|
||||||
|
changePageEvent=(page)=>{
|
||||||
|
const { user_name } = this.state;
|
||||||
|
this.setState({
|
||||||
|
page
|
||||||
|
})
|
||||||
|
this.getTabData(user_name,page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
cancel=()=>{
|
||||||
|
const { hideAddBox }=this.props;
|
||||||
|
this.setState({
|
||||||
|
user_name:undefined,
|
||||||
|
page:1
|
||||||
|
})
|
||||||
|
hideAddBox();
|
||||||
|
}
|
||||||
|
// 确定
|
||||||
|
modalSave=()=>{
|
||||||
|
const { selectedRowKeys } = this.state;
|
||||||
|
console.log(selectedRowKeys);
|
||||||
|
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const { operationPlateId , hideAddBox , getSubModerator } = this.props;
|
||||||
|
const url = `/forum_sections/${plateId}/add_users.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
user_ids:selectedRowKeys,
|
||||||
|
children_section_id:operationPlateId
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
hideAddBox();
|
||||||
|
getSubModerator();
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { visible } = this.props;
|
||||||
|
let { data , total , page , limit } = this.state;
|
||||||
|
const rowSelection = {
|
||||||
|
onChange: (selectedRowKeys, selectedRows) => this.getSelectKeys(selectedRowKeys,selectedRows)
|
||||||
|
};
|
||||||
|
const columns = [{
|
||||||
|
title:"姓名",
|
||||||
|
dataIndex:"username"
|
||||||
|
},{
|
||||||
|
title:"昵称",
|
||||||
|
dataIndex:"nickname"
|
||||||
|
}];
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Modal
|
||||||
|
keyboard={false}
|
||||||
|
title={"添加版主"}
|
||||||
|
visible={ visible }
|
||||||
|
closable={false}
|
||||||
|
footer={null}
|
||||||
|
destroyOnClose={true}
|
||||||
|
centered={true}
|
||||||
|
width="700px"
|
||||||
|
className="addPlateModal"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="edu-txt-right clearfix mb20 pr30">
|
||||||
|
<div style={{width:"400px"}} className="fr">
|
||||||
|
<Search
|
||||||
|
placeholder="请输入用户名进行搜索"
|
||||||
|
enterButton="搜索"
|
||||||
|
onSearch={(value) => this.searchEvent(value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table rowSelection={rowSelection} columns={columns} dataSource={data} size={"small"} pagination={false}/>
|
||||||
|
|
||||||
|
{
|
||||||
|
total && total > limit ?
|
||||||
|
<div className="edu-txt-center mt10">
|
||||||
|
<Pagination current={page} size={"small"} total={total} pageSize={limit} onChange={this.changePageEvent}></Pagination>
|
||||||
|
</div>:""
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className="clearfix mt30 edu-txt-center">
|
||||||
|
<a className="task-btn mr30" onClick={this.cancel}>取消</a>
|
||||||
|
<a className="task-btn task-btn-orange" onClick={this.modalSave}>确定</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default AddModeratorModal;
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
import MenuWraps from '../MenuComponent/Menu';
|
||||||
|
import SendItem from './SubSendItem';
|
||||||
|
import CheckItem from './SubCheckItem'
|
||||||
|
import CheckReplyItem from './SubCheckReplyItem'
|
||||||
|
|
||||||
|
const menu_nav = [
|
||||||
|
{
|
||||||
|
name:"待审查帖子",
|
||||||
|
key:`checkPost`,
|
||||||
|
content:CheckItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"待审查回复",
|
||||||
|
key:`checkReply`,
|
||||||
|
content:CheckReplyItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"已发布的帖子",
|
||||||
|
key:`sendPost`,
|
||||||
|
content:SendItem
|
||||||
|
}
|
||||||
|
]
|
||||||
|
class CheckPublic extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
activeKey:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changeTab=(activeKey)=>{
|
||||||
|
this.setState({
|
||||||
|
activeKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div className="edu-back-white mb20">
|
||||||
|
<MenuWraps
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
className="plate-left-Menu moderatorMenu"
|
||||||
|
menu_nav={menu_nav}
|
||||||
|
defaultUrlKey={"checkPost"}
|
||||||
|
changeTab={this.changeTab}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckPublic;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
import RenderHtml from "../../components/render-html";
|
||||||
|
|
||||||
|
import Customers from '../CustomComponent/Index'
|
||||||
|
import "./manage.css"
|
||||||
|
|
||||||
|
class ItemLeft extends Component {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
// user_url,username , time , image_url :用户链接,用户名,时间,头像,
|
||||||
|
// memo_title , forum_title(待审查帖子以及已发布帖子:帖子名称,发表的论坛名)
|
||||||
|
// source_title , reply_content(待审查回复:帖子来源,回复内容)
|
||||||
|
//memo_id,forum_id,source_id( 待审查帖子id,发表在版块id,来源版块id)
|
||||||
|
const {user_url,id, memo_id,username , time , image_url , memo_title , forum_title ,source_title , reply_content,forum_id,source_id} = this.props;
|
||||||
|
const title_Url= id || memo_id;
|
||||||
|
return(
|
||||||
|
|
||||||
|
<div className="flex1">
|
||||||
|
<p className="flex-align-center mb15">
|
||||||
|
<Customers className="ItemsHeadPhoto flex-align-center" hrefUrl={user_url} img_url={getImageUrl(`images/${image_url}`)} name={username}></Customers>
|
||||||
|
<span className="sendPoint">{time}</span>
|
||||||
|
{ forum_title && <span className="sendPoint">发表在 <Link to={`/forums/theme/${forum_id}`}><span className="green">{forum_title}</span></Link></span> }
|
||||||
|
{ source_title && <span className="sendPoint">来源 <Link to={`/forums/theme/${source_id}`}><span className="green">{source_title}</span></Link></span> }
|
||||||
|
</p>
|
||||||
|
{ memo_title && <p><Link to={`/forums/${title_Url}`}>{memo_title}</Link></p>}
|
||||||
|
{
|
||||||
|
reply_content &&
|
||||||
|
<Link to={`/forums/${title_Url}/detail`}>
|
||||||
|
<RenderHtml className="reply_manage_content" value={reply_content} />
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemLeft;
|
|
@ -0,0 +1,87 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Dropdown } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Nav from '../NavComponent/Index';
|
||||||
|
|
||||||
|
|
||||||
|
class ModeratorNav extends Component {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { current_user , bread_crumb }=this.props;
|
||||||
|
|
||||||
|
const forum_tag = bread_crumb && bread_crumb.forum_tag;
|
||||||
|
const forum = bread_crumb && bread_crumb.forum;
|
||||||
|
// {
|
||||||
|
// name:current_user && current_user.username,
|
||||||
|
// url:`/users/${current_user && current_user.login}`
|
||||||
|
// },
|
||||||
|
const routerMap = [
|
||||||
|
{
|
||||||
|
name: forum && forum.title,
|
||||||
|
url:"/forums"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url:`/forums/theme/${forum_tag && forum_tag.id}`,
|
||||||
|
name:forum_tag && forum_tag.title
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url:`/forums/theme/${forum_tag && forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.id}`,
|
||||||
|
name:forum_tag && forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.title
|
||||||
|
},{
|
||||||
|
name:"版主管理"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 板块导航dropdown显示内容
|
||||||
|
const menu =(item)=> {
|
||||||
|
// console.log("item",item);
|
||||||
|
|
||||||
|
if(item){
|
||||||
|
return(
|
||||||
|
<div className="platePanel">
|
||||||
|
<div className="plateItem">
|
||||||
|
<ul className="plateUl ">
|
||||||
|
{
|
||||||
|
item.map((i,key)=>{
|
||||||
|
return(<li key={key}><Link to={`/forums/manage/${i.id}`}>{i.title}</Link></li>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleFlag = () => {
|
||||||
|
if (forum_tag ) {
|
||||||
|
console.log(forum_tag.children_bread_crumb);
|
||||||
|
if (bread_crumb && bread_crumb.is_children) {
|
||||||
|
return (<div className="padding20-30 edu-back-white font-22 mb20">{forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.title}</div>)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="padding20-30 edu-back-white font-22 mb20">
|
||||||
|
{ forum_tag.children_bread_crumb ?
|
||||||
|
<Dropdown overlay={menu(forum_tag.children_bread_crumb) } >
|
||||||
|
<div>{forum_tag.title}<i className="iconfont icon-xiajiantou font-16 ml10 color-dark-grey"></i></div>
|
||||||
|
</Dropdown>
|
||||||
|
:
|
||||||
|
<span>{forum_tag.title}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<Nav className="mt20 mb30" NavMap = {routerMap} ></Nav>
|
||||||
|
{titleFlag()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModeratorNav;
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import '../exchange.css';
|
||||||
|
import './manage.css';
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
|
||||||
|
class PassItem extends Component {
|
||||||
|
|
||||||
|
passEvent=(checked)=>{
|
||||||
|
const { id , refresh ,page } = this.props;
|
||||||
|
const url = `/memos/${id}/memo_hidden`;
|
||||||
|
axios.post(url,{
|
||||||
|
checked
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
refresh(page);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<p className="ml50">
|
||||||
|
<span className="middle-default-btn small-blue-btn c_point" onClick={()=>this.passEvent(true)}>通过</span>
|
||||||
|
<span className="middle-default-btn ml20 c_point" onClick={()=>this.passEvent(false)}>不通过</span>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PassItem;
|
|
@ -0,0 +1,122 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import './manage.css'
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
class PreApplyPlate extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
applylist:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
this.getApplyInfo(plateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplyInfo=(id)=>{
|
||||||
|
const url=`/forum_sections/${id}/applied_forums`;
|
||||||
|
axios.get(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
applylist:result.data.applied_moderators
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过,拒绝
|
||||||
|
passApplyEvent=(flag,applyId)=>{
|
||||||
|
this.props.confirm({
|
||||||
|
content: `确认${flag?"通过":"拒绝"}版主申请?`,
|
||||||
|
|
||||||
|
onOk: () => {
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
|
||||||
|
const url = `/forum_sections/${plateId}/deal_applies/${applyId}`
|
||||||
|
axios.post(url,{
|
||||||
|
deal_type:flag?1:2
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getApplyInfo(plateId);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { applylist } = this.state;
|
||||||
|
// const applylist = [
|
||||||
|
// {
|
||||||
|
// apply_id: 5,
|
||||||
|
// username: "OpenGCC",
|
||||||
|
// login: "innov",
|
||||||
|
// image_url: "avatars/User/girl.jpg",
|
||||||
|
// user_url: "/users/innov",
|
||||||
|
// user_ip: null,
|
||||||
|
// user_ip_address: "--",
|
||||||
|
// time: "14小时前",
|
||||||
|
// forum_title: "MAC安全",
|
||||||
|
// forum_id: 9,
|
||||||
|
// forum_url: "/memos/forum_memos/9",
|
||||||
|
// parent_forum: {
|
||||||
|
// forum_title: "网络安全网络安全333",
|
||||||
|
// forum_id: 6,
|
||||||
|
// forum_url: "/memos/forum_memos/6"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
const listItem= () => {
|
||||||
|
if(applylist && applylist.length > 0){
|
||||||
|
return(
|
||||||
|
<div className="mt20">
|
||||||
|
<p className="font-16 mb15 color-grey3">版主申请</p>
|
||||||
|
<div className="applyList">
|
||||||
|
{ applylist.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<a href={`/users/${item.login}`} > <img alt="用户头像" src={getImageUrl(`images/${item.image_url}`)} width="36" height="36" className="radius mr15"></img></a>
|
||||||
|
<div className="flex1">
|
||||||
|
<p className="mb10">
|
||||||
|
<a href={`/users/${item.login}`} > <span className="color-blue mr15">{item.username}</span></a>
|
||||||
|
{ item.user_ip && <span className="color-grey9">IP:{item.user_ip}({item.user_ip_address})</span> }
|
||||||
|
</p>
|
||||||
|
<p>申请成为”<Link to={`/forums/theme/${item.forum_id}`}><span className="color-blue">{item.forum_title}</span></Link>“的版主</p>
|
||||||
|
</div>
|
||||||
|
<div className="edu-txt-right">
|
||||||
|
<p className="color-grey9 mb10">{item.time}</p>
|
||||||
|
<p>
|
||||||
|
<span className="middle-default-btn small-blue-btn c_point" onClick={()=>this.passApplyEvent(true,item.apply_id)}>通过</span>
|
||||||
|
<span className="middle-default-btn ml20 c_point" onClick={()=>this.passApplyEvent(false,item.apply_id)}>拒绝</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{listItem()}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default PreApplyPlate;
|
|
@ -0,0 +1,88 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import { Modal , Form , Input } from 'antd';
|
||||||
|
import '../exchange.css';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
class PreCreate extends Component {
|
||||||
|
|
||||||
|
componentDidUpdate=(preState)=>{
|
||||||
|
const { subId , subName } = this.props;
|
||||||
|
if(preState.subId !== subId){
|
||||||
|
if(subId){
|
||||||
|
this.props.form.setFieldsValue({
|
||||||
|
title:subName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定
|
||||||
|
modalSave=()=>{
|
||||||
|
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||||
|
if(!err){
|
||||||
|
// subId存在就是编辑否则就是新增
|
||||||
|
const { refresh , subId } = this.props;
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url = `/forum_sections${subId ? "/rename":""}.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
title:values.title,
|
||||||
|
children_section_id:subId,
|
||||||
|
id: plateId
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
refresh();
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
modalCancel=()=>{
|
||||||
|
this.props.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
|
||||||
|
const { title , visible } = this.props
|
||||||
|
return(
|
||||||
|
<Modal
|
||||||
|
keyboard={false}
|
||||||
|
title={title}
|
||||||
|
visible={ visible }
|
||||||
|
closable={false}
|
||||||
|
footer={null}
|
||||||
|
destroyOnClose={true}
|
||||||
|
centered={true}
|
||||||
|
width="530px"
|
||||||
|
>
|
||||||
|
<div className="task-popup-content">
|
||||||
|
<Form className="formInline">
|
||||||
|
<Form.Item
|
||||||
|
label="标题"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('title', {
|
||||||
|
rules: [{
|
||||||
|
required: true, message: '请输入板块名称',
|
||||||
|
},{
|
||||||
|
max: 5000 , message:'最大限制20个字符'
|
||||||
|
}],
|
||||||
|
})(
|
||||||
|
<Input placeholder="请输入名称,最大限制20个字符" maxLength="60" style={{height:"40px"}}/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<div className="clearfix mt30 edu-txt-center">
|
||||||
|
<a className="task-btn mr30" onClick={this.modalCancel}>取消</a>
|
||||||
|
<a className="task-btn task-btn-orange" onClick={this.modalSave}>确定</a>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const WrappedPreCreate = Form.create({ name: 'PreCreate' })(PreCreate);
|
||||||
|
export default WrappedPreCreate;
|
|
@ -0,0 +1,76 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import CheckPublic from './CheckPublic'
|
||||||
|
import ModeratorNav from './ModeratorNav';
|
||||||
|
|
||||||
|
import ApplyPlate from './PreApplyPlate';
|
||||||
|
import PrePlateManage from './PrePlateManage'
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
class PreModerator extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
activeKey:undefined,
|
||||||
|
bread_crumb:undefined,
|
||||||
|
is_children:false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changeTab=(activeKey)=>{
|
||||||
|
this.setState({
|
||||||
|
activeKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
componentDidMount=()=>{
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
this.getPlateInfo(plateId);
|
||||||
|
}
|
||||||
|
componentDidUpdate=(prevState)=>{
|
||||||
|
let prePlateId = prevState.match.params.plateId;
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
if(prePlateId !== prePlateId){
|
||||||
|
this.getPlateInfo(plateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPlateInfo=(plateId)=>{
|
||||||
|
const url = `/forum_sections/${plateId}/forum_section_header`;
|
||||||
|
axios.get(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
bread_crumb:result.data.bread_crumb,
|
||||||
|
is_children:result.data.bread_crumb && result.data.bread_crumb.is_children
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { bread_crumb , is_children } = this.state;
|
||||||
|
const parentManage =()=> {
|
||||||
|
if(!is_children){
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
<PrePlateManage {...this.props} {...this.state} getPlateInfo={this.getPlateInfo}/>
|
||||||
|
|
||||||
|
{/* 板块申请 */}
|
||||||
|
<ApplyPlate {...this.props} {...this.state}/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div className="newMain">
|
||||||
|
<div className="educontent">
|
||||||
|
<ModeratorNav {...this.props} {...this.state}/>
|
||||||
|
{parentManage()}
|
||||||
|
{/* 审批(二级版主只有这一块) */}
|
||||||
|
<CheckPublic {...this.props} {...this.state} bread_crumb={bread_crumb}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreModerator;
|
|
@ -0,0 +1,286 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getImageUrl} from 'educoder';
|
||||||
|
import PreCreate from './PreCreate';
|
||||||
|
import './manage.css'
|
||||||
|
import '../exchange.css'
|
||||||
|
import {Link} from 'react-router-dom'
|
||||||
|
import axios from 'axios';
|
||||||
|
import update from 'immutability-helper'
|
||||||
|
|
||||||
|
import AddModeratorModal from './AddModeratorModal'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PrePlateManage extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
visible:false,
|
||||||
|
children_tags:undefined,
|
||||||
|
subId:undefined,
|
||||||
|
subName:undefined,
|
||||||
|
// 新增版主有关
|
||||||
|
addVisible:undefined,
|
||||||
|
operationPlateId:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.getSubModerator();
|
||||||
|
}
|
||||||
|
// 获取所有二级板块
|
||||||
|
getSubModerator=()=>{
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url = `/forum_sections/${plateId}/managements`;
|
||||||
|
axios.get(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
children_tags:result.data.forum_tag && result.data.forum_tag.children_tags
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 新建板块和重命名板块后的刷新方法
|
||||||
|
reSetInfo=()=>{
|
||||||
|
this.setState({
|
||||||
|
visible:false,
|
||||||
|
subId:undefined,
|
||||||
|
subName:undefined
|
||||||
|
})
|
||||||
|
this.getSubModerator();
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const { getPlateInfo } = this.props;
|
||||||
|
getPlateInfo(plateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 展开
|
||||||
|
expandEvent=(index,flag)=>{
|
||||||
|
this.setState(
|
||||||
|
(prevState) => ({
|
||||||
|
children_tags : update(prevState.children_tags, {[index]: { expand: {$set: flag} }}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示删除版主的按钮
|
||||||
|
deleteManageEvent=(index,flag)=>{
|
||||||
|
console.log(index)
|
||||||
|
this.setState(
|
||||||
|
(prevState) => ({
|
||||||
|
children_tags : update(prevState.children_tags, {[index]: { isDeleting: {$set: flag} }}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建二级板块
|
||||||
|
createSubPlateEvent=()=>{
|
||||||
|
this.setState({
|
||||||
|
visible:true,
|
||||||
|
subId:undefined,
|
||||||
|
subName:undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 二级板块重命名
|
||||||
|
RenamneSubPlateEvent=(id,name)=>{
|
||||||
|
this.setState({
|
||||||
|
visible:true,
|
||||||
|
subId:id,
|
||||||
|
subName:name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除二级板块
|
||||||
|
DeleteSubPlateEvent=(id)=>{
|
||||||
|
this.props.confirm({
|
||||||
|
content: '确认删除二级板块?',
|
||||||
|
onOk: () => {
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url=`/forum_sections/destroy_forum.json`
|
||||||
|
axios.post(url,{
|
||||||
|
children_section_id:id,
|
||||||
|
id: plateId
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getSubModerator();
|
||||||
|
const { getPlateInfo } = this.props;
|
||||||
|
getPlateInfo(plateId);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭新建弹框
|
||||||
|
colseModalEvent=()=>{
|
||||||
|
this.setState({
|
||||||
|
visible:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除版主
|
||||||
|
deletePlateEvent=(id,name,index,key)=>{
|
||||||
|
console.log(index,'dddd',key);
|
||||||
|
this.props.confirm({
|
||||||
|
content: `是否确认解除“${name}”的二级版主权限?`,
|
||||||
|
onOk: () => {
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url=`/forum_sections/${plateId}/destroy_moderator/${id}`
|
||||||
|
axios.post(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
const { children_tags } = this.state;
|
||||||
|
// 去掉当前选中的版主
|
||||||
|
let tempObj = children_tags[index].forum_moderators;
|
||||||
|
tempObj = tempObj.filter((_, i) => i !== key) ;
|
||||||
|
children_tags[index].forum_moderators = tempObj;
|
||||||
|
this.setState({ children_tags })
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增版主弹框
|
||||||
|
showAddBox=(flag,plateId)=>{
|
||||||
|
this.setState({
|
||||||
|
addVisible:true,
|
||||||
|
operationPlateId:plateId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
hideAddBox=()=>{
|
||||||
|
this.setState({
|
||||||
|
addVisible:false,
|
||||||
|
operationPlateId:undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { visible ,children_tags , subId , subName , addVisible , operationPlateId } = this.state;
|
||||||
|
// console.log("child",children_tags);
|
||||||
|
const forumTagList = ()=>{
|
||||||
|
if(children_tags && children_tags.length>0){
|
||||||
|
return(
|
||||||
|
<div className="subPlateList">
|
||||||
|
{
|
||||||
|
children_tags.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<div className={item.expand ? "subPlateItem active":"subPlateItem"}>
|
||||||
|
<p className="subPlateItem_head">
|
||||||
|
<span>{item.title}</span>
|
||||||
|
<span>
|
||||||
|
<span className="c_point mr10 color-green font-12" onClick={()=>this.RenamneSubPlateEvent(item.id,item.title)}>重命名</span>
|
||||||
|
<span className="c_point color-grey-9 sendPoint pl10" onClick={()=>this.DeleteSubPlateEvent(item.id)}>删除板块</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="mt10 mb10 color-grey3">二级版主</p>
|
||||||
|
<div className="plateManager">
|
||||||
|
{ renderSubList(item.forum_moderators,key,item.isDeleting) }
|
||||||
|
<span className="fr">
|
||||||
|
{/* 展开 */}
|
||||||
|
{
|
||||||
|
item.forum_moderators && item.forum_moderators.length > 5 && !item.expand &&
|
||||||
|
<span className="c_point mr30" onClick={()=>this.expandEvent(key,true)}><i className="iconfont icon-gengduo1 font-36 color-grey-9"></i></span>
|
||||||
|
}
|
||||||
|
{/* 收起 */}
|
||||||
|
{
|
||||||
|
item.forum_moderators && item.forum_moderators.length > 5 && item.expand &&
|
||||||
|
<span className="c_point mr30" onClick={()=>this.expandEvent(key,false)}><i className="iconfont icon-shangjiantou-tianchong font-36 color-grey-9"></i></span>
|
||||||
|
}
|
||||||
|
{/* 新增版主 */}
|
||||||
|
<span className="c_point mr30" onClick={()=>this.showAddBox(true,item.id)}><i className="iconfont icon-roundaddfill color-green font-36"></i></span>
|
||||||
|
{/* 删除版主 */}
|
||||||
|
{
|
||||||
|
item.forum_moderators && item.forum_moderators.length > 0 && !item.isDeleting &&
|
||||||
|
<span className="c_point mr30" onClick={()=>this.deleteManageEvent(key,true)}><i className="iconfont icon-default color-grey-9 font-36"></i></span>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
item.isDeleting && <span className="c_point mr30 completeIcon" onClick={()=>this.deleteManageEvent(key,false)}>完成</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderSubList = (item,index,deletingFlag) =>{
|
||||||
|
if(item){
|
||||||
|
return(
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
item.map((i,key)=>{
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{
|
||||||
|
!i.isDelete &&
|
||||||
|
<li>
|
||||||
|
<a href={`/users/${i.login}`}><img alt="" src={getImageUrl(`images/${i.image_url}`)} width="39" height="39" className="radius mb3"/></a>
|
||||||
|
{
|
||||||
|
deletingFlag &&
|
||||||
|
<i className="iconfont icon-htmal5icon19 deletePlateIcon" onClick={()=>this.deletePlateEvent(`${i.moderator_id}`,`${i.username}`,index,key)}></i>
|
||||||
|
}
|
||||||
|
<a href={`/users/${i.login}`}><span className="task-hide" style={{width:"39px",display:"block"}}>{i.username}</span></a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<div className="padding20-30 edu-back-white font-22 mb20 clearfix">
|
||||||
|
<PreCreate
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
visible={visible}
|
||||||
|
title="新建"
|
||||||
|
refresh={this.reSetInfo}
|
||||||
|
close={this.colseModalEvent}
|
||||||
|
subId={subId}
|
||||||
|
subName={subName}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AddModeratorModal
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
visible = {addVisible}
|
||||||
|
operationPlateId={operationPlateId}
|
||||||
|
hideAddBox={this.hideAddBox}
|
||||||
|
getSubModerator={this.getSubModerator}
|
||||||
|
/>
|
||||||
|
<span className="fl color-grey3 font-16">二级板块管理</span>
|
||||||
|
<a onClick={this.createSubPlateEvent} className="fr middle-default-btn small-green-btn">新建板块</a>
|
||||||
|
</div>
|
||||||
|
{forumTagList()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrePlateManage;
|
|
@ -0,0 +1,146 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spin , Pagination } from 'antd';
|
||||||
|
import '../exchange.css';
|
||||||
|
import './manage.css';
|
||||||
|
import Empty from '../Empty'
|
||||||
|
import ItemLeft from './ItemLeft';
|
||||||
|
import PassItem from './PassItem';
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
class SubCheckItem extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
page:1,
|
||||||
|
data:undefined,
|
||||||
|
isSpin:true,
|
||||||
|
limit:10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.getList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getList = (page) =>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url = `/forum_sections/${plateId}/unchecked_memos.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
page
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁言
|
||||||
|
stopEvent=(id,banned,user_id)=>{
|
||||||
|
const { current_user } = this.props;
|
||||||
|
const url =`/memos/${id}/banned_user.json`
|
||||||
|
axios.post((url),{
|
||||||
|
user_id:user_id,
|
||||||
|
banned
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
const { page } = this.state;
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
console.log("banned",banned);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
changePageEvent=(page)=>{
|
||||||
|
this.setState({
|
||||||
|
page
|
||||||
|
})
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { data , page , isSpin , limit } = this.state;
|
||||||
|
const { current_user } = this.props;
|
||||||
|
|
||||||
|
const pageCom = (
|
||||||
|
data && data.memos_count > limit &&
|
||||||
|
<div className="edu-txt-center pt30 pb30">
|
||||||
|
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const dataList = (
|
||||||
|
|
||||||
|
data && data.memos_lists && data.memos_lists.length > 0 ?
|
||||||
|
<div className="pl30 pr30">
|
||||||
|
{
|
||||||
|
data.memos_lists.map((item,key)=>{
|
||||||
|
const userInfo = {
|
||||||
|
forum_id:item.forum_id,
|
||||||
|
memo_id:item.memo_id,
|
||||||
|
user_url:item.user_url,
|
||||||
|
username:item.username,
|
||||||
|
time:item.time,
|
||||||
|
image_url:item.image_url,
|
||||||
|
memo_title:item.memo_title,
|
||||||
|
forum_title:item.forum_title,
|
||||||
|
user_id:item.user_id
|
||||||
|
}
|
||||||
|
const passItem = {
|
||||||
|
refresh:this.getList,
|
||||||
|
page,
|
||||||
|
id:item.memo_id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="moderatorItems">
|
||||||
|
<div className="df">
|
||||||
|
<ItemLeft {...userInfo}/>
|
||||||
|
<div className="flex-align-bottom between_">
|
||||||
|
{
|
||||||
|
current_user && current_user.admin &&
|
||||||
|
( item.is_banned ?
|
||||||
|
<p className="edu-txt-right color-red mb15 mt12 c_point" onClick={()=>this.stopEvent(item.memo_id,0,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i>已禁言</p>
|
||||||
|
:
|
||||||
|
<p className="edu-txt-right color-grey-9 mb15 mt12 c_point" onClick={()=>this.stopEvent(item.memo_id,1,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i>禁言</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<PassItem {...this.props} {...this.state} {...passItem} ></PassItem>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{ pageCom }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="edu-back-white pt50 pb50">
|
||||||
|
<Empty></Empty>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
{ dataList }
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubCheckItem;
|
|
@ -0,0 +1,148 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spin , Pagination } from 'antd';
|
||||||
|
import '../exchange.css';
|
||||||
|
import './manage.css';
|
||||||
|
import Empty from '../Empty'
|
||||||
|
import ItemLeft from './ItemLeft';
|
||||||
|
import PassItem from './PassItem';
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
class SubCheckReplyItem extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
page:1,
|
||||||
|
data:undefined,
|
||||||
|
isSpin:true,
|
||||||
|
limit:10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.getList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getList = (page) =>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url = `/forum_sections/${plateId}/unchecked_replies.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
page
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁言
|
||||||
|
stopEvent=(id,banned,user_id)=>{
|
||||||
|
const { current_user } = this.props;
|
||||||
|
const url =`/memos/${id}/banned_user.json`
|
||||||
|
axios.post((url),{
|
||||||
|
user_id:user_id,
|
||||||
|
banned
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
const { page } = this.state;
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
changePageEvent=(page)=>{
|
||||||
|
this.setState({
|
||||||
|
page
|
||||||
|
})
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { data , page , isSpin , limit } = this.state;
|
||||||
|
const { current_user } = this.props;
|
||||||
|
|
||||||
|
const pageCom = (
|
||||||
|
data && data.memos_count > limit &&
|
||||||
|
<div className="edu-txt-center pt30 pb30">
|
||||||
|
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const dataList = (
|
||||||
|
data && data.replies_lists && data.replies_lists.length > 0 ?
|
||||||
|
<div className="pl30 pr30">
|
||||||
|
{
|
||||||
|
data.replies_lists.map((item,key)=>{
|
||||||
|
const userInfo = {
|
||||||
|
source_id:item.source_id,
|
||||||
|
user_url:item.user_url,
|
||||||
|
username:item.username,
|
||||||
|
time:item.time,
|
||||||
|
image_url:item.image_url,
|
||||||
|
reply_content:item.reply_content,
|
||||||
|
source_title:item.source_title,
|
||||||
|
user_id:item.user_id,
|
||||||
|
id:item.reply_id
|
||||||
|
}
|
||||||
|
const passItem = {
|
||||||
|
refresh:this.getList,
|
||||||
|
page,
|
||||||
|
id:item.reply_id
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
|
|
||||||
|
<div className="moderatorItems">
|
||||||
|
<div className="df">
|
||||||
|
<ItemLeft {...userInfo}/>
|
||||||
|
<div className="flex-align-bottom between_">
|
||||||
|
{
|
||||||
|
current_user && current_user.admin &&
|
||||||
|
( item.is_banned ?
|
||||||
|
<p className="edu-txt-right color-red mb15 mt12 c_point" onClick={()=>this.stopEvent(item.reply_id,0,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i>已禁言</p>
|
||||||
|
:
|
||||||
|
<p className="edu-txt-right color-grey-9 mb15 mt12 c_point" onClick={()=>this.stopEvent(item.reply_id,1,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i>禁言</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<PassItem {...this.props} {...this.state} {...passItem} ></PassItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
{ pageCom }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="edu-back-white pt50 pb50">
|
||||||
|
<Empty></Empty>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
{ dataList }
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default SubCheckReplyItem;
|
|
@ -0,0 +1,174 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spin , Pagination } from 'antd';
|
||||||
|
import Tags from '../TagComponent/Index';
|
||||||
|
import '../exchange.css';
|
||||||
|
import './manage.css';
|
||||||
|
import Empty from '../Empty'
|
||||||
|
import ItemLeft from './ItemLeft';
|
||||||
|
import PassItem from './PassItem';
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
class SubSendItem extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
page:1,
|
||||||
|
data:undefined,
|
||||||
|
isSpin:true,
|
||||||
|
limit:10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.getList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getList = (page) =>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { plateId } = this.props.match.params;
|
||||||
|
const url = `/forum_sections/${plateId}/checked_memos.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
page
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除-已发布的帖子
|
||||||
|
deleteEvent=(id)=>{
|
||||||
|
const { page } = this.state;
|
||||||
|
const url = `/memos/${id}/destroy.json`;
|
||||||
|
axios.post(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 取消推荐/推荐-已发布的帖子
|
||||||
|
isFineEvent=(id,flag)=>{
|
||||||
|
const { page } = this.state;
|
||||||
|
const url = `/memos/${id}/is_fine.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
is_fine:flag ? 0 : 1
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 取消置顶/置顶-已发布的帖子
|
||||||
|
stickyEvent=(id,flag)=>{
|
||||||
|
const { page } = this.state;
|
||||||
|
const url = `/memos/${id}/set_top_or_down.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
sticky:flag ? 0 : 1
|
||||||
|
}}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁言
|
||||||
|
stopEvent=(id,banned)=>{
|
||||||
|
const { current_user } = this.props;
|
||||||
|
const url =`/memos/${id}/banned_user.json`
|
||||||
|
axios.post((url),{
|
||||||
|
user_id:current_user && current_user.user_id,
|
||||||
|
banned
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
const { page } = this.state;
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
changePageEvent=(page)=>{
|
||||||
|
this.setState({
|
||||||
|
page
|
||||||
|
})
|
||||||
|
this.getList(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { data , page , isSpin , limit } = this.state;
|
||||||
|
|
||||||
|
const pageCom = (
|
||||||
|
data && data.memos_count > limit &&
|
||||||
|
<div className="edu-txt-center pt30 pb30">
|
||||||
|
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
const dataList = (
|
||||||
|
data && data.memos_lists && data.memos_lists.length > 0 ?
|
||||||
|
<div className="pl30 pr30">
|
||||||
|
{
|
||||||
|
data.memos_lists.map((item,key)=>{
|
||||||
|
const userInfo = {
|
||||||
|
forum_id:item.forum_id,
|
||||||
|
memo_id:item.memo_id,
|
||||||
|
user_url:item.user_url,
|
||||||
|
username:item.username,
|
||||||
|
time:item.time,
|
||||||
|
image_url:item.image_url,
|
||||||
|
memo_title:item.memo_title,
|
||||||
|
forum_title:item.forum_title
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div className="moderatorItems">
|
||||||
|
<div className="df">
|
||||||
|
<ItemLeft {...userInfo}/>
|
||||||
|
<div>
|
||||||
|
<p className="edu-txt-right color-red mb15 mt12" style={{height:"33px"}}>
|
||||||
|
<Tags bestClass={item.is_fine ? "mb5 ml15" : undefined} topClass={item.sticky ? "mb10 ml15" : undefined}></Tags>
|
||||||
|
</p>
|
||||||
|
<p className="ml50 color-grey-9">
|
||||||
|
<span className="c_point" onClick={()=>this.stickyEvent(item.memo_id,item.sticky)}>{item.sticky?"取消置顶":"置顶"}</span>
|
||||||
|
<span className="sendPoint c_point font-14" onClick={()=>this.isFineEvent(item.memo_id,item.is_fine)}>{item.is_fine?"取消推荐":"推荐"}</span>
|
||||||
|
<span className="sendPoint c_point font-14" onClick={()=>this.deleteEvent(item.memo_id)}>删除</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{ pageCom }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="edu-back-white pt50 pb50">
|
||||||
|
<Empty></Empty>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
{ dataList }
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default SubSendItem;
|
|
@ -0,0 +1,134 @@
|
||||||
|
|
||||||
|
.moderatorMenu .ant-tabs-ink-bar{
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
.moderatorItems{
|
||||||
|
padding:20px 0px;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
}
|
||||||
|
.moderatorItems:last-child{
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.ItemsHeadPhoto .custom-img{
|
||||||
|
width:36px;
|
||||||
|
height: 36px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.reply_manage_content img{
|
||||||
|
width: 30px
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 一级板块管理-版主申请 */
|
||||||
|
.applyList{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between
|
||||||
|
}
|
||||||
|
.applyList > div{
|
||||||
|
background: #fff;
|
||||||
|
width: 585px;
|
||||||
|
display: flex;
|
||||||
|
padding:20px 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表单form---标题和内容在统一行 */
|
||||||
|
.formInline .ant-row.ant-form-item{
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
.formInline .ant-col.ant-form-item-control-wrapper{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
/* 二级板块管理 */
|
||||||
|
.subPlateList{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content:space-between;
|
||||||
|
}
|
||||||
|
.subPlateList > div{
|
||||||
|
width:585px;
|
||||||
|
height: 180px;
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.subPlateList > div .subPlateItem{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
left: 0px;
|
||||||
|
top:0px;
|
||||||
|
padding-left:30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.subPlateList > div .subPlateItem.active{
|
||||||
|
box-shadow: 0px 5px 26px rgba(0,0,0,0.1);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.subPlateItem_head{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
.subPlateItem.active .plateManager > ul{
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: inline
|
||||||
|
}
|
||||||
|
.plateManager > ul{
|
||||||
|
display: inline-block;
|
||||||
|
width: 345px;
|
||||||
|
height: 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.font-36{font-size: 36px!important;}
|
||||||
|
.plateManager > ul > li{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 12px;
|
||||||
|
align-items: center;
|
||||||
|
float: left;
|
||||||
|
margin-right: 30px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.deletePlateIcon{
|
||||||
|
position: absolute;
|
||||||
|
top: -11px;
|
||||||
|
right: -8px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FF5555;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.completeIcon{
|
||||||
|
background: #5091FF;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
float: right;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加版主弹框 */
|
||||||
|
.addPlateModal .ant-modal-body{
|
||||||
|
padding:30px 0px!important;
|
||||||
|
}
|
||||||
|
/* .addPlateModal .ant-table-tbody > tr > td{
|
||||||
|
padding:10px 5px;
|
||||||
|
} */
|
||||||
|
/*禁言两端对齐*/
|
||||||
|
.between_{
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
.plate-left-Menu .ant-tabs-tab{
|
||||||
|
height: 80px;
|
||||||
|
line-height: 80px;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-left: 30px;
|
||||||
|
margin-right: 0px;
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
.plate-left-Menu .ant-tabs-bar{
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.plate-left-Menu .ant-tabs-extra-content{
|
||||||
|
margin-top: 18px;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Tabs } from 'antd';
|
||||||
|
import './Menu.css';
|
||||||
|
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
|
class MenuCom extends Component{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
activeKey: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
const { defaultUrlKey } = this.props;
|
||||||
|
this.setState({
|
||||||
|
activeKey: defaultUrlKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
changeTabs=(activeKey)=>{
|
||||||
|
this.setState({
|
||||||
|
activeKey:activeKey
|
||||||
|
})
|
||||||
|
const { changeTab } = this.props;
|
||||||
|
changeTab(activeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { menu_nav , btn , ...props } = this.props;
|
||||||
|
|
||||||
|
const tabs = menu_nav.map((tab)=>{
|
||||||
|
const Content = tab.content;
|
||||||
|
return(
|
||||||
|
<TabPane tab={tab.name} key={tab.key}>
|
||||||
|
<Content condition={tab.key} {...this.props} {...this.state}/>
|
||||||
|
</TabPane>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
let { activeKey } = this.state;
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Tabs { ...props } onChange={ this.changeTabs } activeKey={ activeKey } animated={false} tabBarExtraContent={ btn }>
|
||||||
|
{ tabs }
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default MenuCom;
|
|
@ -0,0 +1,115 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import ExchangeRight from '../ExchangeRight'
|
||||||
|
|
||||||
|
import '../exchange.css';
|
||||||
|
import './myExchange.css';
|
||||||
|
|
||||||
|
import Nav from '../NavComponent/Index'
|
||||||
|
|
||||||
|
import MyTopic from './MyTopic';
|
||||||
|
import MyInteresting from './MyInteresting';
|
||||||
|
import MyEnshrine from './MyEnshrine';
|
||||||
|
import MenuWraps from '../MenuComponent/Menu'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const menu_nav = [
|
||||||
|
{
|
||||||
|
name:"我的话题",
|
||||||
|
key:`MyTopic`,
|
||||||
|
content:MyTopic
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"我的收藏",
|
||||||
|
key:`MyEnshrine`,
|
||||||
|
content:MyEnshrine
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"我感兴趣的论坛",
|
||||||
|
key:`MyInteresting`,
|
||||||
|
content:MyInteresting
|
||||||
|
}
|
||||||
|
]
|
||||||
|
class Index extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
activeKey:"MyTopic",
|
||||||
|
data:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.getMain();
|
||||||
|
}
|
||||||
|
|
||||||
|
getMain=()=>{
|
||||||
|
const url =`/my_memos/recommend_memos`;
|
||||||
|
axios.get(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换第一个nav
|
||||||
|
changeTab=(activeKey)=>{
|
||||||
|
// console.log('1--',activeKey);
|
||||||
|
this.setState({
|
||||||
|
activeKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
let pathname = this.props.location.pathname;
|
||||||
|
let urllength = pathname.split("/").length;
|
||||||
|
let urlLast = pathname.split("/")[urllength-1];
|
||||||
|
|
||||||
|
const { current_user } = this.props;
|
||||||
|
const { activeKey , data } = this.state;
|
||||||
|
|
||||||
|
const NavMap=[
|
||||||
|
{
|
||||||
|
url:current_user && current_user.user_url,
|
||||||
|
name:current_user && current_user.username
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url:"/forums",
|
||||||
|
name:"论坛交流"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:activeKey === "MyTopic" ? "我的话题" :activeKey === "MyEnshrine" ? "我的收藏" : "我感兴趣的论坛"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="educontent">
|
||||||
|
<Nav className="mt30" NavMap = {NavMap}></Nav>
|
||||||
|
<div className="clearfix F_panel" style={{marginTop:"10px"}}>
|
||||||
|
<div className="fl with76 pr20">
|
||||||
|
<div>
|
||||||
|
<MenuWraps
|
||||||
|
className="plate-left-Menu mainNav-style"
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
menu_nav={menu_nav}
|
||||||
|
defaultUrlKey={urlLast}
|
||||||
|
changeTab={this.changeTab}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ExchangeRight
|
||||||
|
hideSearchPanel={true}
|
||||||
|
hottest_memos={data && data.hottest_memos}
|
||||||
|
recommend_memos={data && data.recommend_memos}
|
||||||
|
></ExchangeRight>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Index;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import MyTopicItem from './MyTopicItem';
|
||||||
|
|
||||||
|
|
||||||
|
class MyEnshrine extends Component {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<MyTopicItem {...this.props} {...this.state} urlName={'my_memos/my_watched'}></MyTopicItem>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyEnshrine;
|
|
@ -0,0 +1,101 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import Empty from '../Empty';
|
||||||
|
import './myExchange.css';
|
||||||
|
|
||||||
|
import box1 from '../images/box1.png';
|
||||||
|
import box2 from '../images/box2.png';
|
||||||
|
import box3 from '../images/box3.png';
|
||||||
|
import box4 from '../images/box4.png';
|
||||||
|
import box5 from '../images/box5.png';
|
||||||
|
import box6 from '../images/box6.png';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const backImgArray = [box1,box2,box3,box4,box5,box6];
|
||||||
|
class MyInteresting extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
forum_details:undefined,
|
||||||
|
isSpin:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
this.getInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfo = () =>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const url = `/my_memos/my_interested`;
|
||||||
|
axios.get(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
forum_details:result.data.forum_details,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消收藏
|
||||||
|
cancelEnshrineEvent=(id)=>{
|
||||||
|
const url = `/memos/forum_memos/${id}/is_watch.json`;
|
||||||
|
axios.post((url),{
|
||||||
|
is_watch:0
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.getInfo();
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { forum_details , isSpin } = this.state;
|
||||||
|
|
||||||
|
const divList = ( forum_details && forum_details.length > 0 ?
|
||||||
|
<div className="ForumList">
|
||||||
|
{
|
||||||
|
forum_details.map((item,key) => {
|
||||||
|
const index = Number(Number((key)%6)+1)-1;
|
||||||
|
// console.log(index);
|
||||||
|
return(
|
||||||
|
<div className="interestItem">
|
||||||
|
<div className="interestingUpper" style={{backgroundImage:`url(${backImgArray[index]})`}}>
|
||||||
|
<div>
|
||||||
|
<p className="font-20 color-white mb10">{item.title}</p>
|
||||||
|
<p className="color-white font-12 edu-txt-center">{item.memos_count} 个话题</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="interestingOperate">
|
||||||
|
<span className="operateBtn c_point" onClick={()=>this.cancelEnshrineEvent(`${item.id}`)}>取消收藏</span>
|
||||||
|
<Link className="operateBtn color-blue" to={`/forums/theme/${item.id}`}>查看</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="pt50 pb50 edu-back-white"><Empty/></div>
|
||||||
|
)
|
||||||
|
return(
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
<div style={{backgroundColor:"#fafafa"}}>
|
||||||
|
{ divList }
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyInteresting;
|
|
@ -0,0 +1,59 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import MenuWraps from '../MenuComponent/Menu';
|
||||||
|
import MyTopicItem from './MyTopicItem';
|
||||||
|
|
||||||
|
import './myExchange.css';
|
||||||
|
|
||||||
|
|
||||||
|
const menu_nav = [
|
||||||
|
{
|
||||||
|
name:"发表的话题",
|
||||||
|
key:`published`,
|
||||||
|
content:MyTopicItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"回复的话题",
|
||||||
|
key:`replied`,
|
||||||
|
content:MyTopicItem
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"看过的话题",
|
||||||
|
key:`watched`,
|
||||||
|
content:MyTopicItem
|
||||||
|
}
|
||||||
|
]
|
||||||
|
class MyTopic extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
activeKey:"published"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换第2个nav:发表的话题、回复的话题、看过的话题
|
||||||
|
changeTab=(activeKey)=>{
|
||||||
|
this.setState({
|
||||||
|
activeKey
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
let { activeKey } = this.state;
|
||||||
|
return(
|
||||||
|
<div>
|
||||||
|
<MenuWraps
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
className="plate-left-Menu"
|
||||||
|
subActiveKey={activeKey}
|
||||||
|
menu_nav={menu_nav}
|
||||||
|
defaultUrlKey={activeKey}
|
||||||
|
changeTab={this.changeTab}
|
||||||
|
urlName={'my_memos'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyTopic;
|
|
@ -0,0 +1,239 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Dropdown , Menu , Icon , DatePicker , Pagination , Spin } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
import moment from 'moment'
|
||||||
|
import Empty from '../Empty'
|
||||||
|
|
||||||
|
import ExchangeItems from '../ExchangeItem';
|
||||||
|
import './myExchange.css'
|
||||||
|
import '../exchange.css'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
const { SubMenu } = Menu;
|
||||||
|
|
||||||
|
const dateFormat = 'YYYY-MM-DD';
|
||||||
|
|
||||||
|
class MyTopicItem extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
// 筛选条件
|
||||||
|
memo_type:undefined,
|
||||||
|
forum_section_id:undefined,
|
||||||
|
is_hidden:undefined,
|
||||||
|
is_hidden_name:undefined,
|
||||||
|
start_time:undefined,
|
||||||
|
end_time:undefined,
|
||||||
|
page:1,
|
||||||
|
plateName:undefined,
|
||||||
|
// 分页
|
||||||
|
pageSize:15,
|
||||||
|
// 数据
|
||||||
|
data:undefined,
|
||||||
|
isSpin:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
searchEvent=()=>{}
|
||||||
|
|
||||||
|
// 清除
|
||||||
|
clearEvent=()=>{}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 我的话题和我的收藏公用当前组件,但接口不同,根据url判断调用哪个接口
|
||||||
|
getMytopicInfo = (forum_section_id,is_hidden,start_time,end_time,page) =>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { subActiveKey , urlName } = this.props;
|
||||||
|
|
||||||
|
const url = `/${urlName}`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
memo_type:subActiveKey,
|
||||||
|
forum_section_id,
|
||||||
|
is_hidden,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
page
|
||||||
|
}}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新时需要用到
|
||||||
|
refresh=()=>{
|
||||||
|
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
|
||||||
|
this.getMytopicInfo(forum_section_id,is_hidden,start_time,end_time,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换分页
|
||||||
|
changePageEvent = (page) =>{
|
||||||
|
this.setState({
|
||||||
|
page
|
||||||
|
})
|
||||||
|
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
|
||||||
|
this.getMytopicInfo(forum_section_id,is_hidden,start_time,end_time,page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择板块
|
||||||
|
changeForumId=(plateId,name)=>{
|
||||||
|
this.setState({
|
||||||
|
forum_section_id:plateId,
|
||||||
|
plateName:name
|
||||||
|
})
|
||||||
|
const {is_hidden,start_time,end_time , page } = this.state;
|
||||||
|
this.getMytopicInfo(plateId,is_hidden,start_time,end_time,page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择帖子状态
|
||||||
|
forumType=(type)=>{
|
||||||
|
this.setState({
|
||||||
|
is_hidden:type,
|
||||||
|
is_hidden_name:type === "show" ? "已发布的话题":"待审查的话题"
|
||||||
|
})
|
||||||
|
const {forum_section_id,start_time,end_time , page } = this.state;
|
||||||
|
this.getMytopicInfo(forum_section_id,type,start_time,end_time,page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除搜索条件
|
||||||
|
clearEvent=()=>{
|
||||||
|
this.setState({
|
||||||
|
forum_section_id:undefined,
|
||||||
|
plateName:undefined,
|
||||||
|
is_hidden:undefined,
|
||||||
|
is_hidden_name:undefined,
|
||||||
|
start_time:undefined,
|
||||||
|
end_time:undefined
|
||||||
|
})
|
||||||
|
this.getMytopicInfo(undefined,undefined,undefined,undefined,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择开始时间
|
||||||
|
changeBeginEvent=(e,dateString)=>{
|
||||||
|
this.setState({
|
||||||
|
start_time:dateString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择结束时间
|
||||||
|
changeEndEvent=(e,dateString)=>{
|
||||||
|
this.setState({
|
||||||
|
end_time:dateString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
searchEvent=()=>{
|
||||||
|
this.setState({
|
||||||
|
page:1
|
||||||
|
})
|
||||||
|
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
|
||||||
|
this.getMytopicInfo(forum_section_id , is_hidden , start_time , end_time , 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { data , pageSize , page , plateName , is_hidden_name , start_time , end_time , isSpin } = this.state;
|
||||||
|
|
||||||
|
// 选择板块
|
||||||
|
const menuList =(
|
||||||
|
<div className="platePanel">
|
||||||
|
{
|
||||||
|
data && data.forum_sections && data.forum_sections.map(item => {
|
||||||
|
return(
|
||||||
|
<div className="plateItem">
|
||||||
|
<span className="plateItem_h"><a onClick={()=>this.changeForumId(item.id,item.name)}>{item.name}</a></span>
|
||||||
|
<ul className="plateUl">
|
||||||
|
{
|
||||||
|
item.children_tags && item.children_tags.map(i=>{
|
||||||
|
return(<li><a onClick={()=>this.changeForumId(i.id,i.title)}>{i.title}</a></li>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全部帖子
|
||||||
|
const forumList = (
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item onClick={()=>this.forumType('hidden')}>待审查的话题</Menu.Item>
|
||||||
|
<Menu.Item onClick={()=>this.forumType('show')}>已发布的话题</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const pagination = (
|
||||||
|
data && data.memos && data.memos_count > pageSize &&
|
||||||
|
<div className="edu-txt-center pt30 pb50">
|
||||||
|
<Pagination current={page} pageSize={pageSize} total={data.memos_count} showQuickJumper onChange={this.changePageEvent}></Pagination>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const dataList = (
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
<div className="MyTopicSearch">
|
||||||
|
<Dropdown overlay={menuList}>
|
||||||
|
<a className="ant-dropdown-link">
|
||||||
|
{plateName || "选择板块"} <Icon type="down" />
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
<Dropdown overlay={forumList}>
|
||||||
|
<a className="ant-dropdown-link">
|
||||||
|
{ is_hidden_name || '全部帖子' } <Icon type="down" />
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
<div>
|
||||||
|
<span className="mr10">开始日期</span>
|
||||||
|
<DatePicker style={{width:"145px"}}
|
||||||
|
placeholder={'请选择开始时间'}
|
||||||
|
format={dateFormat}
|
||||||
|
value={start_time && moment(start_time,dateFormat)}
|
||||||
|
onChange={this.changeBeginEvent}
|
||||||
|
/>
|
||||||
|
<span className="ml15 mr10">结束日期</span>
|
||||||
|
<DatePicker style={{width:"145px"}}
|
||||||
|
placeholder={'请选择结束时间'}
|
||||||
|
format={dateFormat}
|
||||||
|
value={end_time && moment(end_time,dateFormat)}
|
||||||
|
onChange={this.changeEndEvent}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="df">
|
||||||
|
<a onClick={this.searchEvent} className="small-default-btn small-blue-btn mr15">搜索</a>
|
||||||
|
<a onClick={this.clearEvent} className="small-default-btn">清除</a>
|
||||||
|
</span>
|
||||||
|
<span className="color-grey9 font-12">共<span className="color-blue">{ data && data.memos_count }</span>个结果</span>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
data && data.memos && data.memos.length>0 ?
|
||||||
|
<div>
|
||||||
|
<ExchangeItems memos = { data && data.memos } {...this.props} {...this.state} refresh={this.refresh}></ExchangeItems>
|
||||||
|
{ pagination }
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div className="pt50 pb50 edu-back-white">
|
||||||
|
<Empty/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
return( dataList )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyTopicItem;
|
|
@ -0,0 +1,56 @@
|
||||||
|
.MyTopicSearch{
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
padding:20px 30px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 我感兴趣的论坛 */
|
||||||
|
.ForumList{
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.interestItem{
|
||||||
|
width: 280px;
|
||||||
|
margin:20px 0px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
.interestItem .interestingUpper{
|
||||||
|
height: 150px;
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-size:100% 100%;
|
||||||
|
background-color:#fff ;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.interestingOperate{
|
||||||
|
display: flex;
|
||||||
|
height: 50px;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
.interestingOperate .operateBtn{
|
||||||
|
width: 50%;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
.interestingOperate .operateBtn:first-child::after{
|
||||||
|
height: 24px;
|
||||||
|
width:1px;
|
||||||
|
top:13px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
content: '';
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainNav-style > .ant-tabs-bar .ant-tabs-ink-bar{
|
||||||
|
display: none!important;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React , { PureComponent } from "react";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import './nav.css'
|
||||||
|
|
||||||
|
class Index extends PureComponent{
|
||||||
|
render(){
|
||||||
|
const { NavMap , ...props } = this.props;
|
||||||
|
let navRouter = NavMap && NavMap.map((item) => {
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{
|
||||||
|
item.name ? (item.url ?
|
||||||
|
<Link to={item.url} className="color-grey-9 nav_Link" >{ item.name }</Link>
|
||||||
|
:
|
||||||
|
<span className="color-grey3">{ item.name }</span>)
|
||||||
|
:""
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return(
|
||||||
|
<p {...props}>{ navRouter }</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export default Index;
|
|
@ -0,0 +1,13 @@
|
||||||
|
.nav_Link{
|
||||||
|
position: relative;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.nav_Link::after{
|
||||||
|
position: absolute;
|
||||||
|
right: -15px;
|
||||||
|
content: '>';
|
||||||
|
top:0px;
|
||||||
|
color: #999;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import ExchangeItem from '../ExchangeItem'
|
||||||
|
import Empty from '../Empty'
|
||||||
|
|
||||||
|
import '../exchange.css'
|
||||||
|
import './plate.css'
|
||||||
|
class All extends PureComponent{
|
||||||
|
state = {
|
||||||
|
condition:"all"
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
const { condition } = this.props;
|
||||||
|
this.setState({
|
||||||
|
condition
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
|
||||||
|
const { memos } = this.props;
|
||||||
|
|
||||||
|
const memosList = (
|
||||||
|
memos && memos.length > 0 ?
|
||||||
|
<ExchangeItem memos={ memos } {...this.props} {...this.state}></ExchangeItem>
|
||||||
|
:
|
||||||
|
<div className="pt50 pb50">
|
||||||
|
<Empty/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
return(
|
||||||
|
memosList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default All;
|
|
@ -0,0 +1,263 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Pagination , Spin } from 'antd'
|
||||||
|
import {Link} from 'react-router-dom'
|
||||||
|
import '../exchange.css';
|
||||||
|
import './plate.css'
|
||||||
|
|
||||||
|
import Nav from '../NavComponent/Index';
|
||||||
|
import MenuWraps from '../MenuComponent/Menu';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import PlateRight from './PlateRight'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import All from './All'
|
||||||
|
|
||||||
|
const menu_nav = [
|
||||||
|
{
|
||||||
|
name:"全部",
|
||||||
|
key:`all`,
|
||||||
|
content:All
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"推荐精华",
|
||||||
|
key:`is_fine`,
|
||||||
|
content:All
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"我的话题",
|
||||||
|
key:`my_memos`,
|
||||||
|
content:All
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"我参与的话题",
|
||||||
|
key:`my_topics`,
|
||||||
|
content:All
|
||||||
|
}
|
||||||
|
]
|
||||||
|
class Index extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
collectFlag:false,
|
||||||
|
|
||||||
|
// 列表搜索相关
|
||||||
|
page:1,
|
||||||
|
search:undefined,
|
||||||
|
pageSize:10,
|
||||||
|
select_type:"all",
|
||||||
|
|
||||||
|
memos:undefined,
|
||||||
|
memos_count:0,
|
||||||
|
// 接口返回的所有数据
|
||||||
|
data:undefined,
|
||||||
|
forum_tag :undefined,
|
||||||
|
forum_moders:undefined,
|
||||||
|
forum_sections:undefined,
|
||||||
|
bread_crumb:undefined,
|
||||||
|
current_user:undefined,
|
||||||
|
isSpin:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
this.InitData();
|
||||||
|
}
|
||||||
|
|
||||||
|
InitData=()=>{
|
||||||
|
this.setState({
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
const { page , search , select_type} = this.state;
|
||||||
|
this.getInfos(plateid , page , search , select_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfos = (plateid , page , search , select_type) =>{
|
||||||
|
const url= `/memos/forum_memos/${plateid}.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
page,
|
||||||
|
search,
|
||||||
|
select_type
|
||||||
|
}}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
data:result.data,
|
||||||
|
memos:result.data.memos,
|
||||||
|
memos_count:result.data.memos_count,
|
||||||
|
collectFlag:result.data.watched,
|
||||||
|
forum_tag :result.data.bread_crumb && result.data.bread_crumb.forum_tag,
|
||||||
|
forum_moders:result.data.forum_moders,
|
||||||
|
forum_sections:result.data.forum_sections,
|
||||||
|
bread_crumb:result.data.bread_crumb,
|
||||||
|
current_user:result.data.current_user,
|
||||||
|
isSpin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 点击收藏
|
||||||
|
colectPlate = () =>{
|
||||||
|
const { collectFlag } = this.state;
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
const url = `/memos/forum_memos/${plateid}/is_watch.json`;
|
||||||
|
// is_watch:1为添加关注
|
||||||
|
axios.post(url,{
|
||||||
|
is_watch: collectFlag ? 0 : 1
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
if(result.data.status === 0){
|
||||||
|
this.setState({
|
||||||
|
collectFlag:!collectFlag
|
||||||
|
})
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
searchEvent=(e)=>{
|
||||||
|
this.setState({
|
||||||
|
search:e
|
||||||
|
})
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
const { select_type } = this.state;
|
||||||
|
this.getInfos(plateid , 1 , e , select_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换菜单项
|
||||||
|
changeTabEvent = (key) =>{
|
||||||
|
this.setState({
|
||||||
|
select_type:key,
|
||||||
|
isSpin:true
|
||||||
|
})
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
|
||||||
|
const { search} = this.state;
|
||||||
|
this.getInfos(plateid , 1 , search , key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换分页
|
||||||
|
changePageEvent=(pageNum)=>{
|
||||||
|
this.setState({
|
||||||
|
page:pageNum
|
||||||
|
})
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
|
||||||
|
const { search , select_type} = this.state;
|
||||||
|
this.getInfos(plateid , pageNum , search , select_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let {
|
||||||
|
title,
|
||||||
|
collectFlag ,
|
||||||
|
memos ,
|
||||||
|
forum_tag ,
|
||||||
|
select_type,
|
||||||
|
forum_moders ,
|
||||||
|
forum_sections ,
|
||||||
|
page,
|
||||||
|
data,
|
||||||
|
pageSize,
|
||||||
|
bread_crumb ,
|
||||||
|
current_user,
|
||||||
|
isSpin } = this.state;
|
||||||
|
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
let pathname = this.props.location.pathname;
|
||||||
|
let urlLastLength = pathname.split("/").length;
|
||||||
|
let urlLast = pathname.split("/")[urlLastLength-1];
|
||||||
|
|
||||||
|
const children_bread_crumb = forum_tag && forum_tag.children_bread_crumb;
|
||||||
|
//获取当前版块标题
|
||||||
|
const title_post=forum_tag && ((children_bread_crumb && children_bread_crumb.title) || forum_tag.title);
|
||||||
|
|
||||||
|
// 顶部导航栏
|
||||||
|
const routerMap = [
|
||||||
|
{
|
||||||
|
name:current_user && current_user.username,
|
||||||
|
url:current_user && current_user.user_url
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:bread_crumb && bread_crumb.forum && bread_crumb.forum.title,
|
||||||
|
url:"/forums"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:forum_tag && ((children_bread_crumb && children_bread_crumb.title) || forum_tag.title)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let collectMap = current_user? <span className="mt2 collecting" onClick={() => this.colectPlate()}>{collectFlag ?'取消收藏':'收藏'}</span>:<span className="mt2 collecting" ><a href={`/login`}> 收藏</a> </span>;
|
||||||
|
|
||||||
|
// 菜单行右侧按钮
|
||||||
|
//只有登录管理员可见
|
||||||
|
|
||||||
|
const btn = (current_user && current_user.admin_permission) ?<Link to={`/forums/manage/${plateid}`} className={"color-grey-6"}>版块管理</Link> : undefined;
|
||||||
|
console.log(btn);
|
||||||
|
|
||||||
|
|
||||||
|
// 操作列表item需要刷新页面以及切换菜单项需要重新调用参数
|
||||||
|
const commonEvent = {
|
||||||
|
refresh:this.InitData,
|
||||||
|
changeTab:this.changeTabEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表分页
|
||||||
|
const pageination = (
|
||||||
|
data && data.memos_count > pageSize &&
|
||||||
|
<div className="mb50 edu-txt-center">
|
||||||
|
<Pagination showQuickJumper current={page} total={data && data.memos_count} pageSize={pageSize} onChange={this.changePageEvent}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="clearfix educontent pt20">
|
||||||
|
<Spin spinning={isSpin}>
|
||||||
|
<Nav NavMap = {routerMap} ></Nav>
|
||||||
|
|
||||||
|
<p className="mt30 mb10 clearfix">
|
||||||
|
<span className="font-22 color-grey3 mr20 fl lineh-25 task-hide" style = {{ maxWidth : "900px"}}>{title_post}</span>
|
||||||
|
{ collectMap }
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="clearfix">
|
||||||
|
<div className="fl with76 pr20">
|
||||||
|
<div className={"edu-back-white mb30"}>
|
||||||
|
|
||||||
|
<MenuWraps
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
{...commonEvent}
|
||||||
|
className="plate-left-Menu"
|
||||||
|
menu_nav={menu_nav}
|
||||||
|
btn={btn}
|
||||||
|
defaultUrlKey={urlLast}
|
||||||
|
{...this.state}/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{ pageination }
|
||||||
|
</div>
|
||||||
|
<PlateRight
|
||||||
|
forum_sections={forum_sections}
|
||||||
|
forum_moders={forum_moders}
|
||||||
|
searchEvent={this.searchEvent}
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
></PlateRight>
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Index;
|
|
@ -0,0 +1,93 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import '../exchange.css';
|
||||||
|
import './plate.css'
|
||||||
|
|
||||||
|
import ExchangeRightSearch from '../ExchangeRightSearch'
|
||||||
|
import Custom from '../CustomComponent/Index';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
class PlateRight extends PureComponent {
|
||||||
|
|
||||||
|
// 申请版主
|
||||||
|
applyModerator = () =>{
|
||||||
|
this.props.confirm({
|
||||||
|
content: '是否确认申请版主?',
|
||||||
|
|
||||||
|
onOk: () => {
|
||||||
|
const { plateid } = this.props.match.params;
|
||||||
|
const url = `/forum_sections/${plateid}/user_apply`;
|
||||||
|
axios.post(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
|
||||||
|
const { forum_sections , forum_moders , searchEvent , current_user }= this.props;
|
||||||
|
|
||||||
|
// admin_permission:是否是版主
|
||||||
|
const moderatorInfo = ( forum_moders && forum_moders.length > 0 &&
|
||||||
|
<div className="bc-white mb20">
|
||||||
|
<p className="clearfix r_part_title">
|
||||||
|
<img src={getImageUrl("images/plate/person.png")} width="18px" className="mr10 fl mt7" alt=""/>
|
||||||
|
<span className="color-grey3 font-16 fl">版主</span>
|
||||||
|
{
|
||||||
|
current_user && current_user.admin_permission === false && <a onClick={this.applyModerator} className="applyBtn fr mt3">申请版主</a>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<div className="moderatorPanel">
|
||||||
|
{ forum_moders.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<Custom className="moderatorInfo" hrefUrl={`/users/${item.user_login}`} img_url={getImageUrl(`images/${item.image_url}`)} name={item.username}></Custom>
|
||||||
|
)
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const choiceInfo = ( forum_sections && forum_sections.length > 0 &&
|
||||||
|
<div className="bc-white mb20">
|
||||||
|
<p className="clearfix r_part_title">
|
||||||
|
<img src={getImageUrl("images/plate/plate.png")} width="18px" className="mr10 fl mt7" alt=""/>
|
||||||
|
<span className="color-grey3 font-16 fl">精选版块</span>
|
||||||
|
</p>
|
||||||
|
<ul className="choicePlate">
|
||||||
|
{
|
||||||
|
forum_sections.map((item,key) => {
|
||||||
|
return(
|
||||||
|
<li className="clearfix">
|
||||||
|
<span className="fl"><a href={`/forums/theme/${item.id}`}>{item.title}</a></span>
|
||||||
|
<span className="fr"><a href={`/forums/theme/${item.id}`} className="color-blue">{item.memo_size}</a>个话题</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div className="fl with24">
|
||||||
|
<ExchangeRightSearch {...this.props} {...this.state} searchEvent={searchEvent}></ExchangeRightSearch>
|
||||||
|
|
||||||
|
{ moderatorInfo }
|
||||||
|
|
||||||
|
{ choiceInfo }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default PlateRight;
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
/* plate */
|
||||||
|
.collecting{
|
||||||
|
height: 22px;
|
||||||
|
padding:0px 11px;
|
||||||
|
border: 1px solid #5091FF;
|
||||||
|
color: #5091FF;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-size: 12px;
|
||||||
|
float: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.collected{
|
||||||
|
color: #999999;
|
||||||
|
border:1px solid #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.applyBtn{
|
||||||
|
border:1px solid #5091FF;
|
||||||
|
color: #5091FF;
|
||||||
|
height: 26px;
|
||||||
|
line-height: 26px;
|
||||||
|
padding:0px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 版主 */
|
||||||
|
.moderatorPanel{
|
||||||
|
display: flex;
|
||||||
|
flex-wrap:wrap;
|
||||||
|
padding: 10px 15px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 精选版块 */
|
||||||
|
.choicePlate{
|
||||||
|
padding:10px 20px 0px 20px;
|
||||||
|
}
|
||||||
|
.choicePlate > li{
|
||||||
|
padding: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchForHide .ant-tabs-bar.ant-tabs-top-bar{
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import './post.css'
|
||||||
|
import Item from '../Comments/CommentsItem'
|
||||||
|
import ItemImg from '../Comments/CommentsItemImg'
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import CommentsSend from '../Comments/CommentsSend';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsIndex extends PureComponent{
|
||||||
|
|
||||||
|
// 调用父组件的刷新方法
|
||||||
|
refreshReply=()=>{
|
||||||
|
let { refresh , page} = this.props;
|
||||||
|
refresh(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除评论
|
||||||
|
deleteReplyEvent=(id)=>{
|
||||||
|
const url = `/memos/${id}/destroy.json`
|
||||||
|
axios.post(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.refreshReply();
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
commentReplyEvent=(index,flag)=>{
|
||||||
|
console.log("1",flag);
|
||||||
|
this.props.showCommentEvent(index,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 点赞评论
|
||||||
|
praiseEvent=(id)=>{
|
||||||
|
const url = `/discusses/${id}/plus.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
container_type:'Memo',
|
||||||
|
type:1
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.refreshReply();
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
parseCommentContent = (oldContent) => {
|
||||||
|
if (oldContent && oldContent.startsWith('<') && oldContent.endsWith('>')) {
|
||||||
|
} else if (window.$('#md_div').length) { // 有这个临时处理md内容的dom
|
||||||
|
window.$('#md_div').html('')
|
||||||
|
// markdown to html
|
||||||
|
try {
|
||||||
|
var markdwonParser = window.editormd.markdownToHTML("md_div", {
|
||||||
|
markdown: oldContent,
|
||||||
|
emoji: true,
|
||||||
|
htmlDecode: "style,script,iframe", // you can filter tags decode
|
||||||
|
taskList: true,
|
||||||
|
tex: true, // 默认不解析
|
||||||
|
flowChart: true, // 默认不解析
|
||||||
|
sequenceDiagram: true // 默认不解析
|
||||||
|
});
|
||||||
|
oldContent = window.$('#md_div').html()
|
||||||
|
} catch (e) {
|
||||||
|
// TODO 可能公式parse时报错了
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return oldContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
// 评论列表,当前用户信息
|
||||||
|
const { repliesData , current_user , page } = this.props;
|
||||||
|
const renderNum = (arrs,flag) =>{
|
||||||
|
return(<p>展开其余1条评论</p>)
|
||||||
|
}
|
||||||
|
|
||||||
|
//二级评论
|
||||||
|
const renderChild = (arrs) => {
|
||||||
|
return(
|
||||||
|
<div className="">
|
||||||
|
{
|
||||||
|
arrs.map((child)=>{
|
||||||
|
return(
|
||||||
|
<div className="commentsItem_infos">
|
||||||
|
<ItemImg image_url={child.image_url} ></ItemImg>
|
||||||
|
<div className="flex1">
|
||||||
|
<Item image_url={child.image_url} username={child.username} time={child.time} content={child.content} id={child.id}></Item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const replyInfoWrap = (i,index)=> (
|
||||||
|
<React.Fragment>
|
||||||
|
<p className="edu-txt-right color-grey-6">
|
||||||
|
<span className={ i.user_praise ? "ml30":"ml30 c_point"} onClick={ i.user_praise ? undefined : ()=>this.praiseEvent(i.id)}>
|
||||||
|
<i className={ i.user_praise ? "iconfont icon-dianzan color-grey-9 font-16 mr5" : "iconfont icon-dianzan-xian color-grey3 font-16 mr5"}></i>
|
||||||
|
<span>{i.praise_count}</span>
|
||||||
|
</span>
|
||||||
|
{
|
||||||
|
current_user && (current_user.is_banned === false || current_user.admin) &&
|
||||||
|
<span className="ml30 c_point" onClick={()=>this.commentReplyEvent(index,i.commentsFlag)}>
|
||||||
|
<i className="iconfont icon-pinglun color-grey-9 font-16 mr5"></i><span>{i.replies_count}</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
{
|
||||||
|
i.commentsFlag &&
|
||||||
|
<CommentsSend unique={`sub_${i.id}`} id={i.id} image_url={current_user && current_user.image_url} refresh={this.refreshReply}/>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
|
||||||
|
// 判断是否是管理员或者版主
|
||||||
|
const manage = current_user && (current_user.admin || current_user.banned_permission);
|
||||||
|
const itemMap = repliesData && repliesData.map((i,index)=>{
|
||||||
|
let _content = this.parseCommentContent(i.content);
|
||||||
|
return(
|
||||||
|
<div className="pre_stage" key={index}>
|
||||||
|
<div className="commentsItem_infos">
|
||||||
|
<ItemImg image_url={i.image_url} ></ItemImg>
|
||||||
|
<div className="flex1">
|
||||||
|
<Item username={i.username} time={i.time} content={_content} id={i.id} admin={manage} deleteReplyEvent={this.deleteReplyEvent}></Item>
|
||||||
|
{
|
||||||
|
i.children && i.children.length > 0 &&
|
||||||
|
<div className="sub_stage">
|
||||||
|
{renderChild(i.children)}
|
||||||
|
{/* { i.children.length > 1 && renderNum(i.children)} */}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{replyInfoWrap(i,index)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return( itemMap )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommentsIndex;
|
|
@ -0,0 +1,128 @@
|
||||||
|
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, Modal, message } from 'antd';
|
||||||
|
|
||||||
|
|
||||||
|
import 'antd/lib/upload/style/index.css'
|
||||||
|
import 'antd/lib/modal/style/index.css'
|
||||||
|
import 'antd/lib/style/index.css'
|
||||||
|
import 'antd/lib/message/style/index.css'
|
||||||
|
import './MemoNew.css'
|
||||||
|
// -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
let uploadValidateFailed = false;
|
||||||
|
function beforeUpload(file) {
|
||||||
|
const isJPG = file.type === 'image/jpeg';
|
||||||
|
const isPNG = file.type === 'image/png';
|
||||||
|
const isGIF = file.type === 'image/gif';
|
||||||
|
if (!isJPG && !isPNG && !isGIF) {
|
||||||
|
message.error('只能上传图片文件!(jpg、png或gif)');
|
||||||
|
uploadValidateFailed = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isLt2M = (file.size / 1024 / 1024) < 2;
|
||||||
|
if (!isLt2M) {
|
||||||
|
uploadValidateFailed = true;
|
||||||
|
message.error('图片大小必须小于 2MB!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uploadValidateFailed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageUpload extends Component {
|
||||||
|
state = {
|
||||||
|
previewVisible: false,
|
||||||
|
previewImage: '',
|
||||||
|
fileList: [
|
||||||
|
// {
|
||||||
|
// uid: -1,
|
||||||
|
// name: 'xxx.png',
|
||||||
|
// status: 'done',
|
||||||
|
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCancel = () => {
|
||||||
|
|
||||||
|
this.setState({ previewVisible: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreview = (file) => {
|
||||||
|
this.setState({
|
||||||
|
previewImage: file.url || file.thumbUrl,
|
||||||
|
previewVisible: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRemove = () => {
|
||||||
|
const { onImageUploadRemove } = this.props;
|
||||||
|
onImageUploadRemove()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(newProps, oldProps) {
|
||||||
|
// 初始化值
|
||||||
|
if (newProps.fileList && this.props.fileList.length != newProps.fileList.length) {
|
||||||
|
this.setState({
|
||||||
|
fileList: newProps.fileList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange = ({ fileList }) => {
|
||||||
|
|
||||||
|
const { onImageUploadDone } = this.props;
|
||||||
|
if (fileList.length && fileList[0].status === 'done') {
|
||||||
|
onImageUploadDone(fileList[0])
|
||||||
|
}
|
||||||
|
if (!uploadValidateFailed) {
|
||||||
|
this.setState({ fileList })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { previewVisible, previewImage, fileList } = this.state;
|
||||||
|
const { currentMemoId } = this.props;
|
||||||
|
const uploadButton = (
|
||||||
|
<div className="antuploadName">
|
||||||
|
<div className="antIconName"><Icon type="plus" /></div>
|
||||||
|
<div className="ant-upload-text">点击上传封面</div>
|
||||||
|
<div className="ant-upload-text">只支持JPG、PNG、JPEG大小不超过2M</div>
|
||||||
|
<div className="ant-upload-text">建议尺寸4:3</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="clearfix">
|
||||||
|
<Upload
|
||||||
|
action={"http://123.59.135.93/upload_memo_heads"}
|
||||||
|
listType="picture-card"
|
||||||
|
fileList={fileList}
|
||||||
|
onPreview={this.handlePreview}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
onRemove={this.handleRemove}
|
||||||
|
beforeUpload={beforeUpload}
|
||||||
|
data={{
|
||||||
|
container_type: 'Memo',
|
||||||
|
container_id: currentMemoId ? currentMemoId : ''
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fileList.length >= 1 ? null : uploadButton}
|
||||||
|
</Upload>
|
||||||
|
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
|
||||||
|
<img alt="example" style={{ width: '100%' }} src={previewImage} />
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageUpload;
|
|
@ -0,0 +1,248 @@
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Pagination , Spin } from 'antd';
|
||||||
|
|
||||||
|
import '../exchange.css'
|
||||||
|
import './post.css'
|
||||||
|
|
||||||
|
import Nav from '../NavComponent/Index'
|
||||||
|
import Parse from './Parse';
|
||||||
|
import ExchangeItem from '../ExchangeItem'
|
||||||
|
|
||||||
|
import CommentsIndex from './CommentsIndex';
|
||||||
|
import CommentsSend from '../Comments/CommentsSend';
|
||||||
|
import RenderHtml from "../../components/render-html";
|
||||||
|
|
||||||
|
import FileList from '../../common/FileList';
|
||||||
|
|
||||||
|
import update from 'immutability-helper'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
class Index extends Component{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
// 当前用户是否点赞
|
||||||
|
judge:true,
|
||||||
|
parseNum:0,
|
||||||
|
// 帖子用户信息
|
||||||
|
author_info:undefined,
|
||||||
|
// 当前用户信息
|
||||||
|
current_user:undefined,
|
||||||
|
// 面包屑
|
||||||
|
bread_crumb:undefined,
|
||||||
|
// 当前帖子详情
|
||||||
|
memos:undefined,
|
||||||
|
// 帖子评论
|
||||||
|
repliesData:undefined,
|
||||||
|
// 帖子评论分页相关
|
||||||
|
replyPage:1,
|
||||||
|
replyPageSize:10,
|
||||||
|
replyCount:0,
|
||||||
|
is_Spin:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
this.getInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfo=()=>{
|
||||||
|
this.InitDetailInfo();
|
||||||
|
this.getReply(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
InitDetailInfo = ()=>{
|
||||||
|
let postid = this.props.match.params.postid;
|
||||||
|
const url = `/memos/${postid}.json`;
|
||||||
|
axios.get(url).then((result)=>{
|
||||||
|
if(result.data && result.data.status === -1){
|
||||||
|
this.props.history.push("/forums");
|
||||||
|
}
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
judge:result.data.memo.user_praise,
|
||||||
|
parseNum:result.data.memo && result.data.memo.praises_count,
|
||||||
|
author_info:result.data.author_info,
|
||||||
|
current_user:result.data.current_user,
|
||||||
|
bread_crumb:result.data.bread_crumb,
|
||||||
|
memos:result.data.memo,
|
||||||
|
is_Spin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getReply = (replyPage) =>{
|
||||||
|
let postid = this.props.match.params.postid;
|
||||||
|
const url = `/memos/${postid}/more_reply.json`;
|
||||||
|
axios.get(url,{params:{
|
||||||
|
page:replyPage
|
||||||
|
}}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
repliesData:result.data.memo_replies,
|
||||||
|
replyCount:result.data.memos_count,
|
||||||
|
is_Spin:false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点赞
|
||||||
|
thumbForum = () =>{
|
||||||
|
let postid = this.props.match.params.postid;
|
||||||
|
const url = `/discusses/${postid}/plus.json`
|
||||||
|
axios.post(url,{
|
||||||
|
container_type:'Memo',
|
||||||
|
type:1
|
||||||
|
}).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.InitDetailInfo();
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换评论分页
|
||||||
|
changeReplyEvent=(pageNumber)=>{
|
||||||
|
this.setState({
|
||||||
|
replyPage:pageNumber
|
||||||
|
})
|
||||||
|
this.getReply(pageNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击显示评论输入框
|
||||||
|
commentReplyEvent=(index,flag)=>{
|
||||||
|
console.log("2",flag);
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
(prevState) => ({
|
||||||
|
repliesData : update(prevState.repliesData, {[index]: { commentsFlag: {$set: flag} }}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除评论
|
||||||
|
deleteReplyEvent=(id)=>{
|
||||||
|
const url = `/memos/${id}/destroy.json`
|
||||||
|
axios.post(url).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
let{ replyPage } = this.state;
|
||||||
|
this.getReply(replyPage);
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let {
|
||||||
|
judge , parseNum , author_info , current_user , bread_crumb , memos , repliesData ,
|
||||||
|
replyPage,
|
||||||
|
replyPageSize,
|
||||||
|
replyCount,
|
||||||
|
is_Spin
|
||||||
|
} = this.state;
|
||||||
|
let postid = this.props.match.params.postid;
|
||||||
|
|
||||||
|
const NavMap=[
|
||||||
|
{
|
||||||
|
url:`/users/${current_user && current_user.login}`,
|
||||||
|
name:current_user && current_user.username
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url:"/forums",
|
||||||
|
name:bread_crumb && bread_crumb.forum && bread_crumb.forum.title
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url:`/forums/theme/${bread_crumb && bread_crumb.forum_tag && bread_crumb.forum_tag.id}`,
|
||||||
|
name:bread_crumb && bread_crumb.forum_tag && bread_crumb.forum_tag.title
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name:"帖子详情"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
//插入字段从author_info表内选取
|
||||||
|
const memos_list = [{
|
||||||
|
...memos,
|
||||||
|
image_url:author_info && author_info.image_url,
|
||||||
|
username:author_info && author_info.username,
|
||||||
|
user_login:author_info && author_info.login
|
||||||
|
}];
|
||||||
|
|
||||||
|
// 评论列表
|
||||||
|
const memo_replies = repliesData;
|
||||||
|
const repliesList = (
|
||||||
|
memo_replies && memo_replies.length > 0 &&
|
||||||
|
<div className="commentsForm">
|
||||||
|
<p className="replyTitle">
|
||||||
|
<span className="font-24 color-grey-3 mr20">全部回复</span>
|
||||||
|
<span className="color-grey9 font-20">{replyCount}</span>
|
||||||
|
</p>
|
||||||
|
<CommentsIndex {...this.props} {...this.state} refresh={this.getReply} page={replyPage} showCommentEvent={this.commentReplyEvent}></CommentsIndex>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
// 附件显示
|
||||||
|
const fileListCom = ( memos && memos.attachment_url && <FileList list = { memos.attachment_url } className="fileTeam"></FileList> )
|
||||||
|
|
||||||
|
return(
|
||||||
|
<Spin spinning={is_Spin}>
|
||||||
|
<div className="educontent">
|
||||||
|
<Nav className="mt20 mb20" NavMap = {NavMap}></Nav>
|
||||||
|
|
||||||
|
<div className="educontent-min bc-white mb30">
|
||||||
|
|
||||||
|
{/* 调用和首页共用的列表item,传了enshine就代表底部右侧操作按钮只有收藏或者取消收藏,否则就是置顶、推荐等 */}
|
||||||
|
<ExchangeItem
|
||||||
|
{...this.props}
|
||||||
|
{...this.state}
|
||||||
|
memos={memos_list}
|
||||||
|
current_user = { current_user }
|
||||||
|
detail={true}
|
||||||
|
refresh={this.getInfo}
|
||||||
|
></ExchangeItem>
|
||||||
|
|
||||||
|
<div className="postContent">
|
||||||
|
{/* 帖子详情 */}
|
||||||
|
<RenderHtml className="postDetail" value={memos && memos.content} />
|
||||||
|
{/* 文件列表 */}
|
||||||
|
{ fileListCom }
|
||||||
|
|
||||||
|
{/* 点赞 */}
|
||||||
|
<div className="edu-txt-center pb40">
|
||||||
|
<Parse judge={!judge} current_user = { current_user } num = {parseNum} clickEvent = { this.thumbForum }></Parse>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 发送评论部分 */}
|
||||||
|
{
|
||||||
|
current_user && (current_user.is_banned === false || current_user.admin) &&
|
||||||
|
<CommentsSend unique={`main_Send`} id={postid} refresh={()=>this.getReply(replyPage)}/>
|
||||||
|
}
|
||||||
|
{/* 评论列表 */}
|
||||||
|
{ repliesList }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
replyCount && replyCount > replyPageSize ?
|
||||||
|
<div className="edu-txt-center mb50">
|
||||||
|
<Pagination showQuickJumper current={replyPage} pageSize={replyPageSize} total={replyCount} onChange={this.changeReplyEvent}></Pagination>
|
||||||
|
</div>:""
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,128 @@
|
||||||
|
.compilegoback{
|
||||||
|
float:right;
|
||||||
|
color:#676767 !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compilegodell{
|
||||||
|
float:right;
|
||||||
|
margin-right:20px;
|
||||||
|
color:#dadada;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Releasethetitle{
|
||||||
|
height: 48px !important;
|
||||||
|
padding-left: 20px !important;
|
||||||
|
background:#f4f4f4 !important;
|
||||||
|
}
|
||||||
|
.Releasethetitle:focus{
|
||||||
|
background:#FFF !important;
|
||||||
|
}
|
||||||
|
.Releasethetitle::-webkit-input-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.Releasethetitle:-moz-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.Releasethetitle::-moz-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.Releasethetitle:-ms-input-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.ecSelectbox{
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-selection__placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.antuploadName{
|
||||||
|
width: 280px;
|
||||||
|
height: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.antuploadName div:nth-child(2){
|
||||||
|
color:#999898;
|
||||||
|
font-size:16px;
|
||||||
|
}
|
||||||
|
.antuploadName div:nth-child(3){
|
||||||
|
margin-top: 10px;
|
||||||
|
color:#d2d2d2;
|
||||||
|
}
|
||||||
|
.antuploadName div:nth-child(4){
|
||||||
|
margin-top: 10px;
|
||||||
|
color:#d2d2d2;
|
||||||
|
}
|
||||||
|
.antIconName{
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background: #21B351;
|
||||||
|
border: 1px solid #21B351;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #FFF;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 25px;
|
||||||
|
margin: 40px auto 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.antIconName i{
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
left: 3.8px;
|
||||||
|
}
|
||||||
|
.ReleasTopic{
|
||||||
|
color:#333;
|
||||||
|
font-size:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-calendar-picker-input::-webkit-input-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.ant-calendar-picker-input:-moz-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.ant-calendar-picker-input::-moz-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.ant-calendar-picker-input:-ms-input-placeholder{
|
||||||
|
color:#999999;
|
||||||
|
}
|
||||||
|
.ml57{
|
||||||
|
margin-left:57px;
|
||||||
|
}
|
||||||
|
.newdefalutSubmitbtn{
|
||||||
|
width: 120px !important;
|
||||||
|
height: 38px !important;
|
||||||
|
line-height: 38px !important;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
.newdefalutCancelbtn{
|
||||||
|
width: 120px !important;
|
||||||
|
height: 38px !important;
|
||||||
|
line-height: 38px !important;
|
||||||
|
margin-top: -5px;
|
||||||
|
border: 1px solid #CDCDCD;
|
||||||
|
|
||||||
|
}
|
||||||
|
.ecSelect{
|
||||||
|
background: #f4f4f4 !important;
|
||||||
|
padding-left: 4px !important;
|
||||||
|
height: 48px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 526px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.newLeftgrey{
|
||||||
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
.uploadBtnclick{
|
||||||
|
margin-left: 34px;
|
||||||
|
}
|
||||||
|
.ant-select-selection--multiple{
|
||||||
|
height:100%;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
|
@ -0,0 +1,811 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import { Select } from 'antd';
|
||||||
|
|
||||||
|
import 'antd/lib/select/style/index.css'
|
||||||
|
|
||||||
|
import 'antd/lib/date-picker/style/index.css'
|
||||||
|
|
||||||
|
import zhCN from 'antd/lib/date-picker/locale/zh_CN';
|
||||||
|
|
||||||
|
import './MemoNew.css'
|
||||||
|
import './post.css'
|
||||||
|
import '../exchange.css'
|
||||||
|
|
||||||
|
|
||||||
|
import ImageUpload from './ImageUpload'
|
||||||
|
|
||||||
|
const Option = Select.Option;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MemoNew extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
memoSubject: '',
|
||||||
|
memoContent: '',
|
||||||
|
memoType: undefined,
|
||||||
|
memoTime: undefined,
|
||||||
|
memoRepertoire: '',
|
||||||
|
memoLanguage: [],
|
||||||
|
fileList: [],
|
||||||
|
memoSubjectLength:undefined,
|
||||||
|
repertoires: [],
|
||||||
|
currentSelectRepertoiresIndex: -1,
|
||||||
|
repertoiresTagMap: {},
|
||||||
|
// 主题板块
|
||||||
|
smallOption:undefined,
|
||||||
|
bigPlateId:undefined,
|
||||||
|
smallPlateId:undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommit() {
|
||||||
|
const { memoSubject , memoLanguage, currentMemoId, attachmentData , bigPlateId , smallPlateId} = this.state;
|
||||||
|
const { showSnackbar } = this.props;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
memoSubjectLength : memoSubject.length
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!memoSubject) {
|
||||||
|
showSnackbar('请先输入标题')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let mdVal;
|
||||||
|
try {
|
||||||
|
mdVal = this.taskpass_editormd.getValue()
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
showSnackbar('编辑器还未加载完毕,请稍后')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mdVal) {
|
||||||
|
showSnackbar('请输入话题内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!bigPlateId) {
|
||||||
|
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 = {};
|
||||||
|
let lastIndex = 0;
|
||||||
|
$('#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
|
||||||
|
}
|
||||||
|
lastIndex = index;
|
||||||
|
})
|
||||||
|
|
||||||
|
if (attachmentData) {
|
||||||
|
const { response } = attachmentData
|
||||||
|
lastIndex++;
|
||||||
|
attachmentsMap[lastIndex] = {
|
||||||
|
filename: attachmentData.name,
|
||||||
|
token: '',
|
||||||
|
attachment_id: response.attachment_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMemoId) {
|
||||||
|
this.updateMemo(attachmentsMap)
|
||||||
|
} else {
|
||||||
|
this.newMemo(attachmentsMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCancel() {
|
||||||
|
const { currentMemoId } = this.state;
|
||||||
|
if (currentMemoId) { // 编辑
|
||||||
|
this.props.history.push(`/forums/${currentMemoId}`)
|
||||||
|
} else { // 新建
|
||||||
|
this.props.history.push(`/forums`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onOkTime(value, dateString){
|
||||||
|
this.setState({
|
||||||
|
memoTime:dateString
|
||||||
|
})
|
||||||
|
}
|
||||||
|
updateMemo(attachmentsMap) {
|
||||||
|
const { memoSubject , currentMemoId, content, attachmentData , smallPlateId , bigPlateId} = this.state;
|
||||||
|
|
||||||
|
const mdVal = this.taskpass_editormd.getValue()
|
||||||
|
console.log('isContentEdit: ', mdVal === content);
|
||||||
|
const newMemoUrl = `/memos/${currentMemoId}/update`
|
||||||
|
const params = {
|
||||||
|
content_changed: this.contentChanged,
|
||||||
|
memo: {
|
||||||
|
subject: memoSubject ,
|
||||||
|
content: mdVal,
|
||||||
|
},
|
||||||
|
forum_id:bigPlateId,
|
||||||
|
attachments: attachmentsMap,
|
||||||
|
children_forum_id:smallPlateId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachmentData) {
|
||||||
|
const attachment_id = attachmentData.response.attachment_id
|
||||||
|
if (attachment_id) {
|
||||||
|
params.attachment_id = attachment_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axios.post(newMemoUrl, params)
|
||||||
|
.then((response) => {
|
||||||
|
const { status, message } = response.data;
|
||||||
|
if (status === 0) {
|
||||||
|
window.$("html,body").animate({"scrollTop":0})
|
||||||
|
this.props.history.push(`/forums/${currentMemoId}`)
|
||||||
|
} else {
|
||||||
|
if (message.indexOf("Couldn't find Attachment with") !== -1) {
|
||||||
|
this.props.showSnackbar('附件不存在或正在被删除中,请稍后再试。。。')
|
||||||
|
} else {
|
||||||
|
this.props.showSnackbar(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
newMemo(attachmentsMap) {
|
||||||
|
const { memoSubject , attachmentData , smallPlateId , bigPlateId } = this.state;
|
||||||
|
const mdVal = this.taskpass_editormd.getValue()
|
||||||
|
const newMemoUrl = `/memos/create`
|
||||||
|
const params = {
|
||||||
|
memo: {
|
||||||
|
subject: memoSubject ,
|
||||||
|
content: mdVal
|
||||||
|
},
|
||||||
|
forum_id:bigPlateId,
|
||||||
|
attachments: attachmentsMap,
|
||||||
|
children_forum_id:smallPlateId,
|
||||||
|
}
|
||||||
|
if (attachmentData) {
|
||||||
|
const attachment_id = attachmentData.response.attachment_id
|
||||||
|
if (attachment_id) {
|
||||||
|
params.attachment_id = attachment_id;
|
||||||
|
// params.filename = attachmentData.name // 服务端处理 服务端使用默认的origin_filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
axios.post(newMemoUrl, params).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 {
|
||||||
|
if (message.indexOf("Couldn't find Attachment with") !== -1) {
|
||||||
|
this.props.showSnackbar('附件不存在或正在被删除中,请稍后再试。。。')
|
||||||
|
} else {
|
||||||
|
this.props.showNotification(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMemoDelete(memo) {
|
||||||
|
const deleteUrl = `/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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
$('body>#root').off('onMemoDelete')
|
||||||
|
}
|
||||||
|
componentDidMount(){
|
||||||
|
$('body>#root').on('onMemoDelete', (event) => {
|
||||||
|
// const val = $('body>#root').data('onMemoDelete')
|
||||||
|
const val = window.onMemoDelete ;
|
||||||
|
this.onMemoDelete( JSON.parse(decodeURIComponent(val)) )
|
||||||
|
})
|
||||||
|
const newMemoUrl = `/memos/new`
|
||||||
|
axios.get(newMemoUrl,{
|
||||||
|
// withCredentials: true,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if ( data.current_user ) {
|
||||||
|
this.setState({
|
||||||
|
memo_tag: data.memo_type,
|
||||||
|
memoTime:""
|
||||||
|
})
|
||||||
|
|
||||||
|
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 = `/memos/${match.params.memoId}/edit`;
|
||||||
|
|
||||||
|
axios.get(memoUrl).then((response) => {
|
||||||
|
const current_user = response.data.current_user;
|
||||||
|
if (current_user) {
|
||||||
|
|
||||||
|
const { content, forum_section, id, subject , attachments_url, memo_image_info,
|
||||||
|
memo_type, published_at , children_forum_section} = response.data;
|
||||||
|
let date;
|
||||||
|
let date_value;
|
||||||
|
this.initMD(content, memoId);
|
||||||
|
if(published_at){
|
||||||
|
date = new Date(published_at);
|
||||||
|
date_value=date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
|
||||||
|
}
|
||||||
|
const newState = {
|
||||||
|
currentMemoId: id,
|
||||||
|
attachments_url,
|
||||||
|
memoSubject: subject,
|
||||||
|
memo_tag: memo_type,
|
||||||
|
memoLanguage: forum_section.forum_id,
|
||||||
|
memoTime:date_value,
|
||||||
|
content,
|
||||||
|
bigPlateId: forum_section.forum_id,
|
||||||
|
smallPlateId: children_forum_section.children_forum_id
|
||||||
|
}
|
||||||
|
// 编辑,初始化小板块的下拉选项(如果大板块有值)
|
||||||
|
this.getChildPlate(memo_type,forum_section && forum_section.forum_id);
|
||||||
|
|
||||||
|
if (memo_image_info && memo_image_info.id) {
|
||||||
|
newState.fileList = [{
|
||||||
|
uid: memo_image_info.id,
|
||||||
|
name: memo_image_info.filename,
|
||||||
|
status: 'done',
|
||||||
|
url: memo_image_info.url,
|
||||||
|
}]
|
||||||
|
|
||||||
|
newState.attachmentData = { // 初始化
|
||||||
|
name: memo_image_info.filename,
|
||||||
|
response: {
|
||||||
|
attachment_id: memo_image_info.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({...newState})
|
||||||
|
|
||||||
|
|
||||||
|
// 加载完后滚动条滚动
|
||||||
|
window.$("html,body").animate({"scrollTop":0})
|
||||||
|
|
||||||
|
// this.props.initForumState({
|
||||||
|
// current_user,
|
||||||
|
// tag_list
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.initMD();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initMD(initValue, memoId) {
|
||||||
|
this.contentChanged = false;
|
||||||
|
const placeholder = "";
|
||||||
|
// amp;
|
||||||
|
// 编辑时要传memoId
|
||||||
|
const imageUrl = `/upload_with_markdown?container_id=${memoId?memoId:''}&container_type=Memo`;
|
||||||
|
// 创建editorMd
|
||||||
|
|
||||||
|
setTimeout(()=>{
|
||||||
|
const taskpass_editormd = create_editorMD("memoMD", '100%', 400, placeholder, imageUrl, () => {
|
||||||
|
setTimeout(()=>{
|
||||||
|
taskpass_editormd.resize()
|
||||||
|
taskpass_editormd.cm && taskpass_editormd.cm.refresh()
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
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;
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (value && value.length > 3) {
|
||||||
|
this.props.showSnackbar(`最多选择3个技术标签`)
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
memoLanguage: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onTypeChange(value) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
memoType: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMemoNameChange(e) {
|
||||||
|
this.setState({
|
||||||
|
memoSubject: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
renderMemoType() {
|
||||||
|
const { memo_type } = this.state;
|
||||||
|
if (!memo_type || memo_type.length === 0) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const result = []
|
||||||
|
// memo_type.map((item, index) => <Option value={item.id} key={index} >{item.name}</Option> )
|
||||||
|
memo_type.forEach((item, index) => {
|
||||||
|
result.push(<Option value={item.id} key={index} >{item.name}</Option>)
|
||||||
|
})
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
renderTag() {
|
||||||
|
let { memo_tag } = this.state;
|
||||||
|
if (!memo_tag || memo_tag.length === 0) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
memo_tag.forEach((item, index) => {
|
||||||
|
result.push(<Option value={item.id+''} key={index} >{item.name}</Option>)
|
||||||
|
})
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
renderAttachment() {
|
||||||
|
const { attachments_url } = this.state;
|
||||||
|
const attachments = []
|
||||||
|
attachments_url.forEach((item, index) => {
|
||||||
|
const ar = item.url.split('/')
|
||||||
|
|
||||||
|
attachments.push(
|
||||||
|
<React.Fragment>
|
||||||
|
<span id={`attachments_10${index}`} className="attachment">
|
||||||
|
<i className="fa fa-folder mr5 color-light-grey newLeftgrey" aria-hidden="true"></i>
|
||||||
|
<input type="text" className="upload_filename readonly hidden" name="attachments[2][filename]" readonly="readonly"
|
||||||
|
style={{border:'none', width:'220px',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={item.id}></input>
|
||||||
|
</span>
|
||||||
|
<div className="cl"></div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
_findById(id, arg_items) {
|
||||||
|
if (!arg_items) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const items = arg_items;
|
||||||
|
for(let i = 0; i < items.length; i++) {
|
||||||
|
if (id === items[i].id) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 是否是技术动态吧
|
||||||
|
_isTechShare() {
|
||||||
|
const { memoType, memo_type } = this.state
|
||||||
|
const index = this._findById(memoType, memo_type)
|
||||||
|
return !!index && memo_type[index] && memo_type[index].name.indexOf('技术问答') !== -1
|
||||||
|
}
|
||||||
|
onImageUploadDone = (data) => {
|
||||||
|
this.setState({
|
||||||
|
attachmentData: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onImageUploadRemove = () => {
|
||||||
|
const { attachment_id } = this.state.attachmentData.response;
|
||||||
|
const deleteUrl = `/attachments/${attachment_id}.js?attachment_id=1`;
|
||||||
|
// 获取memo list
|
||||||
|
axios.delete(deleteUrl, {
|
||||||
|
withCredentials: true,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const data = response.data
|
||||||
|
if (data) {
|
||||||
|
this.setState({
|
||||||
|
attachmentData: undefined
|
||||||
|
})
|
||||||
|
this.props.showSnackbar('删除成功');
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换大板块
|
||||||
|
changeLargeOption=(value)=>{
|
||||||
|
const { memo_tag } = this.state;
|
||||||
|
this.getChildPlate(memo_tag,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildPlate =(memo_tag,value)=>{
|
||||||
|
let listFilter = memo_tag && memo_tag.length > 0 && memo_tag.filter((item=> item.id === value))[0];
|
||||||
|
let list = listFilter && listFilter.children_tags;
|
||||||
|
this.setState({
|
||||||
|
smallOption:list && list.map(item=>{
|
||||||
|
return(
|
||||||
|
<Option value={item.id}>{item.title}</Option>
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
bigPlateId:value,
|
||||||
|
smallPlateId: list && list.length > 0 ? (list[0].id || undefined) : undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换二级板块
|
||||||
|
changeSmallOption=(value)=>{
|
||||||
|
this.setState({
|
||||||
|
smallPlateId:value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { match } = this.props
|
||||||
|
const {
|
||||||
|
memoSubject ,
|
||||||
|
attachments_url,
|
||||||
|
currentMemoId,
|
||||||
|
fileList,
|
||||||
|
memoSubjectLength ,
|
||||||
|
memo_tag,
|
||||||
|
smallOption,
|
||||||
|
bigPlateId,
|
||||||
|
smallPlateId
|
||||||
|
} = this.state;
|
||||||
|
const memoId = match.params.memoId;
|
||||||
|
console.log("bigPlateId",bigPlateId);
|
||||||
|
return (
|
||||||
|
<div className="educontent-min postNewform">
|
||||||
|
<p className="font-22 color-grey3 mt30 mb10">{ memoId ? '编辑' : '新建'}</p>
|
||||||
|
|
||||||
|
<div className="edu-back-white mb10 clearfix" id="memoSubject" style={{position:'relative'}}>
|
||||||
|
<div className="padding30">
|
||||||
|
<div className="df">
|
||||||
|
<span className="mr20 new_label"><span className="color-orange">*</span>标题:</span>
|
||||||
|
<div className="flex1">
|
||||||
|
<input type="text" className="input-100-45" maxlength="50"
|
||||||
|
value={memoSubject} onChange={(val)=>this.onMemoNameChange(val)} placeholder="请输入发布标题,最大限制50字符">
|
||||||
|
</input>
|
||||||
|
</div>
|
||||||
|
<div style={{width: '57px'}}>
|
||||||
|
<span className="color-orange mt8 fl" style={memoSubjectLength==0?{display:'block'}:{display:'none'}}>
|
||||||
|
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* 发布时间 */}
|
||||||
|
{/* <div className="df ecSelectbox">
|
||||||
|
<span className="mr30 color-orange pt10"></span>
|
||||||
|
<div className="flex1 mr20">
|
||||||
|
<DatePicker
|
||||||
|
disabledDate={disabledDate}
|
||||||
|
showTime
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
placeholder="请选择发布时间"
|
||||||
|
locale={zhCN}
|
||||||
|
placeholder={"请选择时间"}
|
||||||
|
onChange={(value, dateString)=>this.onOkTime(value, dateString)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{width: '57px'}}>
|
||||||
|
<span className="color-orange mt8 fl none" >
|
||||||
|
<i className="fa fa-exclamation-circle mr3"></i>必填项
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
{/* <p className="color-grey-6 font-16 mb30">内容</p> */}
|
||||||
|
<div className="df mt20">
|
||||||
|
<span className="mr20 new_label"><span className="color-orange">*</span>内容:</span>
|
||||||
|
<div className="flex1">
|
||||||
|
<div className="break_word 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" style={{marginLeft:"70px"}}>
|
||||||
|
<span id={`attachments_fields`} className="attachments_fields newforgeattachment" 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>
|
||||||
|
|
||||||
|
{/* 请求status 422 X-CSRF-Token: eVo38laEF880o3cwZ/0F9kH01q4jMkriuVRemIBq06Y= */}
|
||||||
|
|
||||||
|
<div className="df uploadBtn uploadBtnclick mb20" >
|
||||||
|
<a className="fl" onClick={()=>window.$('#_file').click()}>
|
||||||
|
<span style={{color: '#21B351', fontSize: "14px", marginLeft: "62px"}}>上传附件</span>
|
||||||
|
<span style={{color: '#CDCDCD', fontSize: "14px"}}>(单个文件50M以内)</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/* 选择了技术问答才显示树标签 */}
|
||||||
|
{/* this._isTechShare() &&
|
||||||
|
<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={memoLanguage}
|
||||||
|
placeholder="请选择技术标签"
|
||||||
|
onChange={(e) => this.onTagChange(e)}
|
||||||
|
dropdownStyle={{'maxHeight': '500px', 'overflow': 'auto'}}
|
||||||
|
mode="multiple"
|
||||||
|
tokenSeparators={[';']} >
|
||||||
|
{this.renderTag()}
|
||||||
|
</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="df ecSelectbox mb20">
|
||||||
|
<span className="mr20 new_label"><span className="color-orange">*</span>主题板块:</span>
|
||||||
|
<div className="flex1">
|
||||||
|
{/* <Select className="ecSelect"
|
||||||
|
value={memoType}
|
||||||
|
placeholder="请选择技术标签"
|
||||||
|
onChange={(val)=>this.onTypeChange(val)}>
|
||||||
|
{ this.renderMemoType()}
|
||||||
|
</Select> */}
|
||||||
|
{/* <Select
|
||||||
|
mode="multiple"
|
||||||
|
className="ecSelect"
|
||||||
|
value={memoLanguage}
|
||||||
|
placeholder="请选择技术标签"
|
||||||
|
onChange={(e) => this.onTagChange(e)}
|
||||||
|
dropdownStyle={{'maxHeight': '500px', 'overflow': 'auto'}}
|
||||||
|
>
|
||||||
|
{this.renderTag()}
|
||||||
|
</Select> */}
|
||||||
|
<Select className="selectItem" value={bigPlateId} onChange={this.changeLargeOption}>
|
||||||
|
{
|
||||||
|
|
||||||
|
memo_tag && memo_tag.length > 0 && memo_tag.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<Option value={item.id} key={key}>{item.name}</Option>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
<Select className="selectItem" value={smallPlateId} onChange={this.changeSmallOption}>
|
||||||
|
{ smallOption }
|
||||||
|
</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 className="df">
|
||||||
|
<span className="mr20 new_label">上传封面:</span>
|
||||||
|
<div className="flex1 mr20">
|
||||||
|
<ImageUpload
|
||||||
|
fileList={fileList}
|
||||||
|
currentMemoId={currentMemoId}
|
||||||
|
onImageUploadRemove={this.onImageUploadRemove}
|
||||||
|
onImageUploadDone={this.onImageUploadDone}
|
||||||
|
></ImageUpload>
|
||||||
|
</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 mb50 edu-txt-center">
|
||||||
|
<span className="inline">
|
||||||
|
<a onClick={()=>{ this.onCancel() }} className="defalutCancelbtn mr20 fl newdefalutCancelbtn">返回</a>
|
||||||
|
<a className="defalutSubmitbtn fl newdefalutSubmitbtn" onClick={()=>{this.onCommit()}}>提交</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = [];
|
||||||
|
for (let i = 10; i < 36; i++) {
|
||||||
|
children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MemoNew;
|
|
@ -0,0 +1,277 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import {appendFileSizeToUploadFileAll ,appendFileSizeToUploadFile} from 'educoder';
|
||||||
|
import { Form , Input , Upload ,Button , Select , Icon , message } from "antd";
|
||||||
|
import './post.css'
|
||||||
|
import '../exchange.css'
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import MDEditor from '../../common/MDEditor';
|
||||||
|
|
||||||
|
const Option = Select.Option;
|
||||||
|
const uploadButton = (
|
||||||
|
<div>
|
||||||
|
<i className="iconfont icon-tianjia mb30"></i>
|
||||||
|
<div className="font-16 mb10">点击上传封面</div>
|
||||||
|
<p className="font-12">只支持JPG、PNG、JPEG大小不超过2M建议尺寸4:3</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
function getBase64(img, callback) {
|
||||||
|
debugger;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', () => callback(reader.result));
|
||||||
|
reader.readAsDataURL(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class New extends PureComponent{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.contentMdRef = React.createRef();
|
||||||
|
this.state={
|
||||||
|
// 封面图
|
||||||
|
imageUrl:undefined,
|
||||||
|
// 大板块下拉选项
|
||||||
|
memo_type:undefined,
|
||||||
|
// 小板块
|
||||||
|
smallOption:undefined,
|
||||||
|
// 附件列表
|
||||||
|
contentFileList:undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () =>{
|
||||||
|
this.getInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取信息
|
||||||
|
getInfo = () =>{
|
||||||
|
let url = '/memos/new';
|
||||||
|
axios.get((url)).then((result)=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
memo_type:result.data.memo_type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch((error)=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
changeUploadImage =(info)=>{
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
// Get this url from response in real world.
|
||||||
|
getBase64(info.file.originFileObj, imageUrl =>
|
||||||
|
this.setState({
|
||||||
|
imageUrl
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换大板块
|
||||||
|
changeLargeOption=(value)=>{
|
||||||
|
// console.log(value);
|
||||||
|
const { memo_type } = this.state;
|
||||||
|
let list = memo_type.filter((item=> item.id === value))[0].children_tags;
|
||||||
|
// console.log(list);
|
||||||
|
this.setState({
|
||||||
|
smallOption:list && list.map(item=>{
|
||||||
|
return(
|
||||||
|
<Option value={item.id}>{item.title}</Option>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交
|
||||||
|
handleSubmit=()=>{
|
||||||
|
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||||
|
console.log(values);
|
||||||
|
if(!err){
|
||||||
|
let url=`/memos.json`;
|
||||||
|
axios.post((url),{
|
||||||
|
forum_id:values.bigPlate,
|
||||||
|
children_forum_id:values.smallPlate,
|
||||||
|
memo:{
|
||||||
|
subject:values.title,
|
||||||
|
content:values.description
|
||||||
|
}
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.props.showNotification(result.data.message);
|
||||||
|
this.props.history.push(`/forums/${result.data && result.data.memo_id}`);
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传附件
|
||||||
|
handleContentUploadChange = (info) => {
|
||||||
|
console.log("changeFIle==================>",info);
|
||||||
|
if (info.file.status === 'done' || info.file.status === 'uploading' || info.file.status === 'removed') {
|
||||||
|
let contentFileList = info.fileList;
|
||||||
|
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)});
|
||||||
|
let list = appendFileSizeToUploadFileAll(contentFileList);
|
||||||
|
let arr = list.map(item=>{
|
||||||
|
return ( item.response && item.response.id )
|
||||||
|
})
|
||||||
|
this.setState({
|
||||||
|
filesID:arr,
|
||||||
|
checkFile:arr.length > 0 ? false : true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 上传附件-删除确认框
|
||||||
|
onAttachmentRemove = (file, stateName) => {
|
||||||
|
if(!file.percent || file.percent == 100){
|
||||||
|
this.props.confirm({
|
||||||
|
content: '是否确认删除?',
|
||||||
|
onOk: () => {
|
||||||
|
this.deleteAttachment(file, stateName)
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('Cancel');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAttachment=(file,list)=>{
|
||||||
|
console.log("delete===============>",file)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { getFieldDecorator } = this.props.form;
|
||||||
|
|
||||||
|
/** 上传封面图 */
|
||||||
|
let { imageUrl , memo_type , smallOption , contentFileList } = this.state;
|
||||||
|
|
||||||
|
const largeOption = memo_type && memo_type.length > 0 && memo_type.map(item=>{
|
||||||
|
return(
|
||||||
|
<Option value={item.id}>{item.name}</Option>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
let uploadImg = ( imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton )
|
||||||
|
|
||||||
|
// 上传封面图
|
||||||
|
const uploadCover = {
|
||||||
|
listType:"picture-card",
|
||||||
|
className:"avatar-uploader",
|
||||||
|
showUploadList:false,
|
||||||
|
action:`/uploads.js`,
|
||||||
|
data: { attachment_id: 1 },
|
||||||
|
onChange:this.changeUploadImage,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**上传附件 */
|
||||||
|
const uploadProps = {
|
||||||
|
width: 600,
|
||||||
|
fileList: contentFileList,
|
||||||
|
multiple: true,
|
||||||
|
action: 'http://127.0.0.1:3000/uploads.js',
|
||||||
|
onChange: this.handleContentUploadChange,
|
||||||
|
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
|
||||||
|
beforeUpload: (file) => {
|
||||||
|
console.log('beforeUpload', file.name);
|
||||||
|
const isLt150M = file.size / 1024 / 1024 < 150;
|
||||||
|
if (!isLt150M) {
|
||||||
|
this.props.showNotification('文件大小必须小于150MB!');
|
||||||
|
}
|
||||||
|
return isLt150M;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return(
|
||||||
|
<div className="educontent-min">
|
||||||
|
<p className="font-22 color-grey3 mt30 mb10">新建</p>
|
||||||
|
<div className="bc-white padding30">
|
||||||
|
<Form className="postNewform">
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label="标题"
|
||||||
|
>
|
||||||
|
{getFieldDecorator('title', {
|
||||||
|
rules: [{
|
||||||
|
required: true, message: '请输入帖子标题',
|
||||||
|
},{
|
||||||
|
max: 5000 , message:'最大限制60个字符'
|
||||||
|
}],
|
||||||
|
})(
|
||||||
|
<Input placeholder="请输入标题,最大限制60个字符" maxLength="60" style={{height:"40px"}}/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="内容" className="editorFromItem">
|
||||||
|
{getFieldDecorator('description', {
|
||||||
|
rules: [{
|
||||||
|
required: true, message: '请输入帖子内容',
|
||||||
|
},{
|
||||||
|
max: 5000 , message:'最大限制5000个字符'
|
||||||
|
}],
|
||||||
|
})(
|
||||||
|
<MDEditor ref={this.contentMdRef} placeholder="请输入内容,最大限制5000字符" mdID={'courseContentMD'} refreshTimeout={1000}
|
||||||
|
initValue={""} className="courseMessageMD" ></MDEditor>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Upload {...uploadProps} className="upload_1 newPostUpload">
|
||||||
|
<Button className="uploadBtn">
|
||||||
|
<i className="iconfont icon-fabu font-22 color-blue fl mr10"></i> 上传附件
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
<div className="df mt10">
|
||||||
|
<Form.Item label="主题板块">
|
||||||
|
{
|
||||||
|
getFieldDecorator('bigPlate',{
|
||||||
|
rules:[{
|
||||||
|
required:true,message:'请选择大板块'
|
||||||
|
}]
|
||||||
|
})(
|
||||||
|
<Select className="selectItem" onChange={this.changeLargeOption}>
|
||||||
|
{ largeOption }
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="">
|
||||||
|
{
|
||||||
|
getFieldDecorator('smallPlate',{
|
||||||
|
rules:[{
|
||||||
|
required:true,message:'请选择小板块'
|
||||||
|
}]
|
||||||
|
})(
|
||||||
|
<Select className="selectItem">
|
||||||
|
{ smallOption }
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
<div className="df uploadImageBox">
|
||||||
|
<label className="new_label">上传封面:</label>
|
||||||
|
<Upload {...uploadCover}>
|
||||||
|
{uploadImg}
|
||||||
|
</Upload>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
<p className="clearfix mt20 mb50 edu-txt-center">
|
||||||
|
<span className="inline">
|
||||||
|
<a className="defalutCancelbtn fl mr20">取消</a>
|
||||||
|
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WrappedNewPostForm = Form.create({ name: 'New' })(New);
|
||||||
|
export default WrappedNewPostForm;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
class Parse extends PureComponent{
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let { judge , num , clickEvent ,current_user} = this.props;
|
||||||
|
|
||||||
|
const info = (
|
||||||
|
current_user? (judge ?
|
||||||
|
<a className={`forumParse`} onClick={clickEvent}>
|
||||||
|
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
|
||||||
|
<span>{num}</span>
|
||||||
|
</a>
|
||||||
|
:
|
||||||
|
<span className={`forumParse parsed`} >
|
||||||
|
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
|
||||||
|
<span>{num}</span>
|
||||||
|
</span>
|
||||||
|
):
|
||||||
|
<a className={`forumParse`} href="/login">
|
||||||
|
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
|
||||||
|
<span>{num}</span>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{ info }
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Parse;
|
|
@ -0,0 +1,157 @@
|
||||||
|
.postDetail{
|
||||||
|
padding:20px 0px;
|
||||||
|
}
|
||||||
|
.postContent{
|
||||||
|
margin:0px 30px;
|
||||||
|
border-top: 1px solid #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTeam{
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
.fileTeam > li {
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse.js */
|
||||||
|
.forumParse{
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-flow: column;
|
||||||
|
background: #5091FF;
|
||||||
|
color: #fff!important;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding-top: 7px;
|
||||||
|
}
|
||||||
|
.forumParse.parsed{
|
||||||
|
background:#999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 评论 */
|
||||||
|
.replyTitle{
|
||||||
|
line-height: 30px;
|
||||||
|
padding:20px;
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
}
|
||||||
|
.pre_stage{
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
padding : 10px 0px;
|
||||||
|
}
|
||||||
|
.pre_stage:last-child{
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.pre_stage .sub_stage{
|
||||||
|
padding: 10px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #fafafa;
|
||||||
|
position: relative;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius:4px;
|
||||||
|
}
|
||||||
|
.pre_stage .sub_stage::before{
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
top:-20px;
|
||||||
|
z-index: 1;
|
||||||
|
left: 20px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-width: 10px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent transparent #fafafa transparent;
|
||||||
|
}
|
||||||
|
.commentsItem_infos{
|
||||||
|
display: flex;
|
||||||
|
padding:10px 0px 10px;
|
||||||
|
}
|
||||||
|
.commentsItem_infos .markdown-body{
|
||||||
|
padding:0px;
|
||||||
|
}
|
||||||
|
.commentsItem_infos .editormd-html-preview,.commentsItem_infos .editormd-preview-container{
|
||||||
|
background-color: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 新建 */
|
||||||
|
.postNewform.ant-form .ant-row{
|
||||||
|
display: flex;
|
||||||
|
align-items: top;
|
||||||
|
}
|
||||||
|
.postNewform.ant-form .ant-form-item-control-wrapper{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.postNewform.ant-form .ant-form-item-label,.postNewform .new_label{
|
||||||
|
width: 90px;
|
||||||
|
text-align: right;
|
||||||
|
margin-right: 8px;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
margin-top: 3px;
|
||||||
|
font-size: 16px
|
||||||
|
}
|
||||||
|
.postNewform.ant-form label{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.editorFromItem {
|
||||||
|
align-items: flex-start!important;
|
||||||
|
margin-bottom: 0px!important;
|
||||||
|
}
|
||||||
|
.editorFromItem .editormd.editormd-vertical{
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
.editorFromItem .rememberTip{
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.newPostUpload{
|
||||||
|
margin-left: 84px;
|
||||||
|
}
|
||||||
|
.newPostUpload .uploadBtn{
|
||||||
|
border:none;
|
||||||
|
box-shadow: none;
|
||||||
|
color:#5091FF;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
.newPostUpload .ant-upload-list{
|
||||||
|
padding-left: 100px;
|
||||||
|
}
|
||||||
|
.newPostUpload .ant-upload-list-item-info{
|
||||||
|
padding:0px;
|
||||||
|
font-size: 15px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
max-width: 500px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.selectItem .ant-select-selection--single{
|
||||||
|
width: 200px;
|
||||||
|
margin-right: 20px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.selectItem .ant-select-selection__rendered{
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
/* 上传封面 */
|
||||||
|
.uploadImageBox .ant-upload-picture-card-wrapper{
|
||||||
|
width: 340px;
|
||||||
|
height: 248px;
|
||||||
|
border:none;
|
||||||
|
background: #fafafa;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding:0px 60px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.uploadImageBox .ant-upload.ant-upload-select-picture-card{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.uploadImageBox .ant-upload.ant-upload-select-picture-card i{
|
||||||
|
font-size: 44px!important;
|
||||||
|
color:#999;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
class Index extends PureComponent {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const {count, returnEvent} = this.props;
|
||||||
|
return(
|
||||||
|
<p className="clearfix pl30 pr30 mb10">
|
||||||
|
<span className="fl color-grey3 font-16">共找到相关结果<span className="color-blue">{count}</span>个</span>
|
||||||
|
<span onClick={returnEvent} className="color-grey-6 fr mt2 c_point">返回</span>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
|
||||||
|
class Index extends PureComponent {
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let { topClass , bestClass } = this.props;
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{ topClass && <img alt="" src={getImageUrl("images/plate/top.png")} width="42px" className={ topClass }></img> }
|
||||||
|
{ bestClass && <img alt="" src={getImageUrl("images/plate/best.png")} width="28px" className={ bestClass }></img> }
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -0,0 +1,289 @@
|
||||||
|
.newContainer{
|
||||||
|
padding:60px 0px 120px 0px;
|
||||||
|
}
|
||||||
|
ul,p{
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
.flex1{
|
||||||
|
flex: 1;
|
||||||
|
width: 0
|
||||||
|
}
|
||||||
|
.ant-tabs-nav .ant-tabs-tab-active,.ant-tabs-nav .ant-tabs-tab:hover{
|
||||||
|
color:#21B350!important;
|
||||||
|
}
|
||||||
|
/* shownotification的z-index */
|
||||||
|
.ant-notification{
|
||||||
|
z-index: 100000;
|
||||||
|
}
|
||||||
|
/* 小按钮 */
|
||||||
|
.small-default-btn{
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
padding: 0px 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
background:rgba(244,244,244,1);
|
||||||
|
border-radius:2px;
|
||||||
|
color: #999;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.middle-default-btn{
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 0px 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
background:rgba(244,244,244,1);
|
||||||
|
border-radius:2px;
|
||||||
|
color: #999;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.small-blue-btn{
|
||||||
|
background:rgba(80,145,255,1);
|
||||||
|
color:rgba(255,255,255,1);
|
||||||
|
}
|
||||||
|
.small-green-btn{
|
||||||
|
background:#21B350;
|
||||||
|
color:rgba(255,255,255,1)!important;
|
||||||
|
}
|
||||||
|
.educontent{
|
||||||
|
width: 1200px;
|
||||||
|
margin:0px auto;
|
||||||
|
}
|
||||||
|
.educontent-min{
|
||||||
|
width: 960px;
|
||||||
|
margin:0px auto;
|
||||||
|
}
|
||||||
|
.F_panel{
|
||||||
|
width: 1200px;
|
||||||
|
display: flex;
|
||||||
|
margin:30px auto;
|
||||||
|
}
|
||||||
|
.f_left_head{
|
||||||
|
display: flex;
|
||||||
|
justify-content:space-between;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
padding-left:30px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.f_left_head > ul{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height:80px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
.f_left_head > ul >li{
|
||||||
|
margin-right: 30px;
|
||||||
|
color: #666666
|
||||||
|
}
|
||||||
|
.f_left_head > ul >li a{
|
||||||
|
color: #666666
|
||||||
|
}
|
||||||
|
.f_left_head > ul:first-child > li{
|
||||||
|
font-size: 16px;
|
||||||
|
position: relative;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.f_left_head > ul:first-child > li.active a{
|
||||||
|
color: #5091FF;
|
||||||
|
}
|
||||||
|
.f_left_head > ul:first-child > li.active a:after{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: -25px;
|
||||||
|
height: 2px;
|
||||||
|
background: #5091FF;
|
||||||
|
content: '';
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
.platePanel{
|
||||||
|
top: 24px;
|
||||||
|
background: #fff;
|
||||||
|
left: 0px;
|
||||||
|
box-shadow:0px 6px 16px 0px rgba(7,12,70,0.2);
|
||||||
|
border-radius:4px;
|
||||||
|
max-width: 770px;
|
||||||
|
max-height: 500px;
|
||||||
|
padding:10px 10px 0px 10px;
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.platePanel .plateItem{
|
||||||
|
display: flex;
|
||||||
|
padding-bottom:10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.platePanel .plateItem .plateItem_h{
|
||||||
|
color: #333;
|
||||||
|
margin:5px 20px 0px 10px;
|
||||||
|
}
|
||||||
|
.plateUl{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.plateUl li{
|
||||||
|
position: relative;
|
||||||
|
margin-right: 11px;
|
||||||
|
color: #666;
|
||||||
|
float: left;
|
||||||
|
padding:5px 0px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.plateUl li:after{
|
||||||
|
width: 1px;
|
||||||
|
position: absolute;
|
||||||
|
right: -12px;
|
||||||
|
height:10px;
|
||||||
|
background: #ccc;
|
||||||
|
top:11px;
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
.plateUl li:last-child:after{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.radius{
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.flex1{
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.color-blue {
|
||||||
|
color: #5091FF!important;
|
||||||
|
}
|
||||||
|
.flex-align-center{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.flex-align-top{
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.flex-align-bottom{
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
/* 列表 */
|
||||||
|
.plateTabulation{
|
||||||
|
padding: 0px 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.plateTabulation > li{
|
||||||
|
border-bottom: 1px solid #f4f4f4;
|
||||||
|
padding:20px 0px;
|
||||||
|
}
|
||||||
|
.plateTabulation > li:last-child{
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.exchangeItem-subject{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
flex:1;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
.flex_h{
|
||||||
|
display: flex;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
.sendPoint{
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 20px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
.sendPoint::before{
|
||||||
|
position: absolute;
|
||||||
|
left: -10px;
|
||||||
|
top:3px;
|
||||||
|
height:12px;
|
||||||
|
width:1px ;
|
||||||
|
content: '';
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
p{
|
||||||
|
margin:0px;padding:0px
|
||||||
|
}
|
||||||
|
.edu-txt-center .ant-dropdown-menu-item,.edu-txt-center .ant-dropdown-menu-submenu-title{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.c_point{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_Operate{
|
||||||
|
position: relative;
|
||||||
|
background: #fff;
|
||||||
|
padding:20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top_Operate .send_btn{
|
||||||
|
width: 188px;
|
||||||
|
height: 40px;
|
||||||
|
background: #5091ff;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 40px;
|
||||||
|
display: block;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.top_Operate .searchfrom{
|
||||||
|
position: absolute;
|
||||||
|
width: 40px;
|
||||||
|
right:20px;
|
||||||
|
top:20px;
|
||||||
|
}
|
||||||
|
.top_Operate .searchfrom.ant-input-affix-wrapper .ant-input-suffix{
|
||||||
|
right:-2px
|
||||||
|
}
|
||||||
|
.top_Operate .searchfrom .ant-input-search-icon svg{
|
||||||
|
width: 3em;
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
.top_Operate .searchfrom.active{
|
||||||
|
width: 250px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.r_part_title{
|
||||||
|
border-bottom: 1px solid #F4F4F4;
|
||||||
|
padding:20px;
|
||||||
|
}
|
||||||
|
.r_part_list{
|
||||||
|
padding:20px;
|
||||||
|
}
|
||||||
|
.r_part_list li{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.r_part_list li:last-child{
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
/* InfoComponent */
|
||||||
|
.icon-wrap{
|
||||||
|
margin-left: 30px;
|
||||||
|
color: #ccc;
|
||||||
|
float: left;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.icon-wrap > i{
|
||||||
|
margin-right: 5px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
/*給下拉列表的……添加高宽*/
|
||||||
|
.addheight{
|
||||||
|
background: url(./images/more.png) no-repeat right;
|
||||||
|
width: 30px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
/*帖子列表标题点击变颜色*/
|
||||||
|
.is_onclick:hover{
|
||||||
|
color: #5091FF;
|
||||||
|
}
|
||||||
|
/*帖子详情标题颜色不变*/
|
||||||
|
.color_black{
|
||||||
|
color: black;
|
||||||
|
}
|
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 121 B |
After Width: | Height: | Size: 76 KiB |
|
@ -0,0 +1,98 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Route , Switch } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { TPMIndexHOC } from '../modules/tpm/TPMIndexHOC';
|
||||||
|
import { SnackbarHOC , CNotificationHOC } from 'educoder';
|
||||||
|
|
||||||
|
import ExchangeIndex from './ExchangeIndex';
|
||||||
|
|
||||||
|
import Post from './Post/Index'
|
||||||
|
import PostNew from './Post/MemoNew';
|
||||||
|
import PlateIndex from './Plate/Index';
|
||||||
|
import MyExchange from './MyExchange/Index'
|
||||||
|
import PreManager from './Manage/PreModerator'
|
||||||
|
import axios from 'axios';
|
||||||
|
import './exchange.css';
|
||||||
|
|
||||||
|
class Index extends Component{
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state={
|
||||||
|
current_user:undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount=()=>{
|
||||||
|
const url = `/users/get_user_info.json`;
|
||||||
|
axios.get(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
this.setState({
|
||||||
|
current_user:result.data.current_user
|
||||||
|
})
|
||||||
|
// 给NewHeader传值,当前用户的信息
|
||||||
|
if(result.data.current_user && result.data.current_user.login){
|
||||||
|
this.props.initCommonState(result.data.current_user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate=()=>{
|
||||||
|
this.props.history.listen(()=>{
|
||||||
|
if (document.body.scrollTop || document.documentElement.scrollTop > 0) {
|
||||||
|
window.scrollTo(0, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<Switch {...this.props}>
|
||||||
|
{/* 帖子列表 */}
|
||||||
|
<Route path='/forums/plates/:plateid/all' render={
|
||||||
|
(props) => (<PlateIndex {...this.props} {...this.state} {...props} />)
|
||||||
|
} ></Route>
|
||||||
|
{/* 编辑帖子 */}
|
||||||
|
<Route path={`/forums/:memoId/edit`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<PostNew {...this.props} {...this.state} {...props}></PostNew>)}
|
||||||
|
}></Route>
|
||||||
|
{/* 新建帖子 */}
|
||||||
|
<Route path={`/forums/new`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<PostNew {...this.props} {...this.state} {...props}></PostNew>)}
|
||||||
|
}></Route>
|
||||||
|
{/* 版主管理 */}
|
||||||
|
<Route path={`/forums/manage/:plateId`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<PreManager {...this.props} {...this.state} {...props}></PreManager>)}
|
||||||
|
}></Route>
|
||||||
|
|
||||||
|
{/* 我的话题 */}
|
||||||
|
<Route path={`/forums/MyTopic`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
|
||||||
|
}></Route>
|
||||||
|
<Route path={`/forums/MyEnshrine`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
|
||||||
|
}></Route>
|
||||||
|
<Route path={`/forums/MyInteresting`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
|
||||||
|
}></Route>
|
||||||
|
{/* 帖子详情 */}
|
||||||
|
<Route path={`/forums/:postid`} render={
|
||||||
|
(props) => {
|
||||||
|
return (<Post {...this.props} {...this.state} {...props}></Post>)}
|
||||||
|
}></Route>
|
||||||
|
{/* 论坛首页 */}
|
||||||
|
<Route path={`/forums`} render={
|
||||||
|
(props) => (<ExchangeIndex {...this.props} {...this.state} {...props} />)
|
||||||
|
}></Route>
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default SnackbarHOC() (CNotificationHOC() (TPMIndexHOC ( Index ))) ;
|
|
@ -38,7 +38,7 @@ class Index extends Component {
|
||||||
return (
|
return (
|
||||||
<div className="newMain clearfix">
|
<div className="newMain clearfix">
|
||||||
<Handbook />
|
<Handbook />
|
||||||
<Switch {...this.props}>
|
{/* <Switch {...this.props}>
|
||||||
<Route
|
<Route
|
||||||
path="/projects/:projectsType/new"
|
path="/projects/:projectsType/new"
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
|
@ -67,7 +67,7 @@ class Index extends Component {
|
||||||
<ProjectIndex {...this.props} {...props} />
|
<ProjectIndex {...this.props} {...props} />
|
||||||
)}
|
)}
|
||||||
></Route>
|
></Route>
|
||||||
</Switch>
|
</Switch> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,7 @@ class Detail extends Component {
|
||||||
this.setState({
|
this.setState({
|
||||||
projectDetail: result.data,
|
projectDetail: result.data,
|
||||||
project_id: result.data.project_id,
|
project_id: result.data.project_id,
|
||||||
isManager: result.data.permission && result.data.permission === "Manager",
|
isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"),
|
||||||
isReporter: result.data.permission && result.data.permission === "Reporter",
|
isReporter: result.data.permission && result.data.permission === "Reporter",
|
||||||
isDeveloper: result.data.permission && result.data.permission === "Developer",
|
isDeveloper: result.data.permission && result.data.permission === "Developer",
|
||||||
http_url: result.data.clone_url,
|
http_url: result.data.clone_url,
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Upload, Spin, message } from 'antd';
|
||||||
|
import { getUploadActionUrl } from 'educoder';
|
||||||
|
|
||||||
|
export default (({ imageUrl, getImageUrl }) => {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [url, setUrl] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (imageUrl) {
|
||||||
|
setUrl(imageUrl);
|
||||||
|
}
|
||||||
|
}, [imageUrl])
|
||||||
|
|
||||||
|
function getBase64(img, callback) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', () => callback(reader.result));
|
||||||
|
reader.readAsDataURL(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(info) {
|
||||||
|
if (info.file.status === 'uploading') {
|
||||||
|
setLoading(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
let filelist = info.fileList && info.fileList.length>0 && info.fileList[info.fileList.length-1];
|
||||||
|
if(filelist && filelist.response && filelist.response.status === -1){
|
||||||
|
setLoading(false)
|
||||||
|
setUrl(null)
|
||||||
|
message.error(filelist.response.message)
|
||||||
|
}else{
|
||||||
|
getBase64(info.file.originFileObj, imageUrl =>{
|
||||||
|
setLoading(false),
|
||||||
|
getImageUrl(filelist && filelist.response),
|
||||||
|
setUrl(imageUrl)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function beforeUpload(file) {
|
||||||
|
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||||
|
if (!isJpgOrPng) {
|
||||||
|
message.error('只能上传图片!');
|
||||||
|
}
|
||||||
|
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||||
|
if (!isLt2M) {
|
||||||
|
message.error('图片大小必须小于2MB!');
|
||||||
|
}
|
||||||
|
return isJpgOrPng && isLt2M;
|
||||||
|
}
|
||||||
|
const uploadButton = (
|
||||||
|
<div>
|
||||||
|
{loading ? <Spin size={"small"} /> : <i className="iconfont icon-tianjiadaohang font-20" />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Upload
|
||||||
|
name="file"
|
||||||
|
listType="picture-card"
|
||||||
|
className="avatar-uploader"
|
||||||
|
showUploadList={false}
|
||||||
|
action={getUploadActionUrl()}
|
||||||
|
beforeUpload={beforeUpload}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{url ? <img src={url} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
|
||||||
|
</Upload>
|
||||||
|
)
|
||||||
|
})
|
|
@ -200,3 +200,4 @@ form{
|
||||||
padding:0px;
|
padding:0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { WhiteBack } from "../css/layout";
|
||||||
|
import Title from "./Title";
|
||||||
|
import { Modal, Button, Input , notification, message } from "antd";
|
||||||
|
import Nodata from "../Nodata";
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const TextArea = Input.TextArea;
|
||||||
|
export default (({ content , operation ,plateId }) => {
|
||||||
|
const [word, setWord] = useState(undefined);
|
||||||
|
const [editWord, setEditWord] = useState(undefined);
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [AnnModalType, setAnnModalType] = useState(1); //默认为1:查看,否则为编辑
|
||||||
|
|
||||||
|
let child = document.getElementById("annWords") && document.getElementById("annWords").offsetHeight;
|
||||||
|
useEffect(() => {
|
||||||
|
if (content) {
|
||||||
|
setWord(content.notice);
|
||||||
|
}
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changeShow();
|
||||||
|
}, [word, child , content]);
|
||||||
|
|
||||||
|
// 保存公告
|
||||||
|
function saveAnn() {
|
||||||
|
setWord(editWord);
|
||||||
|
setVisible(false);
|
||||||
|
// 调用保存接口
|
||||||
|
if(editWord){
|
||||||
|
const url = `/forum_sections/${plateId}/edit_notice.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
content:editWord
|
||||||
|
}).then(result=>{
|
||||||
|
if(result && result.data){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
changeShow();
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeShow(){
|
||||||
|
let p = document.getElementById("annContent")&& document.getElementById("annContent").offsetHeight;
|
||||||
|
let c = document.getElementById("annWords") &&document.getElementById("annWords").offsetHeight;
|
||||||
|
if (c > p) {
|
||||||
|
setShow(true);
|
||||||
|
}else{
|
||||||
|
setShow(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 取消编辑公告
|
||||||
|
function cancelAnn() {
|
||||||
|
setEditWord(word);
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看(1)或者编辑(2)
|
||||||
|
function changeAnnModalType(type) {
|
||||||
|
setAnnModalType(type);
|
||||||
|
setVisible(true);
|
||||||
|
setEditWord(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeText(e) {
|
||||||
|
setEditWord(e.target.value);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<WhiteBack>
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
title="公告"
|
||||||
|
closable={true}
|
||||||
|
onCancel={()=>setVisible(false)}
|
||||||
|
footer={
|
||||||
|
AnnModalType === 1 ? false : <div>
|
||||||
|
<Button onClick={cancelAnn}>取消</Button>
|
||||||
|
<Button onClick={saveAnn} type={"primary"}>
|
||||||
|
发布
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{AnnModalType === 1 ? (
|
||||||
|
<p style={{ maxHeight: "100px", overflowY: "auto" }}>{word}</p>
|
||||||
|
) : (
|
||||||
|
<TextArea
|
||||||
|
placeholder="填写公告,1~600字"
|
||||||
|
value={editWord}
|
||||||
|
rows={5}
|
||||||
|
onChange={changeText}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
<Title>
|
||||||
|
<span>公告</span>
|
||||||
|
{
|
||||||
|
operation ?
|
||||||
|
<a onClick={() => changeAnnModalType(2)}>
|
||||||
|
<i className="iconfont icon-bianji3 grey-9"></i>
|
||||||
|
</a>:""
|
||||||
|
}
|
||||||
|
</Title>
|
||||||
|
<div style={{ padding: "10px 30px" }} className="pr">
|
||||||
|
{word ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<div id="annContent" className="annContent">
|
||||||
|
<p id="annWords" className="annWords">
|
||||||
|
{word}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span className="grey-8">版主:{content.name}</span>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<Nodata _html={"暂无公告"} />
|
||||||
|
)}
|
||||||
|
{
|
||||||
|
word && show === true ?
|
||||||
|
<a className="annBtn" onClick={() => changeAnnModalType(1)}>
|
||||||
|
…<span className="green ml4">查看</span>
|
||||||
|
</a> : ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</WhiteBack>
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,51 @@
|
||||||
|
import React , { useState, useEffect } from 'react';
|
||||||
|
import { WhiteBack , Greenline , UDStructure , Grid } from '../css/layout';
|
||||||
|
import UserInfo from './UserInfo';
|
||||||
|
import { getImageUrl } from "educoder";
|
||||||
|
import StarUser from "../../user_info/User/StarUser";
|
||||||
|
export default (({user})=>{
|
||||||
|
useEffect(() => {
|
||||||
|
if(user){
|
||||||
|
setFansCount(user.watchers_count)
|
||||||
|
}
|
||||||
|
},[]);
|
||||||
|
const [fansCount, setFansCount] = useState(0)
|
||||||
|
const fans_count = (count) => {
|
||||||
|
let new_fans_count = fansCount + count
|
||||||
|
setFansCount(new_fans_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<WhiteBack className="authorCard">
|
||||||
|
<UserInfo url={getImageUrl(user && user.image_url)} name={user && user.username} login={user && user.login} column/>
|
||||||
|
<p style={{width:"100%"}} className="task-hide grey-8 mt10 mb15 edu-text-center">{user && user.description ? user.description : "这家伙太懒了,还未填写个人描述!"}</p>
|
||||||
|
{/* {user && !user.is_current_user && (
|
||||||
|
<StarUser
|
||||||
|
current_login={user.current_login}
|
||||||
|
login={user.login}
|
||||||
|
user_id={user.user_id}
|
||||||
|
is_watched={user.watched}
|
||||||
|
is_blocked={user.is_blocked}
|
||||||
|
user_name={user.username}
|
||||||
|
is_blocked_by={user.is_blocked_by}
|
||||||
|
set_fans_count={fans_count}
|
||||||
|
show_block={false}
|
||||||
|
></StarUser>
|
||||||
|
)} */}
|
||||||
|
<Grid className="mt20">
|
||||||
|
<UDStructure>
|
||||||
|
<span>{user && user.memos_count}</span>
|
||||||
|
<span>文章数</span>
|
||||||
|
</UDStructure>
|
||||||
|
<UDStructure>
|
||||||
|
<span>{user && user.replies_count}</span>
|
||||||
|
<span>评论数</span>
|
||||||
|
</UDStructure>
|
||||||
|
<UDStructure>
|
||||||
|
<span>{fansCount}</span>
|
||||||
|
<span>关注者</span>
|
||||||
|
</UDStructure>
|
||||||
|
</Grid>
|
||||||
|
</WhiteBack>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { WhiteBack } from "../css/layout";
|
||||||
|
import Title from './Title';
|
||||||
|
import UserInfo from "./UserInfo";
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
export default (({ author }) => {
|
||||||
|
return (
|
||||||
|
<WhiteBack>
|
||||||
|
<Title>版块活跃作者</Title>
|
||||||
|
<ul className="authorUl">
|
||||||
|
{
|
||||||
|
author && author.length > 0 ?
|
||||||
|
author.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<UserInfo login={item.login} url={getImageUrl(`${item.image_url}`)} name={item.username} column/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
:""
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</WhiteBack>
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React,{useEffect,useState} from 'react';
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
import Nodata from '../Nodata';
|
||||||
|
|
||||||
|
export default (({ memos , selectKey })=>{
|
||||||
|
const [list, setList] = useState(undefined);
|
||||||
|
const [ keys , setKeys ] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (memos) {
|
||||||
|
setList(memos);
|
||||||
|
setKeys(selectKey[0]);
|
||||||
|
}
|
||||||
|
}, [memos , selectKey]);
|
||||||
|
return(
|
||||||
|
<ul className="BestUl">
|
||||||
|
{
|
||||||
|
list && list.length>0 ? list.map((item,key)=>{
|
||||||
|
return(
|
||||||
|
<li className="task-hide"><Link to={`/forums/${item && item.id}/detail`}>{item && item.subject}</Link></li>
|
||||||
|
)
|
||||||
|
}):
|
||||||
|
<Nodata _html={`暂无${keys === "hot" ? "话题":"推荐"}~`} />
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { WhiteBack, AlignCenter } from "../css/layout";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Title from "./Title";
|
||||||
|
import ring from "../image/radius.png";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Img = styled.img`
|
||||||
|
{
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const Prag = styled.p`
|
||||||
|
{
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin: 4px 0 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default ({ recommand }) => {
|
||||||
|
return (
|
||||||
|
<WhiteBack>
|
||||||
|
<Title>精选板块</Title>
|
||||||
|
<ul className="BestModalUl">
|
||||||
|
{recommand && recommand.length > 0
|
||||||
|
? recommand.map((item, key) => {
|
||||||
|
return (
|
||||||
|
<AlignCenter>
|
||||||
|
<Img src={item.picture || ring} />
|
||||||
|
<div className="flex1" style={{ width: "0" }}>
|
||||||
|
<Link to={`/forums/theme/${item.id}`} className="grey-3">{item.title}</Link>
|
||||||
|
<Prag>{item.description || "暂无描述~"}</Prag>
|
||||||
|
{/* <p className="task-hide">{item.description}</p> */}
|
||||||
|
<span className="font-12 grey-9">{item.watchers_count}人收藏<span className="ml15">{item.memos_count}个话题</span></span>
|
||||||
|
</div>
|
||||||
|
</AlignCenter>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</ul>
|
||||||
|
</WhiteBack>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import logo from "../image/radius.png";
|
||||||
|
import { FlexAJ, P } from "../css/layout";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Img = styled.img`
|
||||||
|
{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 25px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Prag = styled.p`
|
||||||
|
{
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-all;
|
||||||
|
height:18px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default (props) => {
|
||||||
|
const list = props.plate;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="doubleItems">
|
||||||
|
{list && list.length > 0
|
||||||
|
? list.map((item, key) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/forums/theme/${item.id}`}
|
||||||
|
style={{
|
||||||
|
borderBottom:
|
||||||
|
key === list.length - 2 && list.length % 2 === 0
|
||||||
|
? "none"
|
||||||
|
: "",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Img src={item && item.picture ? item.picture : logo} />
|
||||||
|
<div className="flex1"style={{width:"0"}}>
|
||||||
|
<div style={{display:"flex"}}>
|
||||||
|
<P style={{maxWidth:"370px"}}>{item.title}</P>
|
||||||
|
<span className="ml15" style={{fontSize:"14px",color:"#999"}}>帖子数:{item.memos_count}</span>
|
||||||
|
</div>
|
||||||
|
<Prag>{item.description || <span className="grey-9">暂无描述~</span>}</Prag>
|
||||||
|
<FlexAJ style={{ fontSize: "12px" }}>
|
||||||
|
<span>版主:{item.user_name}</span>
|
||||||
|
</FlexAJ>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,114 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Menu , Dropdown , notification } from "antd";
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
// permission = {
|
||||||
|
// admin://管理员
|
||||||
|
// banned_permission://管理员、且有置顶、推荐权限
|
||||||
|
// is_currentUser: true, #是否为当前用户,判断是否显示编辑/删除,并隐藏其他的
|
||||||
|
// is_fine: true, #是否精华帖子
|
||||||
|
// sticky: true, #是否置顶
|
||||||
|
// memo_watched: true, #是否收藏
|
||||||
|
// is_deleted:true#是否已经申请删除
|
||||||
|
// }
|
||||||
|
export default ({ id , permission , calbackFunc , confirm }) => {
|
||||||
|
// 置顶、取消置顶
|
||||||
|
function changeSticky(s){
|
||||||
|
let sticky = s ? 0 : 1;//1为置顶,0为取消置顶
|
||||||
|
const url = `/memos/${id}/set_top_or_down.json`;
|
||||||
|
axios.get(url,{
|
||||||
|
params:{
|
||||||
|
sticky
|
||||||
|
}
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
calbackFunc && calbackFunc();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 推荐、取消推荐
|
||||||
|
function changeFine(f){
|
||||||
|
let is_fine = f ? 0 : 1;//1表示加精,0表示取消加精
|
||||||
|
const url = `/memos/${id}/is_fine.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
is_fine
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
calbackFunc && calbackFunc();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关注、取消关注
|
||||||
|
function changeMemoWatched(m){
|
||||||
|
let is_watch = m ? 0 : 1;//1为添加关注,0为取消关注
|
||||||
|
const url = `/memos/${id}/watch_memo.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
is_watch
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
calbackFunc && calbackFunc();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 管理员直接删除帖子
|
||||||
|
function deleteForum(){
|
||||||
|
confirm && confirm({
|
||||||
|
content: '确认删除帖子?',
|
||||||
|
onOk:()=>{
|
||||||
|
const url = `/memos/${id}.json`;
|
||||||
|
axios.delete(url).then(result=>{
|
||||||
|
if(result){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
calbackFunc && calbackFunc();
|
||||||
|
window.location.href="/forums"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布者申请删除、撤销申请删除
|
||||||
|
function sendDeleteForum(d){
|
||||||
|
let is_apply = d ? 0 : 1;//1为申请删除,0为撤销申请删除
|
||||||
|
confirm && confirm({
|
||||||
|
content: '确认申请删帖?',
|
||||||
|
onOk:()=>{
|
||||||
|
const url = `/memos/${id}/confirm_delete.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
is_apply
|
||||||
|
}).then(result=>{
|
||||||
|
if(result){
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
calbackFunc && calbackFunc();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu=(
|
||||||
|
permission &&
|
||||||
|
<Menu style={{minWidth:"100px",textAlign:'center'}}>
|
||||||
|
{ permission.banned_permission && <Menu.Item onClick={()=>changeSticky(permission.sticky)}>{permission.sticky ? "取消置顶":"置顶"}</Menu.Item>}
|
||||||
|
{ permission.banned_permission && <Menu.Item onClick={()=>changeFine(permission.is_fine)}>{permission.is_fine ? "取消推荐":"推荐"}</Menu.Item>}
|
||||||
|
{ permission.login && <Menu.Item onClick={()=>changeMemoWatched(permission.memo_watched)}>{permission.memo_watched ? "取消收藏":"收藏"}</Menu.Item> }
|
||||||
|
{ (permission.admin || permission.is_currentUser) && <Menu.Item><Link to={`/forums/${id}/edit`}>编辑</Link></Menu.Item>}
|
||||||
|
{ permission.admin ?
|
||||||
|
<Menu.Item onClick={()=>deleteForum()}>删除</Menu.Item>
|
||||||
|
:
|
||||||
|
permission.is_currentUser ?
|
||||||
|
<Menu.Item onClick={()=>sendDeleteForum(permission.is_deleted)}>{permission.is_deleted?"撤销申请":"申请删帖"}</Menu.Item>:""
|
||||||
|
}
|
||||||
|
</Menu>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Dropdown overlay={menu} align={"center"} placement={"bottomCenter"}>
|
||||||
|
<i className="iconfont icon-gengduo1"></i>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function Type(value){
|
||||||
|
if(value === "交流"){
|
||||||
|
return (<span className="blue">【交流】</span>)
|
||||||
|
}else if(value === "求助"){
|
||||||
|
return (<span className="orange">【求助】</span>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Tag(value){
|
||||||
|
if(value === "置顶"){
|
||||||
|
return (<span className="tag tagRed">置顶</span>)
|
||||||
|
}else if(value === "精华"){
|
||||||
|
return (<span className="tag tagBlue">精华</span>)
|
||||||
|
}else if(value === "原创"){
|
||||||
|
return (<span className="tag tagOrange">原创</span>)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { FlexAJ, AlignCenter, LeftLine } from "../css/layout";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { getImageUrl } from 'educoder';
|
||||||
|
import { Type, Tag } from "./ItemType";
|
||||||
|
import User from "./User";
|
||||||
|
import Drop from "./ItemDropDown";
|
||||||
|
import Nodata from '../Nodata';
|
||||||
|
|
||||||
|
export default ({ memos , current_user , calbackFunc , confirm }) => {
|
||||||
|
const [list, setList] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (memos) {
|
||||||
|
setList(memos);
|
||||||
|
}
|
||||||
|
}, [memos]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="forumList">
|
||||||
|
{list && list.length > 0 ? (
|
||||||
|
list.map((item, key) => {
|
||||||
|
let permission = {
|
||||||
|
index:key,
|
||||||
|
admin:current_user && current_user.admin,
|
||||||
|
banned_permission:item.banned_permission,
|
||||||
|
login:current_user && current_user.login,
|
||||||
|
is_currentUser:current_user && (item.user_login === current_user.login),
|
||||||
|
is_fine:item.is_fine,
|
||||||
|
sticky:item.sticky,
|
||||||
|
memo_watched:item.memo_watched,
|
||||||
|
user_banned_permission:item.banned_permission,
|
||||||
|
is_deleted:item.apply_destroy
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<FlexAJ>
|
||||||
|
<AlignCenter style={{ marginLeft: "-8px" }}>
|
||||||
|
<span>{Type(item.tag_name)}</span>
|
||||||
|
<Link
|
||||||
|
className="grey-3 task-hide"
|
||||||
|
style={{ maxWidth: "700px" }}
|
||||||
|
to={`/forums/${item.id}/detail`}
|
||||||
|
>
|
||||||
|
{item.subject}
|
||||||
|
</Link>
|
||||||
|
{ item.sticky === true ? <span className="ml8">{Tag("置顶")}</span> : "" }
|
||||||
|
{ item.is_original === true ? <span className="ml8">{Tag("原创")}</span> : "" }
|
||||||
|
{ item.is_fine === true ? <span className="ml8">{Tag("精华")}</span> : "" }
|
||||||
|
</AlignCenter>
|
||||||
|
<AlignCenter>
|
||||||
|
{ item.apply_destroy ? <span className="orange font-12 mr10">已申请删帖</span> : "" }
|
||||||
|
{
|
||||||
|
current_user && current_user.login ?
|
||||||
|
<Drop permission={permission} id={item.id} calbackFunc={calbackFunc} confirm={confirm}/>
|
||||||
|
:""
|
||||||
|
}
|
||||||
|
</AlignCenter>
|
||||||
|
</FlexAJ>
|
||||||
|
<FlexAJ className="mt8">
|
||||||
|
<AlignCenter>
|
||||||
|
<User login={item.user_login} name={item.username} url={getImageUrl(item.image_url)}></User>
|
||||||
|
{item.forum_section_title ? <Link to={`/forums/theme/${item.forum_section_id}`}><LeftLine>{item.forum_section_title}</LeftLine></Link> : "" }
|
||||||
|
{item.published_time ? <LeftLine>{item.published_time}</LeftLine> : "" }
|
||||||
|
</AlignCenter>
|
||||||
|
<span>
|
||||||
|
<span class="icon-wrap">
|
||||||
|
<i class="iconfont icon-zhengyan font-18"></i>
|
||||||
|
<span class="span-text">{item.viewed_count}</span>
|
||||||
|
</span>
|
||||||
|
<span class="icon-wrap">
|
||||||
|
<i class="iconfont icon-dianzan2 font-14"></i>
|
||||||
|
<span class="span-text">{item.praises_count}</span>
|
||||||
|
</span>
|
||||||
|
<span class="icon-wrap">
|
||||||
|
<i class="iconfont icon-pinglun1 font-14"></i>
|
||||||
|
<span class="span-text">{item.replies_count}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</FlexAJ>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) :
|
||||||
|
<AlignCenter style={{height:"400px"}} className="bigNoData"><Nodata _html="暂无数据" /></AlignCenter>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Input } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const Search = Input.Search;
|
||||||
|
export default (({ onSearch , current_user , showLoginDialog })=>{
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
<Search onSearch={onSearch} placeholder="搜索" allowClear/>
|
||||||
|
{
|
||||||
|
current_user && current_user.login ?
|
||||||
|
<Link className="greenbtn" to={`/forums/new`} style={{width:"100%",marginTop:"20px"}}>
|
||||||
|
<i className="iconfont icon-bianjishijuan3x" style={{marginRight:"5px"}}></i>写点什么
|
||||||
|
</Link>
|
||||||
|
:
|
||||||
|
<a className="greenbtn" onClick={showLoginDialog} style={{width:"100%",marginTop:"20px"}}>
|
||||||
|
<i className="iconfont icon-bianjishijuan3x" style={{marginRight:"5px"}}></i>写点什么
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Pagination } from "antd";
|
||||||
|
|
||||||
|
export default (({page,total,changePage , pageSize}) => {
|
||||||
|
return (
|
||||||
|
total > pageSize ?
|
||||||
|
<div className="center" style={{ padding: "25px 0px" }}>
|
||||||
|
<Pagination
|
||||||
|
showQuickJumper
|
||||||
|
current={page}
|
||||||
|
onChange={changePage}
|
||||||
|
total={total}
|
||||||
|
pageSize={pageSize}
|
||||||
|
/>
|
||||||
|
</div>:""
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import { message } from "antd";
|
||||||
|
export default ({ isPrised, num, memo_id, container_type, current_login, props }) => {
|
||||||
|
const [flag, setFlag] = useState(undefined);
|
||||||
|
const [number, setNumber] = useState(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFlag(isPrised);
|
||||||
|
setNumber(num);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function priseForums() {
|
||||||
|
if (current_login) {
|
||||||
|
axios
|
||||||
|
.post(`/memos/${memo_id}/plus.json`, {
|
||||||
|
container_type: container_type,
|
||||||
|
id: memo_id,
|
||||||
|
type: flag ? 0 : 1,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
setNumber(result.data.praise_count);
|
||||||
|
setFlag(!flag);
|
||||||
|
message.success(flag ? "取消点赞" : "已点赞")
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
message.error(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
props.showLoginDialog();
|
||||||
|
return;
|
||||||
|
// window.open("/login", "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="priseBox">
|
||||||
|
<span onClick={priseForums}>
|
||||||
|
<i
|
||||||
|
className={
|
||||||
|
flag ? "iconfont icon-dianzan" : "iconfont icon-dianzan-xian"
|
||||||
|
}
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<span>{number}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Nav = styled.div`{
|
||||||
|
background-color:#fff;
|
||||||
|
padding:20px 30px;
|
||||||
|
border-bottom:1px solid #eee;
|
||||||
|
font-size:16px;
|
||||||
|
color:#333;
|
||||||
|
display:flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items:center;
|
||||||
|
}`
|
||||||
|
|
||||||
|
export default (({children,className})=>{
|
||||||
|
return(
|
||||||
|
<Nav className={className}>{children}</Nav>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,152 @@
|
||||||
|
import React, { useEffect , useState } from "react";
|
||||||
|
import { Button , notification } from "antd";
|
||||||
|
import { WhiteBack, FlexStart, P, FlexAJ, AlignCenter } from "../css/layout";
|
||||||
|
import Radius from "../image/radius.png";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import axios from "axios";
|
||||||
|
import {Link} from 'react-router-dom';
|
||||||
|
|
||||||
|
const Img = styled.img`
|
||||||
|
{
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const Prag = styled.p`
|
||||||
|
{
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin: 10px 0px;
|
||||||
|
text-align: justify;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 54px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const Spanleft = styled.span`
|
||||||
|
{
|
||||||
|
margin-right: 30px;
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const Spanright = styled.span`
|
||||||
|
{
|
||||||
|
margin-left: 30px;
|
||||||
|
color: #888;
|
||||||
|
font-size: 12px;
|
||||||
|
& > label {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export default ({ headData, title , operation , history }) => {
|
||||||
|
const [section, setSection] = useState(undefined);
|
||||||
|
const [sectionUser, setSectionUser] = useState(undefined);
|
||||||
|
const [forumModers, setForumModers] = useState(undefined);
|
||||||
|
const [ watched , setWacth ] = useState(headData && headData.watched);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (headData) {
|
||||||
|
setSection(headData.forum_section);
|
||||||
|
setSectionUser(headData.forum_section_user);
|
||||||
|
setForumModers(headData.forum_moders);
|
||||||
|
setWacth(headData.watched);
|
||||||
|
}
|
||||||
|
}, [headData]);
|
||||||
|
|
||||||
|
// 收藏、取消收藏
|
||||||
|
function saveForum(id){
|
||||||
|
if(id){
|
||||||
|
const url = `/forum_memos/${id}/is_watch.json`;
|
||||||
|
axios.post(url,{
|
||||||
|
is_watch:watched?0:1
|
||||||
|
}).then(result=>{
|
||||||
|
if(result && result.data && result.data.status!=-1){
|
||||||
|
setWacth(!watched);
|
||||||
|
notification.open({message:"提示",description:result.data.message});
|
||||||
|
}
|
||||||
|
}).catch(error=>{
|
||||||
|
console.log(error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toManage(id){
|
||||||
|
history.push(`/forums/manage/${section && section.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WhiteBack style={{ marginBottom: "15px", padding: "20px 30px" }}>
|
||||||
|
<FlexStart>
|
||||||
|
<Img src={section && section.picture ? section.picture : Radius} />
|
||||||
|
<div className="flex1">
|
||||||
|
<FlexAJ>
|
||||||
|
<P style={{ marginBottom: "0px" }}>{section && section.title}</P>
|
||||||
|
<AlignCenter>
|
||||||
|
{
|
||||||
|
operation ?
|
||||||
|
<Button onClick={()=>toManage(section && section.id)}>
|
||||||
|
<i className="iconfont icon-shezhi2"></i>板块管理
|
||||||
|
</Button>
|
||||||
|
:""
|
||||||
|
}
|
||||||
|
|
||||||
|
<Button onClick={()=>saveForum(section && section.id)} style={{ marginLeft: "30px" }}>
|
||||||
|
<i className={watched?"iconfont icon-pingfen-xian":"iconfont icon-pingfen-xian"}></i>{watched?"取消收藏":"收藏"}
|
||||||
|
</Button>
|
||||||
|
</AlignCenter>
|
||||||
|
</FlexAJ>
|
||||||
|
{section && section.description ? (
|
||||||
|
<Prag>{section.description}</Prag>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
<FlexAJ className="mt10">
|
||||||
|
<span>
|
||||||
|
<Spanleft>版主:<Link className="grey-9" to={`/accounts/${sectionUser && sectionUser.user_login}`}>{sectionUser && sectionUser.username}</Link></Spanleft>
|
||||||
|
{forumModers && forumModers.length > 0 ? (
|
||||||
|
<Spanleft>
|
||||||
|
管理员:
|
||||||
|
{forumModers.map((item, key) => {
|
||||||
|
return key < forumModers.length - 1
|
||||||
|
? <span><Link className="grey-9" to={`/accounts/${item.user_login}`}>{item.username}</Link>、</span>
|
||||||
|
: <Link className="grey-9" to={`/accounts/${item.user_login}`}>{item.username}</Link>;
|
||||||
|
})}
|
||||||
|
</Spanleft>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Spanright>
|
||||||
|
版块主题:<label>{section && section.memos_count}</label>
|
||||||
|
</Spanright>
|
||||||
|
{section && section.publish_today_coun ? (
|
||||||
|
<Spanright>
|
||||||
|
今日发帖:<label>{section.publish_today_count}</label>
|
||||||
|
</Spanright>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
{section && section.replies_today_count ? (
|
||||||
|
<Spanright>
|
||||||
|
今日回帖:<label>{section.replies_today_count}</label>
|
||||||
|
</Spanright>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</FlexAJ>
|
||||||
|
</div>
|
||||||
|
</FlexStart>
|
||||||
|
</WhiteBack>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React , { useState , useEffect } from "react";
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export default ({user}) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="urllist">
|
||||||
|
{
|
||||||
|
user && user.login ?
|
||||||
|
<React.Fragment>
|
||||||
|
<Link to={`/accounts/${user.login}/memos`}>
|
||||||
|
<span>我的帖子</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</Link>
|
||||||
|
<Link to={`/accounts/${user && user.login}/stars`}>
|
||||||
|
<span>我的收藏</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</Link>
|
||||||
|
<Link to={`/accounts/${user && user.login}/interesting`}>
|
||||||
|
<span>我感兴趣的论坛</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</Link>
|
||||||
|
</React.Fragment>
|
||||||
|
:
|
||||||
|
<React.Fragment>
|
||||||
|
<a href={"/login"}>
|
||||||
|
<span>我的帖子</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</a>
|
||||||
|
<a href={`/login`}>
|
||||||
|
<span>我的收藏</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</a>
|
||||||
|
<a href={`/login`}>
|
||||||
|
<span>我感兴趣的论坛</span>
|
||||||
|
<i className="iconfont icon-youjiantou"></i>
|
||||||
|
</a>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { AlignCenter } from '../css/layout';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const Img = styled.img`{
|
||||||
|
width:20px;
|
||||||
|
height:20px;
|
||||||
|
border-radius:50%;
|
||||||
|
margin-right:10px;
|
||||||
|
}`
|
||||||
|
const Span = styled.span`{
|
||||||
|
color:#999;
|
||||||
|
}`
|
||||||
|
export default (({name,url,login})=>{
|
||||||
|
return(
|
||||||
|
<Link to={`/accounts/${login}`}>
|
||||||
|
<AlignCenter>
|
||||||
|
<Img src={url}/>
|
||||||
|
<Span>{name}</Span>
|
||||||
|
</AlignCenter>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export default ({ url , name , column , login })=>{
|
||||||
|
const Img = styled.span`
|
||||||
|
display:flex;
|
||||||
|
${column && "flex-direction: column;text-align:center;"}
|
||||||
|
align-items: center;
|
||||||
|
& img{
|
||||||
|
width:30px;
|
||||||
|
height:30px;
|
||||||
|
border-radius:50%;
|
||||||
|
}
|
||||||
|
${!column && `
|
||||||
|
& span{
|
||||||
|
margin-left:8px;
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
return(
|
||||||
|
<Link to={`/accounts/${login}`}>
|
||||||
|
<Img>
|
||||||
|
<img src={url} alt=""/>
|
||||||
|
<span className="task-hide" style={{maxWidth:"84px",textAlign:"center"}}>{name}</span>
|
||||||
|
</Img>
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Pagination, Menu, Spin } from "antd";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Long,
|
||||||
|
Short,
|
||||||
|
Gap,
|
||||||
|
WhiteBack,
|
||||||
|
LeftLine,
|
||||||
|
AlignCenter,
|
||||||
|
} from "./css/layout";
|
||||||
|
import Title from "./Component/Title";
|
||||||
|
import ListItem from "./Component/ListItem";
|
||||||
|
import "./css/All.scss";
|
||||||
|
import ListSearch from "./Component/ListSearch";
|
||||||
|
import UrlItem from "./Component/UrlItem";
|
||||||
|
import BestItem from "./Component/BestItem";
|
||||||
|
import DoubleItem from "./Component/DoubleItem";
|
||||||
|
import "./css/Theme.scss";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const PAGESIZE = 10; // 首页帖子每页只展示5条
|
||||||
|
function aa(props) {
|
||||||
|
const current_user = props.current_user;
|
||||||
|
const [data, setData] = useState(undefined); //接口所有数据
|
||||||
|
const [memos, setMemos] = useState([]); //帖子列表
|
||||||
|
const [sort, setSort] = useState("published_at"); //最新最热
|
||||||
|
const [listSpin, setListSpin] = useState(true);
|
||||||
|
const [memosCount, setMemosCount] = useState([]); //帖子数量
|
||||||
|
|
||||||
|
const [page, setPage] = useState(1); //分页
|
||||||
|
const [search, setSearch] = useState(undefined); //搜索内容
|
||||||
|
const [selectKey, setSelectKey] = useState(["hot"]);
|
||||||
|
|
||||||
|
const [hotMemos, setHotMemos] = useState([]); //热门推荐、版主推荐
|
||||||
|
const [forumSections, setForumSections] = useState([]); //二级模块
|
||||||
|
|
||||||
|
const [plate, setPlate] = useState(undefined); // 所有版块
|
||||||
|
useEffect(() => {
|
||||||
|
init();
|
||||||
|
}, [page, search, sort]);
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
setListSpin(true);
|
||||||
|
let url = `/memos.json`;
|
||||||
|
axios
|
||||||
|
.get(url, {
|
||||||
|
params: { page, search, sort , limit:PAGESIZE },
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result) {
|
||||||
|
setData(result.data);
|
||||||
|
setMemos(result.data.memos);
|
||||||
|
setListSpin(false);
|
||||||
|
setMemosCount(result.data.memos_count);
|
||||||
|
setHotMemos(result.data.hottest_memos);
|
||||||
|
setForumSections(result.data.forum_sections);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function init() {
|
||||||
|
let url = `/forum_sections.json`;
|
||||||
|
axios
|
||||||
|
.get(url, {
|
||||||
|
params: { is_detail: true },
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result) {
|
||||||
|
setPlate(result.data.forum_sections);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
}, []);
|
||||||
|
// 翻页
|
||||||
|
function changePage(page) {
|
||||||
|
setPage(page);
|
||||||
|
}
|
||||||
|
// 搜索
|
||||||
|
function onSearch(e) {
|
||||||
|
setSearch(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeMenu(e) {
|
||||||
|
setSelectKey(e.key);
|
||||||
|
if (e.key === "hot") {
|
||||||
|
setHotMemos(data.hottest_memos);
|
||||||
|
} else {
|
||||||
|
setHotMemos(data.recommend_memos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeSort(value) {
|
||||||
|
setSort(value);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="clearfix educontent pt20">
|
||||||
|
<Box>
|
||||||
|
<Long>
|
||||||
|
<WhiteBack>
|
||||||
|
<Title>
|
||||||
|
<span>论坛首页</span>
|
||||||
|
<AlignCenter>
|
||||||
|
<span
|
||||||
|
onClick={() => changeSort("published_at")}
|
||||||
|
className={
|
||||||
|
sort === "published_at" ? "green cPointer" : "cPointer"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
最新
|
||||||
|
</span>
|
||||||
|
<LeftLine
|
||||||
|
onClick={() => changeSort("replies_count")}
|
||||||
|
className={
|
||||||
|
sort === "published_at" ? "cPointer" : "green cPointer"
|
||||||
|
}
|
||||||
|
style={{ fontSize: "16px" }}
|
||||||
|
>
|
||||||
|
最热
|
||||||
|
</LeftLine>
|
||||||
|
</AlignCenter>
|
||||||
|
</Title>
|
||||||
|
<Spin spinning={listSpin}>
|
||||||
|
<div style={{ minHeight: "868px" }}>
|
||||||
|
<ListItem
|
||||||
|
memos={memos}
|
||||||
|
current_user={current_user}
|
||||||
|
calbackFunc={init}
|
||||||
|
confirm={props.confirm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Spin>
|
||||||
|
<div className="center" style={{ padding: "25px 0px" }}>
|
||||||
|
<Pagination
|
||||||
|
showQuickJumper
|
||||||
|
current={page}
|
||||||
|
onChange={changePage}
|
||||||
|
total={memosCount}
|
||||||
|
pageSize={PAGESIZE}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</WhiteBack>
|
||||||
|
</Long>
|
||||||
|
<Short>
|
||||||
|
<Gap>
|
||||||
|
<WhiteBack style={{ marginBottom: "15px" }}>
|
||||||
|
<div style={{ padding: "20px" }}>
|
||||||
|
<ListSearch onSearch={onSearch} current_user={current_user} showLoginDialog={props && props.showLoginDialog}/>
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: "0px 20px", borderTop: "1px solid #eee" }}>
|
||||||
|
<UrlItem user={current_user} />
|
||||||
|
</div>
|
||||||
|
</WhiteBack>
|
||||||
|
<WhiteBack style={{ marginBottom: "15px" }}>
|
||||||
|
<Title>热门话题</Title>
|
||||||
|
<BestItem memos={data && data.hottest_memos} selectKey={selectKey} />
|
||||||
|
</WhiteBack>
|
||||||
|
<WhiteBack>
|
||||||
|
<Title>版主推荐</Title>
|
||||||
|
<BestItem memos={data && data.recommend_memos} selectKey={selectKey} />
|
||||||
|
</WhiteBack>
|
||||||
|
</Gap>
|
||||||
|
</Short>
|
||||||
|
</Box>
|
||||||
|
{plate && plate.length > 0
|
||||||
|
? plate.map((item, key) => {
|
||||||
|
return item.children_tags && item.children_tags.length > 0 ? (
|
||||||
|
<WhiteBack style={{ marginBottom: "15px" }}>
|
||||||
|
|
||||||
|
{/* <Title className="fwt-600">{item.name}</Title> */}
|
||||||
|
<Title className="fwt-600">
|
||||||
|
<Link to={`/forums/theme/${item.id}`}>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<DoubleItem plate={item.children_tags} {...props} />
|
||||||
|
</WhiteBack>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default aa;
|