解决冲突并提交
This commit is contained in:
commit
1d274985ca
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -3912,7 +3912,6 @@ html>body #ajax-indicator {
|
|||
text-align: center;
|
||||
height: 70px;
|
||||
box-sizing: border-box;
|
||||
min-width: 780px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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
|
||||
},
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -4,7 +4,7 @@ import axios from 'axios';
|
|||
import { getImageUrl } from 'educoder';
|
||||
|
||||
const { Option } = AutoComplete;
|
||||
function AddMember({getID,login}){
|
||||
function AddMember({getID,login,showNotification}){
|
||||
const [ id , setID ] = useState(undefined);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
||||
|
@ -45,7 +45,7 @@ function AddMember({getID,login}){
|
|||
src={getImageUrl(`/${item && item.image_url}`)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
||||
<span className="ml10" style={{ verticalAlign: "middle" }}>
|
||||
{item.username}
|
||||
<span className="color-grey ml10">({item.login})</span>
|
||||
</span>
|
||||
|
@ -66,7 +66,12 @@ function AddMember({getID,login}){
|
|||
};
|
||||
|
||||
function addCollaborator(){
|
||||
getID && getID(id);
|
||||
if(source && source.length>0){
|
||||
getID && getID(id);
|
||||
setSearchKey(undefined);
|
||||
}else{
|
||||
showNotification("请选择存在的用户!");
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import React, { useState , forwardRef, useEffect } from 'react';
|
||||
import { Form , Modal , Input , Radio } from 'antd';
|
||||
import Axios from 'axios';
|
||||
|
||||
export default Form.create()(
|
||||
forwardRef((props)=>{
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!visible){
|
||||
setFieldsValue({
|
||||
code:undefined,
|
||||
role:"developer"
|
||||
})
|
||||
}
|
||||
},[visible])
|
||||
|
||||
function onOk() {
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
const url = `/applied_projects.json`;
|
||||
Axios.post(url,{
|
||||
applied_project:{
|
||||
...values
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setVisible(false);
|
||||
props.showNotification("申请加入项目成功,等待审核!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
function checkValue(rule, value, callback){
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if(value.length < 6 || value.length > 6){
|
||||
callback("请输入6位数的邀请码");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return(
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
title="加入项目"
|
||||
width="480px"
|
||||
visible={visible}
|
||||
centered={true}
|
||||
onOk={onOk}
|
||||
onCancel={()=>setVisible(false)}
|
||||
>
|
||||
<Form layout={'inline'} className="inviteForm">
|
||||
<Form.Item label="项目邀请码">
|
||||
{getFieldDecorator("code",{
|
||||
rules:[
|
||||
{required:true,message:"请输入6位项目邀请码"},
|
||||
{validator:checkValue}
|
||||
]
|
||||
})(
|
||||
<Input placeholder="请输入6位项目邀请码" autoComplete={"off"} maxLength="6" style={{width:"300px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="选择角色">
|
||||
{getFieldDecorator("role",{
|
||||
rules:[{required:true,message:"请选择角色"}]
|
||||
})(
|
||||
<Radio.Group defaultValue={"developer"}>
|
||||
<Radio value="manager">管理员</Radio>
|
||||
<Radio value="developer">开发者</Radio>
|
||||
<Radio value="reporter">报告者</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<a onClick={()=>setVisible(true)}>加入项目</a>
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
)
|
|
@ -2,14 +2,14 @@ import React, { Component } from 'react';
|
|||
import AccountProfile from "../../modules/user/AccountProfile";
|
||||
import { getImageUrl } from 'educoder'
|
||||
import axios from 'axios';
|
||||
import { Modal, Input, message, notification , Dropdown , Menu } from 'antd';
|
||||
import { Input , notification , Dropdown , Menu } from 'antd';
|
||||
|
||||
import LoginDialog from '../../modules/login/LoginDialog';
|
||||
import GotoQQgroup from '../../modal/GotoQQgroup';
|
||||
import HeadSearch from '../Component/HeadSearch';
|
||||
|
||||
import AddProjectModal from './AddProjectModal';
|
||||
import '../../modules/tpm/TPMIndex.css';
|
||||
import logo from '../../modules/tpm/images/logo.png';
|
||||
|
||||
import './header.scss';
|
||||
const $ = window.$
|
||||
|
@ -34,11 +34,9 @@ class NewHeader extends Component {
|
|||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
RadioGroupvalue: undefined,
|
||||
submitapplications: false,
|
||||
isRender: false,
|
||||
showSearchOpentype: false,
|
||||
showTrial: false,
|
||||
setevaluatinghides: false,
|
||||
occupation: 0,
|
||||
|
@ -46,7 +44,6 @@ class NewHeader extends Component {
|
|||
headtypesonClickbool: false,
|
||||
headtypess: "/",
|
||||
settings: null,
|
||||
goshowqqgtounp: false,
|
||||
visiblemyss: false,
|
||||
openSearch:false,
|
||||
}
|
||||
|
@ -131,43 +128,7 @@ class NewHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
submitsubmitapplications = () => {
|
||||
let {
|
||||
submitapplicationssum,
|
||||
submitapplicationsvaluedata
|
||||
} = this.state;
|
||||
this.setState({
|
||||
submitapplications: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
if (submitapplicationssum === 0) {
|
||||
if (submitapplicationsvaluedata !== undefined) {
|
||||
window.location.href = "/courses/" + submitapplicationsvaluedata;
|
||||
}
|
||||
} else if (submitapplicationssum === 1) {
|
||||
if (submitapplicationsvaluedata !== undefined) {
|
||||
window.location.href = "/projects/" + submitapplicationsvaluedata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hidesubmitapplications = () => {
|
||||
this.setState({
|
||||
Addcoursestypes: false,
|
||||
tojoinitemtype: false,
|
||||
tojoinclasstitle: undefined,
|
||||
rolearr: ["", ""],
|
||||
Checkboxteacherchecked: false,
|
||||
Checkboxstudentchecked: false,
|
||||
Checkboxteachingchecked: false,
|
||||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
submitapplications: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
}
|
||||
educoderlogin = () => {
|
||||
//登录账号
|
||||
this.setState({
|
||||
|
@ -206,23 +167,6 @@ class NewHeader extends Component {
|
|||
};
|
||||
|
||||
|
||||
hidetojoinclass = () => {
|
||||
this.setState({
|
||||
tojoinclasstype: false,
|
||||
tojoinitemtype: false,
|
||||
tojoinclasstitle: undefined,
|
||||
rolearr: ["", ""],
|
||||
Checkboxteacherchecked: false,
|
||||
Checkboxstudentchecked: false,
|
||||
Checkboxteachingchecked: false,
|
||||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭
|
||||
cancelModulationModels = () => {
|
||||
this.setState({ isRenders: false })
|
||||
|
@ -314,14 +258,6 @@ class NewHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理弹框
|
||||
setgoshowqqgtounp = (bool) => {
|
||||
this.setState({
|
||||
goshowqqgtounp: bool
|
||||
})
|
||||
}
|
||||
|
||||
addMenu=(list)=>{
|
||||
return(
|
||||
list && list.length >0 &&
|
||||
|
@ -334,10 +270,29 @@ class NewHeader extends Component {
|
|||
)
|
||||
})
|
||||
}
|
||||
<Menu.Item><AddProjectModal showNotification={this.props.showNotification}/></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
renderMenu=(personal)=>{
|
||||
const { current_user } = this.props;
|
||||
return(
|
||||
<Menu className="currentMenu">
|
||||
<Menu.Item>
|
||||
<span title={current_user && current_user.username}>{current_user && current_user.username}</span>
|
||||
</Menu.Item>
|
||||
{
|
||||
personal && personal.length > 0 && personal.map((item,key)=>{
|
||||
return(
|
||||
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
|
||||
)
|
||||
})
|
||||
}
|
||||
<Menu.Item><a onClick={() => this.educoderloginysl()}>退出</a></Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match} = this.props;
|
||||
|
@ -346,17 +301,12 @@ class NewHeader extends Component {
|
|||
tojoinitemtype,
|
||||
tojoinclasstitle,
|
||||
code_notice,
|
||||
checked_notice,
|
||||
AccountProfiletype,
|
||||
submitapplications,
|
||||
submitapplicationsvalue,
|
||||
user,
|
||||
isRender,
|
||||
showSearchOpentype,
|
||||
headtypesonClickbool,
|
||||
headtypess,
|
||||
settings,
|
||||
goshowqqgtounp,
|
||||
openSearch,
|
||||
} = this.state;
|
||||
/*用户名称 用户头像url*/
|
||||
|
@ -453,11 +403,6 @@ class NewHeader extends Component {
|
|||
{...this.props}
|
||||
{...this.state}
|
||||
/> : ""}
|
||||
{
|
||||
goshowqqgtounp === true ?
|
||||
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
|
||||
:""
|
||||
}
|
||||
{
|
||||
settings && settings.nav_logo_url ?
|
||||
<a href={settings && settings.new_course.default_url} className={"fl mr50"} style={{minWidth:"45px"}}>
|
||||
|
@ -524,32 +469,6 @@ class NewHeader extends Component {
|
|||
}
|
||||
</div>:""
|
||||
}
|
||||
<Modal
|
||||
keyboard={false}
|
||||
title="提示"
|
||||
visible={submitapplications}
|
||||
closable={false}
|
||||
footer={null}
|
||||
>
|
||||
<div className="task_popup_con ml30">
|
||||
<div className="mr15">
|
||||
<ul>
|
||||
<div className="task-popup-content">
|
||||
<p className="task-popup-text-center font-16">
|
||||
{submitapplicationsvalue}
|
||||
</p>
|
||||
</div>
|
||||
<li className="clearfix mt10 edu-txt-center">
|
||||
<a className="task-btn mr10"
|
||||
onClick={this.hidesubmitapplications}>取消</a>
|
||||
<a
|
||||
className="task-btn task-btn-orange ml20"
|
||||
onClick={this.submitsubmitapplications}>确定</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
{!user || (user && !user.login) ?
|
||||
<span className="font-15 ml30">
|
||||
|
@ -560,25 +479,11 @@ class NewHeader extends Component {
|
|||
}
|
||||
</span>
|
||||
:
|
||||
<div className="ml30 edu-menu-panel" style={{ height: "70px", lineHeight: "70px" }}>
|
||||
<a href={`/users/${this.props.current_user === undefined ? "" : this.props.current_user.login}/courses`}>
|
||||
<img alt="头像" className="radius" height="34" id="nh_user_logo" name="avatar_image" src={getImageUrl(`/${user.image_url}`)} width="34">
|
||||
</img>
|
||||
<Dropdown placement={`bottomRight`} overlay={this.renderMenu(settings && settings.personal)}>
|
||||
<a href={`/users/${this.props.current_user && this.props.current_user.login}`}>
|
||||
<img alt="头像" src={getImageUrl(`/${user.image_url}`)} className="currentImg"></img>
|
||||
</a>
|
||||
<ul className="edu-menu-list" style={{ top: '60px', textAlign: 'center' }}>
|
||||
<li className="bor-bottom-greyE" style={{cursor:"default",background:"#fff"}}>{this.props.current_user.username}</li>
|
||||
{
|
||||
settings && settings.personal && settings.personal.length > 0 && settings.personal.map((item,key)=>{
|
||||
return(
|
||||
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
|
||||
)
|
||||
})
|
||||
}
|
||||
<li className="bor-top-greyE">
|
||||
<a onClick={() => this.educoderloginysl()}>退出</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Dropdown>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
.dropdownFlex{
|
||||
display:flex;
|
||||
padding:5px;
|
||||
background:#fff;
|
||||
border-radius: 3px;
|
||||
.ant-menu-vertical > .ant-menu-item{
|
||||
|
@ -9,11 +8,57 @@
|
|||
height: 35px;
|
||||
line-height: 35px;
|
||||
margin:0px;
|
||||
&.ant-menu-item-selected{
|
||||
background-color: #fff;
|
||||
a{color: rgba(0, 0, 0, 0.65)!important;}
|
||||
}
|
||||
&.ant-menu-item-active{
|
||||
a{color: #4cacff!important;}
|
||||
}
|
||||
}
|
||||
.ant-menu-vertical{
|
||||
border:none;
|
||||
}
|
||||
}
|
||||
.currentImg{
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.currentMenu{
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
padding:0px;
|
||||
li{
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding:0px;
|
||||
cursor: default;
|
||||
&:hover{
|
||||
background-color: #fff;
|
||||
}
|
||||
&:first-child{
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
&:last-child{
|
||||
border-top: 1px solid #eee;
|
||||
a{
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
}
|
||||
}
|
||||
a{
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
display: block;
|
||||
color: #666;
|
||||
&:hover{
|
||||
color: #fff;
|
||||
background: #4CACFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.newFooter {
|
||||
position: absolute;
|
||||
|
@ -65,4 +110,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.inviteForm{
|
||||
.ant-form-item{
|
||||
margin-right: 0px;
|
||||
}
|
||||
.ant-form-item-label{
|
||||
width: 110px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
|
@ -239,9 +239,11 @@ function CoderDepot(props){
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
let n = fileInfo && fileInfo.name;
|
||||
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
|
||||
|
||||
const { current_user } = props;
|
||||
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && (projectDetail.permission !=="Reporter" || (current_user && current_user.admin));
|
||||
|
||||
return(
|
||||
<WhiteBack>
|
||||
|
@ -311,7 +313,7 @@ function CoderDepot(props){
|
|||
}
|
||||
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a>
|
||||
</div>
|
||||
{ type === "dir" && projectDetail.type !== 2 &&
|
||||
{ fileOperate &&
|
||||
<Dropdown overlay={fileMenu} className="mr20" trigger={['click']}>
|
||||
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button>
|
||||
</Dropdown>
|
||||
|
@ -380,7 +382,7 @@ function CoderDepot(props){
|
|||
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
|
||||
}
|
||||
{/* readme文件显示(显示文件详情时不显示readme文件) */}
|
||||
{ dirInfo && readme ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
|
||||
{ dirInfo && (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
|
||||
</div>
|
||||
</LongWidth>
|
||||
{
|
||||
|
|
|
@ -11,6 +11,8 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
useEffect(()=>{
|
||||
if(readme && readme.content){
|
||||
setContent(readme.content);
|
||||
}else{
|
||||
setContent(undefined);
|
||||
}
|
||||
},[readme])
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ export default ((props)=>{
|
|||
</p>
|
||||
</div>
|
||||
<span>
|
||||
<Link to={`/projects/${owner}/${projectsId}/pulls/new`} className="mr20 color-blue mr30">创建合并请求</Link>
|
||||
<Link to={`/projects/${owner}/${projectsId}/pulls/new/${item.name}`} className="mr20 color-blue mr30">创建合并请求</Link>
|
||||
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight" className="color-green-file">
|
||||
<a className="ant-dropdown-link">
|
||||
<Tooltip title={`下载分支${item.name}`}><Icon type="cloud-download" className="font-18"/></Tooltip>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Spin, Tooltip } from 'antd';
|
||||
import { Spin, Tooltip , message } from 'antd';
|
||||
import { Link, Route, Switch } from 'react-router-dom';
|
||||
import { Content } from '../Component/layout';
|
||||
import { Content , FlexAJ } from '../Component/layout';
|
||||
import DetailBanner from './sub/DetailBanner';
|
||||
import Invite from './sub/Invite';
|
||||
import '../css/index.scss'
|
||||
import './list.css';
|
||||
|
||||
|
@ -389,6 +390,15 @@ class Detail extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
textFunc = (forked_from_project_id,fork_info)=>{
|
||||
return forked_from_project_id && fork_info ?
|
||||
<div className="color-grey-9">
|
||||
<span>forked from </span>
|
||||
<Link to={`/users/${fork_info.fork_project_user_login}`} className="show-user-link color-grey-6">{fork_info.fork_project_user_name}</Link>
|
||||
<span> / </span>
|
||||
<Link to={`/projects/${fork_info.fork_project_user_login}/${fork_info.fork_project_identifier}`} className="color-grey-6">{fork_info.fork_form_name}</Link>
|
||||
</div> : ""
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
|
@ -401,20 +411,11 @@ 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;
|
||||
|
||||
const text = (
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
<React.Fragment>
|
||||
<span>forked from </span>
|
||||
<Link to={`/users/${projectDetail.fork_info.fork_project_user_login}`} className="show-user-link color-grey-ccc">{projectDetail.fork_info.fork_project_user_name}</Link>
|
||||
<span> / </span>
|
||||
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`} className="color-grey-ccc">{projectDetail.fork_info.fork_form_name}</Link>
|
||||
</React.Fragment> : ""
|
||||
);
|
||||
|
||||
const common = {
|
||||
getDetail: this.getDetail,
|
||||
changeOpenDevops:this.changeOpenDevops,
|
||||
|
@ -424,7 +425,7 @@ class Detail extends Component {
|
|||
<div>
|
||||
<div className="detailHeader-wrapper">
|
||||
<div className="normal">
|
||||
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
|
||||
<div className="f-wrap-between" style={{ position: "relative" }}>
|
||||
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
|
||||
{project && project.author &&
|
||||
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
|
||||
|
@ -434,15 +435,7 @@ class Detail extends Component {
|
|||
<span className="ml5 mr5">/</span>
|
||||
<span className="hide-1 flex-1 df">
|
||||
<Link to={`/projects/${owner}/${projectsId}`} className="font-22">{project && project.name}</Link>
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
<Tooltip placement={'right'} title={text}>
|
||||
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`}
|
||||
className="ml10" >
|
||||
<i className="iconfont icon-fork font-18 fl mt6" style={{ color: "#8D90E3" }}></i>
|
||||
</Link>
|
||||
</Tooltip> : ""
|
||||
}
|
||||
|
||||
{
|
||||
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
|
||||
projectDetail.type === 2 ?
|
||||
|
@ -461,8 +454,8 @@ class Detail extends Component {
|
|||
firstSync ? "":
|
||||
<span className="df mt25">
|
||||
{
|
||||
projectDetail && projectDetail.type && projectDetail.type === 2 ?
|
||||
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
|
||||
((current_user && current_user.admin) || isManager) && (projectDetail && projectDetail.type && projectDetail.type === 2) ?
|
||||
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
|
||||
}
|
||||
<span className="detail_tag_btn">
|
||||
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.focusFunc(watched)}>
|
||||
|
@ -515,6 +508,19 @@ class Detail extends Component {
|
|||
</span>
|
||||
}
|
||||
</div>
|
||||
<FlexAJ>
|
||||
<div>
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
this.textFunc(projectDetail.forked_from_project_id,projectDetail.fork_info)
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
{
|
||||
projectDetail && projectDetail.invite_code &&
|
||||
<Invite code={projectDetail.invite_code} />
|
||||
}
|
||||
</FlexAJ>
|
||||
{
|
||||
firstSync ? "" :
|
||||
<DetailBanner
|
||||
|
@ -638,6 +644,11 @@ class Detail extends Component {
|
|||
}
|
||||
></Route>
|
||||
{/* 新建合并请求 */}
|
||||
<Route path="/projects/:owner/:projectsId/pulls/new/:branch"
|
||||
render={
|
||||
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/pulls/new"
|
||||
render={
|
||||
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -18,7 +18,7 @@ class IndexItem extends Component {
|
|||
render() {
|
||||
const { projects } = this.props;
|
||||
return (
|
||||
<div className="project-list minH-670">
|
||||
<div className="project-list minH-670" style={{padding:"0px 20px"}}>
|
||||
{ projects && projects.length > 0 ? projects.map((item, key) => {
|
||||
return (
|
||||
<div className="p-r-Item" key={key}>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
import { Tooltip , message } from 'antd';
|
||||
|
||||
function Invite({code}) {
|
||||
|
||||
function jsCopy(id) {
|
||||
const copyEle = document.querySelector(id); // 获取要复制的节点
|
||||
const range = document.createRange(); // 创造range
|
||||
window.getSelection().removeAllRanges(); //清除页面中已有的selection
|
||||
range.selectNode(copyEle); // 选中需要复制的节点
|
||||
window.getSelection().addRange(range); // 执行选中元素
|
||||
document.execCommand("Copy"); // 执行copy操作
|
||||
message.success('复制成功');
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<span>邀请码: <span id="devitecode">{code}</span></span>
|
||||
<Tooltip title={<p className="edu-txt-center">可以通过邀请码邀请成员加入项目<br/>点击复制邀请码。</p>} placement={"bottom"}>
|
||||
<i className="iconfont icon-fuzhi2 font-16 color-blue ml8" onClick={()=>jsCopy("#devitecode")}></i>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Invite;
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
import React, { useEffect , useState } from 'react';
|
||||
import Axios from 'axios';
|
||||
import { Pagination , Spin , Popconfirm }from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Nodata from '../Nodata';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
|
||||
const limit = 15;
|
||||
function Apply(props) {
|
||||
const username = props.match.params.username;
|
||||
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])
|
||||
|
||||
function getList() {
|
||||
const url = `/users/${username}/applied_projects.json`;
|
||||
Axios.get(url).then(result=>{
|
||||
if(result){
|
||||
setList(result.data.applied_projects);
|
||||
setTotal(result.data.total_count);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 接受
|
||||
function acceptDivert(id){
|
||||
const url = `/users/${username}/applied_projects/${id}/accept.json`;
|
||||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.deleteEvent("apply",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 拒绝
|
||||
function revertDivert(id){
|
||||
const url = `/users/${username}/applied_projects/${id}/refuse.json`;
|
||||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.deleteEvent("apply",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.user && i.user.login}`}><img src={getImageUrl(`/${i.user && i.user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.user && i.user.login}`} className="font-15 mr20">{i.user && i.user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
<FlexAJ>
|
||||
<p>申请以【{i.role === "developer" ?"开发者":i.role === "manager" ? "管理者":"报告者"}】身份加入【{i.project && i.project.name}】项目。是否同意?</p>
|
||||
{
|
||||
i.status === "common" &&
|
||||
<span>
|
||||
<Popconfirm title={`确定同意${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>acceptDivert(i.id)}>
|
||||
<a className="color-blue">同意</a>
|
||||
</Popconfirm>
|
||||
<Popconfirm title={`确定拒绝${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>revertDivert(i.id)}>
|
||||
<a className="color-red ml20">拒绝</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
}
|
||||
{
|
||||
i.status === "accepted" && <span className="color-grey-9">已接受</span>
|
||||
}
|
||||
{
|
||||
i.status === "refused" && <span className="color-grey-9">已拒绝</span>
|
||||
}
|
||||
</FlexAJ>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
{list && list.length === 0 && <Nodata _html="暂无成员申请" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Apply;
|
|
@ -6,6 +6,10 @@ import Loadable from "react-loadable";
|
|||
import Loading from "../../Loading";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
|
||||
const Apply = Loadable({
|
||||
loader: () => import("./Apply"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Notify = Loadable({
|
||||
loader: () => import("./Notify"),
|
||||
loading: Loading,
|
||||
|
@ -18,38 +22,62 @@ function Index(props){
|
|||
const username = props.match.params.username;
|
||||
const pathname = props.history.location.pathname;
|
||||
const user = props.user;
|
||||
const undo_messages = props.undo_messages;
|
||||
|
||||
const [ menu , setMenu ] = useState("notify");
|
||||
const [ messages , setMessages ] = useState(0);
|
||||
const [ transferProjects , setTransferProjects ] = useState(0);
|
||||
const [ messagesCount , setMessagesCount ] = useState(0);
|
||||
const [ transferCount , setTransferCount ] = useState(0);
|
||||
const [ applyCount , setApplyCount ] = useState(0);
|
||||
|
||||
const [ flag , setFlag ] = useState(true);
|
||||
const { current_user } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if((username && current_user && (current_user.login !== username))){
|
||||
props.history.push(`/users/${username}`);
|
||||
}
|
||||
},[current_user,username])
|
||||
|
||||
useEffect(()=>{
|
||||
if(user){
|
||||
setTransferProjects(user.undo_transfer_projects);
|
||||
setTransferCount(user.undo_transfer_projects);
|
||||
setApplyCount(user.undo_join_projects);
|
||||
setMessagesCount(user.undo_messages);
|
||||
}
|
||||
if(undo_messages){
|
||||
setMessages(undo_messages);
|
||||
}
|
||||
},[user,undo_messages])
|
||||
},[user])
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname && username){
|
||||
if(pathname === `/users/${username}/notice`){
|
||||
setMenu("notify");
|
||||
changeNum(user.undo_messages);
|
||||
}
|
||||
if(pathname === `/users/${username}/notice/undo`){
|
||||
setMenu("undo");
|
||||
}
|
||||
if(pathname === `/users/${username}/notice/apply`){
|
||||
setMenu("apply");
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
function fetchUser(){
|
||||
props && props.fetchUser();
|
||||
}
|
||||
},[pathname,user])
|
||||
|
||||
function changeNum(){
|
||||
fetchUser();
|
||||
if(flag){
|
||||
messagesCount && props.deleteUndoEvent(messagesCount);
|
||||
setFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteEvent(type,count) {
|
||||
let c = count;
|
||||
if(type==="apply"){
|
||||
setApplyCount(applyCount-count);
|
||||
}else if(type==="undo"){
|
||||
setTransferCount(applyCount-count);
|
||||
}else{
|
||||
setMessagesCount(0);
|
||||
c = messagesCount;
|
||||
}
|
||||
(c || c===0) && props.deleteUndoEvent(c);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -58,27 +86,39 @@ function Index(props){
|
|||
<li className={menu === "notify" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice`} onClick={changeNum}>
|
||||
<span>通知</span>
|
||||
{messages ? <span className="unNum">{messages}</span>:""}
|
||||
{messagesCount ? <span className="unNum">{messagesCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={menu === "undo" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice/undo`}>
|
||||
<span>接收仓库</span>
|
||||
{transferProjects ? <span className="unNum">{transferProjects}</span>:""}
|
||||
{transferCount ? <span className="unNum">{transferCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={menu === "apply" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice/apply`}>
|
||||
<span>成员申请</span>
|
||||
{applyCount ? <span className="unNum">{applyCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/users/:username/notice/apply"
|
||||
render={(p) => {
|
||||
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/notice/undo"
|
||||
render={(p) => {
|
||||
return <UndoEvent {...props} {...p} fetchUser={fetchUser}/>;
|
||||
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/notice"
|
||||
render={(p) => {
|
||||
return <Notify {...props} {...p} fetchUser={fetchUser}/>;
|
||||
return <Notify {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -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;
|
||||
|
@ -37,15 +26,14 @@
|
|||
min-width: 23px;
|
||||
text-align: center;
|
||||
background-color: #ffe4b3;
|
||||
margin-top: 19px;
|
||||
margin-top: 27px;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.notifyList{
|
||||
padding:0px 20px;
|
||||
min-height: 400px;
|
||||
padding:0px 30px;
|
||||
li{
|
||||
display: flex;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
|
|
@ -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,15 @@ function Notify(props){
|
|||
const [ list , setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
|
||||
useEffect(()=>{
|
||||
props && props.deleteEvent("notify",0);
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
if(username){
|
||||
setIsSpin(true);
|
||||
getList();
|
||||
}
|
||||
},[username,page])
|
||||
|
@ -28,6 +34,7 @@ function Notify(props){
|
|||
if(result){
|
||||
setList(result.data.applied_messages);
|
||||
setTotal(result.data.total_count);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
@ -40,12 +47,25 @@ function Notify(props){
|
|||
return <p>取消转移【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库</p>
|
||||
case 'common':
|
||||
return <p>正在将【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库转移给【<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link>】</p>
|
||||
case 'successed':
|
||||
case 'successed':
|
||||
return <p>【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库成功转移给【<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link>】</p>
|
||||
default:
|
||||
return <p>拒绝转移【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库</p>
|
||||
}
|
||||
}else{
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
function renderApplyStatus(status,applied) {
|
||||
let { project } = applied;
|
||||
if(status){
|
||||
switch(status){
|
||||
case 'successed':
|
||||
return <p>已通过你加入【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】项目的申请</p>
|
||||
default:
|
||||
return <p>已拒绝你加入【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】项目的申请</p>
|
||||
}
|
||||
}else{
|
||||
return ""
|
||||
}
|
||||
|
@ -53,38 +73,40 @@ function Notify(props){
|
|||
|
||||
return(
|
||||
<div>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<div>
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.login}`}><img src={getImageUrl(`/${i.applied_user && i.applied_user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
{renderStatus(i.status,i.applied)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.login}`}><img src={getImageUrl(`/${i.applied_user && i.applied_user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
{ i.applied_type === "AppliedProject" ? renderApplyStatus(i.status,i.applied):renderStatus(i.status,i.applied)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
{list && list.length === 0 && <Nodata _html="暂无通知" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
:
|
||||
""
|
||||
}
|
||||
{list && list.length === 0 && <Nodata _html="暂无通知" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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=>{})
|
||||
}
|
||||
|
@ -39,7 +42,7 @@ function UndoEvent(props){
|
|||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.fetchUser();
|
||||
props && props.deleteEvent("undo",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
@ -50,16 +53,17 @@ function UndoEvent(props){
|
|||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.fetchUser();
|
||||
props && props.deleteEvent("undo",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
|
@ -73,7 +77,7 @@ function UndoEvent(props){
|
|||
</p>
|
||||
<FlexAJ>
|
||||
<p className="color-grey-6">请求将仓库【<Link to={`/projects/${i.project && i.project.owner && i.project.owner.login}/${i.project && i.project.identifier}`}>{i.project && i.project.name}</Link>】
|
||||
转移给【<Link to={`/users/${i.owner && i.owner.login}`}>{i.owner && i.owner.name}</Link>】,是否接受?</p>
|
||||
转移给【<Link to={`/users/${i.owner && i.owner.login}`}>{i.owner && i.owner.name}</Link>】,是否接受?</p>
|
||||
{
|
||||
i.status === "common" &&
|
||||
<span>
|
||||
|
@ -101,10 +105,11 @@ function UndoEvent(props){
|
|||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
</div>
|
||||
:
|
||||
""
|
||||
}
|
||||
</Spin>
|
||||
{list && list.length === 0 && <Nodata _html="暂无接收信息" />}
|
||||
{
|
||||
total > limit &&
|
||||
|
|
|
@ -34,7 +34,7 @@ function Collaborator(props){
|
|||
}
|
||||
{
|
||||
nav === "1" ?
|
||||
<AddMember getID={getID} login/>
|
||||
<AddMember getID={getID} login showNotification={props.showNotification}/>
|
||||
:
|
||||
<AddGroup getGroupID={getGroupID} organizeId={owner}/>
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ export default ((props) => {
|
|||
<WhiteBack style={{minHeight:"400px"}}>
|
||||
<Title>
|
||||
<span>团队成员管理</span>
|
||||
<AddMember getID={getID}/>
|
||||
<AddMember getID={getID} showNotification={props.showNotification}/>
|
||||
</Title>
|
||||
<FlexAJ className="padding20-30">
|
||||
<div style={{ width: "580px" }}>
|
||||
|
|
|
@ -144,7 +144,7 @@ export default Form.create()(
|
|||
</div>
|
||||
<p className="mt20">
|
||||
<Button type="primary" className="mr30" onClick={createOrganize}>创建组织</Button>
|
||||
<Button className="grey">取消</Button>
|
||||
<Button className="grey" onClick={()=>{window.history.back(-1)}}>取消</Button>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -97,7 +97,9 @@ class ForkUsers extends Component {
|
|||
<div>
|
||||
<Link
|
||||
to={`/projects/${item.login}/${item.identifier}`}
|
||||
className="font-16 text-primary hide-1 task-hide max-w-200"
|
||||
className="font-16 text-primary task-hide max-w-200"
|
||||
style={{display:"block"}}
|
||||
title={item.name}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
|
|
|
@ -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 {
|
|||
</div>
|
||||
<div className="ml12">
|
||||
<div>
|
||||
<a
|
||||
href={`/users/${item.login}`}
|
||||
className="font-16 text-primary hide-1 task-hide max-w-200"
|
||||
<Link
|
||||
to={`/users/${item.login}`}
|
||||
className="font-16 text-primary task-hide max-w-200"
|
||||
style={{display:"block"}}
|
||||
title={item.name}
|
||||
>
|
||||
{item.name}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="font-12 text-gray grid-item pb5">
|
||||
<i className="iconfont icon-shijian user-join-time"></i>
|
||||
|
|
|
@ -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,
|
||||
// }))
|
||||
}))
|
||||
}));
|
|
@ -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(
|
||||
<div id="Calendar" style={{height:"210px"}}></div>
|
||||
)
|
||||
}
|
||||
export default Calendar;
|
|
@ -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<textList.length;i++){
|
||||
cyList.push([textList[i],textList.length-i])
|
||||
}
|
||||
div.setOption({
|
||||
backgroundColor:'transparent',
|
||||
shape: 'circle',
|
||||
// ellipticity: 1,//椭圆
|
||||
maxFontSize: 40,//最大字号
|
||||
minFontSize: 12,//最小字号
|
||||
ellipticity: 1,//椭圆
|
||||
maxFontSize: 21,//最大字号
|
||||
minFontSize: 16,//最小字号
|
||||
tooltip: {
|
||||
show: true
|
||||
show: false
|
||||
},
|
||||
list: cyList,
|
||||
color(word, weight, fontSize, distance, theta) { //自定义颜色、透明度
|
||||
if (fontSize>=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(
|
||||
<div id="cloud" style={{height:"200px"}}></div>
|
||||
<div id="cloud" style={{height:"120px"}}></div>
|
||||
)
|
||||
}
|
||||
export default Cloud;
|
|
@ -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);
|
||||
|
|
|
@ -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: '报告者'}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import './style.scss';
|
||||
|
||||
|
||||
function Round({num,color,name}) {
|
||||
return(
|
||||
<div className={"annulusBasics"}>
|
||||
<div className={"centerCircle"} style={{color:`${color}`}}>
|
||||
<span><span className="score">{num}</span>分</span>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
<div className={"annulusOuter"} style={{border:`7px solid ${color}`}}></div>
|
||||
{num > 50 ?
|
||||
<div className={"leftRectangle"} style={{ transform: `rotate(${180/50 *(num - 50)}deg)` }}></div>
|
||||
:
|
||||
<div className={"leftRectangle"} ></div>
|
||||
}
|
||||
{num < 50 ?
|
||||
<div className={"rightRectangle"} style={{ transform: `rotate(${180/50 * num}deg)` }}></div>
|
||||
:
|
||||
<div className={"rightRectangle"} style={{ background: color }}></div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Round;
|
|
@ -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;
|
||||
}
|
|
@ -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(
|
||||
<ul className="infosActivity">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={``}><img src={getImageUrl(`/system/lets/letter_avatars/2/D/169_162_140/120.png`)} alt="" className="aImg"/></Link>
|
||||
<div className="aInfos">
|
||||
<AlignCenter>
|
||||
<Link to={``} className="name">{i.user_name}</Link>
|
||||
<span className="time">{i.action_time}</span>
|
||||
{i.priority && TagInfo(`${i.priority}`,"")}
|
||||
{i.issue_status && <span className="status">{i.issue_status}</span> }
|
||||
</AlignCenter>
|
||||
<p className="aDesc">{i.action_type}:{i.name}</p>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
export default Activity;
|
|
@ -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;i<c.length;i++){
|
||||
let filter = copyAllList.filter(j=>j.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(
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={'选择精选项目'}
|
||||
closable={true}
|
||||
width={500}
|
||||
className="ConcentrateBox"
|
||||
onCancel={onCancel}
|
||||
onOk={onOk}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="operateDiv">
|
||||
<p>最多可选取6个公开仓库</p>
|
||||
<Search
|
||||
placeholder="请输入项目名称进行搜索"
|
||||
onSearch={onSearch}
|
||||
enterButton="搜索"
|
||||
allowClear
|
||||
value={search}
|
||||
onChange={(e)=>setSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="listbox">
|
||||
<Checkbox.Group value={value} onChange={chooseProject} style={{width:"100%"}}>
|
||||
{
|
||||
copyList && copyList.length >0 && copyList.map((i,k)=>{
|
||||
return(
|
||||
<Checkbox value={i.id} disabled={disable && (value.filter(j=>j === i.id).length===0)}>{i.author && i.author.name}/{i.name}</Checkbox>
|
||||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
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 && <Checkbox value={i.id} disabled={disable && (value.filter(j=>j === i.id).length===0)}>{i.author && i.author.name}/{i.name}</Checkbox>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Checkbox.Group>
|
||||
</div>
|
||||
{ total > limit && page < pageSize && <div className="morelist" onClick={()=>setPage(page+1)}>查看更多</div> }
|
||||
{ (list && list.length === 0) && (copyList && copyList.length === 0) && <div style={{textAlign:"center"}}>您还没有公开的{search && `“${search}”`}项目,先去<Link to={`/projects/deposit/new`} className="color-blue">新建项目</Link></div> }
|
||||
</Spin>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default ConcentrateBox;
|
|
@ -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(
|
||||
<React.Fragment>
|
||||
<Box visible={visible} onCancel={()=>setVisible(false)} onSure={onSure} username={userLogin} choosed={value}/>
|
||||
{
|
||||
list && list.length>0 &&
|
||||
<div className="concentrate">
|
||||
<FlexAJ>
|
||||
<span className="font-18">精选项目</span>
|
||||
{ current && <a className="color-blue" onClick={()=>setVisible(true)}>自定义精选项目</a> }
|
||||
</FlexAJ>
|
||||
<div>
|
||||
<ul className="concentrateUl">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/projects/${i.author && i.author.login}/${i.identifier}`} className="name">{i.name}</Link>
|
||||
<p className="task-hide desc">{i.description}</p>
|
||||
<AlignCenter>
|
||||
{ i.category && <span className="tagName">{i.category.name}</span> }
|
||||
<span className="pariseCount"><i className="iconfont icon-guanzhu"></i>{i.watchers_count}</span>
|
||||
<span className="forkCount"><i className="iconfont icon-fork"></i>{i.forked_count}</span>
|
||||
</AlignCenter>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
list && list.length === 0 && current && <div className="ConcentrateTip"><i className="iconfont icon-tishi2"></i>你还没有设置精选项目,<a onClick={()=>setVisible(true)}>点击设置</a></div>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
export default ConcentrateProject;
|
|
@ -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(
|
||||
<Option key={i}>{i}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
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(
|
||||
<div>
|
||||
<Pie />
|
||||
<Line />
|
||||
<Radar />
|
||||
<Cloud />
|
||||
<div>
|
||||
<ConcentrateProject userLogin={username} current={current_user && (current_user.login === username)}/>
|
||||
</div>
|
||||
<div className="recentStatic">
|
||||
<p className="font-18 mb15">近期活动统计</p>
|
||||
<div className="echartBox"><Line data={statisticData}/></div>
|
||||
</div>
|
||||
<div className="calendarStatic">
|
||||
<FlexAJ style={{marginBottom:"15px"}}>
|
||||
<span className="font-18">贡献度</span>
|
||||
<Select style={{width:"200px"}} placeholder="选择年份" value={year} onSelect={(e)=>{setYear(e);setActivityDate(undefined)}}>
|
||||
{ yearList && renderYear(yearList) }
|
||||
</Select>
|
||||
</FlexAJ>
|
||||
<div className="echartBox">
|
||||
<Calendar time={year} userLogin={username} chooseTime={chooseTime}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="activeStatic">
|
||||
<span className="font-18">动态</span>
|
||||
{ projectTrends && projectTrends.length > 0 && <Activity list = {projectTrends}/> }
|
||||
{ projectTrends && projectTrends.length === 0 && <Nodata _html="暂无动态" />}
|
||||
{ total > aLimit && <div style={{textAlign:'center',paddingBottom:"30px"}}><Pagination pageSize={aLimit} current={page} total={total} onChange={(p)=>setPage(p)}/></div> }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,67 @@ $flex:flex;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userDescription{
|
||||
color: #666666;
|
||||
line-height: 18px;
|
||||
text-align: left;
|
||||
margin:10px 0px;
|
||||
word-break: break-all;
|
||||
text-align: justify;
|
||||
font-size: 16px;
|
||||
}
|
||||
.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 +166,7 @@ ul.ant-menu.menuStyle{
|
|||
line-height: 70px;
|
||||
padding:0px;
|
||||
margin-right: 30px!important;
|
||||
border-bottom:transparent!important;
|
||||
}
|
||||
}
|
||||
.disposeInfo{
|
||||
|
@ -135,4 +205,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:"";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,50 @@ class Infos extends Component {
|
|||
project_type: undefined,
|
||||
route_type: undefined,
|
||||
undo_events: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)
|
||||
|
@ -85,43 +123,21 @@ class Infos extends Component {
|
|||
});
|
||||
const { current_user } = this.props;
|
||||
const { username } = this.props.match.params;
|
||||
const { pathname } = this.props.location;
|
||||
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;
|
||||
this.setState({
|
||||
user: result.data,
|
||||
isSpin: false,
|
||||
undo_events:e
|
||||
});
|
||||
};
|
||||
|
||||
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)=>{
|
||||
|
@ -138,7 +154,6 @@ class Infos extends Component {
|
|||
this.setState({
|
||||
route_type: undefined,
|
||||
project_type:"notice",
|
||||
notice:true
|
||||
},()=>{
|
||||
this.props.history.push(`/users/${user && user.login}/notice`);
|
||||
this.fetchUser();
|
||||
|
@ -160,43 +175,59 @@ class Infos extends Component {
|
|||
this.props.history.push(`/users/${user && user.login}/organizes`)
|
||||
}
|
||||
|
||||
resetUser=()=>{
|
||||
const { resetUserInfo } = this.props;
|
||||
|
||||
this.fetchUser();
|
||||
resetUserInfo && resetUserInfo();
|
||||
}
|
||||
|
||||
// 修改待办事项右侧的数量
|
||||
deleteUndoEvent=(count)=>{
|
||||
let { undo_events } = this.state;
|
||||
let undo = undo_events - count;
|
||||
this.setState({
|
||||
undo_events:undo
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { current_user, mygetHelmetapi } = this.props;
|
||||
const { current_user } = this.props;
|
||||
const { username } = this.props.match.params;
|
||||
|
||||
const { user, isSpin, project_type, route_type , undo_events , undo_messages } = this.state;
|
||||
const { user, isSpin, route_type , undo_events , menuKey } = this.state;
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="new-content-flex">
|
||||
<div className="list-left" style={{border:"none"}}>
|
||||
<div className="bgcF">
|
||||
<div className="list-l-Menu text-center pd20 ">
|
||||
<Avatar
|
||||
size={110}
|
||||
src={getImageUrl(`/${user && user.image_url}`)}
|
||||
/>
|
||||
{user && user.user_identity && (
|
||||
<div className="mt-n15 position-relative">
|
||||
<Tag color="#FF6E21" style={{marginRight:"0px"}}>{user && user.user_identity}</Tag>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-center mt15 font-16 fwb">
|
||||
<div className="list-l-Menu text-center" style={{padding:"20px 25px"}}>
|
||||
<span className="headimg">
|
||||
<img src={getImageUrl(`/${user && user.image_url}`)} alt=""/>
|
||||
<span>
|
||||
{
|
||||
user && user.gender===1?
|
||||
<i className="iconfont icon-nan"></i>
|
||||
:
|
||||
<i className="iconfont icon-nv"></i>
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div className="text-center mt15 font-24 task-hide" title={user && user.username}>
|
||||
{user && user.username}
|
||||
</div>
|
||||
|
||||
<div className="userDescription">
|
||||
{user && user.description}
|
||||
</div>
|
||||
<div>
|
||||
{user && current_user && user.login === current_user.login && (
|
||||
<div className="user-info-star-button ">
|
||||
<Button
|
||||
block
|
||||
className="text-button-grey"
|
||||
href={`${
|
||||
mygetHelmetapi &&mygetHelmetapi.new_course&&
|
||||
mygetHelmetapi.new_course.edit_account
|
||||
}`}
|
||||
target="_blank"
|
||||
onClick={()=>this.props.history.push(`/users/${user.login}/info`)}
|
||||
type="primary"
|
||||
>
|
||||
{" "}
|
||||
<i className="iconfont icon-shezhi4 font-15 mr5"></i>
|
||||
|
@ -216,7 +247,7 @@ class Infos extends Component {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="width100 inline-block mt20">
|
||||
<div className="focusBox">
|
||||
<Link
|
||||
to={`/users/${user && user.login}/watchers`}
|
||||
className={`with50 text-center pull-left ${route_type === "watchers" ? "text-primary" : ""}`}
|
||||
|
@ -234,163 +265,116 @@ class Infos extends Component {
|
|||
<span>{user && user.watched_count}</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{current_user && user && user.login === current_user.login ? (
|
||||
<div className="bgcF">
|
||||
<div className="list-l-Menu">
|
||||
<li className={project_type && project_type === "notice" ? "active" : ""}>
|
||||
<p onClick={() => this.undo_link()}>
|
||||
<span className="font-16 color-grey-3">
|
||||
<i className="iconfont icon-dahuizhongzuo3x font-15 mr5"></i>
|
||||
待办事项
|
||||
</span>
|
||||
<span className="text-yellow font-16">
|
||||
{undo_events}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
):""}
|
||||
|
||||
<div className="bgcF">
|
||||
<ul className="list-l-Menu">
|
||||
<li className="MenuTitle" onClick={() => this.change_project_type(undefined)}>
|
||||
<i className="iconfont icon-xiangmuleixing font-15 mr5"></i>
|
||||
项目类型
|
||||
<i className="iconfont icon-youjiantou font-15 mr20 color-grey-9 pull-right"></i>
|
||||
</li>
|
||||
<li className={project_type && project_type === "common" ? "active" : ""} onClick={() => this.change_project_type("common")}>
|
||||
<p>
|
||||
<span className="font-16">开源托管项目</span>
|
||||
<span className="color-blue">
|
||||
{user && user.common_projects_count}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
<li className={project_type && project_type === "mirror" ? "active" : ""} onClick={() => this.change_project_type("mirror")}>
|
||||
<p>
|
||||
<span className="font-16">开源镜像项目</span>
|
||||
<span className="color-blue">
|
||||
{user && user.mirror_projects_count}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
<li className={project_type && project_type === "sync_mirror" ? "active" : ""} onClick={() => this.change_project_type("sync_mirror")}>
|
||||
<p>
|
||||
<span className="font-16">镜像托管项目</span>
|
||||
<span className="color-blue">
|
||||
{user && user.sync_mirror_projects_count}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{
|
||||
current_user && current_user.login && current_user.login === username &&
|
||||
<div className="bgcF">
|
||||
<ul className="list-l-Menu">
|
||||
<li className="MenuTitle">
|
||||
<i className="iconfont icon-gongzuoliu font-15 mr5"></i>
|
||||
DevOps工作流
|
||||
<i className="iconfont icon-youjiantou font-15 mr20 color-grey-9 pull-right"></i>
|
||||
</li>
|
||||
<li className={project_type && project_type === "CIService" ? "active" : ""} onClick={() => this.change_devops_type("CIService")}>
|
||||
<p>
|
||||
<span className="font-16 color-grey-3">CI服务</span>
|
||||
</p>
|
||||
</li>
|
||||
{/* <li className={project_type && project_type === "CDService" ? "active" : ""} onClick={() => this.change_devops_type("CDService")}>
|
||||
<p>
|
||||
<span className="font-16">CD服务</span>
|
||||
<span className="color-blue">
|
||||
{user && user.common_projects_count}
|
||||
</span>
|
||||
</p>
|
||||
</li> */}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
<div className="bgcF">
|
||||
<div className="list-l-Menu">
|
||||
<li className={project_type && project_type === "organizes" ? "active" : ""}>
|
||||
<p onClick={() => this.organize_link()} >
|
||||
<span className="font-16 color-grey-3">
|
||||
<i className="iconfont icon-itsm-liuchengguanli font-15 mr5"></i>
|
||||
组织
|
||||
</span>
|
||||
<span className="color-blue font-16">
|
||||
{user && user.user_org_count}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
{
|
||||
user && (user.province || user.custom_department || user.email) ?
|
||||
<div className="infoBox">
|
||||
{ user.province && <div><i className="iconfont icon-weizhi"></i><span>{user.province}</span><span title={user.city}>{user.city}</span></div> }
|
||||
{ user.custom_department && <div><i className="iconfont icon-danwei"></i><span title={user.custom_department}>{user.custom_department}</span></div> }
|
||||
{ user.email && <div><i className="iconfont icon-youxiangrenzheng"></i><span title={user.email}>{user.email}</span></div> }
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="list-right">
|
||||
{ !route_type && menuKey &&
|
||||
<Menu selectedKeys={[menuKey]} mode={`horizontal`} className="infosRightMenu">
|
||||
<Menu.Item key="0"><Link to={`/users/${user && user.login}`}><i className="iconfont icon-gailan"></i>概览</Link></Menu.Item>
|
||||
<Menu.Item key="1"><Link to={`/users/${user && user.login}/statistics`}><i className="iconfont icon-shujutongji"></i>数据统计</Link></Menu.Item>
|
||||
<Menu.Item key="2"><Link to={`/users/${user && user.login}/projects/common`}><i className="iconfont icon-xiangmu"></i>项目</Link></Menu.Item>
|
||||
{
|
||||
current_user && user && user.login === current_user.login ?
|
||||
<Menu.Item key="3"><Link to={`/users/${user && user.login}/notice`}><i className="iconfont icon-daibanshixiang"></i>待办事项<span className="menuNum">({undo_events})</span></Link></Menu.Item>
|
||||
:""
|
||||
}
|
||||
{
|
||||
current_user && current_user.login && current_user.login === username ?
|
||||
<Menu.Item key="4"><Link to={`/users/${user && user.login}/devops/CIService`}><i className="iconfont icon-gongzuoliu1"></i>DevOps工作流</Link></Menu.Item>
|
||||
:""
|
||||
}
|
||||
<Menu.Item key="5"><Link to={`/users/${user && user.login}/organizes`}><i className="iconfont icon-zuzhi"></i>组织<span className="menuNum">({user && user.user_org_count})</span></Link></Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
{user && (
|
||||
<div>
|
||||
<Switch {...this.props}>
|
||||
<Switch {...this.props}>
|
||||
<Route
|
||||
path="/users/:username/watchers"
|
||||
render={() => {
|
||||
return <WatchsUser {...this.props} {...this.state} userType="watchers" fetchUser={this.fetchUser}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/watchers"
|
||||
render={() => {
|
||||
return <WatchsUser {...this.props} {...this.state} userType="watchers" />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/notice"
|
||||
render={() => {
|
||||
return <Notice {...this.props} {...this.state} fetchUser={this.fetchUser}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/fan_users"
|
||||
render={() => {
|
||||
return <FanUser {...this.props} {...this.state} userType="fan_users"/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/devops/CDService"
|
||||
render={() => {
|
||||
return <InfosDevOpsCD {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/devops/CIService"
|
||||
render={() => {
|
||||
return <InfosDevOps {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/projects/:project_type"
|
||||
render={() => {
|
||||
return <InfosUser {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/organizes"
|
||||
render={() => {
|
||||
return <Organize {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username"
|
||||
render={(props) => {
|
||||
return <InfosUser {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/"
|
||||
render={(props) => {
|
||||
return <InfosUser {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
</div>
|
||||
path="/users/:username/notice"
|
||||
render={() => {
|
||||
return <Notice {...this.props} {...this.state} deleteUndoEvent={this.deleteUndoEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/fan_users"
|
||||
render={() => {
|
||||
return <FanUser {...this.props} {...this.state} userType="fan_users" fetchUser={this.fetchUser}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/devops/CDService"
|
||||
render={() => {
|
||||
return <InfosDevOpsCD {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/devops/CIService"
|
||||
render={() => {
|
||||
return <InfosDevOps {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/projects/:project_type"
|
||||
render={() => {
|
||||
return <InfosUser {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/organizes"
|
||||
render={() => {
|
||||
return <Organize {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/info"
|
||||
render={() => {
|
||||
return <UpdateInfo {...this.props} {...this.state} resetUser={this.resetUser}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/password"
|
||||
render={() => {
|
||||
return <UpdateInfo {...this.props} {...this.state}/>;
|
||||
}}
|
||||
></Route>
|
||||
|
||||
<Route
|
||||
path="/users/:username/statistics"
|
||||
render={(props) => {
|
||||
return <Statistics {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username"
|
||||
render={(props) => {
|
||||
return <GeneralView {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/"
|
||||
render={(props) => {
|
||||
return <GeneralView {...this.props} {...this.state} />;
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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 (
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="list-r-operation">
|
||||
<div className="list-r-operation" style={{padding:"20px"}}>
|
||||
<Search
|
||||
placeholder="输入项目名称关键字进行搜索"
|
||||
enterButton="搜索"
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
import React , { forwardRef, useEffect } from 'react';
|
||||
import { Form , Checkbox , Input, Radio , Button , Cascader } from 'antd';
|
||||
import { AlignCenter } from '../../Component/layout';
|
||||
import './Index.scss';
|
||||
import { locData } from '../../Utils/locData';
|
||||
import Axios from 'axios';
|
||||
|
||||
const { TextArea } = Input;
|
||||
export default Form.create()(
|
||||
forwardRef((props)=>{
|
||||
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);
|
||||
props.history.push(`/users/${username}`)
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<Form layout={'inline'} className="formBase">
|
||||
<Form.Item label="邮箱">
|
||||
{getFieldDecorator("email",{
|
||||
rules:[{required:true,message:"请输入邮箱账号"}]
|
||||
})(
|
||||
<Input placeholder="请输入您的邮箱账号" disabled style={{width:"400px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="">
|
||||
{getFieldDecorator("show_email",{
|
||||
rules:[],
|
||||
valuePropName:"checked"
|
||||
})(
|
||||
<Checkbox>在个人主页展示</Checkbox>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="姓名">
|
||||
{getFieldDecorator("real_name",{
|
||||
rules:[{required:true,message:"请输入姓名"}]
|
||||
})(
|
||||
<Input placeholder="请输入您的姓名" maxLength={"20"} style={{width:"400px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div>
|
||||
<Form.Item label="性别">
|
||||
{getFieldDecorator("gender",{
|
||||
rules:[{required:true,message:"请选择性别"}]
|
||||
})(
|
||||
<Radio.Group>
|
||||
<Radio value={1}>男</Radio>
|
||||
<Radio value={0}>女</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item label="单位名称">
|
||||
{getFieldDecorator("custom_department",{
|
||||
rules:[{required:true,message:"请输入单位名称"}]
|
||||
})(
|
||||
<Input placeholder="请输入单位名称" maxLength="30" style={{width:"400px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="">
|
||||
{getFieldDecorator("show_department",{
|
||||
rules:[],
|
||||
valuePropName:"checked"
|
||||
})(
|
||||
<Checkbox>在个人主页展示</Checkbox>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="地区">
|
||||
{getFieldDecorator("location",{
|
||||
rules:[]
|
||||
})(
|
||||
<Cascader placeholder="请选择省份城市" options={locData} style={{width:"400px"}}></Cascader>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="">
|
||||
{getFieldDecorator("show_location",{
|
||||
rules:[],
|
||||
valuePropName:"checked"
|
||||
})(
|
||||
<Checkbox>在个人主页展示</Checkbox>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="简介">
|
||||
{getFieldDecorator("description",{
|
||||
rules:[]
|
||||
})(
|
||||
<TextArea placeholder="请输入您的自我简介" rows={4} maxLength="140" style={{width:"600px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<AlignCenter>
|
||||
<span className="ant-form-item-label"></span>
|
||||
<Button type={"primary"} onClick={submit}>提交</Button>
|
||||
<Button type={"default"} onClick={()=>props.history.push(`/users/${username}`)} className="ml20">取消</Button>
|
||||
</AlignCenter>
|
||||
</Form>
|
||||
)
|
||||
})
|
||||
)
|
|
@ -0,0 +1,50 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import './Index.scss';
|
||||
import { Menu } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Base from './Base';
|
||||
import Password from './Password';
|
||||
|
||||
function Index(props){
|
||||
const { username } = props && props.match && props.match.params;
|
||||
const { pathname } = props && props.location;
|
||||
const { current_user } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if((username && current_user && (current_user.login !== username))){
|
||||
props.history.push(`/users/${username}`);
|
||||
}
|
||||
},[current_user,username])
|
||||
|
||||
const [ key , setKey ] = useState("0");
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname){
|
||||
if(pathname === `/users/${username}/info`){
|
||||
setKey("0");
|
||||
}else{
|
||||
setKey("1");
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
|
||||
return(
|
||||
<div>
|
||||
<Menu selectedKeys={[key]} mode={'horizontal'} className="infosRightMenu">
|
||||
<Menu.Item key="0"><Link to={`/users/${username}/info`}>基本资料</Link></Menu.Item>
|
||||
{/* <Menu.Item key="1"><Link to={`/users/${username}/password`}>密码管理</Link></Menu.Item> */}
|
||||
</Menu>
|
||||
<div style={{padding:"20px"}}>
|
||||
{
|
||||
key === "0" ?
|
||||
<Base {...props}/>
|
||||
:
|
||||
<Password {...props}/>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Index;
|
|
@ -0,0 +1,14 @@
|
|||
.formBase{
|
||||
.ant-form-item-label{
|
||||
width: 80px;
|
||||
display: block;
|
||||
}
|
||||
.ant-form-explain{
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.formBase.passMan{
|
||||
.ant-form-item-label{
|
||||
width: 92px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
import React , { forwardRef, useState } from 'react';
|
||||
import { Form , Input , Button } from 'antd';
|
||||
import { AlignCenter } from '../../Component/layout';
|
||||
import Axios from 'axios';
|
||||
|
||||
|
||||
export default Form.create()(
|
||||
forwardRef((props)=>{
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
|
||||
const { username } = props && props.match && props.match.params;
|
||||
const { current_user} = props;
|
||||
|
||||
const [ oldPass , setOldPass ] = useState(undefined);
|
||||
const [ oldPassRepeat , setOldPassRepeat ] = useState(undefined);
|
||||
|
||||
function submit() {
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
submitFunc(values);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function submitFunc(values) {
|
||||
const url = `/accounts/change_password.json`;
|
||||
Axios.post(url,{
|
||||
login:current_user && current_user.login,
|
||||
...values
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
props.showNotification("密码重置成功!")
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 判断两次输入的密码是否一致
|
||||
function checkIdentifier(rule, value, callback , inputValue){
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if (value && inputValue && value !== inputValue) {
|
||||
callback("两次输入的密码不一致");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
var reg = /(?!.*\s)(?!^[\u4e00-\u9fa5]+$)(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^.{8,16}$/;
|
||||
|
||||
function checkNewPass(rule, value, callback) {
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if (!reg.test(value)) {
|
||||
callback("8-16个字符,不包含空格,必须包含数字,字母或字符至少两种");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return(
|
||||
<Form layout={'inline'} className="formBase passMan">
|
||||
<Form.Item label="旧密码">
|
||||
{getFieldDecorator("old_password",{
|
||||
rules:[
|
||||
{required:true,message:"请输入旧密码"},
|
||||
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback,oldPassRepeat)}
|
||||
]
|
||||
})(
|
||||
<Input.Password placeholder="请输入旧密码" style={{width:"400px"}} onChange={(e)=>{setOldPass(e.target.value)}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="重复旧密码">
|
||||
{getFieldDecorator("old_password_repeat",{
|
||||
rules:[
|
||||
{required:true,message:"请重新输入旧密码"},
|
||||
{validator:(rule, value, callback)=>checkIdentifier(rule, value, callback,oldPass)}
|
||||
]
|
||||
})(
|
||||
<Input.Password placeholder="请重新输入旧密码" style={{width:"400px"}} onChange={(e)=>{setOldPassRepeat(e.target.value)}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="新密码">
|
||||
{getFieldDecorator("password",{
|
||||
rules:[
|
||||
{required:true,message:"请输入新密码"},
|
||||
{validator:checkNewPass}
|
||||
]
|
||||
})(
|
||||
<Input.Password placeholder="请输入新密码" style={{width:"400px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<AlignCenter style={{marginTop:"20px"}}>
|
||||
<span className="ant-form-item-label"></span>
|
||||
<Button type={"primary"} onClick={submit}>提交</Button>
|
||||
<Button type={"default"} onClick={()=>props.history.push(`/users/${username}`)} className="ml20">取消</Button>
|
||||
</AlignCenter>
|
||||
</Form>
|
||||
)
|
||||
})
|
||||
)
|
|
@ -0,0 +1,209 @@
|
|||
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 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`;
|
||||
let b = (dates && dates.length > 0 && dates[0]) ? Date.parse(dates[0])/1000 : undefined;
|
||||
let e = (dates && dates.length > 0 && dates[1]) ? Date.parse(dates[1])/1000 : undefined;
|
||||
Axios.get(url,{
|
||||
params:{
|
||||
start_time:b,
|
||||
end_time:e
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setPieData(result.data.role);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
|
||||
// 获取开发能力接口数据
|
||||
useEffect(()=>{
|
||||
getRadarSta();
|
||||
},[rDates])
|
||||
|
||||
function getRadarSta() {
|
||||
const url = `/users/${username}/statistics/develop.json`;
|
||||
let b = (rDates && rDates.length > 0 && rDates[0]) ? Date.parse(rDates[0])/1000 : undefined;
|
||||
let e = (rDates && rDates.length > 0 && rDates[1]) ? Date.parse(rDates[1])/1000 : undefined;
|
||||
Axios.get(url,{
|
||||
params:{
|
||||
start_time:b,
|
||||
end_time:e
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setRadarData(result.data);
|
||||
let score = result.data.user && result.data.user.each_language_score;
|
||||
if(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:parseFloat(percent[item]*100).toFixed(1),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`;
|
||||
let b = (cData && cData.length > 0 && cData[0]) ? Date.parse(cData[0])/1000 : undefined;
|
||||
let e = (cData && cData.length > 0 && cData[1]) ? Date.parse(cData[1])/1000 : undefined;
|
||||
Axios.get(url,{
|
||||
params:{
|
||||
start_time:b,
|
||||
end_time:e
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setCloudData(result.data);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div className="boxes">
|
||||
<FlexAJ style={{marginBottom:"15px"}}>
|
||||
<span className="font-18">开发能力</span>
|
||||
<RangePicker
|
||||
disabledDate={disabledDate}
|
||||
onChange={(val,dateString) => {setRDates(dateString)}}
|
||||
format={dateFormat}
|
||||
/>
|
||||
</FlexAJ>
|
||||
<div className="echartBox">
|
||||
<p>从五个维度刻画你的开发能力:语言能力、影响力、贡献度、活跃度、项目经验,同时可以查看社区平均数据。</p>
|
||||
<Radar data={radarData}/>
|
||||
{
|
||||
topThree && topThree.length > 0 &&
|
||||
<div className="roundBox">
|
||||
<Round num={topThree[0][1]} color="#FF7F69" name={topThree[0][0]}/>
|
||||
{ topThree[1] && <Round num={topThree[1][1]} color="#99dfff" name={topThree[1][0]}/> }
|
||||
{ topThree[2] && <Round num={topThree[2][1]} color="#ff9e48" name={topThree[2][0]}/> }
|
||||
</div>
|
||||
}
|
||||
{
|
||||
percentData &&
|
||||
<div className="pBox">
|
||||
<div class="progress">
|
||||
{
|
||||
percentData.map((i,k)=>{
|
||||
return(
|
||||
<span style={{width: `${i.p}%`, backgroundColor:`${i.color}`}}></span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className="progresstip">
|
||||
{
|
||||
percentData.map((i,k)=>{
|
||||
return(
|
||||
<span>
|
||||
<i class="zero" style={{backgroundColor: `${i.color}`}}></i><span>{i.name}</span>
|
||||
<span>{`${i.p}%`}</span>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="boxes">
|
||||
<FlexAJ style={{marginBottom:"15px"}}>
|
||||
<span className="font-18">角色定位</span>
|
||||
<RangePicker
|
||||
disabledDate={disabledDate}
|
||||
onChange={(val,dateString) => {setDates(dateString)}}
|
||||
format={dateFormat}
|
||||
/>
|
||||
</FlexAJ>
|
||||
<div className="echartBox">
|
||||
<p>根据你在项目开发过程中的工作,对你在项目中扮演的角色进行定位,包括:创建者、管理者、开发者、测试者、文档工作者等。</p>
|
||||
<Pie data={pieData}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="boxes">
|
||||
<FlexAJ style={{marginBottom:"15px"}}>
|
||||
<span className="font-18">专业定位</span>
|
||||
<RangePicker
|
||||
disabledDate={disabledDate}
|
||||
onChange={(val,dateString) => {setCData(dateString)}}
|
||||
format={dateFormat}
|
||||
/>
|
||||
</FlexAJ>
|
||||
<div className="echartBox">
|
||||
<p>展示你擅长、关注、感兴趣的专业范围,通过你参与项目、收藏项目、关注项目、复刻项目等数据来统计。</p>
|
||||
<Cloud data={cloudData}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Index;
|
|
@ -0,0 +1,64 @@
|
|||
.boxes{
|
||||
padding:15px 0px;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
import React , { useEffect , useState } from 'react';
|
||||
import { Dropdown, Menu , Pagination } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Search from '../Component/Search';
|
||||
import Item from './Team-item';
|
||||
import Nodata from '../Nodata';
|
||||
|
@ -15,7 +14,7 @@ function Team(props){
|
|||
const [ sort_direction , setSort_direction ] = useState("asc");
|
||||
const [ sort_by ,setSort_by ] = useState("created_at");
|
||||
const [ search ,setSearch ] = useState(undefined);
|
||||
console.log(props);
|
||||
|
||||
const { username } = props.match.params;
|
||||
useEffect(()=>{
|
||||
if(username){
|
||||
|
@ -47,14 +46,23 @@ function Team(props){
|
|||
<Menu.Item value="num_users" key="num_users">用户数排序</Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
|
||||
function newFunc() {
|
||||
const { checkIfLogin , showLoginDialog } = props;
|
||||
if(checkIfLogin()){
|
||||
props.history.push(`/organize/new`);
|
||||
}else{
|
||||
showLoginDialog && showLoginDialog();
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<div className="headerbox">
|
||||
<div>
|
||||
<Search value={search} onChange={(e)=>setSearch(e.target.value)} placeholder="请输入项目名称关键字进行搜索" onSearch={onSearch}/>
|
||||
<Search value={search} onChange={(e)=>setSearch(e.target.value)} placeholder="请输入组织名称关键字进行搜索" onSearch={onSearch}/>
|
||||
</div>
|
||||
<p>
|
||||
<Link to={`/organize/new`}><i className="iconfont icon-xinjian1 mr3 font-14"></i>新建组织</Link>
|
||||
<a onClick={newFunc}><i className="iconfont icon-xinjian1 mr3 font-14"></i>新建组织</a>
|
||||
<Dropdown overlay={menu}>
|
||||
<a>排序<i className="iconfont icon-sanjiaoxing-down ml3 font-14"></i></a>
|
||||
</Dropdown>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { Component } from "react";
|
||||
import { Input, Spin, Pagination } from "antd";
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from "axios";
|
||||
import Nodata from "../Nodata";
|
||||
import UserList from "../UsersList/user_list";
|
||||
|
@ -29,26 +29,24 @@ class CommonList extends Component {
|
|||
isSpin: true,
|
||||
});
|
||||
axios.get(url, {
|
||||
params: {
|
||||
page,
|
||||
limit,
|
||||
search,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
this.setState({
|
||||
users: result.data.users,
|
||||
total: result.data.count,
|
||||
isSpin: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
params: {
|
||||
page,
|
||||
limit,
|
||||
search,
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result && result.data) {
|
||||
this.setState({
|
||||
isSpin: false,
|
||||
users: result.data.users,
|
||||
total: result.data.count,
|
||||
isSpin: false
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
isSpin: false,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//切换页数
|
||||
|
@ -63,6 +61,12 @@ class CommonList extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
successFunc=()=>{
|
||||
const { fetchUser } = this.props;
|
||||
this.get_watchers();
|
||||
fetchUser && fetchUser();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { users, isSpin, total, search, limit, page } = this.state;
|
||||
const { userType, login, current_user } = this.props;
|
||||
|
@ -85,9 +89,9 @@ class CommonList extends Component {
|
|||
|
||||
return (
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="pd20 minH-670">
|
||||
<div className="minH-670">
|
||||
<div className="grid-item pb20 bbt">
|
||||
<h3 style={{marginBottom:"0px"}}>{userType === "watchers" ? `${title_type}关注的` : `关注${title_type}的`}</h3>
|
||||
<h3 style={{marginBottom:"0px"}}><Link to={`/users/${login}`} ><i className="iconfont icon-zuojiantou color-grey-9 font-16 mr8"></i></Link>{userType === "watch_users" ? `${title_type}关注的` : `关注${title_type}的`}</h3>
|
||||
<div className="text-right">
|
||||
<Search
|
||||
placeholder="输入名称进行搜索"
|
||||
|
@ -103,7 +107,7 @@ class CommonList extends Component {
|
|||
|
||||
{users && users.length > 0 ? (
|
||||
<div className="w-100 inline-block">
|
||||
<UserList users={users} userClass={"w-33"} current_user={current_user} successFunc={this.get_watchers}></UserList>
|
||||
<UserList users={users} userClass={"w-33"} type_title={'关注列表'} current_user={current_user} successFunc={this.successFunc}></UserList>
|
||||
</div>
|
||||
) : (
|
||||
<Nodata _html={`暂时没有数据~`} />
|
||||
|
|
|
@ -3,10 +3,10 @@ import CommonLists from "./common_lists"
|
|||
class FanUsers extends Component {
|
||||
|
||||
render() {
|
||||
const {user, current_user} = this.props
|
||||
const {user, current_user , fetchUser} = this.props
|
||||
return (
|
||||
<div>
|
||||
{user && user.login && <CommonLists userType="fan_users" login={user.login} current_user={current_user} />}
|
||||
{user && user.login && <CommonLists userType="fan_users" login={user.login} current_user={current_user} fetchUser={fetchUser}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
.list-left{
|
||||
width: 26%;
|
||||
padding-right: 20px;
|
||||
padding-right: 30px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
|||
width:74%;
|
||||
background: #fff;
|
||||
padding:0px !important;
|
||||
border:1px solid #eee;
|
||||
border:none!important;
|
||||
}
|
||||
|
||||
.text-center{text-align: center;}
|
||||
|
@ -30,7 +30,7 @@
|
|||
.position-relative{position: relative;}
|
||||
.mr-5{margin-right: 5px;}
|
||||
a.text-button-grey{
|
||||
color: rgba(0, 0, 0, 0.65)
|
||||
color: #fff;
|
||||
}
|
||||
.user-info-star-button{margin: 20px 50px 0 50px}
|
||||
.list-l-p{
|
||||
|
|
|
@ -2,10 +2,10 @@ import React, { Component } from "react";
|
|||
import CommonLists from "./common_lists"
|
||||
class WatcherUsers extends Component {
|
||||
render() {
|
||||
const {user, current_user} = this.props
|
||||
const {user, current_user , fetchUser } = this.props;
|
||||
return (
|
||||
<div className="minH-650">
|
||||
{user && user.login && <CommonLists userType="watch_users" login={user.login} current_user={current_user} />}
|
||||
{user && user.login && <CommonLists userType="watch_users" login={user.login} current_user={current_user} fetchUser={fetchUser}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -210,6 +210,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
const common = {
|
||||
showLoginDialog: this.showLoginDialog,
|
||||
checkIfLogin: this.checkIfLogin,
|
||||
resetUserInfo:this.fetchUsers
|
||||
};
|
||||
return (
|
||||
<div className="indexHOC">
|
||||
|
|
Loading…
Reference in New Issue