开源夏令营-《开源项目动态可视化展示》 #583
File diff suppressed because it is too large
Load Diff
|
|
@ -12,6 +12,7 @@ import SmoothLineTwo from './smoothLineTwo';
|
|||
import Demo from './demo';
|
||||
import ProjectScale from './ProjectScale';
|
||||
import Pie from './pie';
|
||||
import Echarts from './VisualEcharts/Echarts'
|
||||
|
||||
|
||||
import ActivityItem from './ActivityItem';
|
||||
|
|
@ -257,6 +258,7 @@ class Activity extends Component{
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
<Echarts {...this.props}></Echarts>
|
||||
{
|
||||
ai_shang_v1_url &&
|
||||
<div className="normalBox mt20">
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,37 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import '../activity.css';
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
|
||||
class CommitItem extends Component {
|
||||
timeTransfer = (time_stamp)=>{
|
||||
const time = new Date(time_stamp)
|
||||
return `${time.getFullYear()}-${String(time.getMonth() + 1).padStart(2, '0')}-${String(time.getDate()).padStart(2, '0')} ${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
render() {
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
const { commit } = this.props;
|
||||
return (
|
||||
<div className="activity_item">
|
||||
<div className="flex1">
|
||||
{
|
||||
<p className="itemLine">
|
||||
<Link to={`/${owner}/${projectsId}/tree/${commit.sha}`} className="color-blue font-16">{commit.sha}版本</Link>
|
||||
|
||||
</p >
|
||||
}
|
||||
<p className="itemLine mt10">
|
||||
<Link to={`/${commit.author && commit.author.login}`} className="show-user-link">
|
||||
<img alt="" src={getImageUrl(`/${commit && commit.author.image_url}`)} className="createImage" />
|
||||
<span className="mr20">{commit && commit.author.name}</span>
|
||||
</Link>
|
||||
{commit.commit_time && <span className="color-grey-9">创建于<span className="ml2 color-grey-6">{this.timeTransfer(commit.commit_time * 1000)}</span></span>}
|
||||
</p >
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default CommitItem;
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
import React, { Component } from 'react';
|
||||
import echarts from 'echarts';
|
||||
import '../activity.css'
|
||||
import axios from 'axios'
|
||||
import NoneData from '../../Nodata';
|
||||
|
||||
class Contributor extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.contributions_list = undefined,
|
||||
this.contributors_total_count = undefined,
|
||||
this.pulls_list = undefined,
|
||||
this.pr_total_count = undefined
|
||||
this.topBarEchart = undefined
|
||||
this.PRPlotEchart = undefined
|
||||
this.topBar = React.createRef();
|
||||
this.PRPlot = React.createRef()
|
||||
}
|
||||
|
||||
topYClick = (params)=>{
|
||||
let login_name = undefined
|
||||
this.contributions_list.forEach(contribution=>{
|
||||
if(contribution.name == params.value){
|
||||
login_name = contribution.login
|
||||
}
|
||||
})
|
||||
window.open(`/${login_name}`, "_blank");
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
const topChart = echarts.init(this.topBar.current);
|
||||
var option;
|
||||
option = {
|
||||
dataset: [{
|
||||
source: [['score','amount', 'product']],
|
||||
},
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: {}
|
||||
},
|
||||
top: "4%",
|
||||
right:'5%'
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '贡献者排行榜',
|
||||
textStyle: {
|
||||
fontSize: 13
|
||||
},
|
||||
top: '5%'
|
||||
},
|
||||
grid: { containLabel: true },
|
||||
xAxis: { name: '贡献数', max: 100 },
|
||||
yAxis: { type: 'category', triggerEvent: true,},
|
||||
visualMap: {
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
text: ['High Score', 'Low Score'],
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#65B581', '#FFCE34', '#FD665F']
|
||||
},
|
||||
bottom: "5%"
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
encode: {
|
||||
x: 'amount',
|
||||
y: 'product'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
topChart.setOption(option)
|
||||
this.topBarEchart = topChart
|
||||
topChart.on('click', 'yAxis.category', this.topYClick)
|
||||
|
||||
|
||||
const PRPlot = echarts.init(this.PRPlot.current);
|
||||
var option;
|
||||
// 由于未提供 PR 信息,暂使用模拟数据替代
|
||||
let base = +new Date(1988, 9, 3);
|
||||
let oneDay = 24 * 3600 * 1000;
|
||||
let data = [[base, Math.random() * 300]];
|
||||
for (let i = 1; i < 20000; i++) {
|
||||
let now = new Date((base += oneDay));
|
||||
data.push([+now, Math.round((Math.random() - 0.5) * 20 + data[i - 1][1])]);
|
||||
}
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
return [pt[0], '10%'];
|
||||
}
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '请求(PR)分布',
|
||||
textStyle: {
|
||||
fontSize: 13
|
||||
},
|
||||
top: '5%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%']
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 20
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
}
|
||||
],
|
||||
legend:{
|
||||
data: ['开启中', '已合并', '已拒绝'],
|
||||
top: '10%'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '开启中',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
name: '已合并',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: data
|
||||
},
|
||||
{
|
||||
name: '已拒绝',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: data
|
||||
},
|
||||
]
|
||||
};
|
||||
PRPlot.setOption(option);
|
||||
this.PRPlotEchart = PRPlot
|
||||
}
|
||||
|
||||
dataUpdate = (contributions_list, pulls_list)=>{
|
||||
this.contributions_list = contributions_list
|
||||
this.pulls_list = pulls_list
|
||||
let contributions = contributions_list
|
||||
|
||||
let top_source = []
|
||||
contributions.sort((a, b)=> a.contributions - b.contributions)
|
||||
contributions.forEach(contribution=>{
|
||||
let name = contribution.name
|
||||
if(name.length > 8){
|
||||
name = name.slice(0,8) + '...'
|
||||
}
|
||||
top_source.push([contribution.contributions, contribution.contributions, name])
|
||||
})
|
||||
const bar_x_max = parseInt(top_source[top_source.length-1][0] * 1.1)
|
||||
|
||||
const bar_x_min = parseInt(top_source[0][0] * 0.9)
|
||||
if (this.topBar.current){
|
||||
let myChart = this.topBarEchart
|
||||
let title = undefined
|
||||
if (top_source.length >= 10){
|
||||
title = "贡献 Top 10 排行榜 (总数: "+ top_source.length +")"
|
||||
}else{
|
||||
title = "贡献值排行榜"
|
||||
}
|
||||
myChart.setOption(
|
||||
{
|
||||
title: {
|
||||
left: 'center',
|
||||
text: title,
|
||||
textStyle: {
|
||||
fontSize: 13
|
||||
},
|
||||
top: '5%'
|
||||
},
|
||||
dataset: [
|
||||
{
|
||||
dimensions: ['score', 'amount', 'product'],
|
||||
source: top_source
|
||||
},
|
||||
{
|
||||
transform: {
|
||||
type: 'sort',
|
||||
config: { dimension: 'score', order: 'desc' }
|
||||
}
|
||||
},
|
||||
],
|
||||
tooltip : {
|
||||
formatter: function (params) {
|
||||
return `${params.data[2]}贡献: ${params.data[0]}`
|
||||
}
|
||||
},
|
||||
xAxis: { name: '贡献数', max: bar_x_max, minInterval: 1,},
|
||||
visualMap: {
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
text: ['High Score', 'Low Score'],
|
||||
min: bar_x_min,
|
||||
max: bar_x_max,
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#65B581', '#FFCE34', '#FD665F']
|
||||
},
|
||||
bottom: "5%"
|
||||
},
|
||||
})
|
||||
|
||||
if(top_source.length == 0){
|
||||
myChart = <NoneData _html="暂时还没有相关数据!" />
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const {contributions_list, contributors_total_count
|
||||
,pulls_list, pr_total_count} = this.props
|
||||
// && pulls_list
|
||||
if(contributions_list){
|
||||
this.dataUpdate(contributions_list, pulls_list)
|
||||
}
|
||||
return (
|
||||
<div style={{width: '100%', overflow: 'hidden'}}>
|
||||
<div ref={this.topBar} style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left'}}></div>
|
||||
<div ref={this.PRPlot} style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left'
|
||||
}}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Contributor
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
import React, { Component } from 'react';
|
||||
import echarts from 'echarts';
|
||||
import '../activity.css'
|
||||
import axios from 'axios'
|
||||
import Code from './Code'
|
||||
import Contributor from './Contributor'
|
||||
import GitHistory from './GitHistory'
|
||||
import ForkList from './ForkList'
|
||||
|
||||
// 负责数据获取业务,并安排四个子模块的位置
|
||||
class Echarts extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
contributions_list: undefined,
|
||||
contributors_total_count: undefined,
|
||||
pr_total_count: undefined,
|
||||
pulls_list: undefined,
|
||||
pr_open_count: undefined,
|
||||
pr_close_count: undefined,
|
||||
pr_merged_count: undefined,
|
||||
commits: undefined,
|
||||
commits_count: undefined,
|
||||
}
|
||||
this.eChartsRef = React.createRef();
|
||||
}
|
||||
|
||||
// /api/v1/:owner/:repo/commits.json?owner=&repo=&page=&limit=
|
||||
getInfoCommits = ()=>{
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/v1/${owner}/${projectsId}/commits.json`;
|
||||
const limit = 50
|
||||
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner: owner, repo: projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
this.setState({
|
||||
commits_count: result.data.total_count,
|
||||
})
|
||||
const round = Math.ceil(this.state.commits_count / limit)
|
||||
let commits = new Array()
|
||||
for (var i = 0; i <= round - 1; i++) {
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner: owner, repo: projectsId, limit: 50, page: i + 1
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
commits = commits.concat(result.data.commits)
|
||||
if(this.state.commits_count == commits.length){
|
||||
this.setState({
|
||||
commits: commits})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GET /api/:owner/:repo/contributors.json
|
||||
getInfoContributors = ()=>{
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/contributors.json`;
|
||||
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner: owner, repo: projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
this.setState(
|
||||
{
|
||||
contributors_total_count: result.data.total_count,
|
||||
contributions_list: result.data.list.reverse(),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PR数据接口:/api/v1/:owner/:repo/pulls.json?owner=&repo=
|
||||
getInfoPR = () => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/pulls.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner: owner, repo: projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
this.setState(
|
||||
{
|
||||
pr_total_count: result.data.search_count,
|
||||
pr_open_count:result.data.open_count,
|
||||
pr_close_count:result.data.close_count,
|
||||
pr_merged_count:result.data.merged_issues_size,
|
||||
pulls_list: result.data.issues,
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.getInfoContributors()
|
||||
this.getInfoPR()
|
||||
this.getInfoCommits()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
contributions_list,
|
||||
contributors_total_count,
|
||||
pr_total_count,
|
||||
pulls_list,
|
||||
commits_count,
|
||||
commits
|
||||
} = this.state
|
||||
|
||||
const figures = new Array(
|
||||
<Code {...this.props}></Code>,
|
||||
<Contributor {...this.props} contributions_list={contributions_list}
|
||||
contributors_total_count={contributors_total_count}
|
||||
pr_total_count={pr_total_count}
|
||||
pulls_list={pulls_list}></Contributor>,
|
||||
<GitHistory {...this.props} commits={commits}
|
||||
commits_count={commits_count}></GitHistory>,
|
||||
<ForkList {...this.props} commits={commits}
|
||||
commits_count={commits_count}></ForkList>);
|
||||
const figure_title = new Array("代码统计", "贡献者",
|
||||
"历史变更图", "复刻列表");
|
||||
const contents = []
|
||||
|
||||
figures.forEach((figure, index) => {
|
||||
contents.push(<div className="normalBox" style={{marginTop: 10}}>
|
||||
<div class="normalBox-title">{figure_title[index]}</div>
|
||||
<div class="boxpart">{figure}</div>
|
||||
</div>)}
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{contents}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default Echarts;
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
import React, { Component } from 'react';
|
||||
import echarts from 'echarts';
|
||||
import '../activity.css'
|
||||
import CommitItem from './CommitItem'
|
||||
import NoneData from '../../Nodata';
|
||||
|
||||
class ForkList extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
dataZoomStart: undefined,
|
||||
dataZoomEnd: undefined,
|
||||
seCommitsList: undefined
|
||||
}
|
||||
this.time = {
|
||||
minDate: undefined,
|
||||
maxDate:undefined,
|
||||
during_time_stamp:undefined
|
||||
}
|
||||
this.commits = undefined
|
||||
this.eChartsRef = React.createRef();
|
||||
this.myChart = undefined;
|
||||
this.dataZoomFunction = this.dataZoomFunction.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
const myChart = echarts.init(this.eChartsRef.current);
|
||||
this.myChart = myChart
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip : {
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '复刻(Fork)分布',
|
||||
textStyle: {
|
||||
fontSize: 13
|
||||
},
|
||||
top: '5%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%']
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 1000000
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 1000000
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: undefined
|
||||
},
|
||||
],
|
||||
barCategoryGap: '80%'
|
||||
};
|
||||
myChart.setOption(option);
|
||||
myChart.on('dataZoom', "mouseup", this.dataZoomFunction);
|
||||
myChart.getZr().on('click', this.dataBarFunction)
|
||||
|
||||
// 延时两秒,初始化commit列表
|
||||
setTimeout(this.layoutTime, 5000);
|
||||
}
|
||||
|
||||
layoutTime = ()=>{
|
||||
if(this.commits){
|
||||
let commits = this.commits
|
||||
commits.sort((a, b) => b.commit_time - a.commit_time)
|
||||
this.setState({
|
||||
seCommitsList: commits
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
timeTransfer = (time_stamp)=>{
|
||||
const time = new Date(time_stamp)
|
||||
return `${time.getFullYear()}-${String(time.getMonth() + 1).padStart(2, '0')}-${String(time.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
dataBarFunction = (params)=>{
|
||||
let pointInPixel = [params.offsetX, params.offsetY]
|
||||
let xIndex = undefined
|
||||
if (this.myChart.containPixel('grid', pointInPixel)) {
|
||||
xIndex = this.myChart.convertFromPixel({ seriesIndex: 0 }, [params.offsetX, params.offsetY])[0]
|
||||
}
|
||||
const bar_date = this.timeTransfer(xIndex)
|
||||
let seCommitsList = new Array()
|
||||
if(this.commits){
|
||||
this.commits.forEach(commit=>{
|
||||
if(this.timeTransfer(commit.commit_time * 1000) == bar_date){
|
||||
seCommitsList.push(commit)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
seCommitsList
|
||||
})
|
||||
}
|
||||
|
||||
dataZoomFunction = (params)=>{
|
||||
let start = undefined
|
||||
let end = undefined
|
||||
let seCommitsList = new Array()
|
||||
if (params.batch){
|
||||
start = params.batch[0].start
|
||||
end = params.batch[0].end
|
||||
}
|
||||
else{
|
||||
start = params.start
|
||||
end = params.end
|
||||
}
|
||||
let boundary = 3600 * 24 * 1000
|
||||
start = new Date(+this.time.minDate + (start/100) * this.time.during_time_stamp - boundary)
|
||||
end = new Date(+this.time.minDate + (end/100) * this.time.during_time_stamp + boundary)
|
||||
|
||||
if(this.commits){
|
||||
this.commits.forEach(commit=>{
|
||||
if((start <= commit.commit_time * 1000) && (end >= commit.commit_time * 1000)){
|
||||
seCommitsList.push(commit)
|
||||
}
|
||||
})
|
||||
}
|
||||
seCommitsList.sort((a, b) => b.commit_time - a.commit_time);
|
||||
this.setState({
|
||||
start,
|
||||
end,
|
||||
seCommitsList
|
||||
})
|
||||
}
|
||||
|
||||
dataUpdate = (commits)=>{
|
||||
let data = new Array()
|
||||
const countByDay = {};
|
||||
|
||||
commits.forEach(commit=>{
|
||||
const dayString = this.timeTransfer(commit.commit_time * 1000);
|
||||
countByDay[dayString] = (countByDay[dayString] || 0) + 1;
|
||||
})
|
||||
for (const date in countByDay) {
|
||||
const count = countByDay[date];
|
||||
data.push([+new Date(date), count]);
|
||||
}
|
||||
const dates = Object.keys(countByDay).map(dateString => new Date(dateString));
|
||||
const minDate = new Date(Math.min(...dates));
|
||||
const maxDate = new Date(Math.max(...dates));
|
||||
const during_time_stamp = (+maxDate) - (+minDate)
|
||||
this.time = {
|
||||
minDate,
|
||||
maxDate,
|
||||
during_time_stamp,
|
||||
}
|
||||
|
||||
if(this.myChart){
|
||||
let myChart = this.myChart
|
||||
myChart.setOption(
|
||||
{
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (params) {
|
||||
const time = new Date(params[0].axisValue)
|
||||
return `${time.getFullYear()}年${time.getMonth() + 1}月${time.getDate()}日</br> 提交次数:${params[0].data[1]}`
|
||||
}
|
||||
},
|
||||
// 数据为仿真数据,后端未提供 fork 信息数据接口
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: data
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {commits, commits_count} = this.props
|
||||
const {dataZoomStart, dataZoomEnd, seCommitsList} = this.state
|
||||
if(commits_count && commits &&(commits.length == commits_count)){
|
||||
this.commits = commits
|
||||
this.dataUpdate(commits)
|
||||
}
|
||||
return (
|
||||
<div style={{width: '100%', overflow: 'hidden'}}>
|
||||
<div ref={this.eChartsRef} style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left'}}></div>
|
||||
<div style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
{
|
||||
seCommitsList && seCommitsList.length > 0 ?
|
||||
<div className="activity_list">
|
||||
{
|
||||
seCommitsList && seCommitsList.map(commit=>{
|
||||
return (<CommitItem {...this.props} commit={commit} ></CommitItem>)
|
||||
})
|
||||
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<NoneData _html="暂时还没有相关数据!" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ForkList
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
import React, { Component } from 'react';
|
||||
import echarts from 'echarts';
|
||||
import '../activity.css'
|
||||
import CommitItem from './CommitItem'
|
||||
import NoneData from '../../Nodata';
|
||||
|
||||
class GitHistory extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
dataZoomStart: undefined,
|
||||
dataZoomEnd: undefined,
|
||||
seCommitsList: undefined
|
||||
}
|
||||
this.time = {
|
||||
minDate: undefined,
|
||||
maxDate:undefined,
|
||||
during_time_stamp:undefined
|
||||
}
|
||||
this.commits = undefined
|
||||
this.myChart = undefined;
|
||||
this.eChartsRef = React.createRef();
|
||||
this.dataZoomFunction = this.dataZoomFunction.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
const myChart = echarts.init(this.eChartsRef.current);
|
||||
this.myChart = myChart
|
||||
var option;
|
||||
|
||||
option = {
|
||||
tooltip : {
|
||||
},
|
||||
title: {
|
||||
left: 'center',
|
||||
text: '提交(Commit)分布',
|
||||
textStyle: {
|
||||
fontSize: 13
|
||||
},
|
||||
top: '5%'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
// boundaryGap: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%']
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 1000000
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 1000000
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
// smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: undefined
|
||||
},
|
||||
],
|
||||
barCategoryGap: '80%'
|
||||
};
|
||||
myChart.setOption(option);
|
||||
myChart.on('dataZoom', "mouseup", this.dataZoomFunction);
|
||||
myChart.getZr().on('click', this.dataBarFunction)
|
||||
|
||||
// 延时两秒,初始化commit列表
|
||||
setTimeout(this.layoutTime, 5000);
|
||||
}
|
||||
|
||||
layoutTime = ()=>{
|
||||
if(this.commits){
|
||||
let commits = this.commits
|
||||
commits.sort((a, b) => b.commit_time - a.commit_time)
|
||||
this.setState({
|
||||
seCommitsList: commits
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
timeTransfer = (time_stamp)=>{
|
||||
const time = new Date(time_stamp)
|
||||
return `${time.getFullYear()}-${String(time.getMonth() + 1).padStart(2, '0')}-${String(time.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
dataBarFunction = (params)=>{
|
||||
let pointInPixel = [params.offsetX, params.offsetY]
|
||||
let xIndex = undefined
|
||||
if (this.myChart.containPixel('grid', pointInPixel)) {
|
||||
xIndex = this.myChart.convertFromPixel({ seriesIndex: 0 }, [params.offsetX, params.offsetY])[0]
|
||||
}
|
||||
const bar_date = this.timeTransfer(xIndex)
|
||||
let seCommitsList = new Array()
|
||||
if(this.commits){
|
||||
this.commits.forEach(commit=>{
|
||||
if(this.timeTransfer(commit.commit_time * 1000) == bar_date){
|
||||
seCommitsList.push(commit)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
seCommitsList
|
||||
})
|
||||
}
|
||||
|
||||
dataZoomFunction = (params)=>{
|
||||
let start = undefined
|
||||
let end = undefined
|
||||
let seCommitsList = new Array()
|
||||
if (params.batch){
|
||||
start = params.batch[0].start
|
||||
end = params.batch[0].end
|
||||
}
|
||||
else{
|
||||
start = params.start
|
||||
end = params.end
|
||||
}
|
||||
let boundary = 3600 * 24 * 1000
|
||||
start = new Date(+this.time.minDate + (start/100) * this.time.during_time_stamp - boundary)
|
||||
end = new Date(+this.time.minDate + (end/100) * this.time.during_time_stamp + boundary)
|
||||
|
||||
if(this.commits){
|
||||
this.commits.forEach(commit=>{
|
||||
if((start <= commit.commit_time * 1000) && (end >= commit.commit_time * 1000)){
|
||||
seCommitsList.push(commit)
|
||||
}
|
||||
})
|
||||
}
|
||||
seCommitsList.sort((a, b) => b.commit_time - a.commit_time);
|
||||
this.setState({
|
||||
start,
|
||||
end,
|
||||
seCommitsList
|
||||
})
|
||||
}
|
||||
|
||||
dataUpdate = (commits)=>{
|
||||
let data = new Array()
|
||||
const countByDay = {};
|
||||
|
||||
commits.forEach(commit=>{
|
||||
const dayString = this.timeTransfer(commit.commit_time * 1000);
|
||||
countByDay[dayString] = (countByDay[dayString] || 0) + 1;
|
||||
})
|
||||
for (const date in countByDay) {
|
||||
const count = countByDay[date];
|
||||
data.push([+new Date(date), count]);
|
||||
}
|
||||
const dates = Object.keys(countByDay).map(dateString => new Date(dateString));
|
||||
const minDate = new Date(Math.min(...dates));
|
||||
const maxDate = new Date(Math.max(...dates));
|
||||
const during_time_stamp = (+maxDate) - (+minDate)
|
||||
this.time = {
|
||||
minDate,
|
||||
maxDate,
|
||||
during_time_stamp,
|
||||
}
|
||||
|
||||
if(this.myChart){
|
||||
let myChart = this.myChart
|
||||
myChart.setOption(
|
||||
{
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (params) {
|
||||
const time = new Date(params[0].axisValue)
|
||||
return `${time.getFullYear()}年${time.getMonth() + 1}月${time.getDate()}日</br> 提交次数:${params[0].data[1]}`
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
// smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {},
|
||||
data: data
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {commits, commits_count} = this.props
|
||||
const {dataZoomStart, dataZoomEnd, seCommitsList} = this.state
|
||||
if(commits_count && commits &&(commits.length == commits_count)){
|
||||
this.commits = commits
|
||||
this.dataUpdate(commits)
|
||||
}
|
||||
return (
|
||||
<div style={{width: '100%', overflow: 'hidden'}}>
|
||||
<div ref={this.eChartsRef} style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left'}}></div>
|
||||
<div style={{
|
||||
height: 400,
|
||||
width: "50%",
|
||||
float: 'left',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
{
|
||||
seCommitsList && seCommitsList.length > 0 ?
|
||||
<div className="activity_list">
|
||||
{
|
||||
seCommitsList && seCommitsList.map(commit=>{
|
||||
return (<CommitItem {...this.props} commit={commit} ></CommitItem>)
|
||||
})
|
||||
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<NoneData _html="暂时还没有相关数据!" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GitHistory
|
||||
Loading…
Reference in New Issue