mock平台

TimeLine.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import React, { PureComponent as Component } from 'react';
  2. import { Timeline, Spin, Row, Col, Tag, Avatar, Button, Modal, AutoComplete } from 'antd';
  3. import PropTypes from 'prop-types';
  4. import { connect } from 'react-redux';
  5. import { formatTime } from '../../common.js';
  6. import showDiffMsg from '../../../common/diff-view.js';
  7. import variable from '../../constants/variable';
  8. import { Link } from 'react-router-dom';
  9. import { fetchNewsData, fetchMoreNews } from '../../reducer/modules/news.js';
  10. import { fetchInterfaceList } from '../../reducer/modules/interface.js';
  11. import ErrMsg from '../ErrMsg/ErrMsg.js';
  12. const jsondiffpatch = require('jsondiffpatch/dist/jsondiffpatch.umd.js');
  13. const formattersHtml = jsondiffpatch.formatters.html;
  14. import 'jsondiffpatch/dist/formatters-styles/annotated.css';
  15. import 'jsondiffpatch/dist/formatters-styles/html.css';
  16. import './TimeLine.scss';
  17. import { timeago } from '../../../common/utils.js';
  18. // const Option = AutoComplete.Option;
  19. const { Option, OptGroup } = AutoComplete;
  20. const AddDiffView = props => {
  21. const { title, content, className } = props;
  22. if (!content) {
  23. return null;
  24. }
  25. return (
  26. <div className={className}>
  27. <h3 className="title">{title}</h3>
  28. <div dangerouslySetInnerHTML={{ __html: content }} />
  29. </div>
  30. );
  31. };
  32. AddDiffView.propTypes = {
  33. title: PropTypes.string,
  34. content: PropTypes.string,
  35. className: PropTypes.string
  36. };
  37. // timeago(new Date().getTime() - 40);
  38. @connect(
  39. state => {
  40. return {
  41. newsData: state.news.newsData,
  42. curpage: state.news.curpage,
  43. curUid: state.user.uid
  44. };
  45. },
  46. {
  47. fetchNewsData,
  48. fetchMoreNews,
  49. fetchInterfaceList
  50. }
  51. )
  52. class TimeTree extends Component {
  53. static propTypes = {
  54. newsData: PropTypes.object,
  55. fetchNewsData: PropTypes.func,
  56. fetchMoreNews: PropTypes.func,
  57. setLoading: PropTypes.func,
  58. loading: PropTypes.bool,
  59. curpage: PropTypes.number,
  60. typeid: PropTypes.number,
  61. curUid: PropTypes.number,
  62. type: PropTypes.string,
  63. fetchInterfaceList: PropTypes.func
  64. };
  65. constructor(props) {
  66. super(props);
  67. this.state = {
  68. bidden: '',
  69. loading: false,
  70. visible: false,
  71. curDiffData: {},
  72. apiList: []
  73. };
  74. this.curSelectValue = '';
  75. }
  76. getMore() {
  77. const that = this;
  78. if (this.props.curpage <= this.props.newsData.total) {
  79. this.setState({ loading: true });
  80. this.props
  81. .fetchMoreNews(
  82. this.props.typeid,
  83. this.props.type,
  84. this.props.curpage + 1,
  85. 10,
  86. this.curSelectValue
  87. )
  88. .then(function() {
  89. that.setState({ loading: false });
  90. if (that.props.newsData.total === that.props.curpage) {
  91. that.setState({ bidden: 'logbidden' });
  92. }
  93. });
  94. }
  95. }
  96. handleCancel = () => {
  97. this.setState({
  98. visible: false
  99. });
  100. };
  101. componentWillMount() {
  102. this.props.fetchNewsData(this.props.typeid, this.props.type, 1, 10);
  103. if (this.props.type === 'project') {
  104. this.getApiList();
  105. }
  106. }
  107. openDiff = data => {
  108. this.setState({
  109. curDiffData: data,
  110. visible: true
  111. });
  112. };
  113. async getApiList() {
  114. let result = await this.props.fetchInterfaceList({
  115. project_id: this.props.typeid,
  116. limit: 'all'
  117. });
  118. this.setState({
  119. apiList: result.payload.data.data.list
  120. });
  121. }
  122. handleSelectApi = selectValue => {
  123. this.curSelectValue = selectValue;
  124. this.props.fetchNewsData(this.props.typeid, this.props.type, 1, 10, selectValue);
  125. };
  126. render() {
  127. let data = this.props.newsData ? this.props.newsData.list : [];
  128. const curDiffData = this.state.curDiffData;
  129. let logType = {
  130. project: '项目',
  131. group: '分组',
  132. interface: '接口',
  133. interface_col: '接口集',
  134. user: '用户',
  135. other: '其他'
  136. };
  137. const children = this.state.apiList.map(item => {
  138. let methodColor = variable.METHOD_COLOR[item.method ? item.method.toLowerCase() : 'get'];
  139. return (
  140. <Option title={item.title} value={item._id + ''} path={item.path} key={item._id}>
  141. {item.title}{' '}
  142. <Tag
  143. style={{ color: methodColor.color, backgroundColor: methodColor.bac, border: 'unset' }}
  144. >
  145. {item.method}
  146. </Tag>
  147. </Option>
  148. );
  149. });
  150. children.unshift(
  151. <Option value="" key="all">
  152. 选择全部
  153. </Option>
  154. );
  155. if (data && data.length) {
  156. data = data.map((item, i) => {
  157. let interfaceDiff = false;
  158. // 去掉了 && item.data.interface_id
  159. if (item.data && typeof item.data === 'object') {
  160. interfaceDiff = true;
  161. }
  162. return (
  163. <Timeline.Item
  164. dot={
  165. <Link to={`/user/profile/${item.uid}`}>
  166. <Avatar src={`/api/user/avatar?uid=${item.uid}`} />
  167. </Link>
  168. }
  169. key={i}
  170. >
  171. <div className="logMesHeade">
  172. <span className="logoTimeago">{timeago(item.add_time)}</span>
  173. {/*<span className="logusername"><Link to={`/user/profile/${item.uid}`}><Icon type="user" />{item.username}</Link></span>*/}
  174. <span className="logtype">{logType[item.type]}动态</span>
  175. <span className="logtime">{formatTime(item.add_time)}</span>
  176. </div>
  177. <span className="logcontent" dangerouslySetInnerHTML={{ __html: item.content }} />
  178. <div style={{ padding: '10px 0 0 10px' }}>
  179. {interfaceDiff && <Button onClick={() => this.openDiff(item.data)}>改动详情</Button>}
  180. </div>
  181. </Timeline.Item>
  182. );
  183. });
  184. } else {
  185. data = '';
  186. }
  187. let pending =
  188. this.props.newsData.total <= this.props.curpage ? (
  189. <a className="logbidden">以上为全部内容</a>
  190. ) : (
  191. <a className="loggetMore" onClick={this.getMore.bind(this)}>
  192. 查看更多
  193. </a>
  194. );
  195. if (this.state.loading) {
  196. pending = <Spin />;
  197. }
  198. let diffView = showDiffMsg(jsondiffpatch, formattersHtml, curDiffData);
  199. return (
  200. <section className="news-timeline">
  201. <Modal
  202. style={{ minWidth: '800px' }}
  203. title="Api 改动日志"
  204. visible={this.state.visible}
  205. footer={null}
  206. onCancel={this.handleCancel}
  207. >
  208. <i>注: 绿色代表新增内容,红色代表删除内容</i>
  209. <div className="project-interface-change-content">
  210. {diffView.map((item, index) => {
  211. return (
  212. <AddDiffView
  213. className="item-content"
  214. title={item.title}
  215. key={index}
  216. content={item.content}
  217. />
  218. );
  219. })}
  220. {diffView.length === 0 && <ErrMsg type="noChange" />}
  221. </div>
  222. </Modal>
  223. {this.props.type === 'project' && (
  224. <Row className="news-search">
  225. <Col span="3">选择查询的 Api:</Col>
  226. <Col span="10">
  227. <AutoComplete
  228. onSelect={this.handleSelectApi}
  229. style={{ width: '100%' }}
  230. placeholder="Select Api"
  231. optionLabelProp="title"
  232. filterOption={(inputValue, options) => {
  233. if (options.props.value == '') return true;
  234. if (
  235. options.props.path.indexOf(inputValue) !== -1 ||
  236. options.props.title.indexOf(inputValue) !== -1
  237. ) {
  238. return true;
  239. }
  240. return false;
  241. }}
  242. >
  243. {/* {children} */}
  244. <OptGroup label="other">
  245. <Option value="wiki" path="" title="wiki">
  246. wiki
  247. </Option>
  248. </OptGroup>
  249. <OptGroup label="api">{children}</OptGroup>
  250. </AutoComplete>
  251. </Col>
  252. </Row>
  253. )}
  254. {data ? (
  255. <Timeline className="news-content" pending={pending}>
  256. {data}
  257. </Timeline>
  258. ) : (
  259. <ErrMsg type="noData" />
  260. )}
  261. </section>
  262. );
  263. }
  264. }
  265. export default TimeTree;