add comment

chromesetting
tangjiang 5 years ago
parent ddc636a684
commit cd56975094

@ -52,7 +52,7 @@ export function initAxiosInterceptors(props) {
//proxy="http://47.96.87.25:48080"
proxy="https://pre-newweb.educoder.net"
proxy="https://test-newweb.educoder.net"
proxy="https://test-jupyterweb.educoder.net"
// proxy="https://test-jupyterweb.educoder.net"
//proxy="http://192.168.2.63:3001"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求

@ -3,22 +3,24 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-17 17:32:55
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-18 17:51:44
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:27:41
*/
import './index.scss';
import React, { useState } from 'react';
import { Form, Button, Input } from 'antd';
import QuillForEditor from '../../quillForEditor';
import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'
import {formatDelta} from './util';
const FormItem = Form.Item;
function CommentForm (props) {
const {
commentCtxChagne,
onCancel,
onSubmit,
form
form,
type
} = props;
const { getFieldDecorator } = form;
@ -34,22 +36,25 @@ function CommentForm (props) {
// const { form: { getFieldDecorator } } = props;
const [showQuill, setShowQuill] = useState(false);
// 点击输入框
const handleInputClick = () => {
const handleInputClick = (type) => {
setShowQuill(true);
}
// 取消
const handleCancle = () => {
setShowQuill(false);
onCancel && onCancel();
setCtx('');
props.form.resetFields();
onCancel && onCancel();
}
// 编辑器内容变化时
const handleContentChange = (content) => {
console.log('编辑器内容', content);
setCtx(content);
try {
const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
// const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert();
// props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')});
props.form.setFieldsValue({'comment': _html});
props.form.setFieldsValue({'comment': content});
} catch (error) {
console.log(error);
}
@ -63,13 +68,25 @@ function CommentForm (props) {
const content = ctx;
props.form.setFieldsValue({'comment': ''});
setCtx('');
console.log(content);
onSubmit && onSubmit(content);
const _html = formatDelta(content.ops);
onSubmit && onSubmit(_html);
}
});
}
const handleShowImage = (url) => {
alert(url);
}
// const _clazz = type === 'bottom' ? 'comment_form_bottom_area' : 'comment_form_area';
let _clazz;
if (type === 'bottom') {
_clazz = showQuill ? 'comment_form_bottom_area active' : 'comment_form_bottom_area';
} else {
_clazz = 'comment_form_area';
}
return (
<Form>
<Form className={_clazz}>
<FormItem>
{
getFieldDecorator('comment', {
@ -78,13 +95,13 @@ function CommentForm (props) {
],
})(
<Input
onClick={handleInputClick}
onClick={() => handleInputClick(type)}
placeholder="说点儿什么~"
className={showQuill ? '' : 'show_input'}
style={{
height: showQuill ? '0px' : '40px',
overflow: showQuill ? 'hidden' : 'auto',
opacity: showQuill ? 0 : 1,
transition: 'all .3s'
}}
/>
)
@ -98,14 +115,15 @@ function CommentForm (props) {
overflow: showQuill ? 'none' : 'hidden',
transition: 'all 0.3s'
}}
style={{ height: '150px', overflowY: 'auto' }}
style={{ height: '150px' }}
placeholder="说点儿什么~"
options={options}
value={ctx}
showUploadImage={handleShowImage}
onContentChange={handleContentChange}
/>
</FormItem>
<FormItem style={{ textAlign: 'right' }}>
<FormItem style={{ textAlign: 'right', display: showQuill ? 'block' : 'none' }}>
<Button onClick={handleCancle}>取消</Button>
<Button onClick={handleSubmit} type="primary" style={{ marginLeft: '10px'}}>发送</Button>
</FormItem>

@ -3,16 +3,19 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-18 10:49:46
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-18 11:39:23
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:05:14
*/
import './index.scss';
import React from 'react';
import { Icon } from 'antd';
// import MyIcon from '../MyIcon';
function CommentIcon ({
type, // 图标类型
count, // 评论数
iconClick,
iconColor,
theme,
...props
}) {
@ -21,10 +24,11 @@ function CommentIcon ({
iconClick && iconClick();
}
const _className = [undefined, null, ''].includes(count) ? 'comment_count_none' : 'comment_count';
return (
<span className={`comment_icon_count ${props.className}`} onClick={ handleSpanClick }>
<Icon className="comment_icon" type={type} />
<span className="comment_count">{ count }</span>
<Icon className="comment_icon" type={type} style={{ color: iconColor }} theme={theme}/>
<span className={_className}>{ count }</span>
</span>
)
}

@ -3,104 +3,133 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-17 17:35:17
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-19 18:02:28
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:11:59
*/
import './index.scss';
import React, { useState } from 'react';
import CommentIcon from './CommentIcon';
import { getImageUrl, CNotificationHOC } from 'educoder'
import { Icon } from 'antd';
import moment from 'moment';
// import QuillForEditor from '../../quillForEditor';
import CommentForm from './CommentForm';
// import {ModalConfirm} from '../ModalConfirm';
function CommentItem ({
options,
confirm
confirm,
comment,
submitDeleteComment,
submitChildComment,
likeComment,
showOrHideComment
}) {
// 显示评论输入框
const [showQuill, setShowQuill] = useState(false);
// 加载更多评论内容
const [showMore, setShowMore] = useState(false);
// const [showMore, setShowMore] = useState(false);
// 显示子列数
const [showItemCount, setShowItemCount] = useState(1);
// 箭头方向
const [arrow, setArrow] = useState(false);
// 删除评论
const deleteComment = () => {
console.log('删除评论...');
const {
author = {}, // 作者
id, // 评论id
content, // 回复内容
time, // 回复时间
hidden, // 是否隐藏
// hack_id, // OJ的ID
praise_count, // 点赞数
user_praise, // 当前用户是否点赞
can_delete,
children = [] // 子回复
} = comment;
// 删除评论 type: parent | child, id
const deleteComment = (id) => {
confirm({
title: '提示',
content: (<p>确定要删除该条回复吗?</p>),
content: ('确定要删除该条回复吗?'),
onOk () {
console.log('点击了删除');
console.log('点击了删除', id);
submitDeleteComment && submitDeleteComment(id);
}
});
// ModalConfirm('提示', (<p>确定要删除该条回复吗?</p>), () => {
// console.log('点击了删除');
// });
}
// 评论头像
const commentAvatar = (url) => (
<img className="item-flex flex-image" src='https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg' alt=""/>
const commentAvatar = (author) => (
<img
className="item-flex flex-image"
src={author.image_url ? getImageUrl(`/images/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
alt=""
/>
);
// 评论信息
const commentInfo = () => (
<p className="item-header">
<span className="item-name">用户名</span>
<span className="item-time">{moment(new Date(), 'YYYYMMDD HHmmss').fromNow()}</span>
<span className="item-close"><Icon type="close" onClick={deleteComment}/></span>
</p>
);
const commentInfo = (id, author, time, can_delete) => {
const _classNames = can_delete ? 'item-close' : 'item-close hide';
return (
<div className="item-header">
<span className="item-name">{author.name || ''}</span>
<span className="item-time">{time || ''}</span>
<span className={_classNames}>
<Icon type="close" onClick={() => deleteComment(id)}/>
</span>
</div>
);
};
// 评论内容
const commentCtx = (ctx) => (
<p className="item-ctx">
这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容
</p>
<p className="item-ctx" dangerouslySetInnerHTML={{ __html: ctx}}></p>
);
// 加载更多
const handleOnLoadMore = () => {
if (!arrow) {
// 展开所有
} else {
// 收起
}
const handleOnLoadMore = (len) => {
setShowItemCount(!arrow ? len : 1);
setArrow(!arrow);
};
// 评论追加内容
const commentAppend = () => {
const commentAppend = (children = []) => {
const len = children.length;
const _moreClass = len > 1 ? 'comment_item_loadmore show' : 'comment_item_loadmore'
const lastTxt = len - showItemCount;
const renderChild = (children) => {
return children.map((child, i) => {
const {
id, // 评论id
author = {},
time,
content,
can_delete
} = child;
const showOrHide = i < showItemCount ? 'comment_item_show' : 'comment_item_hide';
return (
<li
key={`child_${i}`}
className={showOrHide}
>
<div className="comment_item_area">
{commentAvatar(author)}
<div className="item-flex item-desc">
{commentInfo(id, author, time, can_delete)}
{commentCtx(content)}
</div>
</div>
</li>
);
})
}
const _clazz = len > 0 ? 'comment_item_append_list active' : 'comment_item_append_list';
return (
<ul className="comment_item_append_list">
<li className="comment_item_area">
{commentAvatar()}
<div className="item-flex item-desc">
{commentInfo()}
{commentCtx()}
</div>
</li>
<li className="comment_item_area">
{commentAvatar()}
<div className="item-flex item-desc">
{commentInfo()}
{commentCtx()}
</div>
</li>
<li className="comment_item_area">
{commentAvatar()}
<div className="item-flex item-desc">
{commentInfo()}
{commentCtx()}
</div>
</li>
<ul className={_clazz}>
{renderChild(children)}
<li className="comment_item_loadmore" onClick={handleOnLoadMore}>
<p className="loadmore-txt">展开其余23条评论</p>
<li className={_moreClass} onClick={() => handleOnLoadMore(len)}>
<p className="loadmore-txt">展开其余{lastTxt}条评论</p>
<p className="loadmore-icon">
<Icon type={!arrow ? 'down' : 'up'}/>
</p>
@ -109,7 +138,14 @@ function CommentItem ({
);
};
// 点击图标
const handleIconClick = () => {}
const handleShowOrHide = (id, hidden) => {
showOrHideComment && showOrHideComment(id, hidden);
}
// 点赞
const handleClickLick = (id) => {
likeComment && likeComment(id);
}
// 点击评论icon
const handleClickMessage = () => {
@ -122,23 +158,28 @@ function CommentItem ({
}
// 点击保存
const handleClickSubmit = (content) => {
// 保存并关闭
setShowQuill(false);
console.log('获取保存内容', content);
const handleClickSubmit = (id) => {
return (ctx) => {
setShowQuill(false);
submitChildComment && submitChildComment(id, ctx);
}
}
return (
<li className="comment_item_area">
{commentAvatar()}
{commentAvatar(author)}
<div className="item-flex item-desc">
{commentInfo()}
{commentCtx()}
{commentInfo(id, author, time, can_delete)}
{commentCtx(content)}
{commentAppend()}
{commentAppend(children)}
<div className="comment_icon_area">
<CommentIcon className='comment-icon-margin' type="eye" count="100" iconClick={handleIconClick}/>
<CommentIcon
className='comment-icon-margin'
type={!hidden ? "eye" : 'eye-invisible'}
iconClick={() => handleShowOrHide(id, !hidden ? 1 : 0)}
/>
{/* 回复 */}
<CommentIcon
className='comment-icon-margin'
@ -146,7 +187,14 @@ function CommentItem ({
iconClick={handleClickMessage}
/>
{/* 点赞 */}
<CommentIcon/>
<CommentIcon
iconColor={ user_praise ? '#5091FF' : '' }
className='comment-icon-margin'
theme={user_praise ? 'filled' : ''}
type="like"
count={praise_count}
iconClick={() => handleClickLick(id)}
/>
</div>
<div
@ -154,7 +202,7 @@ function CommentItem ({
className="comment_item_quill">
<CommentForm
onCancel={handleClickCancel}
onSubmit={handleClickSubmit}
onSubmit={handleClickSubmit(id)}
/>
</div>
</div>

@ -3,16 +3,41 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-17 17:34:00
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-18 11:48:09
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 11:30:14
*/
import './index.scss';
import React from 'react';
import CommentItem from './CommentItem';
function CommentList ({}) {
function CommentList (props) {
const {
commentLists, // 评论列表
submitChildComment,
submitDeleteComment,
likeComment,
showOrHideComment
} = props;
const {comments = []} = commentLists;
const renderLi = () => {
return comments.map((item, index) => {
return (
<CommentItem
key={`item_${index}`}
submitChildComment={submitChildComment}
submitDeleteComment={submitDeleteComment}
comment={item}
likeComment={likeComment}
showOrHideComment={showOrHideComment}
/>
);
});
}
return (
<ul className="comment_list_wrapper">
<CommentItem />
{renderLi()}
</ul>
);
}

@ -3,18 +3,40 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-17 17:31:33
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-18 11:47:39
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 15:47:07
*/
import React from 'react';
import CommentForm from './CommentForm';
// import CommentForm from './CommentForm';
import CommentList from './CommentList';
function Comment (props) {
const {
commentLists,
// addComment,
// cancelComment,
addChildComment,
likeComment,
showOrHideComment,
submitDeleteComment
} = props;
// const handleCancelComment = () => {
// cancelComment && cancelComment();
// };
return (
<React.Fragment>
<CommentForm />
<CommentList />
{/* <CommentForm
onCancel={handleCancelComment}
onSubmit={addComment}
/> */}
<CommentList
likeComment={likeComment}
showOrHideComment={showOrHideComment}
commentLists={commentLists}
submitChildComment={addChildComment}
submitDeleteComment={submitDeleteComment}
/>
</React.Fragment>
);
}

@ -8,8 +8,14 @@ $ml: 20px;
.comment_list_wrapper{
box-sizing: border-box;
border-top: 1px solid $bdColor;
// border-top: 1px solid $bdColor;
.comment_item_show{
display: block;
}
.comment_item_hide{
display: none;
}
.comment_item_area{
display: flex;
padding: 20px 0;
@ -34,8 +40,13 @@ $ml: 20px;
margin-left: $ml;
}
.item-close{
float: right;
display: inline-block;
cursor: pointer;
float: right;
}
.item-close.hide{
display: none;
}
}
.item-ctx{
@ -50,13 +61,16 @@ $ml: 20px;
margin-top: 10px;
.comment-icon-margin{
margin-left: 30px;
margin-left: 20px;
}
.comment-icon-margin-10{
margin-left: 10px;
}
}
.comment_item_quill{
margin-top: 20px;
}
// .comment_item_quill{
// // margin-top: 10px;
// }
}
.comment_icon_count{
cursor: pointer;
@ -71,6 +85,9 @@ $ml: 20px;
margin-left: 10px;
transition: color .3s;
}
.comment_count_none{
margin-left: 0;
}
&:hover{
.comment_icon,
@ -80,11 +97,15 @@ $ml: 20px;
}
}
.comment_item_append_list{
display: none;
position: relative;
background-color: $bgColor;
border-radius: 5px;
padding: 0 15px 10px;
margin: 15px 0;
&.active{
display: block;
}
&::before {
position: absolute;
left: 15px;
@ -98,6 +119,7 @@ $ml: 20px;
}
.comment_item_loadmore{
display: none;
padding-top: 10px;
cursor: pointer;
.loadmore-txt,
@ -106,6 +128,41 @@ $ml: 20px;
text-align: center;
font-size: $fz12;
}
&.show{
display: block;
}
}
}
}
.comment_form_area,
.comment_form_bottom_area{
width: 100%;
}
.comment_form_area{
position: relative;
background: #fff;
// top: 10px;
.ant-form-explain{
padding-left: 0px;
}
.show_input{
margin-top: 10px;
}
}
.comment_form_bottom_area{
position: relative;
background: #fff;
top: 10px;
&.active{
position: absolute;
background: #fff;
left: 0px;
right: 0px;
top: -220px;
padding: 0 20px;
}
}

@ -0,0 +1,78 @@
/*
* @Description: quill delta -> html
* @Author: tangjiang
* @Github:
* @Date: 2019-12-24 08:51:25
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 10:45:44
*/
export const formatDelta = (deltas) => {
let formatted = [];
deltas.forEach(element => {
let text = null;
// 没有图片时
if (!element['insert']['image']) {
text = element['insert']; // 获取插入的内容
// 元素有属性时
if (element['attributes']) {
// 获取所有的key值
const keys = Object.keys(element['attributes']);
keys.forEach(key => {
text = operate(text, key, element['attributes'][key]);
});
}
} else {
const image = element['insert']['image'];
const {url, alt} = image;
if (url && (url.startsWith('http') || url.startsWith('https'))) {
text = `
<img
src="${url}"
style="{display: 'inline-block'}"
width="60px"
height="30px"
alt="${alt}"
/>
`;
// text = "<img src="+url+" width='60px' height='30px' onclick='' alt="+alt+"/>";
}
}
formatted.push(text);
});
return formatted.join('');
}
/**
* @param {*} text 文本内容
* @param {*} key 属性key
* @param {*} value 属性key对应的值
*/
export const operate = (text, key, value) => {
let operatedText = null;
switch (key) {
case 'bold':
operatedText = `<strong>${text}</strong>`;
break;
case 'italic':
operatedText = `<i>${text}</i>`;
break;
case 'strike':
operatedText = `<s>${text}</s>`;
break;
case 'underline':
operatedText = `<u>${text}</u>`;
break;
case 'link':
operatedText = `<a href="${value}" target="blank">${text}</a>`;
break;
default:
operatedText = text;
}
return operatedText;
}

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-12-16 15:50:45
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-17 16:44:48
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 09:44:03
*/
import Quill from "quill";
@ -43,8 +43,8 @@ export default class ImageBlot extends BlockEmbed {
alt: node.getAttribute('alt'),
url: node.getAttribute('src'),
onclick: node.onclick,
// width: node.width,
// height: node.height,
width: node.width,
height: node.height,
display: node.getAttribute('display')
};
}

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 10:58:37
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 14:22:38
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 16:48:56
*/
import './index.scss';
import React from 'react';
@ -19,8 +19,17 @@ const TextNumber = (props) => {
* type: 内容 文字或图标
* onIconClick: 点击图标时的回调函数
*/
const { text, number, position = 'horizontal', type = 'label', onIconClick} = props;
const {
text,
number,
position = 'horizontal',
type = 'label',
onIconClick,
className,
theme = 'outlined'
} = props;
// console.log('style=====>>>>>>', style);
const handleIconClick = () => {
onIconClick && onIconClick();
}
@ -35,11 +44,17 @@ const TextNumber = (props) => {
}
return '';
}
const renderCtx = () => {
const renderCtx = (className, theme) => {
if (type === 'icon') { // 图标加文字时
const _className = `text_number_area text_icon_numb flex_${position} ${className}`;
return (
<div className={`text_number_area text_icon_numb flex_${position}`}>
<Icon onClick={handleIconClick} type={text} className={'numb_icon'}></Icon>
<div className={_className}>
<Icon
theme={theme}
onClick={handleIconClick}
type={text}
className={'numb_icon'}
></Icon>
{renderNumb()}
</div>
)
@ -54,7 +69,7 @@ const TextNumber = (props) => {
}
return (
<React.Fragment>
{renderCtx()}
{renderCtx(className, theme)}
</React.Fragment>
);
}

@ -9,7 +9,7 @@ import './index.scss';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';// import { Form } from 'antd';
import { Button, Modal } from 'antd';
import { Button } from 'antd';
import LeftPane from './leftpane';
import RightPane from './rightpane';
import { withRouter } from 'react-router';

@ -11,7 +11,6 @@ import React, { useState, useMemo } from 'react';
// import { Tabs } from 'antd';
import EditorTab from './editorTab';
import PrevTab from './prevTab';
import CommitTab from './commitTab';
// const { TabPane } = Tabs;

@ -3,19 +3,112 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 09:49:35
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-17 17:46:05
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:10:14
*/
import './index.scss';
import React from 'react';
import React, { useEffect } from 'react';
import Comment from '../../../../../common/components/comment';
import { connect } from 'react-redux';
import actions from '../../../../../redux/actions';
const CommentTask = (props) => {
const {
identifier,
commentLists,
addComment,
likeComment,
deleteComment,
getCommentLists,
showOrHideComment,
replayChildComment
} = props;
useEffect(() => {
if (identifier) {
// 获取评论列表数据
getCommentLists(identifier);
}
}, [identifier]);
// 添加评论
const handleAddComment = (ctx) => {
console.log('添加的评论内容: ', ctx);
addComment(identifier, {
comments: {
content: ctx
}
});
};
// 添加子评论
const handleAddChildComment = (parentId, ctx) => {
replayChildComment(identifier, {
comments: {
content: ctx,
parent_id: parentId
}
});
}
// 删除评论
const handleSubmitDeleteComment = (id) => {
console.log('删除评论:', identifier, id);
deleteComment(identifier, id);
}
// 点赞
const handleLikeComment = (id) => {
likeComment(identifier, id, {
container_type: 'Discuss',
type: 1
});
}
// 显示或隐藏
const handleShowOrHideComment = (id, hidden) => {
showOrHideComment(identifier, id, {
hidden
});
}
return (
<div className="task_comment_task">
<Comment />
<Comment
commentLists={commentLists}
addComment={handleAddComment}
addChildComment={handleAddChildComment}
likeComment={handleLikeComment}
showOrHideComment={handleShowOrHideComment}
submitDeleteComment={handleSubmitDeleteComment}
/>
</div>
)
}
export default CommentTask;
const mapStateToProps = (state) => {
const {
commentLists // 评论列表
} = state.commentReducer;
const {
comment_identifier
} = state.ojForUserReducer;
return {
commentLists,
identifier: comment_identifier
}
}
const mapDispatchToProps = (dispatch) => ({
// getCommentLists: (identifier) => dispatch(action.getCommentLists(identifier))
getCommentLists: (identifier) => dispatch(actions.getCommentLists(identifier)),
addComment: (identifier, comments) => dispatch(actions.addComment(identifier, comments)),
replayChildComment: (identifier, comment) => dispatch(actions.replayChildComment(identifier, comment)),
deleteComment: (identifier, id) => dispatch(actions.deleteComment(identifier, id)),
likeComment: (identifier, id, params) => dispatch(actions.likeComment(identifier, id, params)),
showOrHideComment: (identifier, id, params) => dispatch(actions.showOrHideComment(identifier, id, params)),
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(CommentTask);

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-23 11:33:41
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-19 18:03:22
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:16:54
// */
import './index.scss';
import React, { useState, useEffect, useMemo } from 'react';
@ -15,12 +15,19 @@ import CommitRecord from './commitRecord';
import TaskDescription from './taskDescription';
import TextNumber from './../../components/textNumber';
import actions from '../../../../redux/actions';
import CommentForm from '../../../../common/components/comment/CommentForm';
// const { TabPane } = Tabs;
const LeftPane = (props) => {
const { hack, userCodeTab } = props;
const { pass_count, submit_count } = hack;
const {
pass_count,
submit_count,
praises_count, /* 点赞数 */
comments_count, /* 评论数*/
user_praise // 用户是否点赞
} = hack;
const [defaultActiveKey, setDefaultActiveKey] = useState('comment');
const navItem = [
@ -32,10 +39,10 @@ const LeftPane = (props) => {
title: '提交记录',
key: 'record'
},
// {
// title: '评论',
// key: 'comment'
// }
{
title: '评论',
key: 'comment'
}
];
const Comp = {
@ -45,7 +52,6 @@ const LeftPane = (props) => {
};
useEffect(() => {
console.log('====>>>>', userCodeTab);
setDefaultActiveKey(userCodeTab);
}, [userCodeTab])
@ -69,18 +75,34 @@ const LeftPane = (props) => {
// 点击消息
const handleClickMessage = () => {
console.log('点击的消息图标---------');
// 切换到评论tab
setDefaultActiveKey('comment');
}
// 点击点赞
const handleClickLike = () => {
console.log('点击的Like---------');
}
// 对OJ进行点赞
const {id, identifier } = props.hack;
props.likeComment(identifier, id, {
container_type: 'Hack',
type: 1
});
};
// 点击不喜欢
const handleClickDisLike = () => {
console.log('点击的DisLike---------');
}
// const handleClickDisLike = () => {
// console.log('点击的DisLike---------');
// }
// 添加评论
const handleAddComment = (ctx) => {
console.log('添加的评论内容: ', ctx, props.identifier);
props.identifier && props.addComment(props.identifier, {
comments: {
content: ctx
}
});
};
return (
<React.Fragment>
@ -91,26 +113,46 @@ const LeftPane = (props) => {
{ renderComp }
</div>
<div className={'number_area'}>
<div className="number_flex flex_count">
<div className="number_flex flex_count" style={{ display: defaultActiveKey !== 'comment' ? 'flex' : 'none'}}>
<TextNumber text="通过次数" number={pass_count} position="vertical"/>
<Divider type="vertical" style={{ height: '20px', margin: '10px 20px' }}/>
<TextNumber text="提交次数" number={submit_count} position="vertical"/>
</div>
<div className="number_flex flex_quill" style={{ display: defaultActiveKey === 'comment' ? 'flex' : 'none'}}>
<CommentForm
onSubmit={handleAddComment}
type="bottom"
/>
</div>
<div className="number_flex flex_info">
<TextNumber text="message" number={comments_count} type="icon" onIconClick={handleClickMessage}/>
<TextNumber
className={user_praise ? 'like active' : 'like'}
text="like"
number={praises_count}
theme={user_praise ? 'filled' : ''}
type="icon"
onIconClick={handleClickLike}/>
{/* <TextNumber text="dislike" number={0} type="icon" onIconClick={handleClickDisLike}/> */}
</div>
</div>
</React.Fragment>
);
}
const mapStateToProps = (state) => {
const { hack, userCodeTab} = state.ojForUserReducer;
const { hack, userCodeTab, comment_identifier} = state.ojForUserReducer;
return {
hack,
userCodeTab
userCodeTab,
identifier: comment_identifier
}
}
// changeUserCodeTab
const mapDispatchToProps = (dispatch) => ({
changeUserCodeTab: (key) => dispatch(actions.changeUserCodeTab(key))
changeUserCodeTab: (key) => dispatch(actions.changeUserCodeTab(key)),
likeComment: (identifier, id, params) => dispatch(actions.likeComment(identifier, id, params)),
addComment: (identifier, comments) => dispatch(actions.addComment(identifier, comments))
});
export default connect(
mapStateToProps,

@ -21,13 +21,33 @@
background: #fff;
.flex_count,
.flex_info{
.flex_info,
.flex_quill{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.flex_info{
width: 200px;
// width: 140px;
justify-content: flex-end;
.like{
margin-left: 20px
}
.like.active{
.numb_icon{
color: #5091FF;
}
}
}
.flex_quill{
// position: relative;
flex: 1;
align-items: center;
height: 100%;
top: 10px;
margin-right: 20px;
}
}

@ -24,6 +24,7 @@ const types = {
DELETE_TEST_CASE: 'DELETE_TEST_CASE', // 删除测试用例
SAVE_TEST_CASE: 'SAVE_TEST_CASE', // 保存测试用例
SAVE_USE_TEST_CASE_VALUE: 'SAVE_USE_TEST_CASE_VALUE', // 用户自定义测试用例值
CHANGE_PUBLISH_VALUE: 'CHANGE_PUBLISH_VALUE', // 改变发布状态值
CLEAR_JSFORM_STORE: 'CLEAR_JSFORM_STORE', // 清空测试用例
SAVE_EDIT_OJ_FORM_AND_TEST_CASE: 'SAVE_EDIT_OJ_FORM_AND_TEST_CASE', // 保存根据id获取的表单及测试用例值
TEST_CODE_STATUS: 'TEST_CODE_STATUS', // 代码调试状态
@ -53,6 +54,7 @@ const types = {
SAVE_EDITOR_CODE: 'SAVE_EDITOR_CODE', // 保存详情页面中编辑时的代码
CLICK_OPERATE_TYPE: 'CLICK_OPERATE_TYPE', // 点击类型
CLEAR_OJ_FOR_USER_REDUCER: 'CLEAR_OJ_FOR_USER_REDUCER', // 退出时清空 ojForUserReducer保存内容
ADD_OJ_LIKE_COUNT: 'ADD_OJ_LIKE_COUNT', // 增加点赞数
/*** jupyter */
GET_JUPYTER_DATA_SETS: 'GET_JUPYTER_DATA_SETS', // jupyter 数据集
GET_JUPYTER_TPI_URL: 'GET_JUPYTER_TPI_URL', // 获取 jupyter url
@ -67,6 +69,12 @@ const types = {
CHANGE_JYPYTER_TIME: 'CHANGE_JYPYTER_TIME',//增加15分钟
CHANGE_EXTENDED_TIME: 'CHANGE_EXTENDED_TIME',//延时
CHANGE_UPDETA_SPIN: 'CHANGE_UPDETA_SPIN',//加载
/*** 评论 */
ADD_COMMENTS: 'ADD_COMMENTS', // 添加评论
GET_COMMENT_LISTS: 'GET_COMMENT_LISTS', // 获取评论列表
REPLAY_CHILD_COMMENTS: 'REPLAY_CHILD_COMMENTS', // 子回复
DELETE_COMMENTS: 'DELETE_COMMENTS', // 删除评论
SAVE_COMMENT_IDENTIFIER: 'SAVE_COMMENT_IDENTIFIER' // 评论时的identifier
}
export default types;

@ -0,0 +1,114 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-23 10:53:25
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 16:17:00
*/
import types from "./actionTypes";
import {
fetchAddComment,
fetchCommentLists,
fetchAddChildComment,
fetchDeleteComment,
fetchLikeComment,
fetchShowOrHideComment
} from '../../services/commentService';
// 添加评论
export const addComment = (identifier, comments) => {
return (dispatch) => {
fetchAddComment(identifier, comments).then(res => {
if (res.status === 200) {
// 重新加载评论列表
dispatch(getCommentLists(identifier));
}
});
}
};
// 获取评论列表
export const getCommentLists = (identifier) => {
return (dispatch) => {
fetchCommentLists(identifier).then(res => {
console.log('获取评论列表: ====>>>>', res);
if (res.status === 200) {
const {data} = res;
dispatch({
type: types.GET_COMMENT_LISTS,
payload: data
})
}
});
}
}
// 子回复
export const replayChildComment = (identifier, comment) => {
return (dispatch) => {
fetchAddChildComment(identifier, comment).then(res => {
// console.log('添加子评论成功: ====>>>>', res);
if (res.status === 200) {
// 重新加载评论列表
dispatch(getCommentLists(identifier));
}
});
}
}
// 删除评论
export const deleteComment = (identifier, delId) => {
return (dispatch) => {
fetchDeleteComment(identifier, delId).then(res => {
if (res.status === 200) {
// 重新加载评论列表
dispatch(getCommentLists(identifier));
}
});
}
}
// 点赞
export const likeComment = (identifier, id, params, cb) => {
return (dispatch) => {
fetchLikeComment(id, params).then(res => {
if (res.status === 200) {
// 重新加载评论列表
const {container_type} = params;
// if (container_type === 'Discuss') {
// dispatch(getCommentLists(identifier))
// } else if {
// }
const {praise_count} = res.data;
switch (container_type) {
case 'Discuss':
dispatch(getCommentLists(identifier))
break;
case 'Hack':
dispatch({
type: types.ADD_OJ_LIKE_COUNT,
payload: praise_count
});
break;
default:
break;
}
}
})
}
}
// 显示或隐藏评论
export const showOrHideComment = (identifier, id, params) => {
return (dispatch) => {
fetchShowOrHideComment(identifier, id, params).then(res => {
if (res.status === 200) {
// 重新加载评论列表
dispatch(getCommentLists(identifier));
}
});
}
}

@ -65,6 +65,15 @@ import {
getUserInfoForNew
} from './user';
import {
addComment,
getCommentLists,
replayChildComment,
deleteComment,
likeComment,
showOrHideComment
} from './comment';
import {
getJupyterTpiDataSet,
getJupyterTpiUrl,
@ -137,6 +146,13 @@ export default {
reset_with_tpi,
addjypertime,
active_with_tpi,
updataspinning
updataspinning,
// isUpdateCodeCtx
// 评论
addComment,
getCommentLists,
replayChildComment,
deleteComment,
likeComment,
showOrHideComment,
}

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 13:42:11
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-20 19:30:30
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-23 11:55:48
*/
import types from "./actionTypes";
import { Base64 } from 'js-base64';

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-20 16:35:46
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-20 19:54:09
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-23 10:55:28
*/
import types from './actionTypes';
import CONST from '../../constants';
@ -86,16 +86,16 @@ const payloadInfo = (key, value, errMsg, validateInfo) => ({
});
// 接口调用成功后,跳转至列表页
function linkToDev (dispatch, props) {
toStore('oj_description', '');
dispatch({
type: types.IS_MY_SOURCE,
payload: true
});
setTimeout(() => {
props.history.push('/problems');
}, 1000);
}
// function linkToDev (dispatch, props) {
// toStore('oj_description', '');
// dispatch({
// type: types.IS_MY_SOURCE,
// payload: true
// });
// setTimeout(() => {
// props.history.push('/problems');
// }, 1000);
// }
// 表单提交验证
export const validateOjForm = (props, type) => {
@ -250,18 +250,24 @@ export const validateOjForm = (props, type) => {
if (type === 'publish') {
// 提示发布信息
publishTask(identifier).then(res => {
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
if (res.data.status === 0) {
// message.success('发布成功!');
notification.success({
message: '提示',
description: '发布成功!'
});
linkToDev(dispatch, props);
// linkToDev(dispatch, props);
// 改变发布状态值 0 => 1
dispatch({
type: types.CHANGE_PUBLISH_VALUE,
payload: 1
});
}
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
}).catch(() => {
dispatch({
type: types.PUBLISH_LOADING_STATUS,
@ -273,25 +279,32 @@ export const validateOjForm = (props, type) => {
fetchPostOjForm(paramsObj).then(res => {
if (res.status === 200) { // 保存成功后,重新跳转至列表页
if (res.data.status === 0) {
// 改变按钮loading状态
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
// message.success(paramsObj['submitType'] === 'update' ? '更新成功' : '保存成功');
notification.success({
message: '提示',
description: paramsObj['submitType'] === 'update' ? '更新成功' : '保存成功'
});
linkToDev(dispatch, props);
}
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
const {identifier} = res.data;
// 保存成功后的identifier
identifier && dispatch({
type: types.SAVE_OJ_FORM_ID,
payload: identifier
});
// 保存或更新后调用start接口
// linkToDev(dispatch, props);
}
}).catch(err => {
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
}
);
}}
).catch(err => {
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
});
}
}
}
@ -312,7 +325,12 @@ export const handleClickCancelPublish = (props, identifier) => {
message: '提示',
description: '撤销发布成功!'
});
linkToDev(dispatch, props);
// 改变发布状态值
dispatch({
type: types.CHANGE_PUBLISH_VALUE,
payload: 0
});
// linkToDev(dispatch, props);
}
}
}).catch(() => {

@ -0,0 +1,42 @@
import types from "../actions/actionTypes";
/*
* @Description: 评论reducer
* @Author: tangjiang
* @Github:
* @Date: 2019-12-23 10:35:31
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-23 14:51:42
*/
const initialState = {
comments: {
content: '' // 评论内容
},
commentLists: {}, // 评论列表
pages: {
limit: 20,
page: 1
}
};
const commentReducer = (state = initialState, action) => {
const { payload, type } = action;
switch (type) {
case types.ADD_COMMENTS:
return {
...state
}
case types.GET_COMMENT_LISTS:
return {
...state,
commentLists: Object.assign({}, payload)
}
default:
return {
...state
}
}
}
export default commentReducer;

@ -14,6 +14,7 @@ import ojForUserReducer from './ojForUserReducer';
import commonReducer from './commonReducer';
import userReducer from './userReducer';
import jupyterReducer from './jupyterReducer';
import commentReducer from './commentReducer';
export default combineReducers({
testReducer,
@ -22,5 +23,6 @@ export default combineReducers({
ojForUserReducer,
commonReducer,
userReducer,
jupyterReducer
jupyterReducer,
commentReducer
});

@ -3,11 +3,12 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 13:41:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-20 14:46:07
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 16:35:09
*/
import types from "../actions/actionTypes";
import { Base64 } from 'js-base64';
import actions from "../actions";
const initialState = {
user_program_identifier: '', // 开启OJ题的唯一标题
@ -26,6 +27,7 @@ const initialState = {
notice: false, // 通知
hadCodeUpdate: false, // 更新代码
operateType: '', // 点击类型: 调度或提交
comment_identifier: '' // 用户评论时使用的 identifier
};
const ojForUserReducer = (state = initialState, action) => {
@ -50,7 +52,8 @@ const ojForUserReducer = (state = initialState, action) => {
return {
...state,
hack: Object.assign({}, hack),
test_case: Object.assign({}, test_case)
test_case: Object.assign({}, test_case),
comment_identifier: hack.identifier
}
case types.COMMIT_RECORD_DETAIL:
let result = action.payload.data;
@ -179,6 +182,23 @@ const ojForUserReducer = (state = initialState, action) => {
hadCodeUpdate: false, // 更新代码
operateType: '', // 点击类型: 调度或提交
};
// 保存评论时用的 identifer
case types.SAVE_COMMENT_IDENTIFIER:
return {
...state,
comment_identifier: actions.payload
};
// 是否点赞
case types.ADD_OJ_LIKE_COUNT:
let _count = state.hack.praises_count;
let _user_praise = state.hack.user_praise;
_count = +action.payload > 0 ? _count + 1 : _count - 1;
_user_praise = +action.payload > 0 ? true : false;
const _hack = Object.assign({}, state.hack, {praises_count: _count, user_praise: _user_praise});
return {
...state,
hack: _hack
}
default:
return state;
}

@ -3,8 +3,8 @@
* @Author: tangjiang
* @Github:
* @Date: 2019-11-20 16:40:32
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-20 16:40:52
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-23 10:12:59
*/
import { Base64 } from 'js-base64';
import types from '../actions/actionTypes';
@ -158,9 +158,10 @@ const ojFormReducer = (state = initialState, action) => {
testCasesValidate: [...tempTestValicate]
};
case types.SAVE_OJ_FORM_ID:
state.identifier = action.payload;
// state.identifier = action.payload;
return {
...state
...state,
identifier: action.payload
}
case types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE: // 保存编辑的值
/**
@ -211,6 +212,11 @@ const ojFormReducer = (state = initialState, action) => {
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default',
isPublish: status
}
case types.CHANGE_PUBLISH_VALUE:
return {
...state,
isPublish: action.payload
};
case types.CLEAR_JSFORM_STORE:
state = Object.assign({}, init);
return {

@ -0,0 +1,45 @@
/*
* @Description: 评论 service
* @Author: tangjiang
* @Github:
* @Date: 2019-12-23 10:43:27
* @LastEditors : tangjiang
* @LastEditTime : 2019-12-24 17:10:49
*/
import axios from 'axios';
// 添加评论
export async function fetchAddComment (identifier, params) {
const url = `/problems/${identifier}/comments.json`;
return axios.post(url, params);
}
// 获取评论列表
export async function fetchCommentLists (identifier) {
const url = `/problems/${identifier}/comments.json`;
return axios.get(url);
}
// 添加子评论
export async function fetchAddChildComment (identifier, params) {
const url = `/problems/${identifier}/comments/reply.json`;
return axios.post(url, params);
}
// 删除评论
export async function fetchDeleteComment (identifier, id) {
const url = `/problems/${identifier}/comments/${id}.json`;
return axios.delete(url);
}
// 点赞
export async function fetchLikeComment (id, params) {
const url = `/discusses/${id}/plus.json`;
return axios.post(url, params);
}
// 显示或隐藏
export async function fetchShowOrHideComment (identifier, id, params) {
const url = `/problems/${identifier}/comments/${id}/hidden.json`;
return axios.post(url, params);
}
Loading…
Cancel
Save