diff --git a/package-lock.json b/package-lock.json index 6f4ada7df..267c0020c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5075,6 +5075,11 @@ "zrender": "4.3.2" } }, + "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 0ae4df362..20880eaeb 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "dotenv": "4.0.0", "dotenv-expand": "4.2.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", diff --git a/public/css/iconfont.css b/public/css/iconfont.css index 3d27dfda1..7035af055 100644 --- a/public/css/iconfont.css +++ b/public/css/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 2340181 */ - src: url('iconfont.woff2?t=1620900836917') format('woff2'), - url('iconfont.woff?t=1620900836917') format('woff'), - url('iconfont.ttf?t=1620900836917') format('truetype'); + src: url('iconfont.woff2?t=1622517296245') format('woff2'), + url('iconfont.woff?t=1622517296245') format('woff'), + url('iconfont.ttf?t=1622517296245') format('truetype'); } .iconfont { @@ -13,6 +13,58 @@ -moz-osx-font-smoothing: grayscale; } +.icon-youxiang:before { + content: "\e8b2"; +} + +.icon-danwei:before { + content: "\e8a7"; +} + +.icon-daibanshixiang:before { + content: "\e8a8"; +} + +.icon-gailan:before { + content: "\e8ab"; +} + +.icon-nan:before { + content: "\e8ac"; +} + +.icon-nv:before { + content: "\e8ad"; +} + +.icon-gongzuoliu1:before { + content: "\e8ae"; +} + +.icon-shujutongji:before { + content: "\e8b1"; +} + +.icon-xiangmu:before { + content: "\e8b3"; +} + +.icon-zuzhi:before { + content: "\e8b4"; +} + +.icon-arrowRight:before { + content: "\e863"; +} + +.icon-jiantouloukong-zuo:before { + content: "\e861"; +} + +.icon-jiantouloukong-you:before { + content: "\e862"; +} + .icon-fenxiang1:before { content: "\e89c"; } @@ -2441,7 +2493,7 @@ content: "\e7f3"; } -.icon-dianzan1:before { +.icon-dianzaned:before { content: "\e639"; } diff --git a/public/css/iconfont.js b/public/css/iconfont.js index bb4188cdf..068c8e0a5 100644 --- a/public/css/iconfont.js +++ b/public/css/iconfont.js @@ -1 +1 @@ -!function(c){var l,a,h,i,o,z,t='',p=(p=document.getElementsByTagName("script"))[p.length-1].getAttribute("data-injectcss");if(p&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function s(){o||(o=!0,h())}l=function(){var c,l,a;(a=document.createElement("div")).innerHTML=t,t=null,(l=a.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",c=l,(a=document.body).firstChild?(l=a.firstChild).parentNode.insertBefore(c,l):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(a=function(){document.removeEventListener("DOMContentLoaded",a,!1),l()},document.addEventListener("DOMContentLoaded",a,!1)):document.attachEvent&&(h=l,i=c.document,o=!1,(z=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(z,50)}s()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,s())})}(window); \ No newline at end of file +!function(c){var l,a,h,i,o,z,t='',p=(p=document.getElementsByTagName("script"))[p.length-1].getAttribute("data-injectcss");if(p&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function v(){o||(o=!0,h())}l=function(){var c,l,a;(a=document.createElement("div")).innerHTML=t,t=null,(l=a.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",c=l,(a=document.body).firstChild?(l=a.firstChild).parentNode.insertBefore(c,l):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(a=function(){document.removeEventListener("DOMContentLoaded",a,!1),l()},document.addEventListener("DOMContentLoaded",a,!1)):document.attachEvent&&(h=l,i=c.document,o=!1,(z=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(z,50)}v()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,v())})}(window); \ No newline at end of file diff --git a/public/css/iconfont.json b/public/css/iconfont.json index ab6ae3253..877958d72 100644 --- a/public/css/iconfont.json +++ b/public/css/iconfont.json @@ -5,6 +5,97 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "21936935", + "name": "邮箱", + "font_class": "youxiang", + "unicode": "e8b2", + "unicode_decimal": 59570 + }, + { + "icon_id": "21936924", + "name": "单位", + "font_class": "danwei", + "unicode": "e8a7", + "unicode_decimal": 59559 + }, + { + "icon_id": "21936925", + "name": "待办事项", + "font_class": "daibanshixiang", + "unicode": "e8a8", + "unicode_decimal": 59560 + }, + { + "icon_id": "21936928", + "name": "概览", + "font_class": "gailan", + "unicode": "e8ab", + "unicode_decimal": 59563 + }, + { + "icon_id": "21936929", + "name": "男", + "font_class": "nan", + "unicode": "e8ac", + "unicode_decimal": 59564 + }, + { + "icon_id": "21936930", + "name": "女", + "font_class": "nv", + "unicode": "e8ad", + "unicode_decimal": 59565 + }, + { + "icon_id": "21936931", + "name": "工作流", + "font_class": "gongzuoliu1", + "unicode": "e8ae", + "unicode_decimal": 59566 + }, + { + "icon_id": "21936934", + "name": "数据统计", + "font_class": "shujutongji", + "unicode": "e8b1", + "unicode_decimal": 59569 + }, + { + "icon_id": "21936936", + "name": "项目", + "font_class": "xiangmu", + "unicode": "e8b3", + "unicode_decimal": 59571 + }, + { + "icon_id": "21936937", + "name": "组织", + "font_class": "zuzhi", + "unicode": "e8b4", + "unicode_decimal": 59572 + }, + { + "icon_id": "14835599", + "name": "右箭头", + "font_class": "arrowRight", + "unicode": "e863", + "unicode_decimal": 59491 + }, + { + "icon_id": "21151489", + "name": "箭头镂空-左", + "font_class": "jiantouloukong-zuo", + "unicode": "e861", + "unicode_decimal": 59489 + }, + { + "icon_id": "21151557", + "name": "箭头镂空-右", + "font_class": "jiantouloukong-you", + "unicode": "e862", + "unicode_decimal": 59490 + }, { "icon_id": "21568989", "name": "分享", @@ -4257,7 +4348,7 @@ { "icon_id": "1004630", "name": "点赞2", - "font_class": "dianzan1", + "font_class": "dianzaned", "unicode": "e639", "unicode_decimal": 58937 }, diff --git a/public/css/iconfont.ttf b/public/css/iconfont.ttf index 23ebbd47d..9507b420e 100644 Binary files a/public/css/iconfont.ttf and b/public/css/iconfont.ttf differ diff --git a/public/css/iconfont.woff b/public/css/iconfont.woff index 6b353baae..5bb7cb805 100644 Binary files a/public/css/iconfont.woff and b/public/css/iconfont.woff differ diff --git a/public/css/iconfont.woff2 b/public/css/iconfont.woff2 index 3ec15ab0c..3f6b85611 100644 Binary files a/public/css/iconfont.woff2 and b/public/css/iconfont.woff2 differ diff --git a/src/forge/Component/layout.jsx b/src/forge/Component/layout.jsx index 57b20c1e1..adaf4dbf3 100644 --- a/src/forge/Component/layout.jsx +++ b/src/forge/Component/layout.jsx @@ -28,6 +28,11 @@ export const AlignTop = styled.div`{ display:flex; align-items: flex-start; }` +export const AlignAJBottom = styled.div`{ + display:flex; + justify-content: space-between; + align-items: flex-end; +}` // 左右结构 export const Box = styled.div`{ display:flex; diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js index 4d55f824c..9b19a5c50 100644 --- a/src/forge/Head/Header.js +++ b/src/forge/Head/Header.js @@ -559,12 +559,12 @@ class NewHeader extends Component { :
- +
{ diff --git a/src/forge/Main/CoderDepotReadme.jsx b/src/forge/Main/CoderDepotReadme.jsx index 135b0ee1a..b04c0dfe0 100644 --- a/src/forge/Main/CoderDepotReadme.jsx +++ b/src/forge/Main/CoderDepotReadme.jsx @@ -11,6 +11,8 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){ useEffect(()=>{ if(readme && readme.content){ setContent(readme.content); + }else{ + setContent(undefined); } },[readme]) diff --git a/src/forge/Main/CoderRootBranch.js b/src/forge/Main/CoderRootBranch.js index 94411defa..5e9a84ca0 100644 --- a/src/forge/Main/CoderRootBranch.js +++ b/src/forge/Main/CoderRootBranch.js @@ -40,7 +40,7 @@ export default ((props)=>{

- 创建合并请求 + 创建合并请求 diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index f991cc23f..61b2df722 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -401,6 +401,7 @@ class Detail extends Component { const urlFlag = (urlArr.length === 3); const { projectsId , owner } = this.props.match.params; + const { current_user } = this.props; let pathname = checkPathname(projectsId,owner,url); const { state } = this.props.history.location; @@ -461,8 +462,8 @@ class Detail extends Component { firstSync ? "": { - projectDetail && projectDetail.type && projectDetail.type === 2 ? - 同步镜像 : "" + ((current_user && current_user.admin) || isManager) && (projectDetail && projectDetail.type && projectDetail.type === 2) ? + 同步镜像 : "" } this.focusFunc(watched)}> @@ -638,6 +639,11 @@ class Detail extends Component { } > {/* 新建合并请求 */} + () + } + > () diff --git a/src/forge/Main/Index.scss b/src/forge/Main/Index.scss index 2a207dfdd..ef3cbc0f5 100644 --- a/src/forge/Main/Index.scss +++ b/src/forge/Main/Index.scss @@ -161,9 +161,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/Main/IndexItem.js b/src/forge/Main/IndexItem.js index 49b191857..f11ee7172 100644 --- a/src/forge/Main/IndexItem.js +++ b/src/forge/Main/IndexItem.js @@ -18,7 +18,7 @@ class IndexItem extends Component { render() { const { projects } = this.props; return ( -
+
{ projects && projects.length > 0 ? projects.map((item, key) => { return (
diff --git a/src/forge/Main/list.css b/src/forge/Main/list.css index 9854a0a31..bdf93163f 100644 --- a/src/forge/Main/list.css +++ b/src/forge/Main/list.css @@ -91,6 +91,7 @@ display: flex; border-bottom:1px solid rgba(238,238,238,1); padding:22px 0px; + justify-content: flex-start; } .boxShandow{ box-shadow:0px 2px 20px 10px rgba(0,0,0,0.03); @@ -100,6 +101,7 @@ height: 60px; border-radius: 50%; margin-right: 22px; + margin-top: 8px; } .p-r-Infos{ flex: 1; @@ -108,6 +110,7 @@ .p-r-name{ display: flex; justify-content: space-between; + align-items: center; } .p-r-name > p{ flex: 1; diff --git a/src/forge/Merge/NewMerge.js b/src/forge/Merge/NewMerge.js index 3cbe4ddcd..1ba7960ab 100644 --- a/src/forge/Merge/NewMerge.js +++ b/src/forge/Merge/NewMerge.js @@ -9,13 +9,14 @@ const Option = Select.Option; class NewMerge extends Component { constructor(props) { super(props); + const { branch } = this.props.match.params; this.state = { data: undefined, branches: undefined, merge_branches: undefined, merge_projects: undefined, merge: "master", - pull: "master", + pull: branch, id: undefined, is_fork: false, projects_names: undefined, @@ -113,16 +114,19 @@ class NewMerge extends Component { } set_default_pull = (branches) => { - if(branches && branches.length>0){ - let default_pull = branches.filter((e) => e.name === "master") - if (default_pull.length > 0){ - this.setState({ - pull:default_pull[0].name - }) - }else{ - this.setState({ - pull:"master" - }) + const { branch } = this.props.match.params; + if(!branch){ + if(branches && branches.length>0){ + let default_pull = branches.filter((e) => e.name === "master") + if (default_pull.length > 0){ + this.setState({ + pull:default_pull[0].name + }) + }else{ + this.setState({ + pull:"master" + }) + } } } } @@ -163,10 +167,12 @@ class NewMerge extends Component { }; selectBrach = (type, value) => { + const { projectsId , owner } = this.props.match.params; this.state[type] = value; this.ischeckmerge(); let { id ,merge , pull } = this.state; if(type==="pull"){ + this.props.history.push(`/projects/${owner}/${projectsId}/pulls/new/${pull}`) this.compareProject(id,value,merge); }else{ this.compareProject(id,pull,value); diff --git a/src/forge/Notice/Index.scss b/src/forge/Notice/Index.scss index c7bb50ebe..a50c34131 100644 --- a/src/forge/Notice/Index.scss +++ b/src/forge/Notice/Index.scss @@ -1,31 +1,20 @@ .noticeMenu{ - padding:0px 20px; + padding:0px 30px; display: flex; border-bottom: 1px solid #eee; - height: 54px; - line-height: 54px; li{ font-size: 16px; padding:0px; - margin:0px 30px 0px 20px!important; - height: 54px; - line-height: 54px; + margin-right:30px; + height: 70px; + line-height: 70px; position: relative; transform: none; a{ display: flex; - &>span{ - position: relative; - } } - &.active a span:first-child::after{ - position: absolute; - bottom: 0px; - height: 2px; - left: 0px; - content: ""; - background-color: #1890ff; - width: 100%; + &.active a span{ + color: #1890ff; } .unNum{ color: #d38900; @@ -44,8 +33,7 @@ } } .notifyList{ - padding:0px 20px; - min-height: 400px; + padding:0px 30px; li{ display: flex; border-bottom: 1px solid #eee; diff --git a/src/forge/Notice/Notify.jsx b/src/forge/Notice/Notify.jsx index d927f56f8..c68271b38 100644 --- a/src/forge/Notice/Notify.jsx +++ b/src/forge/Notice/Notify.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import Nodata from '../Nodata'; -import {Pagination } from 'antd'; +import { Pagination , Spin } from 'antd'; import { Link } from 'react-router-dom'; import { getImageUrl } from 'educoder'; import Axios from "axios"; @@ -11,9 +11,11 @@ function Notify(props){ const [ list , setList ] = useState(undefined); const [ page , setPage ] = useState(1); const [ total , setTotal ] = useState(0); + const [ isSpin , setIsSpin ] = useState(true); useEffect(()=>{ if(username){ + setIsSpin(true); getList(); } },[username,page]) @@ -28,6 +30,7 @@ function Notify(props){ if(result){ setList(result.data.applied_messages); setTotal(result.data.total_count); + setIsSpin(false); } }).catch(error=>{}) } @@ -45,7 +48,6 @@ function Notify(props){ default: return

拒绝转移【{project && project.name}】仓库

} - }else{ return "" } @@ -53,9 +55,10 @@ function Notify(props){ return(
- { - list && list.length > 0 ? -
+ +
+ { + list && list.length > 0 ?
    { list.map((i,k)=>{ @@ -74,10 +77,11 @@ function Notify(props){ }) }
+ : + "" + }
- : - "" - } +
{list && list.length === 0 && } { total > limit && diff --git a/src/forge/Notice/UndoEvent.jsx b/src/forge/Notice/UndoEvent.jsx index afb5c7060..68557a9e9 100644 --- a/src/forge/Notice/UndoEvent.jsx +++ b/src/forge/Notice/UndoEvent.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import Nodata from '../Nodata'; import { FlexAJ } from '../Component/layout'; -import { Pagination , Popconfirm } from 'antd'; +import { Pagination , Popconfirm , Spin } from 'antd'; import { Link } from 'react-router-dom'; import { getImageUrl } from 'educoder'; import Axios from 'axios'; @@ -12,9 +12,11 @@ function UndoEvent(props){ const [ list , setList ] = useState(undefined); const [ page , setPage ] = useState(1); const [ total , setTotal ] = useState(0); + const [ isSpin , setIsSpin ] = useState(true); useEffect(()=>{ if(username){ + setIsSpin(true); getList(); } },[username,page]) @@ -29,6 +31,7 @@ function UndoEvent(props){ if(result){ setList(result.data.applied_transfer_projects); setTotal(result.data.total_count); + setIsSpin(false); } }).catch(error=>{}) } @@ -57,9 +60,10 @@ function UndoEvent(props){ return(
- { - list && list.length > 0 ? -
+ +
+ { + list && list.length > 0 ?
    { list.map((i,k)=>{ @@ -73,7 +77,7 @@ function UndoEvent(props){

    请求将仓库【{i.project && i.project.name}】 - 转移给【{i.owner && i.owner.name}】,是否接受?

    + 转移给【{i.owner && i.owner.name}】,是否接受?

    { i.status === "common" && @@ -101,10 +105,11 @@ function UndoEvent(props){ }) }
+ : + "" + }
- : - "" - } +
{list && list.length === 0 && } { total > limit && diff --git a/src/forge/Team/New.jsx b/src/forge/Team/New.jsx index f4a9880b9..1f06dc22f 100644 --- a/src/forge/Team/New.jsx +++ b/src/forge/Team/New.jsx @@ -144,7 +144,7 @@ export default Form.create()(

- +

) diff --git a/src/forge/UsersList/fork_users.js b/src/forge/UsersList/fork_users.js index aaaa9b627..e38f46190 100644 --- a/src/forge/UsersList/fork_users.js +++ b/src/forge/UsersList/fork_users.js @@ -97,7 +97,9 @@ class ForkUsers extends Component {
{item.name} diff --git a/src/forge/UsersList/user_list.js b/src/forge/UsersList/user_list.js index 592e98068..c3324dc5f 100644 --- a/src/forge/UsersList/user_list.js +++ b/src/forge/UsersList/user_list.js @@ -1,5 +1,6 @@ import React, { Component } from "react"; import { getImageUrl } from "educoder"; +import { Link } from 'react-router-dom'; import FocusButton from "./focus_button"; import { Button } from "antd"; import "./list.css"; @@ -25,12 +26,14 @@ class UserList extends Component {
diff --git a/src/forge/Utils/locData.js b/src/forge/Utils/locData.js index f1d38dbf3..5ebba6ccb 100644 --- a/src/forge/Utils/locData.js +++ b/src/forge/Utils/locData.js @@ -5338,10 +5338,10 @@ export const locData = Array.from(province.keys()).map(p=>({ label:p, children:Array.from(province.get(p).keys()).map((c,key)=>({ value:c, - label:c, - children:Array.from(area.keys())[key].map(a=>({ - value:a, - label:a, - })) + label:c, + // children:Array.from(area.keys())[key].map(a=>({ + // value:a, + // label:a, + // })) })) })); \ No newline at end of file diff --git a/src/forge/users/Echart/Calendar.jsx b/src/forge/users/Echart/Calendar.jsx new file mode 100644 index 000000000..743a0a551 --- /dev/null +++ b/src/forge/users/Echart/Calendar.jsx @@ -0,0 +1,145 @@ +import React ,{ useEffect, useState } from 'react'; +import * as echarts from 'echarts'; +import moment from 'moment'; +import Axios from 'axios'; + +function Calendar({ userLogin , time , chooseTime }) { + const [ endT, setEndT ] = useState(""); + const [ baginT, setBaginT ] = useState(""); + + useEffect(()=>{ + if(time){ + let e,b = ""; + if(parseInt(time,0) === parseInt(moment().get('year'),0)){ + let y = moment().get('year'); + let m = moment().get('month'); + let d = moment().get('date'); + e = `${y}-${m+1}-${d}`; + b = `${y-1}-${m+1}-${d}`; + }else{ + e = `${time}-12-31`; + b = `${time}-01-01`; + } + setEndT(e); + setBaginT(b); + } + },[time]) + + useEffect(()=>{ + if(baginT && endT){ + getData(); + } + },[baginT,endT]) + + function getData() { + const url = `/users/${userLogin}/headmaps.json`; + Axios.get(url).then(result=>{ + if(result && result.data){ + let m = result.data.headmaps; + m.sort(compare('contributions')); + let max = m[m.length -1].contributions; + Init(m,max); + } + }).catch(error=>{}) + } + + function compare(property){ + return function(a,b){ + var value1 = a[property]; + var value2 = b[property]; + return value1 - value2; + } + } + + function getVirtulData(data) { + var date = +echarts.number.parseDate(baginT); + var end = +echarts.number.parseDate(endT); + var dayTime = 3600 * 24 * 1000; + var array = []; + for (var time = date; time <= end; time += dayTime) { + let stamp = timestampToTime(time); + let stampFilter = data.filter(i=>i.date === stamp); + if(stampFilter && stampFilter.length > 0){ + array.push([stampFilter[0].date,stampFilter[0].contributions]); + }else{ + array.push([stamp,0]); + } + } + return array; + } + + function timestampToTime(timestamp) { + var date = new Date(timestamp); + var Y = date.getFullYear() + '-'; + var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-'; + var D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + return Y+M+D; + } + + function Init(data,max) { + var huan_val = document.getElementById("Calendar"); + var myEcharts = echarts.init(huan_val); + let option = { + title: { + show:false + }, + tooltip: { + formatter:function(params){ + return params.data[0] + ': ' + params.data[1] + '个贡献'; + } + }, + visualMap: { + min: 0, + max: max, + type: 'piecewise', + orient: 'horizontal', + left: 'center', + bottom: 40, + inRange:{ + color:['#fafafa', '#216e39'] + } + }, + calendar: { + top: 50, + left: 40, + right: 30, + cellSize: ['auto', 13], + range: [baginT, endT], + splitLine:{ + show:false, + lineStyle:{ + color:"#fff", + width:1, + type:"solid" + } + }, + itemStyle: { + borderWidth: 0.5 + }, + yearLabel: {show: false}, + monthLabel:{ + nameMap:"cn" + }, + dayLabel:{ + nameMap:"cn", + firstDay:1 + } + }, + series: { + type: 'heatmap', + coordinateSystem: 'calendar', + data: getVirtulData(data) + } + }; + myEcharts.setOption(option); + myEcharts.on('click', function (params) { + chooseTime(params.data); + }); + } + + + return( +
+ ) +} +export default Calendar; \ No newline at end of file diff --git a/src/forge/users/Echart/Cloud.jsx b/src/forge/users/Echart/Cloud.jsx index cbe182a26..778b30e74 100644 --- a/src/forge/users/Echart/Cloud.jsx +++ b/src/forge/users/Echart/Cloud.jsx @@ -4,41 +4,48 @@ 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<60;i++){ - cyList.push([textList[parseInt(Math.random()*textList.length)],Math.round(Math.random()*10)+1]) + for(let i=0;i=12 && fontSize<=20) { - return 'rgb(29,227,250,0.3)'; - } else if(fontSize>20 && fontSize<=30){ - return 'rgb(29,227,250,0.6)'; - }else if(fontSize>30 && fontSize<=40){ - return 'rgb(29,227,250)'; + switch(fontSize){ + case 21: + return "#f8e367" + case 20: + return "#99dfff" + case 19: + return "#ff9e48" + case 18: + return "#5ea6ff" + case 17: + return "#58c0f0" + default: + return "#bcbcbc" } }, }) } return( -
+
) } export default Cloud; \ No newline at end of file diff --git a/src/forge/users/Echart/Line.jsx b/src/forge/users/Echart/Line.jsx index 2092b87c1..e8c9ac644 100644 --- a/src/forge/users/Echart/Line.jsx +++ b/src/forge/users/Echart/Line.jsx @@ -2,88 +2,75 @@ import React ,{ useEffect } from 'react'; import * as echarts from 'echarts'; function Line({data}) { - useEffect(()=>{ - Init(); - },[]) useEffect(()=>{ if(data){ - Init(); + Init(data); } },[data]) - function Init() { + function Init(d) { var huan_val = document.getElementById("Line"); var myEcharts = echarts.init(huan_val); let option = { - color: ["#f8e367", "#99dfff", "#58c0f0", "#5ea6ff", "#ff9e48", "#bcbcbc"], + color: ["#f8e367", "#58c0f0", "#ff9e48"], title: { - text: '近期活动统计', - left: '3%' + show:false }, tooltip: { trigger: 'axis' }, legend: { - data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎'], - right: '3%' + data: ['提交数', '易修数', '合并请求数'], + right: 'center', + bottom: '4%', }, grid: { - left: '3%', - right: '4%', - bottom: '3%', + left: '4%', + right: '5%', + bottom: '16%', containLabel: true }, toolbox: { - feature: { - // saveAsImage: {} - } + feature: { + // saveAsImage: {} + } }, xAxis: { - type: 'category', - boundaryGap: false, - data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] + type: 'category', + boundaryGap: false, + data: d.dates, + axisTick:{ + show:false + } }, yAxis: { - type: 'value', - axisLine:{ - show:false - }, - axisTick:{ - show:false - } + type: 'value', + axisLine:{ + show:false + }, + axisTick:{ + show:false + }, + minInterval:1, + splitNumber: 5, }, series: [ - { - name: '邮件营销', - type: 'line', - stack: '总量', - data: [120, 132, 101, 134, 90, 230, 210] - }, - { - name: '联盟广告', - type: 'line', - stack: '总量', - data: [220, 182, 191, 234, 290, 330, 310] - }, - { - name: '视频广告', - type: 'line', - stack: '总量', - data: [150, 232, 201, 154, 190, 330, 410] - }, - { - name: '直接访问', - type: 'line', - stack: '总量', - data: [320, 332, 301, 334, 390, 330, 320] - }, - { - name: '搜索引擎', - type: 'line', - stack: '总量', - data: [820, 932, 901, 934, 1290, 1330, 1320] - } + { + name: '提交数', + type: 'line', + data: d.commits_count + }, + { + name: '易修数', + type: 'line', + data: d.issues_count + }, + { + name: '合并请求数', + type: 'line', + data: d.pull_requests_count + } ] }; myEcharts.setOption(option); diff --git a/src/forge/users/Echart/Pie.jsx b/src/forge/users/Echart/Pie.jsx index 460d7f001..d5489b981 100644 --- a/src/forge/users/Echart/Pie.jsx +++ b/src/forge/users/Echart/Pie.jsx @@ -3,32 +3,27 @@ import echarts from 'echarts/lib/echarts'; import 'echarts/lib/chart/pie'; function Pie({data}) { - useEffect(()=>{ - Init(); - },[]) useEffect(()=>{ if(data){ - Init(); + Init(data); } },[data]) - function Init() { + function Init(d) { var huan_val = document.getElementById("Pie"); var chart = echarts.init(huan_val); let option = { - color: ["#f8e367", "#99dfff", "#58c0f0", "#5ea6ff", "#ff9e48", "#bcbcbc"], + color: ["#f8e367", "#5ea6ff", "#ff9e48", "#99dfff"], title: { - text: '角色定位', - top:"5%", - left:"3%" + show:false }, tooltip: { trigger: 'item' }, legend: { top: '5%', - right: '3%' + right: 'center' }, series: [ { @@ -56,11 +51,10 @@ function Pie({data}) { show: false }, data: [ - {value: 1048, name: '搜索引擎'}, - {value: 735, name: '直接访问'}, - {value: 580, name: '邮件营销'}, - {value: 484, name: '联盟广告'}, - {value: 300, name: '视频广告'} + {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: '报告者'} ] } ] diff --git a/src/forge/users/Echart/Radar.jsx b/src/forge/users/Echart/Radar.jsx index f427442ed..5a75f2c8a 100644 --- a/src/forge/users/Echart/Radar.jsx +++ b/src/forge/users/Echart/Radar.jsx @@ -3,58 +3,55 @@ import echarts from 'echarts/lib/echarts' import 'echarts/lib/chart/radar'; function Radar({data}) { - useEffect(()=>{ - Init(); - },[]) useEffect(()=>{ if(data){ - Init(); + Init(data); } },[data]) - function Init() { + 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: { - text: '开发能力', - top:"0", - left:"3%" + show:false }, legend: { - data: ['预算分配', '实际开销'], - top:"0", + data: ['个人能力(personal)', '社区平均(average)'], + top:"3%", right:"center" }, + tooltip: { + trigger: 'item' + }, radar: { // shape: 'circle', indicator: [ - { name: '销售', max: 6500}, - { name: '管理', max: 16000}, - { name: '信息技术', max: 30000}, - { name: '客服', max: 38000}, - { name: '研发', max: 52000}, - { name: '市场', max: 25000} + { name: '影响力', max: 100}, + { name: '贡献度', max: 100}, + { name: '活跃度', max: 100}, + { name: '项目经验', max: 100}, + { name: '语言能力', max: 100}, ], center:["50%","55%"] }, series: [{ - name: '预算 vs 开销', + name: '', type: 'radar', data: [ { - value: [4200, 3000, 20000, 35000, 50000, 18000], - name: '预算分配' + value: d.user && [d.user.influence, d.user.contribution, d.user.activity, d.user.experience, d.user.language], + name: '个人能力(personal)' }, { - value: [5000, 14000, 28000, 26000, 42000, 21000], - name: '实际开销' + value: d.platform && [d.platform.influence, d.platform.contribution, d.platform.activity, d.platform.experience, d.platform.language], + name: '社区平均(average)' } ] }] - }; + }; myEcharts.setOption(option); } 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/GeneralView/Activity.jsx b/src/forge/users/GeneralView/Activity.jsx new file mode 100644 index 000000000..ce09e885f --- /dev/null +++ b/src/forge/users/GeneralView/Activity.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { AlignCenter } from '../../Component/layout'; +import { TagInfo } from '../../Utils/TagColor'; +import { getImageUrl } from 'educoder'; + +function Activity({list}) { + return( +
    + { + list.map((i,k)=>{ + return( +
  • + +
    + + {i.user_name} + {i.action_time} + {i.priority && TagInfo(`${i.priority}`,"")} + {i.issue_status && {i.issue_status} } + +

    {i.action_type}:{i.name}

    +
    +
  • + ) + }) + } +
+ ) +} +export default Activity; \ No newline at end of file diff --git a/src/forge/users/GeneralView/ConcentrateBox.jsx b/src/forge/users/GeneralView/ConcentrateBox.jsx new file mode 100644 index 000000000..3ee941947 --- /dev/null +++ b/src/forge/users/GeneralView/ConcentrateBox.jsx @@ -0,0 +1,172 @@ +import React, { useEffect, useState } from 'react'; +import { Modal , Checkbox , Spin , Input } from 'antd'; +import Axios from 'axios'; +import { Link } from 'react-router-dom'; + +const { Search } = Input; +const limit = 20; +function ConcentrateBox({ visible , onCancel , onSure , username , choosed }) { + const [ page , setPage ]= useState(1); + const [ total , setTotal ]= useState(0); + const [ pageSize , setPageSize ] = useState(false); + const [ search , setSearch ] = useState(undefined); + + const [ list , setList ]= useState([]); + const [ value , setValue ]= useState([]); + const [ isSpin , setIsSpin ]= useState(true); + const [ disable , setDisable ] = useState(false); + + const [ copyList , setCopyList ] = useState([]); + const [ copyAllList , setCopyAllList ] = useState([]); + + useEffect(()=>{ + if(visible){ + setIsSpin(true); + getProjectList(); + }else{ + setSearch(undefined); + setCopyAllList([]); + setCopyList([]); + setList([]); + } + },[visible]) + + useEffect(()=>{ + if(page>1){ + setIsSpin(true); + getProjectList(page,undefined); + } + },[page]) + + + useEffect(()=>{ + if(visible && choosed && choosed.length >0 ){ + setValue(choosed); + } + },[visible,choosed]) + + useEffect(()=>{ + if(value && value.length === 6){ + setDisable(true); + }else{ + setDisable(false); + } + },[value]) + + function getProjectList(p,s) { + const url = `/users/${username}/projects.json`; + Axios.get(url,{ + params:{ + page:p,limit,is_public: "public",search:s,choosed + } + }).then(result=>{ + if(result && result.data){ + let e = !search ? mergeArrayMerge(list,result.data.projects) : result.data.projects; + setCopyAllList(!search ? e : copyAllList); + setTotal(result.data.count); + setList(e); + setIsSpin(false); + // 查看更多需要页数 + let s = parseInt(result.data.count/limit,0); + let y = result.data.count%limit; + setPageSize(y>0?s+1:s); + } + }).catch(error=>{}) + } + + function mergeArrayMerge (array1, array2) { + array1.map((v, index) => { + if (v !== '') { + let idx = array2.indexOf(v); + if (idx > -1) { + array2.splice(idx, 1) + } + } + }); + array1 = array1.concat(array2); + return array1 + } + + function saveList(c) { + // 将选中的复制下来保存到copyList数组里 + if(c && c.length > 0){ + let l = [] + for(var i=0;ij.id === c[i]); + if(filter && filter.length>0){ + l.push(filter[0]); + } + } + setCopyList(l); + } + } + + function onOk() { + onSure && onSure(value); + setValue([]); + } + + function chooseProject(e) { + setValue(e); + } + + // 搜索 + function onSearch(params) { + setCopyAllList(list); + value && value.length > 0 ? saveList(value) : setCopyList([]); + setPage(1); + setSearch(params); + getProjectList(1,params); + } + + + return( + + +
+

最多可选取6个公开仓库

+ setSearch(e.target.value)} + /> +
+
+ + { + copyList && copyList.length >0 && copyList.map((i,k)=>{ + return( + j === i.id).length===0)}>{i.author && i.author.name}/{i.name} + ) + }) + } + { + list && list.length > 0 && list.map((i,k)=>{ + let c = copyList && copyList.length >0 && copyList.filter(j=>j.id === i.id).length !== 0; + return( + !c && j === i.id).length===0)}>{i.author && i.author.name}/{i.name} + ) + }) + } + +
+ { total > limit && page < pageSize &&
setPage(page+1)}>查看更多
} + { (list && list.length === 0) && (copyList && copyList.length === 0) &&
您还没有公开的{search && `“${search}”`}项目,先去新建项目
} +
+
+ ) +} +export default ConcentrateBox; \ No newline at end of file diff --git a/src/forge/users/GeneralView/ConcentrateProject.jsx b/src/forge/users/GeneralView/ConcentrateProject.jsx new file mode 100644 index 000000000..bfcf290d5 --- /dev/null +++ b/src/forge/users/GeneralView/ConcentrateProject.jsx @@ -0,0 +1,83 @@ +import React, { useEffect, useState } from 'react'; +import { FlexAJ , AlignCenter } from '../../Component/layout'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; +import Box from './ConcentrateBox'; + +function ConcentrateProject({userLogin,current}) { + const [ list , setList ] = useState(undefined); + const [ visible , setVisible ] = useState(false); + const [ value , setValue ] = useState([]); + + useEffect(()=>{ + getList(); + },[]) + + function getList() { + const url = `/users/${userLogin}/is_pinned_projects.json`; + axios.get(url).then(result=>{ + if(result && result.data){ + let p = result.data.projects; + setList(p); + if(p && p.length > 0){ + let array = p.map(i=>{ + return i.project_id + }) + setValue(array); + } + } + }).catch(erroer=>{}) + } + + function onSure(is_pinned_project_ids) { + if(is_pinned_project_ids && is_pinned_project_ids.length===0){ + setValue([]); + } + const url = `/users/${userLogin}/is_pinned_projects/pin.json`; + axios.post(url,{ + is_pinned_project_ids + }).then(result=>{ + if(result && result.data){ + setVisible(false); + getList(); + } + }).catch(error=>{}) + } + return( + + setVisible(false)} onSure={onSure} username={userLogin} choosed={value}/> + { + list && list.length>0 && +
+ + 精选项目 + { current && setVisible(true)}>自定义精选项目 } + +
+
    + { + list.map((i,k)=>{ + return( +
  • + {i.name} +

    {i.description}

    + + { i.category && {i.category.name} } + {i.watchers_count} + {i.forked_count} + +
  • + ) + }) + } +
+
+
+ } + { + list && list.length === 0 && current &&
你还没有设置精选项目,setVisible(true)}>点击设置
+ } +
+ ) +} +export default ConcentrateProject; \ No newline at end of file diff --git a/src/forge/users/GeneralView/Index.jsx b/src/forge/users/GeneralView/Index.jsx index 9a76e9aa1..ded435827 100644 --- a/src/forge/users/GeneralView/Index.jsx +++ b/src/forge/users/GeneralView/Index.jsx @@ -1,22 +1,127 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import './Index.scss'; import 'echarts/lib/component/tooltip'; import 'echarts/lib/component/title'; import 'echarts/lib/component/legend' import 'echarts/lib/component/markPoint'; -import Pie from '../Echart/Pie'; -import Line from '../Echart/Line'; -import Radar from '../Echart/Radar'; -import Cloud from '../Echart/Cloud'; +import { Select , Pagination } from 'antd'; +import { FlexAJ } from '../../Component/layout'; +import Line from '../Echart/Line'; +import Calendar from '../Echart/Calendar'; +import ConcentrateProject from './ConcentrateProject'; +import Activity from './Activity'; +import moment from 'moment'; +import Axios from 'axios'; +import Nodata from '../../Nodata'; + +const { Option } = Select; +const aLimit = 5; function Index(props) { + const [ page , setPage ] = useState(1); + const [ total , setTotal ] = useState(0); + const [ projectTrends , setProjectTrends ] = useState(undefined); + + const [ year , setYear ] = useState(moment().get('year')); + const [ yearList , setYearList ] = useState(undefined); + const [ activityDate , setActivityDate ] = useState(undefined); + + const [ statisticData , setStatisticData ] = useState(undefined); + + const username = props.match.params.username; + const current_user = props.current_user; + const user = props.user; + + useEffect(()=>{ + if(user){ + let c = user.created_time && user.created_time.split("-")[0]; + let y = moment().get('year'); + let array = [] + for(var i = y ; i >= parseInt(c,0);i--){ + array.push(i); + } + setYearList(array); + } + },[user]) + + // 在贡献度日历表中选择一个时间 + function chooseTime(data) { + if(data){ + setPage(1); + setActivityDate(data[0]); + } + } + + // 年份下拉框option + function renderYear(list){ + return list.map((i,k)=>{ + return( + + ) + }) + } + + useEffect(()=>{ + getActivity(); + },[activityDate,page]) + + // 获取动态列表 + function getActivity() { + const url = `/users/${username}/project_trends.json`; + Axios.get(url,{ + params:{ + date:activityDate, + limit:aLimit,page + } + }).then(result=>{ + if(result && result.data){ + setProjectTrends(result.data.project_trends); + setTotal(result.data.total_count); + } + }).catch(error=>{}) + } + + // 获取近期活动统计 + useEffect(()=>{ + getStatistics(); + },[]) + + function getStatistics() { + const url = `/users/${username}/statistics/activity.json`; + Axios.get(url).then(result=>{ + if(result && result.data){ + setStatisticData(result.data); + } + }).catch(error=>{}) + } + return(
- - - - +
+ +
+
+

近期活动统计

+
+
+
+ + 贡献度 + + +
+ +
+
+
+ 动态 + { projectTrends && projectTrends.length > 0 && } + { projectTrends && projectTrends.length === 0 && } + { total > aLimit &&
setPage(p)}/>
} +
) } diff --git a/src/forge/users/GeneralView/Index.scss b/src/forge/users/GeneralView/Index.scss index e69de29bb..e79f68580 100644 --- a/src/forge/users/GeneralView/Index.scss +++ b/src/forge/users/GeneralView/Index.scss @@ -0,0 +1,146 @@ +.concentrate{ + padding:20px 0px 0px; + .concentrateUl{ + display: flex; + flex-wrap: wrap; + li{ + width: 48.5%; + margin-right: 3%; + margin-top: 20px; + display: flex; + flex-direction: column; + justify-content: flex-start; + border:1px solid #dedede; + padding:15px 20px; + cursor: default ; + &:nth-child(2n){ + margin-right: 0px; + } + .name{ + font-size: 16px; + color: #4CACFF; + } + .desc{ + color: #999; + } + .tagName{ + display: block; + background-color: #f7f5f5; + border-radius: 3px; + color: #666; + padding:0px 10px; + height: 22px; + line-height: 22px; + font-size: 13px; + } + .pariseCount,.forkCount{ + i{ + font-size: 14px!important; + margin-right: 4px; + } + color: #999; + margin-left: 20px; + } + } + } +} +.ConcentrateTip{ + margin:20px 20px 0px; + padding:5px 20px; + border:1px solid rgb(248, 56, 56); + border-radius: 4px; + background-color: rgba(248, 56, 56,0.1); + color: rgb(248, 56, 56); + display: flex; + align-items: center; + a{ + color: #4cacff; + } + i{ + font-size: 15px!important; + margin-right: 5px; + } +} +.ConcentrateBox{ + .ant-modal-body{ + min-height: 258px; + padding:0px; + padding-bottom: 15px; + } + .listbox{ + max-height: 210px; + overflow-y: auto; + padding-left: 30px; + } + .operateDiv{ + padding:20px 30px + } + .morelist{ + text-align: center; + padding-top:15px; + cursor: pointer; + } + .ant-checkbox-group{ + display: flex; + flex-wrap: wrap; + .ant-checkbox-wrapper{ + width: 50%; + margin-left: 0px!important; + display: flex; + .ant-checkbox{ + padding-top: 3px; + } + span:last-child{ + flex:1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + } +} +.recentStatic{ + padding: 30px 0px; +} +.calendarStatic,.activeStatic{ + .infosActivity{ + padding-bottom: 30px; + &>li{ + display: flex; + border-bottom:1px solid #eee; + align-items: center; + padding:20px 0px; + .aImg{ + width: 48px; + height: 48px; + margin-right: 20px; + border-radius: 50%; + } + .aInfos{ + flex: 1; + span{ + margin-left: 20px; + margin-right: 0px; + } + .name{ + font-size: 16px; + color:#333; + } + .time,.status{ + color: #999; + } + .aDesc{ + color: #666; + margin-top: 5px; + line-height: 20px; + } + } + &:last-child{ + border-bottom:none; + } + } + } +} +.activeStatic{ + padding-top: 20px; +} \ No newline at end of file diff --git a/src/forge/users/Index.scss b/src/forge/users/Index.scss index d8d4ecd4f..71d06226e 100644 --- a/src/forge/users/Index.scss +++ b/src/forge/users/Index.scss @@ -1,6 +1,6 @@ $flex:flex; .headerbox{ - padding:20px 40px; + padding:20px; border-bottom: 1px solid #E0E0E0; display: $flex; align-items: center; @@ -24,8 +24,17 @@ $flex:flex; margin-right: -1px; } } + +.echartBox{ + border:1px solid #DEDEDE; + &>p{ + color: #999; + padding:15px 20px; + text-align: center; + } +} .contentBox{ - padding:20px 40px 0px 40px; + padding:20px 20px 0px 20px; & > div{ margin-bottom: 20px; display: $flex; @@ -60,7 +69,7 @@ $flex:flex; } } .infosType{ - padding:20px 30px 0px 30px; + padding:20px 30px 0px 20px; display: flex; justify-content: space-between; .infoStatus{ @@ -88,7 +97,66 @@ $flex:flex; } } } - +.userDescription{ + color: #666666; + line-height: 18px; + text-align: left; + margin:10px 0px; + word-break: break-all; + text-align: justify; +} +.focusBox,.infoBox{ + width: 100%!important; + display: inline-block; + margin-top: 30px; + padding-top: 30px; + border-top: 1px solid #f1f1f1; +} +.infoBox{ + padding-bottom: 10px; + text-align: left; + line-height: 28px; + color: #666; + margin-top: 20px; + &>div{ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + i{ + color: #DEDEDE; + font-size: 15px!important; + } + span{ + margin-left:10px ; + } +} +.headimg{ + position: relative; + display: block; + img{ + width: 110px; + height: 110px; + border-radius: 50%; + } + span{ + position: absolute; + bottom: -6px; + right: 0px; + left: 65px; + i{ + font-size: 25px!important; + border-radius: 50%; + background-color: #fff; + &.icon-nan{ + color: #1890FF; + } + &.icon-nv{ + color: pink; + } + } + } +} ul.ant-menu.menuStyle{ padding:0px 30px; font-size: 16px; @@ -97,6 +165,7 @@ ul.ant-menu.menuStyle{ line-height: 70px; padding:0px; margin-right: 30px!important; + border-bottom:transparent!important; } } .disposeInfo{ @@ -135,4 +204,36 @@ ul.ant-menu.menuStyle{ padding:28px 0px; border-bottom: 1px solid #eee; } +} + +.infosRightMenu{ + .ant-menu-item{ + padding:0px; + margin:0px 20px!important; + font-size: 17px; + height: 32px; + line-height: 0px; + border-bottom: 2px solid transparent!important; + position: relative; + a{ + & >i{ + font-size: 15px!important; + margin-right: 8px; + } + } + .menuNum{ + font-size: 12px; + margin-left: 3px; + color: #FF6E21; + } + &.ant-menu-item-selected::before{ + position: absolute; + width: 100%; + bottom: -1px; + height: 2px; + left: 0px; + background-color: #1890ff; + content:""; + } + } } \ No newline at end of file diff --git a/src/forge/users/Infos.js b/src/forge/users/Infos.js index 7e4e660a4..a415b3d58 100644 --- a/src/forge/users/Infos.js +++ b/src/forge/users/Infos.js @@ -1,6 +1,6 @@ import React, { Component } from "react"; import { Link } from "react-router-dom"; -import { Avatar, Tag, Button, Spin } from "antd"; +import { Button, Spin , Menu } from "antd"; import FocusButton from "../UsersList/focus_button"; import axios from "axios"; @@ -14,7 +14,10 @@ import './Index.scss'; import Loadable from "react-loadable"; import Loading from "../../Loading"; - +const UpdateInfo = Loadable({ + loader: () => import("./Material/Index"), + loading: Loading, +}); const InfosDevOps = Loadable({ loader: () => import("./devOpsCI"), loading: Loading, @@ -24,6 +27,10 @@ const InfosDevOpsCD = Loadable({ loading: Loading, }); +const Statistics = Loadable({ + loader: () => import("./Statistics/Index"), + loading: Loading, +}); const GeneralView = Loadable({ loader: () => import("./GeneralView/Index"), loading: Loading, @@ -59,19 +66,51 @@ class Infos extends Component { project_type: undefined, route_type: undefined, undo_events:0, - undo_messages:0 + undo_messages:0, + menuKey:"0" }; } + renderPath=(pathname)=>{ + const { username } = this.props.match.params; + if(pathname === `/users/${username}`){ + this.setState({menuKey:"0",route_type:undefined}); + }else if(pathname === `/users/${username}/statistics`){ + this.setState({menuKey:"1",route_type:undefined}); + }else if(pathname.indexOf(`/users/${username}/projects`)>-1){ + this.setState({menuKey:"2",route_type:undefined}); + }else if(pathname.indexOf(`/users/${username}/notice`)>-1){ + this.setState({menuKey:"3",route_type:undefined}); + }else if(pathname.indexOf(`/users/${username}/devops`)>-1){ + this.setState({menuKey:"4",route_type:undefined}); + }else if(pathname === `/users/${username}/organizes`){ + this.setState({menuKey:"5",route_type:undefined}); + }else if(pathname === `/users/${username}/watchers`){ + this.setState({menuKey:undefined,route_type:"watchers"}); + }else if(pathname === `/users/${username}/fan_users`){ + this.setState({menuKey:undefined,route_type:"fan_users"}); + }else{ + this.setState({menuKey:undefined,route_type:undefined}); + } + } + componentDidMount = () => { this.fetchUser(); + const { pathname } = this.props.location; + this.renderPath(pathname); }; + componentDidUpdate=(prevProps)=>{ const { username } = this.props.match.params; const prevUser = prevProps.match.params.username; if(prevUser && username && prevUser !== username){ this.fetchUser(); } + const { pathname } = this.props.location; + const prevPath = prevProps.location.pathname; + if(prevPath && pathname && prevPath !== pathname){ + this.renderPath(pathname); + } this.props.history.listen(()=>{ if (document.body.scrollTop || document.documentElement.scrollTop > 0) { window.scrollTo(0, 0) @@ -89,39 +128,23 @@ class Infos extends Component { const { notice } = this.state; let url = `/users/${username || (current_user && current_user.login)}.json`; - axios - .get(url) - .then((result) => { - let e = result.data && result.data.undo_events; - let p = result.data && result.data.undo_messages; - let n = notice || pathname === `/users/${username}/notice` ; - this.setState({ - user: result.data, - isSpin: false, - undo_events:n ? (e-p) : e, - undo_messages:0, - notice:n - }); - }) - .catch((error) => { - this.setState({ - isSpin: false, - }); + axios.get(url).then((result) => { + let e = result.data && result.data.undo_events; + let p = result.data && result.data.undo_messages; + let n = notice || pathname === `/users/${username}/notice` ; + this.setState({ + user: result.data, + isSpin: false, + undo_events:n ? (e-p) : e, + undo_messages:0, + notice:n }); - }; - - change_project_type = (type) => { - const {user} = this.state - this.setState({ - project_type: type , - route_type: undefined }) - let url = `/users/${user && user.login}` - if (type){ - url = `/users/${user && user.login}/projects/${type}` - } - this.props.history.push(url) - + .catch((error) => { + this.setState({ + isSpin: false, + }); + }); }; change_devops_type=(type)=>{ @@ -160,43 +183,50 @@ class Infos extends Component { this.props.history.push(`/users/${user && user.login}/organizes`) } + resetUser=()=>{ + const { resetUserInfo } = this.props; + + this.fetchUser(); + resetUserInfo && resetUserInfo(); + } render() { - const { current_user, mygetHelmetapi } = this.props; + const { current_user, mygetHelmetapi , resetUserInfo } = this.props; const { username } = this.props.match.params; - - const { user, isSpin, project_type, route_type , undo_events , undo_messages } = this.state; + const { user, isSpin, project_type, route_type , undo_events , undo_messages , menuKey } = this.state; return (
-
- - {user && user.user_identity && ( -
- {user && user.user_identity} -
- )} -
+
+ + + + { + user && user.gender===1? + + : + + } + + + +
{user && user.username}
- +
+ {user && user.description} +
{user && current_user && user.login === current_user.login && (
)}
-
+
{user && user.watched_count}
-
-
- {current_user && user && user.login === current_user.login ? ( -
-
-
  • -

    this.undo_link()}> - - - 待办事项 - - - {undo_events} - -

    -
  • -
    -
    - ):""} - -
    -
      -
    • this.change_project_type(undefined)}> - - 项目类型 - -
    • -
    • this.change_project_type("common")}> -

      - 开源托管项目 - - {user && user.common_projects_count} - -

      -
    • -
    • this.change_project_type("mirror")}> -

      - 开源镜像项目 - - {user && user.mirror_projects_count} - -

      -
    • -
    • this.change_project_type("sync_mirror")}> -

      - 镜像托管项目 - - {user && user.sync_mirror_projects_count} - -

      -
    • -
    -
    - { - current_user && current_user.login && current_user.login === username && -
    -
      -
    • - - DevOps工作流 - -
    • -
    • this.change_devops_type("CIService")}> -

      - CI服务 -

      -
    • - {/*
    • this.change_devops_type("CDService")}> -

      - CD服务 - - {user && user.common_projects_count} - -

      -
    • */} -
    -
    - } -
    -
    -
  • -

    this.organize_link()} > - - - 组织 - - - {user && user.user_org_count} - -

    -
  • + { + user && (user.province || user.custom_department || user.email) ? +
    + { user.province &&
    {user.province}{user.city}
    } + { user.custom_department &&
    {user.custom_department}
    } + { user.email &&
    {user.email}
    } +
    + :"" + }
    + { !route_type && menuKey && + + 概览 + 数据统计 + 项目 + { + current_user && user && user.login === current_user.login ? + 待办事项({undo_events}) + :"" + } + { + current_user && current_user.login && current_user.login === username ? + DevOps工作流 + :"" + } + 组织({user && user.user_org_count}) + + } {user && ( -
    - + + { + return ; + }} + > { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - { - return ; - }} - > - -
    + path="/users/:username/notice" + render={() => { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + + { + return ; + }} + > + { + return ; + }} + > + { + return ; + }} + > + )}
    -
    +
    ); diff --git a/src/forge/users/InfosUser.js b/src/forge/users/InfosUser.js index 3a185a786..e79e1021e 100644 --- a/src/forge/users/InfosUser.js +++ b/src/forge/users/InfosUser.js @@ -100,9 +100,6 @@ class InfosUser extends Component { const {is_public} = this.state const new_is_public = is_public === check_is_public ? undefined : check_is_public - // this.setState({ - // is_public: new_is_public - // }) this.state.is_public = new_is_public this.get_projects(new_is_public); } @@ -176,7 +173,7 @@ class InfosUser extends Component { return ( -
    +
    { + const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form; + const { username } = props && props.match && props.match.params; + const { user , resetUser , current_user } = props; + + useEffect(()=>{ + if(current_user && current_user.login){ + setFieldsValue({ + ...current_user, + location:current_user.province && [current_user.province,current_user.city] + }) + } + },[current_user]) + + function submit() { + validateFields((error,values)=>{ + if(!error){ + submitFunc(values); + } + }) + } + + function submitFunc(values) { + const url = `/users/${username}.json`; + const params={ + user: { + nickname: values.real_name, + user_extension_attributes: { + province: values.location && values.location[0], + city: values.location && values.location[1], + ...values + } + } + } + Axios.put(url,params).then(result=>{ + if(result && result.data){ + props.showNotification("资料修改成功!") + resetUser && resetUser(result.data); + } + }).catch(error=>{}) + } + + return( +
    + + {getFieldDecorator("email",{ + rules:[{required:true,message:"请输入邮箱账号"}] + })( + + )} + + + {getFieldDecorator("show_email",{ + rules:[], + valuePropName:"checked" + })( + 在个人主页展示 + )} + + + {getFieldDecorator("real_name",{ + rules:[{required:true,message:"请输入姓名"}] + })( + + )} + +
    + + {getFieldDecorator("gender",{ + rules:[{required:true,message:"请选择性别"}] + })( + + + + + )} + +
    + + {getFieldDecorator("custom_department",{ + rules:[{required:true,message:"请输入单位名称"}] + })( + + )} + + + {getFieldDecorator("show_department",{ + rules:[], + valuePropName:"checked" + })( + 在个人主页展示 + )} + + + {getFieldDecorator("location",{ + rules:[] + })( + + )} + + + {getFieldDecorator("show_location",{ + rules:[], + valuePropName:"checked" + })( + 在个人主页展示 + )} + + + {getFieldDecorator("description",{ + rules:[] + })( +