diff --git a/src/App.js b/src/App.js index aed2f9a3..4d953e6b 100644 --- a/src/App.js +++ b/src/App.js @@ -61,6 +61,10 @@ const Shixunnopage = Loadable({ loader: () => import('./modules/404/Shixunnopage'), loading: Loading, }) +const ExchangeForums = Loadable({ + loader: () => import("./exchange/index"), + loading: Loading, +}); // 新版论坛交流 const Forums = Loadable({ loader: () => import("./forums/Index"), @@ -245,6 +249,13 @@ class App extends Component { )} > + {/* 论坛交流板块管理 */} + ( + + )} + > {/* 问吧、论坛交流 */} { + return( +
  • + + {item.filename} + ({bytesToSize(`${item.filesize}`)}) +
  • + ) + }) + return( + + ) + } +} + +export default FileList; \ No newline at end of file diff --git a/src/common/MDEditor.js b/src/common/MDEditor.js new file mode 100644 index 00000000..1b57d79b --- /dev/null +++ b/src/common/MDEditor.js @@ -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 = '您上次有已保存的数据,是否恢复 ? / 不恢复'; + $("#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: "
    ", + testIcon1: "
    ", + nullBtton: "
    点击插入填空项
    ", + }, + //这个配置在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 ( + +
    + {/* padding10-20 */} +
    + +
    +
    +
    +
    + { + noStorage == true ? '' : +
    +

     

    + {/* {noStorage == true ? ' ' :

     

    } */} +
    + } +
    + ) + } +} + + diff --git a/src/common/mdEditor.css b/src/common/mdEditor.css new file mode 100644 index 00000000..37a65ef9 --- /dev/null +++ b/src/common/mdEditor.css @@ -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: "微软雅黑","宋体"; +} \ No newline at end of file diff --git a/src/exchange/Comments/CommentsItem.js b/src/exchange/Comments/CommentsItem.js new file mode 100644 index 00000000..1dd4850a --- /dev/null +++ b/src/exchange/Comments/CommentsItem.js @@ -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 && + deleteReplyEvent(id)} + onCancel={this.cancelEvent} + okText="确定" + cancelText="取消" + > + 删除 + + ) + return( + +

    + {username} + {time} + + { adminDelete } +

    + +
    + ) + } +} + +export default CommentsItem; \ No newline at end of file diff --git a/src/exchange/Comments/CommentsItemImg.js b/src/exchange/Comments/CommentsItemImg.js new file mode 100644 index 00000000..83db6ceb --- /dev/null +++ b/src/exchange/Comments/CommentsItemImg.js @@ -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( + 用户头像 + ) + } +} + +export default CommentsItemImg; \ No newline at end of file diff --git a/src/exchange/Comments/CommentsSend.js b/src/exchange/Comments/CommentsSend.js new file mode 100644 index 00000000..a0a5c713 --- /dev/null +++ b/src/exchange/Comments/CommentsSend.js @@ -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 && + ) + return( +
    + { imgWrap } +
    + + {getFieldDecorator('replyContent', { + rules: [{ + required: true, message: '请输入评论内容', + },{ + max: 5000 , message:'最大限制5000个字符' + }], + })( + + )} + +

    + +

    +
    +
    + ) + } +} +const WrappedCommentsSendForm = Form.create({ name: 'CommentsSend' })(CommentsSend); +export default WrappedCommentsSendForm; diff --git a/src/exchange/Comments/comment.css b/src/exchange/Comments/comment.css new file mode 100644 index 00000000..e69de29b diff --git a/src/exchange/CustomComponent/Index.js b/src/exchange/CustomComponent/Index.js new file mode 100644 index 00000000..df5c0ff8 --- /dev/null +++ b/src/exchange/CustomComponent/Index.js @@ -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 ( + + 用户头像 + {name} + + ) + } +} + +export default Index; \ No newline at end of file diff --git a/src/exchange/CustomComponent/custom.css b/src/exchange/CustomComponent/custom.css new file mode 100644 index 00000000..c9ab33df --- /dev/null +++ b/src/exchange/CustomComponent/custom.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/Empty.js b/src/exchange/Empty.js new file mode 100644 index 00000000..e4d98252 --- /dev/null +++ b/src/exchange/Empty.js @@ -0,0 +1,15 @@ +import React, { PureComponent } from 'react'; + +import nodata from './images/nodata.png'; +class Empty extends PureComponent{ + + render(){ + return( +
    + 暂无数据 +

    暂无数据

    +
    + ) + } +} +export default Empty; \ No newline at end of file diff --git a/src/exchange/ExchangeIndex.js b/src/exchange/ExchangeIndex.js new file mode 100644 index 00000000..3a4f2040 --- /dev/null +++ b/src/exchange/ExchangeIndex.js @@ -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 = ( +
    + { + forum_sections && forum_sections.map(item => { + return( +
    + {item.name} +
      + { + item.children_tags && item.children_tags.map(i=>{ + return(
    • {i.title}
    • ) + }) + } +
    +
    + ) + }) + } +
    + ); + + + + // 数据列表和暂无数据 + const dataList = memos && memos.length > 0 ? + + : +
    + + return( +
    +
    +
    +
    + +
      +
    • 我的话题
    • +
    • 我的收藏
    • +
    • 我感兴趣的论坛
    • +
    +
    + + { dataList } + +
    + { + memos_count > pageSize && +
    + } +
    + +
    + ) + } +} +export default ExchangeIndex; \ No newline at end of file diff --git a/src/exchange/ExchangeItem.js b/src/exchange/ExchangeItem.js new file mode 100644 index 00000000..cf0911ae --- /dev/null +++ b/src/exchange/ExchangeItem.js @@ -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( + + + + ) + }else if(detail){ + return( + this.enShrineEvent(id,memo_watched)}> + { memo_watched ? '取消收藏' : '收藏' } + + ) + } + } + } + + // 右侧dropdown的选项 + InitlistMenu = (id,sticky,is_fine,memo_watched) => ( + + this.topEvent(sticky,id)} > { sticky ? '取消置顶' : '置顶' } + this.bestEvent(is_fine,id)}> { is_fine ? '取消推荐' : '推荐' } + this.enShrineEvent(id,memo_watched)}> { memo_watched ? '取消收藏' : '收藏' } + this.editEvent(id)}> 编辑 + this.deleteEvent(id)}> 删除 + + ) + + + // 取消收藏+收藏 + 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`; + + 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`; + + 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( +
  • +
    +
    + +

    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} +

    + {/* 右侧的下拉操作菜单项:登录后才能有右侧信息 */} + { current_user && this.InitItemMenu(item.id,item.sticky,item.is_fine,item.memo_watched) } +
    +
    +
    + {/* 判断用户是否登录 */} + + { item.forum_section_title && 发表在{item.forum_section_title} } + { item.time && {item.time} } + + { + item.new_reply && item.new_reply.username && + + 最新回复: + {item.new_reply.username} + {item.new_reply.time} + + + } + + {/* 论坛首页和论坛详情公用,不同情况下左浮动或者有浮动 */} + + + + + + +
    +
  • + ) + }) + return( +
    + { ListItem } +
    + ) + } +} +export default ExchangeItem; diff --git a/src/exchange/ExchangeRight.js b/src/exchange/ExchangeRight.js new file mode 100644 index 00000000..24cc677a --- /dev/null +++ b/src/exchange/ExchangeRight.js @@ -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 && ( +
    +

    + + 热门话题 +

    +
      + { + hottest_memos.map((apply)=>{ + return( +
    • {apply.subject}
    • + ) + }) + } +
    +
    + ) + + // 版主推荐 + const moderatorList = recommend_memos && recommend_memos.length > 0 && ( +
    +

    + + 版主推荐 +

    +
      + { + recommend_memos.map((apply)=>{ + return( +
    • {apply.subject}
    • + ) + }) + } +
    +
    + ) + + return( +
    + { + !hideSearchPanel && + } + + + {/* 热门话题 */} + {hotList} + + {/* 版主推荐 */} + { moderatorList } +
    + ) + } +} +export default ExchangeRight; \ No newline at end of file diff --git a/src/exchange/ExchangeRightSearch.js b/src/exchange/ExchangeRightSearch.js new file mode 100644 index 00000000..f11ffaa1 --- /dev/null +++ b/src/exchange/ExchangeRightSearch.js @@ -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(发布话题) + }else{ + return(发布话题) + } + } + return( +
    + {sendBtn()} + { + searchDefault ? + + : + + } +
    + ) + } +} +export default ExchangeRightSearch; \ No newline at end of file diff --git a/src/exchange/InfoComponent/Index.js b/src/exchange/InfoComponent/Index.js new file mode 100644 index 00000000..7d1d69ad --- /dev/null +++ b/src/exchange/InfoComponent/Index.js @@ -0,0 +1,19 @@ +import React, { PureComponent } from 'react'; + +class Index extends PureComponent { + + render() { + const { icon , count = 0 , ...props } = this.props; + + return ( + + + + {count} + + + ) + } +} + +export default Index; \ No newline at end of file diff --git a/src/exchange/Manage/AddModeratorModal.js b/src/exchange/Manage/AddModeratorModal.js new file mode 100644 index 00000000..f403d3ce --- /dev/null +++ b/src/exchange/Manage/AddModeratorModal.js @@ -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{ + + }) + } + 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( + +
    +
    +
    + this.searchEvent(value)} + /> +
    +
    + + + { + total && total > limit ? +
    + +
    :"" + } + +
    + 取消 + 确定 +
    + + + ) + } +} +export default AddModeratorModal; \ No newline at end of file diff --git a/src/exchange/Manage/CheckPublic.js b/src/exchange/Manage/CheckPublic.js new file mode 100644 index 00000000..a10b008e --- /dev/null +++ b/src/exchange/Manage/CheckPublic.js @@ -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( +
    + +
    + ) + } +} + +export default CheckPublic; \ No newline at end of file diff --git a/src/exchange/Manage/ItemLeft.js b/src/exchange/Manage/ItemLeft.js new file mode 100644 index 00000000..dd693e13 --- /dev/null +++ b/src/exchange/Manage/ItemLeft.js @@ -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( + +
    +

    + + {time} + { forum_title && 发表在 {forum_title} } + { source_title && 来源 {source_title} } +

    + { memo_title &&

    {memo_title}

    } + { + reply_content && + + + + } +
    + + ) + } +} + +export default ItemLeft; \ No newline at end of file diff --git a/src/exchange/Manage/ModeratorNav.js b/src/exchange/Manage/ModeratorNav.js new file mode 100644 index 00000000..07dc9f3f --- /dev/null +++ b/src/exchange/Manage/ModeratorNav.js @@ -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( +
    +
    +
      + { + item.map((i,key)=>{ + return(
    • {i.title}
    • ) + }) + } +
    +
    +
    + ) + } + } + + const titleFlag = () => { + if (forum_tag ) { + console.log(forum_tag.children_bread_crumb); + if (bread_crumb && bread_crumb.is_children) { + return (
    {forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.title}
    ) + } else { + + return ( +
    + { forum_tag.children_bread_crumb ? + +
    {forum_tag.title}
    +
    + : + {forum_tag.title} + } +
    + ) + } + } + + } + return( +
    + + {titleFlag()} +
    + ) + } +} + +export default ModeratorNav; \ No newline at end of file diff --git a/src/exchange/Manage/PassItem.js b/src/exchange/Manage/PassItem.js new file mode 100644 index 00000000..91496e0d --- /dev/null +++ b/src/exchange/Manage/PassItem.js @@ -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( +

    + this.passEvent(true)}>通过 + this.passEvent(false)}>不通过 +

    + ) + } +} + +export default PassItem; \ No newline at end of file diff --git a/src/exchange/Manage/PreApplyPlate.js b/src/exchange/Manage/PreApplyPlate.js new file mode 100644 index 00000000..6c67f166 --- /dev/null +++ b/src/exchange/Manage/PreApplyPlate.js @@ -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( +
    +

    版主申请

    +
    + { applylist.map((item,key)=>{ + return( +
    + 用户头像 +
    +

    + {item.username} + { item.user_ip && IP:{item.user_ip}({item.user_ip_address}) } +

    +

    申请成为”{item.forum_title}“的版主

    +
    +
    +

    {item.time}

    +

    + this.passApplyEvent(true,item.apply_id)}>通过 + this.passApplyEvent(false,item.apply_id)}>拒绝 +

    +
    +
    + ) + }) + } +
    +
    + ) + } + } + return( + + {listItem()} + + ) + } +} +export default PreApplyPlate; \ No newline at end of file diff --git a/src/exchange/Manage/PreCreate.js b/src/exchange/Manage/PreCreate.js new file mode 100644 index 00000000..ec133f97 --- /dev/null +++ b/src/exchange/Manage/PreCreate.js @@ -0,0 +1,87 @@ +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/${plateId}${subId ? "/rename":""}.json`; + axios.post(url,{ + title:values.title, + children_section_id:subId + }).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( + +
    +
    + + {getFieldDecorator('title', { + rules: [{ + required: true, message: '请输入板块名称', + },{ + max: 5000 , message:'最大限制20个字符' + }], + })( + + )} + +
    + 取消 + 确定 +
    + +
    +
    + ) + } +} +const WrappedPreCreate = Form.create({ name: 'PreCreate' })(PreCreate); +export default WrappedPreCreate; \ No newline at end of file diff --git a/src/exchange/Manage/PreModerator.js b/src/exchange/Manage/PreModerator.js new file mode 100644 index 00000000..f72c4ac3 --- /dev/null +++ b/src/exchange/Manage/PreModerator.js @@ -0,0 +1,77 @@ +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( + + + + {/* 板块申请 */} + + + ) + } + } + return( +
    + + + + {parentManage()} + + {/* 审批(二级版主只有这一块) */} + +
    + ) + } +} + +export default PreModerator; \ No newline at end of file diff --git a/src/exchange/Manage/PrePlateManage.js b/src/exchange/Manage/PrePlateManage.js new file mode 100644 index 00000000..0bfbe443 --- /dev/null +++ b/src/exchange/Manage/PrePlateManage.js @@ -0,0 +1,285 @@ +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/${plateId}/destroy.json` + axios.post(url,{ + children_section_id:id + }).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( +
    + { + children_tags.map((item,key)=>{ + return( +
    +
    +

    + {item.title} + + this.RenamneSubPlateEvent(item.id,item.title)}>重命名 + this.DeleteSubPlateEvent(item.id)}>删除板块 + +

    +

    二级版主

    +
    + { renderSubList(item.forum_moderators,key,item.isDeleting) } + + {/* 展开 */} + { + item.forum_moderators && item.forum_moderators.length > 5 && !item.expand && + this.expandEvent(key,true)}> + } + {/* 收起 */} + { + item.forum_moderators && item.forum_moderators.length > 5 && item.expand && + this.expandEvent(key,false)}> + } + {/* 新增版主 */} + this.showAddBox(true,item.id)}> + {/* 删除版主 */} + { + item.forum_moderators && item.forum_moderators.length > 0 && !item.isDeleting && + this.deleteManageEvent(key,true)}> + } + { + item.isDeleting && this.deleteManageEvent(key,false)}>完成 + } + + +
    +
    +
    + ) + }) + } +
    + ) + } + } + + const renderSubList = (item,index,deletingFlag) =>{ + if(item){ + return( +
      + { + item.map((i,key)=>{ + return( + + { + !i.isDelete && +
    • + + { + deletingFlag && + this.deletePlateEvent(`${i.moderator_id}`,`${i.username}`,index,key)}> + } + {i.username} +
    • + } +
      + + ) + }) + } +
    + ) + } + } + return( +
    +
    + + + + 二级板块管理 + 新建板块 +
    + {forumTagList()} +
    + ) + } +} + +export default PrePlateManage; \ No newline at end of file diff --git a/src/exchange/Manage/SubCheckItem.js b/src/exchange/Manage/SubCheckItem.js new file mode 100644 index 00000000..97654782 --- /dev/null +++ b/src/exchange/Manage/SubCheckItem.js @@ -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 && +
    + +
    + ) + + const dataList = ( + + data && data.memos_lists && data.memos_lists.length > 0 ? +
    + { + 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( +
    +
    + +
    + { + current_user && current_user.admin && + ( item.is_banned ? +

    this.stopEvent(item.memo_id,0,item.user_id)}>已禁言

    + : +

    this.stopEvent(item.memo_id,1,item.user_id)}>禁言

    + ) + } + + +
    + +
    + +
    + ) + }) + } + { pageCom } +
    + : +
    + +
    + ) + + return( + + { dataList } + + ) + } +} + +export default SubCheckItem; \ No newline at end of file diff --git a/src/exchange/Manage/SubCheckReplyItem.js b/src/exchange/Manage/SubCheckReplyItem.js new file mode 100644 index 00000000..7c92542f --- /dev/null +++ b/src/exchange/Manage/SubCheckReplyItem.js @@ -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 && +
    + +
    + ) + + const dataList = ( + data && data.replies_lists && data.replies_lists.length > 0 ? +
    + { + 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( + +
    +
    + +
    + { + current_user && current_user.admin && + ( item.is_banned ? +

    this.stopEvent(item.reply_id,0,item.user_id)}>已禁言

    + : +

    this.stopEvent(item.reply_id,1,item.user_id)}>禁言

    + ) + } + + +
    +
    + +
    + + + ) + }) + } + + { pageCom } +
    + : +
    + +
    + ) + + return( + + { dataList } + + ) + } +} + + +export default SubCheckReplyItem; \ No newline at end of file diff --git a/src/exchange/Manage/SubSendItem.js b/src/exchange/Manage/SubSendItem.js new file mode 100644 index 00000000..2a0e49d4 --- /dev/null +++ b/src/exchange/Manage/SubSendItem.js @@ -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`; + 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`; + 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 && +
    + +
    + ) + const dataList = ( + data && data.memos_lists && data.memos_lists.length > 0 ? +
    + { + 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( +
    +
    + +
    +

    + +

    +

    + this.stickyEvent(item.memo_id,item.sticky)}>{item.sticky?"取消置顶":"置顶"} + this.isFineEvent(item.memo_id,item.is_fine)}>{item.is_fine?"取消推荐":"推荐"} + this.deleteEvent(item.memo_id)}>删除 +

    +
    +
    +
    + ) + }) + } + { pageCom } +
    + : +
    + +
    + ) + + return( + + { dataList } + + ) + } +} +export default SubSendItem; \ No newline at end of file diff --git a/src/exchange/Manage/manage.css b/src/exchange/Manage/manage.css new file mode 100644 index 00000000..d698800a --- /dev/null +++ b/src/exchange/Manage/manage.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/MenuComponent/Menu.css b/src/exchange/MenuComponent/Menu.css new file mode 100644 index 00000000..303e3150 --- /dev/null +++ b/src/exchange/MenuComponent/Menu.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/MenuComponent/Menu.js b/src/exchange/MenuComponent/Menu.js new file mode 100644 index 00000000..2dd8c0e8 --- /dev/null +++ b/src/exchange/MenuComponent/Menu.js @@ -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( + + + + ) + }) + + let { activeKey } = this.state; + + return( + + { tabs } + + ) + } +} +export default MenuCom; \ No newline at end of file diff --git a/src/exchange/MyExchange/Index.js b/src/exchange/MyExchange/Index.js new file mode 100644 index 00000000..19b914c9 --- /dev/null +++ b/src/exchange/MyExchange/Index.js @@ -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( +
    + +
    +
    +
    + +
    +
    + +
    +
    + + ) + } +} +export default Index; \ No newline at end of file diff --git a/src/exchange/MyExchange/MyEnshrine.js b/src/exchange/MyExchange/MyEnshrine.js new file mode 100644 index 00000000..ec4b1a93 --- /dev/null +++ b/src/exchange/MyExchange/MyEnshrine.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react'; + +import MyTopicItem from './MyTopicItem'; + + +class MyEnshrine extends Component { + + render(){ + return( +
    + +
    + ) + } +} + +export default MyEnshrine; \ No newline at end of file diff --git a/src/exchange/MyExchange/MyInteresting.js b/src/exchange/MyExchange/MyInteresting.js new file mode 100644 index 00000000..73bcd785 --- /dev/null +++ b/src/exchange/MyExchange/MyInteresting.js @@ -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 ? +
    + { + forum_details.map((item,key) => { + const index = Number(Number((key)%6)+1)-1; + // console.log(index); + return( +
    +
    +
    +

    {item.title}

    +

    {item.memos_count} 个话题

    +
    +
    + + this.cancelEnshrineEvent(`${item.id}`)}>取消收藏 + 查看 + +
    + ) + }) + } +
    + : +
    + ) + return( + +
    + { divList } +
    +
    + ) + } +} + +export default MyInteresting; \ No newline at end of file diff --git a/src/exchange/MyExchange/MyTopic.js b/src/exchange/MyExchange/MyTopic.js new file mode 100644 index 00000000..541cd2aa --- /dev/null +++ b/src/exchange/MyExchange/MyTopic.js @@ -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( +
    + +
    + ) + } +} + +export default MyTopic; \ No newline at end of file diff --git a/src/exchange/MyExchange/MyTopicItem.js b/src/exchange/MyExchange/MyTopicItem.js new file mode 100644 index 00000000..f3728d70 --- /dev/null +++ b/src/exchange/MyExchange/MyTopicItem.js @@ -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 =( +
    + { + data && data.forum_sections && data.forum_sections.map(item => { + return( +
    + this.changeForumId(item.id,item.name)}>{item.name} + +
    + ) + }) + } +
    + ) + + // 全部帖子 + const forumList = ( + + this.forumType('hidden')}>待审查的话题 + this.forumType('show')}>已发布的话题 + + ) + + + const pagination = ( + data && data.memos && data.memos_count > pageSize && +
    + +
    + ) + + const dataList = ( + +
    + + + {plateName || "选择板块"} + + + + + { is_hidden_name || '全部帖子' } + + +
    + 开始日期 + + 结束日期 + +
    + + 搜索 + 清除 + + { data && data.memos_count }个结果 +
    + { + data && data.memos && data.memos.length>0 ? +
    + + { pagination } +
    + : +
    + +
    + } +
    + ) + return( dataList ) + } +} + +export default MyTopicItem; \ No newline at end of file diff --git a/src/exchange/MyExchange/myExchange.css b/src/exchange/MyExchange/myExchange.css new file mode 100644 index 00000000..291a69aa --- /dev/null +++ b/src/exchange/MyExchange/myExchange.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/NavComponent/Index.js b/src/exchange/NavComponent/Index.js new file mode 100644 index 00000000..915158cb --- /dev/null +++ b/src/exchange/NavComponent/Index.js @@ -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( + + { + item.name ? (item.url ? + { item.name } + : + { item.name }) + :"" + } + + ) + }) + return( +

    { navRouter }

    + ) + } + +} +export default Index; \ No newline at end of file diff --git a/src/exchange/NavComponent/nav.css b/src/exchange/NavComponent/nav.css new file mode 100644 index 00000000..b453349b --- /dev/null +++ b/src/exchange/NavComponent/nav.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/Plate/All.js b/src/exchange/Plate/All.js new file mode 100644 index 00000000..07e403a5 --- /dev/null +++ b/src/exchange/Plate/All.js @@ -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 ? + + : +
    + +
    + ) + return( + memosList + ) + } +} +export default All; \ No newline at end of file diff --git a/src/exchange/Plate/Index.js b/src/exchange/Plate/Index.js new file mode 100644 index 00000000..e931978f --- /dev/null +++ b/src/exchange/Plate/Index.js @@ -0,0 +1,264 @@ +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); + + console.log("c",children_bread_crumb) + // 顶部导航栏 + 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? this.colectPlate()}>{collectFlag ?'取消收藏':'收藏'}: 收藏 ; + + // 菜单行右侧按钮 + //只有登录管理员可见 + + const btn = (current_user && current_user.admin_permission) ?版块管理 : undefined; + console.log(btn); + + + // 操作列表item需要刷新页面以及切换菜单项需要重新调用参数 + const commonEvent = { + refresh:this.InitData, + changeTab:this.changeTabEvent + } + + // 列表分页 + const pageination = ( + data && data.memos_count > pageSize && +
    + +
    + ) + + + return( +
    + + + +

    + {title_post} + { collectMap } +

    + +
    +
    +
    + + + +
    + { pageination } +
    + +
    +
    +
    + ) + } +} +export default Index; \ No newline at end of file diff --git a/src/exchange/Plate/PlateRight.js b/src/exchange/Plate/PlateRight.js new file mode 100644 index 00000000..20a96c3f --- /dev/null +++ b/src/exchange/Plate/PlateRight.js @@ -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 && +
    +

    + + 版主 + { + current_user && current_user.admin_permission === false && 申请版主 + } +

    +
    + { forum_moders.map((item,key)=>{ + return( + + ) + }) } +
    +
    + ) + + + const choiceInfo = ( forum_sections && forum_sections.length > 0 && +
    +

    + + 精选版块 +

    + +
    + ) + + + return( +
    + + + { moderatorInfo } + + { choiceInfo } +
    + ) + } +} +export default PlateRight; \ No newline at end of file diff --git a/src/exchange/Plate/plate.css b/src/exchange/Plate/plate.css new file mode 100644 index 00000000..f1bb1cad --- /dev/null +++ b/src/exchange/Plate/plate.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/Post/CommentsIndex.js b/src/exchange/Post/CommentsIndex.js new file mode 100644 index 00000000..ed878d8d --- /dev/null +++ b/src/exchange/Post/CommentsIndex.js @@ -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(

    展开其余1条评论

    ) + } + + //二级评论 + const renderChild = (arrs) => { + return( +
    + { + arrs.map((child)=>{ + return( +
    + +
    + +
    +
    + ) + }) + } +
    + ) + } + + const replyInfoWrap = (i,index)=> ( + +

    + this.praiseEvent(i.id)}> + + {i.praise_count} + + { + current_user && (current_user.is_banned === false || current_user.admin) && + this.commentReplyEvent(index,i.commentsFlag)}> + {i.replies_count} + + } +

    + { + i.commentsFlag && + + } +
    + ) + + // 判断是否是管理员或者版主 + 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( +
    +
    + +
    + + { + i.children && i.children.length > 0 && +
    + {renderChild(i.children)} + {/* { i.children.length > 1 && renderNum(i.children)} */} +
    + } + + {replyInfoWrap(i,index)} +
    +
    +
    + ) + }) + + return( itemMap ) + } +} + +export default CommentsIndex; \ No newline at end of file diff --git a/src/exchange/Post/ImageUpload.js b/src/exchange/Post/ImageUpload.js new file mode 100644 index 00000000..dc15691a --- /dev/null +++ b/src/exchange/Post/ImageUpload.js @@ -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 = ( +
    +
    +
    点击上传封面
    +
    只支持JPG、PNG、JPEG大小不超过2M
    +
    建议尺寸4:3
    +
    + ); + + return ( +
    + + {fileList.length >= 1 ? null : uploadButton} + + + example + +
    + ); + } +} + +export default ImageUpload; diff --git a/src/exchange/Post/Index.js b/src/exchange/Post/Index.js new file mode 100644 index 00000000..90beaa1d --- /dev/null +++ b/src/exchange/Post/Index.js @@ -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 && +
    +

    + 全部回复 + {replyCount} +

    + +
    + ) + + // 附件显示 + const fileListCom = ( memos && memos.attachment_url && ) + + return( + +
    + + +
    + + {/* 调用和首页共用的列表item,传了enshine就代表底部右侧操作按钮只有收藏或者取消收藏,否则就是置顶、推荐等 */} + + +
    + {/* 帖子详情 */} + + {/* 文件列表 */} + { fileListCom } + + {/* 点赞 */} +
    + +
    + + {/* 发送评论部分 */} + { + current_user && (current_user.is_banned === false || current_user.admin) && + this.getReply(replyPage)}/> + } + {/* 评论列表 */} + { repliesList } +
    +
    + { + replyCount && replyCount > replyPageSize ? +
    + +
    :"" + } + +
    +
    + ) + } +} + +export default Index; \ No newline at end of file diff --git a/src/exchange/Post/MemoNew.css b/src/exchange/Post/MemoNew.css new file mode 100644 index 00000000..7a7cc758 --- /dev/null +++ b/src/exchange/Post/MemoNew.css @@ -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; +} diff --git a/src/exchange/Post/MemoNew.js b/src/exchange/Post/MemoNew.js new file mode 100644 index 00000000..23a55717 --- /dev/null +++ b/src/exchange/Post/MemoNew.js @@ -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 : "
    ", + testIcon1 : "
    " + }, + //这个配置在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 + } + /* + + + + 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( $('') ) + $('head').append( $(``) ) + } + }).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( + + ) + }) + 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) => ) + memo_type.forEach((item, index) => { + result.push() + }) + + 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() + }) + + return result; + + } + renderAttachment() { + const { attachments_url } = this.state; + const attachments = [] + attachments_url.forEach((item, index) => { + const ar = item.url.split('/') + + attachments.push( + + + + + + + +
    + + +
    +
    +
    + ) + }) + 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( + + ) + }), + 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 ( +
    +

    { memoId ? '编辑' : '新建'}

    + +
    +
    +
    + *标题: +
    + this.onMemoNameChange(val)} placeholder="请输入发布标题,最大限制50字符"> + +
    +
    + + 必填项 + +
    +
    + + + {/* 发布时间 */} + {/*
    + +
    + this.onOkTime(value, dateString)} + /> +
    +
    + + 必填项 + +
    +
    */} + + {/*

    内容

    */} +
    + *内容: +
    +
    + +
    +
    +
    + + 必填项 + +
    +
    + +
    + + { attachments_url && !!attachments_url.length && + this.renderAttachment() + } + + + {debugger;window.addInputFiles( window.$('.file_selector')[0] ) }} + style={{'display':'none'}} type="file"> + + + + + {/* 请求status 422 X-CSRF-Token: eVo38laEF880o3cwZ/0F9kH01q4jMkriuVRemIBq06Y= */} + + + {/* 选择了技术问答才显示树标签 */} + {/* this._isTechShare() && +
    +
    +

    技术标签

    +
    + * + +
    + +
    + +
    + + 必填项 + +
    +
    +
    +
    */} +
    + *主题板块: +
    + {/* */} + {/* */} + + +
    +
    + + 必填项 + +
    +
    + + {/* */} +
    + 上传封面: +
    + +
    +
    + + 必填项 + +
    +
    +
    +
    + + +
    + ); + } +} + +const children = []; +for (let i = 10; i < 36; i++) { + children.push(); +} + +export default MemoNew; diff --git a/src/exchange/Post/New.js b/src/exchange/Post/New.js new file mode 100644 index 00000000..889e36e3 --- /dev/null +++ b/src/exchange/Post/New.js @@ -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 = ( +
    + +
    点击上传封面
    +

    只支持JPG、PNG、JPEG大小不超过2M建议尺寸4:3

    +
    +) + +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( + + ) + }) + }) + } + + // 提交 + 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( + + ) + }) + + let uploadImg = ( imageUrl ? avatar : 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( +
    +

    新建

    +
    +
    + + + {getFieldDecorator('title', { + rules: [{ + required: true, message: '请输入帖子标题', + },{ + max: 5000 , message:'最大限制60个字符' + }], + })( + + )} + + + {getFieldDecorator('description', { + rules: [{ + required: true, message: '请输入帖子内容', + },{ + max: 5000 , message:'最大限制5000个字符' + }], + })( + + )} + + + + +
    + + { + getFieldDecorator('bigPlate',{ + rules:[{ + required:true,message:'请选择大板块' + }] + })( + + ) + } + + + { + getFieldDecorator('smallPlate',{ + rules:[{ + required:true,message:'请选择小板块' + }] + })( + + ) + } + +
    +
    + + + {uploadImg} + +
    + +
    +

    + + 取消 + + +

    + +
    + ) + } +} + +const WrappedNewPostForm = Form.create({ name: 'New' })(New); +export default WrappedNewPostForm; \ No newline at end of file diff --git a/src/exchange/Post/Parse.js b/src/exchange/Post/Parse.js new file mode 100644 index 00000000..2cb38398 --- /dev/null +++ b/src/exchange/Post/Parse.js @@ -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 ? + + + {num} + + : + + + {num} + + ): + + + {num} + + ) + return( + + { info } + + ) + } +} + +export default Parse; \ No newline at end of file diff --git a/src/exchange/Post/post.css b/src/exchange/Post/post.css new file mode 100644 index 00000000..f9062c42 --- /dev/null +++ b/src/exchange/Post/post.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/SearchInfoComponent/Index.js b/src/exchange/SearchInfoComponent/Index.js new file mode 100644 index 00000000..0e527b23 --- /dev/null +++ b/src/exchange/SearchInfoComponent/Index.js @@ -0,0 +1,16 @@ +import React, { PureComponent } from 'react'; + +class Index extends PureComponent { + + render(){ + const {count, returnEvent} = this.props; + return( +

    + 共找到相关结果{count} + 返回 +

    + ) + } +} + +export default Index; diff --git a/src/exchange/TagComponent/Index.js b/src/exchange/TagComponent/Index.js new file mode 100644 index 00000000..2b670f44 --- /dev/null +++ b/src/exchange/TagComponent/Index.js @@ -0,0 +1,18 @@ +import React, { PureComponent } from 'react'; + +import { getImageUrl } from 'educoder'; + +class Index extends PureComponent { + + render(){ + let { topClass , bestClass } = this.props; + return( + + { topClass && } + { bestClass && } + + ) + } +} + +export default Index; \ No newline at end of file diff --git a/src/exchange/exchange.css b/src/exchange/exchange.css new file mode 100644 index 00000000..439cc4c4 --- /dev/null +++ b/src/exchange/exchange.css @@ -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; +} \ No newline at end of file diff --git a/src/exchange/images/box1.png b/src/exchange/images/box1.png new file mode 100644 index 00000000..dacf54e3 Binary files /dev/null and b/src/exchange/images/box1.png differ diff --git a/src/exchange/images/box2.png b/src/exchange/images/box2.png new file mode 100644 index 00000000..e7d68225 Binary files /dev/null and b/src/exchange/images/box2.png differ diff --git a/src/exchange/images/box3.png b/src/exchange/images/box3.png new file mode 100644 index 00000000..f1fb2e4f Binary files /dev/null and b/src/exchange/images/box3.png differ diff --git a/src/exchange/images/box4.png b/src/exchange/images/box4.png new file mode 100644 index 00000000..34054d3e Binary files /dev/null and b/src/exchange/images/box4.png differ diff --git a/src/exchange/images/box5.png b/src/exchange/images/box5.png new file mode 100644 index 00000000..072c174f Binary files /dev/null and b/src/exchange/images/box5.png differ diff --git a/src/exchange/images/box6.png b/src/exchange/images/box6.png new file mode 100644 index 00000000..0215a25d Binary files /dev/null and b/src/exchange/images/box6.png differ diff --git a/src/exchange/images/more.png b/src/exchange/images/more.png new file mode 100644 index 00000000..c665263f Binary files /dev/null and b/src/exchange/images/more.png differ diff --git a/src/exchange/images/nodata.png b/src/exchange/images/nodata.png new file mode 100644 index 00000000..7c01edf3 Binary files /dev/null and b/src/exchange/images/nodata.png differ diff --git a/src/exchange/index.js b/src/exchange/index.js new file mode 100644 index 00000000..761e4f0b --- /dev/null +++ b/src/exchange/index.js @@ -0,0 +1,91 @@ +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=>{ + + }) + } + + render(){ + return( + + {/* 帖子列表 */} + () + } > + {/* 编辑帖子 */} + { + return ()} + }> + {/* 新建帖子 */} + { + return ()} + }> + {/* 版主管理 */} + { + return ()} + }> + + {/* 我的话题 */} + { + return ()} + }> + { + return ()} + }> + { + return ()} + }> + {/* 帖子详情 */} + { + return ()} + }> + {/* 论坛首页 */} + () + }> + + ) + } +} +export default SnackbarHOC() (CNotificationHOC() (TPMIndexHOC ( Index ))) ; \ No newline at end of file diff --git a/src/forums/New.jsx b/src/forums/New.jsx index ddb6efe9..b20d6027 100644 --- a/src/forums/New.jsx +++ b/src/forums/New.jsx @@ -8,7 +8,6 @@ import Attachments from '../forge/Upload/attachment'; import "./css/All.scss"; import axios from "axios"; -const TextArea = Input.TextArea; const Option = Select.Option; // forum_id: values.forum_id, //大主题版块的id, int类型 (必填)