diff --git a/package-lock.json b/package-lock.json index 65734943b..31e8c6291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5070,6 +5070,11 @@ "zrender": "4.3.0" } }, + "echarts-wordcloud": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/echarts-wordcloud/download/echarts-wordcloud-2.0.0.tgz?cache=0&sync_timestamp=1610779172014&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fecharts-wordcloud%2Fdownload%2Fecharts-wordcloud-2.0.0.tgz", + "integrity": "sha1-Uu+BeJWAH/6emd0brKt2hrLewEo=" + }, "editor.md": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/editor.md/-/editor.md-1.5.0.tgz", diff --git a/package.json b/package.json index edc6e18e7..2e61175be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forge", - "version": "0.1.0", + "version": "3.0.0", "private": true, "dependencies": { "@monaco-editor/react": "^2.3.0", @@ -14,6 +14,7 @@ "babel-jest": "20.0.3", "babel-loader": "7.1.2", "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-polyfill": "^6.26.0", "babel-preset-react-app": "^3.1.1", "babel-runtime": "6.26.0", "bizcharts": "^3.5.8", @@ -29,7 +30,8 @@ "dompurify": "^2.0.15", "dotenv": "4.0.0", "dotenv-expand": "4.2.0", - "echarts": "^4.7.0", + "echarts": "^4.9.0", + "echarts-wordcloud": "^2.0.0", "editor.md": "^1.5.0", "eslint": "4.10.0", "eslint-config-react-app": "^2.1.0", @@ -46,6 +48,7 @@ "install": "^0.12.2", "jest": "20.0.4", "js-base64": "^2.5.2", + "js2wordcloud": "^1.1.12", "katex": "^0.11.1", "lodash": "^4.17.15", "loglevel": "^1.6.8", @@ -62,7 +65,7 @@ "postcss-loader": "2.0.8", "promise": "8.0.1", "prop-types": "^15.6.1", - "qrcode.react": "^1.0.0", + "qrcode.react": "^1.0.1", "qs": "^6.9.3", "quill": "^1.3.7", "quill-delta-to-html": "^0.11.0", @@ -92,6 +95,7 @@ "react-resizable": "^1.10.1", "react-router": "^4.2.0", "react-router-dom": "^4.2.2", + "react-slick": "^0.28.1", "react-split-pane": "^0.1.91", "react-url-query": "^1.5.0", "react-zmage": "^0.8.5-beta.31", @@ -102,6 +106,7 @@ "scroll-into-view": "^1.14.2", "showdown": "^1.9.1", "showdown-katex": "^0.8.0", + "slick-carousel": "^1.8.1", "store": "^2.0.12", "style-loader": "0.19.0", "styled-components": "^4.4.1", @@ -111,7 +116,9 @@ "webpack-dev-server": "^3.10.3", "webpack-manifest-plugin": "^2.2.0", "whatwg-fetch": "2.0.3", - "wrap-md-editor": "^0.2.20" + "wrap-md-editor": "^0.2.20", + "xterm": "4.8.1", + "xterm-addon-fit": "0.4.0" }, "scripts": { "start": "node --max_old_space_size=15360 scripts/start.js", diff --git a/src/forge/Main/Index.scss b/src/forge/Main/Index.scss index 837608b03..6b6592696 100644 --- a/src/forge/Main/Index.scss +++ b/src/forge/Main/Index.scss @@ -129,9 +129,7 @@ height: 7px; margin-top: 12px; span{ - border-left: 1px solid #fff; &:first-child{ - border-left: none; border-radius: 10px 0px 0px 10px; } &:last-child{ diff --git a/src/forge/users/Echart/Cloud.jsx b/src/forge/users/Echart/Cloud.jsx index 3b53108a1..778b30e74 100644 --- a/src/forge/users/Echart/Cloud.jsx +++ b/src/forge/users/Echart/Cloud.jsx @@ -4,12 +4,14 @@ import Js2WordCloud from 'js2wordcloud/dist/js2wordcloud.js' function Cloud({data}) { useEffect(()=>{ - optionChart1(); - },[]) + if(data){ + optionChart1(data); + } + },[data]) - function optionChart1(){ + function optionChart1(d){ var div = new Js2WordCloud(document.getElementById('cloud')) - let textList=['服务', '细致', '意识', '踏实','开发', '反馈'] + let textList= d.categories; let cyList=[] for(let i=0;i +
) } export default Cloud; \ No newline at end of file diff --git a/src/forge/users/Echart/Pie.jsx b/src/forge/users/Echart/Pie.jsx new file mode 100644 index 000000000..d5489b981 --- /dev/null +++ b/src/forge/users/Echart/Pie.jsx @@ -0,0 +1,69 @@ +import React ,{ useEffect } from 'react'; +import echarts from 'echarts/lib/echarts'; +import 'echarts/lib/chart/pie'; + +function Pie({data}) { + + useEffect(()=>{ + if(data){ + Init(data); + } + },[data]) + + function Init(d) { + var huan_val = document.getElementById("Pie"); + var chart = echarts.init(huan_val); + let option = { + color: ["#f8e367", "#5ea6ff", "#ff9e48", "#99dfff"], + title: { + show:false + }, + tooltip: { + trigger: 'item' + }, + legend: { + top: '5%', + right: 'center' + }, + series: [ + { + name: '', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2 + }, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: '40', + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: [ + {value: d.developer && d.developer.count, name: '开发者'}, + {value: d.manager && d.manager.count, name: '管理员'}, + {value: d.owner && d.owner.count, name: '创建者'}, + {value: d.reporter && d.reporter.count, name: '报告者'} + ] + } + ] + }; + chart.setOption(option); + } + + return( +
+ ) +} +export default Pie; \ No newline at end of file diff --git a/src/forge/users/Echart/Radar.jsx b/src/forge/users/Echart/Radar.jsx new file mode 100644 index 000000000..354309589 --- /dev/null +++ b/src/forge/users/Echart/Radar.jsx @@ -0,0 +1,62 @@ +import React ,{ useEffect } from 'react'; +import echarts from 'echarts/lib/echarts' +import 'echarts/lib/chart/radar'; + +function Radar({data}) { + + useEffect(()=>{ + if(data){ + Init(data); + } + },[data]) + + function Init(d) { + var huan_val = document.getElementById("radar"); + var myEcharts = echarts.init(huan_val); + let option = { + color: ["#f8e367", "#99dfff", "#58c0f0", "#5ea6ff", "#ff9e48", "#bcbcbc"], + title: { + show:false + }, + legend: { + data: ['个人能力(personal)', '社区平均(average)'], + top:"3%", + right:"center" + }, + tooltip: { + trigger: 'item' + }, + radar: { + // shape: 'circle', + indicator: [ + { name: '影响力', max: 100}, + { name: '贡献度', max: 100}, + { name: '活跃度', max: 100}, + { name: '项目经验', max: 100}, + { name: '语言能力', max: 100}, + ], + center:["50%","55%"] + }, + series: [{ + name: '', + type: 'radar', + data: [ + { + value: d.user && [d.user.influence, d.user.contribution, d.user.activity, d.user.experience, d.user.language], + name: '个人能力(personal)' + }, + { + value: d.platform && [d.platform.influence, d.platform.contribution, d.platform.activity, d.platform.experience, d.platform.language], + name: '社区平均(average)' + } + ] + }] + }; + myEcharts.setOption(option); + } + + return( +
+ ) +} +export default Radar; \ No newline at end of file diff --git a/src/forge/users/Echart/Round.jsx b/src/forge/users/Echart/Round.jsx new file mode 100644 index 000000000..ebcb13671 --- /dev/null +++ b/src/forge/users/Echart/Round.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import './style.scss'; + + +function Round({num,color,name}) { + return( +
+
+ {num} + {name} +
+
+ {num > 50 ? +
+ : +
+ } + {num < 50 ? +
+ : +
+ } +
+ ) +} +export default Round; \ No newline at end of file diff --git a/src/forge/users/Echart/style.scss b/src/forge/users/Echart/style.scss new file mode 100644 index 000000000..5a2aae387 --- /dev/null +++ b/src/forge/users/Echart/style.scss @@ -0,0 +1,69 @@ +.annulusBasics { + width : 74px; + height : 74px; + position : relative; + overflow : hidden; + border-radius: 50%; + text-align : center; + z-index : 1; +} +//圆环中间的白色 +.centerCircle { + position : absolute; + z-index : 10; + border-radius: 50%; + width : 60px; + height : 60px; + background : #fff; + transform : translate(7px, 7px); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + line-height: 20px; + font-size: 12px; + .score{ + font-size: 18px; + } +} + //圆环百分比时出现圆环边框的颜色 +.annulusOuter { + position : absolute; + top : 0; + left : 0; + width : 74px; + height : 74px; + border : 12px solid #FF7F69; + border-radius: 50%; +} +//左边遮住圆环颜色的长方形 +.leftRectangle { + position : absolute; + background : #EBEEF5; + width : 37px; + height : 74px; + transform-origin: right; +} +//右边遮住圆环颜色的长方形 +.rightRectangle { + position : absolute; + background : #EBEEF5; + transform-origin: left; + left : 37px; + width : 37px; + height : 74px; + transform : rotate(0deg); +} +//弥补hidde在移动端失效的圆环 +.repairAnnulus{ + position : absolute; + width : 74px; + height : 74px; + z-index : 20; + border-radius: 50%; + box-sizing : content-box; +//改外边框的时候,位置也要改下 + border : 20px solid #ffffff; + top : -20px; + left : -20px; +} \ No newline at end of file diff --git a/src/forge/users/Statistics/Index.jsx b/src/forge/users/Statistics/Index.jsx index 3b20ea1ea..ee4e25e24 100644 --- a/src/forge/users/Statistics/Index.jsx +++ b/src/forge/users/Statistics/Index.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React , {useEffect, useState} from 'react'; import './Index.scss'; import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/title'; @@ -8,13 +8,184 @@ import 'echarts/lib/component/markPoint'; import Pie from '../Echart/Pie'; import Cloud from '../Echart/Cloud'; import Radar from '../Echart/Radar'; +import Round from '../Echart/Round'; +import { DatePicker } from 'antd'; +import moment from 'moment'; + +import { FlexAJ } from '../../Component/layout'; +import Axios from 'axios'; + +const { RangePicker } = DatePicker; + const dateFormat = 'YYYY-MM-DD'; +function Index(props) { + const username = props.match.params.username; + const [ dates, setDates] = useState(undefined); + const [ pieData, setPieData ] = useState([]); + const [ rDates, setRDates ] = useState(undefined); + const [ radarData, setRadarData ] = useState([]); + + const [ topThree , setTopThree] = useState(undefined); + const [ percentData , setPercentData ] = useState(undefined); + + const [ cData, setCData ] = useState(undefined); + const [ cloudData, setCloudData ] = useState(undefined); + + const disabledDate = current => { + return current && current > moment().endOf('day'); + }; + + // 获取角色定位接口数据 + useEffect(()=>{ + getRoleSta(); + },[dates]) + + function getRoleSta() { + const url = `/users/${username}/statistics/role.json`; + Axios.get(url,{ + params:{ + start_time:dates && dates[0], + end_time:dates && dates[1] + } + }).then(result=>{ + if(result && result.data){ + setPieData(result.data.role); + } + }).catch(error=>{}) + } + + + // 获取开发能力接口数据 + useEffect(()=>{ + getRadarSta(); + },[rDates]) + + function getRadarSta() { + const url = `/users/${username}/statistics/develop.json`; + Axios.get(url,{ + params:{ + start_time:rDates && rDates[0], + end_time:rDates && rDates[1] + } + }).then(result=>{ + if(result && result.data){ + setRadarData(result.data); + let score = result.data.user && result.data.user.each_language_score; + var sortData = [] + for (var item in score) { + sortData.push([item, score[item]]) + } + sortData.sort(function(a, b) { + return b[1] - a[1]; + }); + setTopThree(sortData.slice(0,3)); + + let percent = result.data.user && result.data.user.languages_percent; + let arr = []; + Object.keys(percent).map((item,key)=>{ + arr.push({name:item,p:percent[item]*100,color:getColor()}); + }) + setPercentData(arr); + } + }).catch(error=>{}) + } +  function getColor(){ + let str = "#"; + let arr = ["1","2","3","4","4","5","6","7","8","9","a","b","c","d","e","f"]; + for(var i=0;i<6;i++){ + let num = parseInt(Math.random() * 16,0); + str+=arr[num]; + } + return str; + } + + //用户专业定位 + useEffect(()=>{ + getCloudSta(); + },[cData]) + + function getCloudSta() { + const url = `/users/${username}/statistics/major.json`; + Axios.get(url,{ + params:{ + start_time:rDates && rDates[0], + end_time:rDates && rDates[1] + } + }).then(result=>{ + if(result && result.data){ + setCloudData(result.data); + } + }).catch(error=>{}) + } -function Index() { return(
- - - +
+ + 开发能力 + {setRDates(dateString)}} + format={dateFormat} + /> + + + { + topThree && +
+ + + +
+ } + { + percentData && +
+
+ { + percentData.map((i,k)=>{ + return( + + ) + }) + } +
+
+ { + percentData.map((i,k)=>{ + return( + + {i.name} + {`${i.p}%`} + + ) + }) + } +
+
+ } +
+
+ + 角色定位 + {setDates(dateString)}} + format={dateFormat} + /> + + +
+
+ + 专业定位 + {setCData(dateString)}} + format={dateFormat} + /> + + +
) } diff --git a/src/forge/users/Statistics/Index.scss b/src/forge/users/Statistics/Index.scss index e69de29bb..a2f944b90 100644 --- a/src/forge/users/Statistics/Index.scss +++ b/src/forge/users/Statistics/Index.scss @@ -0,0 +1,64 @@ +.boxes{ + padding:20px 30px; + .roundBox{ + margin:20px auto; + display: flex; + justify-content: center; + & >div{ + margin:0px 30px; + } + } + .pBox{ + width: 400px; + margin:40px auto 20px; + .progress{ + width: 360px; + margin:0px auto; + display: flex; + background-color: #fafafa; + border-radius: 10px; + height: 7px; + span:first-child { + border-radius: 10px 0px 0px 10px; + } + span:last-child { + border-radius: 0px 10px 10px 0px; + } + } + .progresstip { + margin-top: 15px; + flex-wrap: wrap; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + & > span { + padding-left: 15px; + position: relative; + min-width: 33%; + display: flex; + justify-content: flex-start; + i.zero { + display: block; + border-radius: 50%; + height: 8px; + width: 8px; + margin-top:10px; + margin-right: 5px; + } + span { + color: #666; + } + span:last-child { + color: #999; + margin-left: 5px; + } + } + } + } +} \ No newline at end of file