fork from bc4552c5a8
This commit is contained in:
287
client/components/TimeLine/TimeLine.js
Normal file
287
client/components/TimeLine/TimeLine.js
Normal file
@@ -0,0 +1,287 @@
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import { Timeline, Spin, Row, Col, Tag, Avatar, Button, Modal, AutoComplete } from 'antd';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { formatTime } from '../../common.js';
|
||||
import showDiffMsg from '../../../common/diff-view.js';
|
||||
import variable from '../../constants/variable';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { fetchNewsData, fetchMoreNews } from '../../reducer/modules/news.js';
|
||||
import { fetchInterfaceList } from '../../reducer/modules/interface.js';
|
||||
import ErrMsg from '../ErrMsg/ErrMsg.js';
|
||||
const jsondiffpatch = require('jsondiffpatch/dist/jsondiffpatch.umd.js');
|
||||
const formattersHtml = jsondiffpatch.formatters.html;
|
||||
import 'jsondiffpatch/dist/formatters-styles/annotated.css';
|
||||
import 'jsondiffpatch/dist/formatters-styles/html.css';
|
||||
import './TimeLine.scss';
|
||||
import { timeago } from '../../../common/utils.js';
|
||||
|
||||
// const Option = AutoComplete.Option;
|
||||
const { Option, OptGroup } = AutoComplete;
|
||||
|
||||
const AddDiffView = props => {
|
||||
const { title, content, className } = props;
|
||||
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h3 className="title">{title}</h3>
|
||||
<div dangerouslySetInnerHTML={{ __html: content }} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
AddDiffView.propTypes = {
|
||||
title: PropTypes.string,
|
||||
content: PropTypes.string,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
// timeago(new Date().getTime() - 40);
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
newsData: state.news.newsData,
|
||||
curpage: state.news.curpage,
|
||||
curUid: state.user.uid
|
||||
};
|
||||
},
|
||||
{
|
||||
fetchNewsData,
|
||||
fetchMoreNews,
|
||||
fetchInterfaceList
|
||||
}
|
||||
)
|
||||
class TimeTree extends Component {
|
||||
static propTypes = {
|
||||
newsData: PropTypes.object,
|
||||
fetchNewsData: PropTypes.func,
|
||||
fetchMoreNews: PropTypes.func,
|
||||
setLoading: PropTypes.func,
|
||||
loading: PropTypes.bool,
|
||||
curpage: PropTypes.number,
|
||||
typeid: PropTypes.number,
|
||||
curUid: PropTypes.number,
|
||||
type: PropTypes.string,
|
||||
fetchInterfaceList: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
bidden: '',
|
||||
loading: false,
|
||||
visible: false,
|
||||
curDiffData: {},
|
||||
apiList: []
|
||||
};
|
||||
this.curSelectValue = '';
|
||||
}
|
||||
|
||||
getMore() {
|
||||
const that = this;
|
||||
|
||||
if (this.props.curpage <= this.props.newsData.total) {
|
||||
this.setState({ loading: true });
|
||||
this.props
|
||||
.fetchMoreNews(
|
||||
this.props.typeid,
|
||||
this.props.type,
|
||||
this.props.curpage + 1,
|
||||
10,
|
||||
this.curSelectValue
|
||||
)
|
||||
.then(function() {
|
||||
that.setState({ loading: false });
|
||||
if (that.props.newsData.total === that.props.curpage) {
|
||||
that.setState({ bidden: 'logbidden' });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false
|
||||
});
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.props.fetchNewsData(this.props.typeid, this.props.type, 1, 10);
|
||||
if (this.props.type === 'project') {
|
||||
this.getApiList();
|
||||
}
|
||||
}
|
||||
|
||||
openDiff = data => {
|
||||
this.setState({
|
||||
curDiffData: data,
|
||||
visible: true
|
||||
});
|
||||
};
|
||||
|
||||
async getApiList() {
|
||||
let result = await this.props.fetchInterfaceList({
|
||||
project_id: this.props.typeid,
|
||||
limit: 'all'
|
||||
});
|
||||
this.setState({
|
||||
apiList: result.payload.data.data.list
|
||||
});
|
||||
}
|
||||
|
||||
handleSelectApi = selectValue => {
|
||||
this.curSelectValue = selectValue;
|
||||
this.props.fetchNewsData(this.props.typeid, this.props.type, 1, 10, selectValue);
|
||||
};
|
||||
|
||||
render() {
|
||||
let data = this.props.newsData ? this.props.newsData.list : [];
|
||||
|
||||
const curDiffData = this.state.curDiffData;
|
||||
let logType = {
|
||||
project: '项目',
|
||||
group: '分组',
|
||||
interface: '接口',
|
||||
interface_col: '接口集',
|
||||
user: '用户',
|
||||
other: '其他'
|
||||
};
|
||||
|
||||
const children = this.state.apiList.map(item => {
|
||||
let methodColor = variable.METHOD_COLOR[item.method ? item.method.toLowerCase() : 'get'];
|
||||
return (
|
||||
<Option title={item.title} value={item._id + ''} path={item.path} key={item._id}>
|
||||
{item.title}{' '}
|
||||
<Tag
|
||||
style={{ color: methodColor ? methodColor.color : '#cfefdf', backgroundColor: methodColor ? methodColor.bac : '#00a854', border: 'unset' }}
|
||||
>
|
||||
{item.method}
|
||||
</Tag>
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
|
||||
children.unshift(
|
||||
<Option value="" key="all">
|
||||
选择全部
|
||||
</Option>
|
||||
);
|
||||
|
||||
if (data && data.length) {
|
||||
data = data.map((item, i) => {
|
||||
let interfaceDiff = false;
|
||||
// 去掉了 && item.data.interface_id
|
||||
if (item.data && typeof item.data === 'object') {
|
||||
interfaceDiff = true;
|
||||
}
|
||||
return (
|
||||
<Timeline.Item
|
||||
dot={
|
||||
<Link to={`/user/profile/${item.uid}`}>
|
||||
<Avatar src={`/api/user/avatar?uid=${item.uid}`} />
|
||||
</Link>
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
<div className="logMesHeade">
|
||||
<span className="logoTimeago">{timeago(item.add_time)}</span>
|
||||
{/*<span className="logusername"><Link to={`/user/profile/${item.uid}`}><Icon type="user" />{item.username}</Link></span>*/}
|
||||
<span className="logtype">{logType[item.type]}动态</span>
|
||||
<span className="logtime">{formatTime(item.add_time)}</span>
|
||||
</div>
|
||||
<span className="logcontent" dangerouslySetInnerHTML={{ __html: item.content }} />
|
||||
<div style={{ padding: '10px 0 0 10px' }}>
|
||||
{interfaceDiff && <Button onClick={() => this.openDiff(item.data)}>改动详情</Button>}
|
||||
</div>
|
||||
</Timeline.Item>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
data = '';
|
||||
}
|
||||
let pending =
|
||||
this.props.newsData.total <= this.props.curpage ? (
|
||||
<a className="logbidden">以上为全部内容</a>
|
||||
) : (
|
||||
<a className="loggetMore" onClick={this.getMore.bind(this)}>
|
||||
查看更多
|
||||
</a>
|
||||
);
|
||||
if (this.state.loading) {
|
||||
pending = <Spin />;
|
||||
}
|
||||
let diffView = showDiffMsg(jsondiffpatch, formattersHtml, curDiffData);
|
||||
|
||||
return (
|
||||
<section className="news-timeline">
|
||||
<Modal
|
||||
style={{ minWidth: '800px' }}
|
||||
title="Api 改动日志"
|
||||
visible={this.state.visible}
|
||||
footer={null}
|
||||
onCancel={this.handleCancel}
|
||||
>
|
||||
<i>注: 绿色代表新增内容,红色代表删除内容</i>
|
||||
<div className="project-interface-change-content">
|
||||
{diffView.map((item, index) => {
|
||||
return (
|
||||
<AddDiffView
|
||||
className="item-content"
|
||||
title={item.title}
|
||||
key={index}
|
||||
content={item.content}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{diffView.length === 0 && <ErrMsg type="noChange" />}
|
||||
</div>
|
||||
</Modal>
|
||||
{this.props.type === 'project' && (
|
||||
<Row className="news-search">
|
||||
<Col span="3">选择查询的 Api:</Col>
|
||||
<Col span="10">
|
||||
<AutoComplete
|
||||
onSelect={this.handleSelectApi}
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Select Api"
|
||||
optionLabelProp="title"
|
||||
filterOption={(inputValue, options) => {
|
||||
if (options.props.value == '') return true;
|
||||
if (
|
||||
options.props.path.indexOf(inputValue) !== -1 ||
|
||||
options.props.title.indexOf(inputValue) !== -1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
{/* {children} */}
|
||||
<OptGroup label="other">
|
||||
<Option value="wiki" path="" title="wiki">
|
||||
wiki
|
||||
</Option>
|
||||
</OptGroup>
|
||||
<OptGroup label="api">{children}</OptGroup>
|
||||
</AutoComplete>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{data ? (
|
||||
<Timeline className="news-content" pending={pending}>
|
||||
{data}
|
||||
</Timeline>
|
||||
) : (
|
||||
<ErrMsg type="noData" />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TimeTree;
|
||||
206
client/components/TimeLine/TimeLine.scss
Normal file
206
client/components/TimeLine/TimeLine.scss
Normal file
@@ -0,0 +1,206 @@
|
||||
|
||||
.project-interface-change-content{
|
||||
min-height: 350px;
|
||||
max-height: 1000px;
|
||||
min-width: 784px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.item-content{
|
||||
.title{
|
||||
margin: 10px 0;
|
||||
}
|
||||
.content{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.news-box {
|
||||
display: -webkit-box;
|
||||
-webkit-box-flex: 1;
|
||||
margin: 0px auto 0 auto;
|
||||
font-size: 0.14rem;
|
||||
background: #FFF;
|
||||
display: block;
|
||||
min-height: 550px;
|
||||
border-radius: 4px;
|
||||
.news-timeline{
|
||||
padding-left: 125px;
|
||||
color: #6b6c6d;
|
||||
.news-search{
|
||||
height:35px;
|
||||
line-height: 30px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.news-content{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.ant-timeline-item{
|
||||
min-height: 60px;
|
||||
.ant-timeline-item-head-custom {
|
||||
padding: 0;
|
||||
width: 0;
|
||||
left: -14px;
|
||||
}
|
||||
.ant-timeline-item-head{
|
||||
// width: 40px;
|
||||
// height: 40px;
|
||||
// left: -13px;
|
||||
top: 13px;
|
||||
// // border-color:#e1e3e4;
|
||||
// border:2px solid #e1e3e4;
|
||||
// border-radius: 50%;
|
||||
.anticon{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ant-timeline-item-tail{
|
||||
// top: 30px;
|
||||
}
|
||||
.ant-avatar {
|
||||
border: 2px solid #e1e3e4;
|
||||
box-sizing: content-box;
|
||||
border-radius: 50%;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.ant-avatar{
|
||||
// border:2px solid gray;
|
||||
}
|
||||
|
||||
.logusername{
|
||||
color: #4eaef3;
|
||||
padding: 0px 16px 0px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.logtype{
|
||||
padding-right: 16px;
|
||||
}
|
||||
.logtime{
|
||||
padding-right: 16px;
|
||||
|
||||
}
|
||||
.logcontent{
|
||||
display: block;
|
||||
padding-left: 8px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.logoTimeago{
|
||||
position: absolute;
|
||||
left: -80px;
|
||||
top: 5px;
|
||||
color: #c0c1c1;
|
||||
}
|
||||
.logbidden{
|
||||
color: #c0c1c1;
|
||||
cursor: default;
|
||||
padding: 8px !important;
|
||||
}
|
||||
.loggetMore{
|
||||
line-height: 30px;
|
||||
color: #4eaef3;
|
||||
}
|
||||
|
||||
.ant-timeline-item{
|
||||
&:after{
|
||||
content: "";
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.ant-timeline-item-content{
|
||||
background-color: #fafafa;
|
||||
float: left;
|
||||
width: auto;
|
||||
margin-left: 40px;
|
||||
padding: 0px;
|
||||
padding-bottom: 10px;
|
||||
// min-width: 300px;
|
||||
// max-width: 600px;
|
||||
width: 625px;
|
||||
border-radius: 8px;
|
||||
|
||||
.logMesHeade{
|
||||
padding: 8px 8px 8px 8px;
|
||||
line-height: 24px;
|
||||
background-color: #eceef1;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.logoTimeago{
|
||||
left: -120px;
|
||||
}
|
||||
.logcontent{
|
||||
// text-indent: 2em;
|
||||
line-height: 1.5em;
|
||||
margin-top: 16px;
|
||||
padding: 0px 16px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.ant-timeline-item-pending{
|
||||
padding: 0px;
|
||||
.ant-timeline-item-content{
|
||||
padding: 0px;
|
||||
width: auto;
|
||||
margin-top: 16px;
|
||||
.loggetMore{
|
||||
margin: 0px;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.logHead{
|
||||
height: 80px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
padding: 24px 0px;
|
||||
overflow: hidden;
|
||||
.breadcrumb-container{
|
||||
float: left;
|
||||
min-width:100px;
|
||||
}
|
||||
.projectDes{
|
||||
color: #7b7b7b;
|
||||
font-size: 25px;
|
||||
float: left;
|
||||
line-height: 0.9em;
|
||||
}
|
||||
.Mockurl{
|
||||
width: 600px !important;
|
||||
float: right;
|
||||
color: #7b7b7b;
|
||||
>span{
|
||||
float: left;
|
||||
line-height: 30px;
|
||||
}
|
||||
p{
|
||||
width: 65%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 4px 7px;
|
||||
height: 28px;
|
||||
cursor: text;
|
||||
font-size: 13px;
|
||||
color: rgba(0,0,0,.65);
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
-webkit-transition: all .3s;
|
||||
transition: all .3s;
|
||||
overflow-x:auto;
|
||||
}
|
||||
button{
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user