diff --git a/package-lock.json b/package-lock.json index 4a4f6a361..691afe35a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3934,6 +3934,11 @@ "warning": "^4.0.3" } }, + "cropperjs": { + "version": "1.5.12", + "resolved": "https://registry.npmmirror.com/cropperjs/download/cropperjs-1.5.12.tgz", + "integrity": "sha1-2cDbK/uMDXadUXOej5FrvEThD1A=" + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -14227,6 +14232,14 @@ "countup.js": "^2.0.8" } }, + "react-cropper": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/react-cropper/download/react-cropper-2.1.8.tgz?cache=0&sync_timestamp=1634379691101&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Freact-cropper%2Fdownload%2Freact-cropper-2.1.8.tgz", + "integrity": "sha1-vzWn3mV2n4rTV+iuiE55H+P+khI=", + "requires": { + "cropperjs": "^1.5.12" + } + }, "react-datepicker": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.14.1.tgz", diff --git a/package.json b/package.json index 00ef9788f..01f6c2821 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "react-content-loader": "^3.1.1", "react-cookies": "^0.1.1", "react-countup": "^6.1.0", + "react-cropper": "^2.1.8", "react-datepicker": "^2.14.1", "react-dev-utils": "^9.2.0-next.80", "react-dom": "^16.13.1", diff --git a/src/forge/Component/layout.jsx b/src/forge/Component/layout.jsx index adaf4dbf3..dcc133e85 100644 --- a/src/forge/Component/layout.jsx +++ b/src/forge/Component/layout.jsx @@ -7,6 +7,8 @@ export const Banner = styled.div`{ border-bottom:1px solid #eee; background-color:#fff; border-radius:5px 5px 0px 0px; + justify-content: space-between; + display: flex; }` export const AlignCenterBetween = styled.div`{ display:flex; diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js index acdf2dea1..dd0710ecb 100644 --- a/src/forge/Head/Header.js +++ b/src/forge/Head/Header.js @@ -42,8 +42,8 @@ class NewHeader extends Component { setevaluatinghides: false, occupation: 0, mydisplay: false, - headtypesonClickbool: false, - headtypess: "/", + // headtypesonClickbool: false, + // headtypess: "/", settings: null, visiblemyss: false, openSearch:false, @@ -137,12 +137,12 @@ class NewHeader extends Component { AccountProfiletype: false }) }; - headtypesonClick = (url, bool) => { - this.setState({ - headtypess: url, - headtypesonClickbool: bool, - }) - } + // headtypesonClick = (url, bool) => { + // this.setState({ + // headtypess: url, + // headtypesonClickbool: bool, + // }) + // } //获取数据为空的时候 gettablogourlnull = () => { this.setState({ @@ -295,12 +295,14 @@ class NewHeader extends Component { {...this.props} {...this.state} /> : ""} - { - publicNav && - - ccf - - } +
+ { + publicNav && + + ccf + + } +
{ settings && settings.nav_logo_url ? @@ -316,26 +318,26 @@ class NewHeader extends Component { { settings.navbar && settings.navbar.map((item, key) => { var new_link = item.link; - var user_login = current_user && current_user.login; - var is_hidden = item.hidden - if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) { - if (user_login) { - if (new_link.indexOf("courses") > -1) { - new_link = new_link.replace(/courses/g, user_login + "/courses") - } else if (new_link.indexOf("contests") > -1) { - new_link = new_link.replace(/contests/g, user_login + "/contests") - } - } else { - is_hidden = true - } - } - if (user_login && (new_link && new_link.indexOf("homes") > -1)) { - new_link = new_link.replace(/homes/g, user_login + "/user_activities") - } + // var user_login = current_user && current_user.login; + var is_hidden = item.hidden; + // if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) { + // if (user_login) { + // if (new_link.indexOf("courses") > -1) { + // new_link = new_link.replace(/courses/g, user_login + "/courses") + // } else if (new_link.indexOf("contests") > -1) { + // new_link = new_link.replace(/contests/g, user_login + "/contests") + // } + // } else { + // is_hidden = true + // } + // } + // if (user_login && (new_link && new_link.indexOf("homes") > -1)) { + // new_link = new_link.replace(/homes/g, user_login + "/user_activities") + // } var waiLian = (new_link && str.filter(item=>new_link.indexOf(item)>-1) ); var wl = waiLian && waiLian.length>0; return ( -
  • this.headtypesonClick(item.link, true)} className={`${this.matchpaths(new_link) === true ? 'pr active' : 'pr'}`} style={!is_hidden ? { display: 'flex' } : { display: 'none' }}> +
  • {item.name}
  • ) @@ -350,7 +352,6 @@ class NewHeader extends Component { { current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)? - {/* */} :"" } diff --git a/src/forge/Main/Diff.jsx b/src/forge/Main/Diff.jsx index 985b03a8c..51eca8816 100644 --- a/src/forge/Main/Diff.jsx +++ b/src/forge/Main/Diff.jsx @@ -36,8 +36,9 @@ const Infos = styled.div` } & > .f-wrap-between { padding: 14px 20px 14px 16px; - border-radius: 3px 3px 0px 0px; + border-radius: 0px 0px 3px 3px; border: 1px solid #D0D0D0; + border-top: none; .df{ align-items: center; & .underline:hover{ diff --git a/src/forge/Order/MilepostDetail.js b/src/forge/Order/MilepostDetail.js index e67dac9bf..66322ae18 100644 --- a/src/forge/Order/MilepostDetail.js +++ b/src/forge/Order/MilepostDetail.js @@ -321,7 +321,7 @@ class MilepostDetail extends Component { { search_count > limit? -
    +
    :"" } diff --git a/src/forge/Order/order.css b/src/forge/Order/order.css index 60bb5d2bb..c35a18274 100644 --- a/src/forge/Order/order.css +++ b/src/forge/Order/order.css @@ -232,6 +232,9 @@ border-bottom: 1px solid #eee; padding: 16px 0px 16px 20px; } +.issueItem:last-child{ + border-bottom: none; +} .issueNo { padding: 0px 5px; border-radius: 4px; diff --git a/src/forge/Order/order.js b/src/forge/Order/order.js index 8a7bc9287..ef100805b 100644 --- a/src/forge/Order/order.js +++ b/src/forge/Order/order.js @@ -6,7 +6,7 @@ import moment from 'moment'; import NoneData from "../Nodata"; import OrderItem from "./OrderItem"; -import CheckProfile from '../Component/ProfileModal/Profile'; +import cookie from 'react-cookies'; import axios from "axios"; @@ -76,6 +76,14 @@ class order extends Component { } componentDidMount = () => { + // const selectParams = cookie.load('selectParams'); + // let states = selectParams.select_params; + // this.setState({ + // ...states + // },()=>{ + // this.getSelectList(); + // this.getIssueList('1'); + // }) this.getSelectList(); this.getIssueList('1'); }; @@ -102,11 +110,11 @@ class order extends Component { // 获取列表数据 getIssueList = (status_type, begin, end) => { + cookie.remove('selectParams'); this.setState({ isSpin: true }) const { select_params } = this.state; - console.log(select_params); const { projectsId, owner } = this.props.match.params; const url = `/${owner }/${projectsId}/issues.json`; axios @@ -120,7 +128,9 @@ class order extends Component { }) .then((result) => { if (result) { - const issues = result.data.issues + const issues = result.data.issues; + // let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000); + // cookie.save('selectParams', {states:this.state},{ expires: inFifteenMinutes,path:`/${owner}/${projectsId}/issues` }); this.setState({ data: result.data, issues: issues, @@ -193,11 +203,12 @@ class order extends Component { select_params, author_id, assigned_to_id + },()=>{ + if (!toGet) { + const { status_type } = this.state; + this.getIssueList(status_type); + } }); - if (!toGet) { - const { status_type } = this.state; - this.getIssueList(status_type); - } }; renderMenu = (array, name, id, toGet) => { @@ -852,7 +863,7 @@ class order extends Component { )} { search_count > select_params.limit ? -
    +
    { if(checkIfLogin() === false){ props.history.push('/login'); } },[]) + + + function onCancelAvatar(){ + setAvatarVisible(false); + const { resetUserInfo } = props; + resetUserInfo && resetUserInfo(); + } return(
    + { + avatarVisible && + + }
    - +
    + + setAvatarVisible(true)}>修改头像 +
    {current_user && current_user.username}
      diff --git a/src/forge/SecuritySetting/Index.scss b/src/forge/SecuritySetting/Index.scss index ede91c86d..7d2998765 100644 --- a/src/forge/SecuritySetting/Index.scss +++ b/src/forge/SecuritySetting/Index.scss @@ -193,4 +193,31 @@ } .blue-Purple{ color: #466AFF !important; +} + +.userHeadPhoto{ + width: 48px; + position: relative; + margin:0px auto; + .userUpdateBox{ + font-size: 11px!important; + width: 100%; + height: 100%; + line-height: 48px; + text-align: center; + position: absolute; + left: 0px; + top:0px; + background-color: rgba(0, 0, 0, 0.2); + display: none!important; + color: white!important; + transition: 1s; + border-radius: 50%; + cursor: pointer; + } + &:hover{ + .userUpdateBox{ + display: block!important; + } + } } \ No newline at end of file diff --git a/src/forge/Settings/Webhooks/Index.jsx b/src/forge/Settings/Webhooks/Index.jsx index 3af71ee1e..1cc171c47 100644 --- a/src/forge/Settings/Webhooks/Index.jsx +++ b/src/forge/Settings/Webhooks/Index.jsx @@ -76,10 +76,8 @@ function Index(props) { subTitle={`删除后未来事件将不会推送至此Webhook地址:${url}`} /> - - Webhooks(网络钩子) - - + Webhooks(网络钩子) +

      每当特定事件(如push代码,合并请求被编辑)发生时,我们将通过webhook给您提供的远程URL发送post请求。您可以在我们的webhooks指南中了解更多信息

      diff --git a/src/forge/Settings/Webhooks/New.jsx b/src/forge/Settings/Webhooks/New.jsx index 22b3899d7..d05a261ec 100644 --- a/src/forge/Settings/Webhooks/New.jsx +++ b/src/forge/Settings/Webhooks/New.jsx @@ -287,10 +287,10 @@ function New({ form , match , showNotification , history }) { 合并请求标签 合并请求的标签被更新或清除 */} - + {/* 合并请求审查 合并请求被批准、拒绝或提出审查意见,审查人员的修改,审查线程已解决或未解决 - + */} {/* 合并请求被同步 合并请求被同步 diff --git a/src/forge/Team/Component/AddMemberBox.jsx b/src/forge/Team/Component/AddMemberBox.jsx new file mode 100644 index 000000000..77b0786aa --- /dev/null +++ b/src/forge/Team/Component/AddMemberBox.jsx @@ -0,0 +1,69 @@ +import React ,{ useState , useEffect } from 'react'; +import { Modal , Spin } from 'antd'; +import axios from 'axios'; + +function AddMemberBox({className,orzId,history,OIdentifier}){ + const [ visible , setVisible ] = useState(false); + const [ isSpin , setIsSpin ] = useState(true); + const [ list , setList ] = useState(undefined); + + useEffect(()=>{ + if(visible && orzId){ + InitData(); + } + },[orzId,visible]) + + function InitData(){ + const url = `/organizations/${orzId}/teams.json`; + axios.get(url,{ + params:{ + is_full:true + } + }).then(result=>{ + if(result){ + setList(result.data.teams); + setIsSpin(false); + } + }).catch(error=>{}) + } + + function chooseGroup(id){ + history.push(`/${OIdentifier}/teams/${id}/setting/member`); + } + return( +
      + setVisible(false)} + footer={null} + className="addMemberBody" + > +
      +

      请选择想要添加成员的组织团队

      + +
      + { + list && list.length > 0 ? +
        + { + list.map((i,k)=>{ + return( +
      • chooseGroup(i.id)}>{i.nickname}
      • + ) + }) + } +
      + :"" + } +
      +
      +
      +
      + setVisible(true)}>+ 添加成员 +
      + ) +} +export default AddMemberBox; \ No newline at end of file diff --git a/src/forge/Team/Index.scss b/src/forge/Team/Index.scss index 112379734..0538c4660 100644 --- a/src/forge/Team/Index.scss +++ b/src/forge/Team/Index.scss @@ -369,4 +369,48 @@ } .hide{ display: none; +} + +.addMemberBtn{ + display: flex; + height: 34px; + line-height: 32px; + padding:0px 14px; + border-radius: 3px; + border:1px solid #d0d0d0; + font-size: 14px; + &:hover{ + border-color: #466AFF; + } +} + +.addMemberBody{ + .ant-modal-body{ + padding:0px; + } + .addForGroupList{ + padding:20px 40px; + max-height: 315px; + overflow-y: auto; + ul{ + display: flex; + flex-wrap: wrap; + width: 450px; + margin:0px auto; + } + li{ + cursor: pointer; + border:1px solid #d0d0d0; + border-radius:4px; + margin:0px 10px 15px 10px!important; + height: 40px; + line-height: 40px; + width: 130px; + text-align: center; + &.active,&:hover{ + color: #fff; + background-color: rgba(65, 84, 241, 1); + } + } + } } \ No newline at end of file diff --git a/src/forge/Team/Setting/TeamSettingMember.jsx b/src/forge/Team/Setting/TeamSettingMember.jsx index 255897d68..9124390f6 100644 --- a/src/forge/Team/Setting/TeamSettingMember.jsx +++ b/src/forge/Team/Setting/TeamSettingMember.jsx @@ -7,6 +7,7 @@ import styled from 'styled-components'; import { getImageUrl } from 'educoder'; import axios from 'axios'; import { Link } from 'react-router-dom'; +import AddMemberBox from '../Component/AddMemberBox'; const Img = styled.img`{ width:30px; @@ -14,7 +15,8 @@ const Img = styled.img`{ border-radius:50%; }` const limit = 15; -export default (({organizeDetail})=>{ +export default (({organizeDetail,history,match})=>{ + const OIdentifier = match.params.OIdentifier; const [ page , setPage ] = useState(1); const [ total , setTotal ] = useState(0); const [ search , setSearch ] = useState(undefined); @@ -116,6 +118,12 @@ export default (({organizeDetail})=>{ + 添加用户 */} +
      diff --git a/src/forge/Team/TeamMember.jsx b/src/forge/Team/TeamMember.jsx index 0a5abe1e3..d21d514f3 100644 --- a/src/forge/Team/TeamMember.jsx +++ b/src/forge/Team/TeamMember.jsx @@ -4,9 +4,11 @@ import Cards from '../Component/MemberCards'; import axios from 'axios'; import Nodata from '../Nodata'; import { Pagination , Spin } from 'antd'; +import AddMemberBox from './Component/AddMemberBox'; const limit = 15; -function TeamMember({organizeDetail,current_user}){ +function TeamMember({organizeDetail,current_user,history,match}){ + const OIdentifier = match.params.OIdentifier; const [ page , setPage ] = useState(1); const [ total , setTotal ] = useState(0); const [ isSpin , setIsSpin ] = useState(true); @@ -34,7 +36,17 @@ function TeamMember({organizeDetail,current_user}){ return( - 组织成员 + 组织成员 + { + organizeDetail && organizeDetail.is_admin && + + } +
      { diff --git a/src/forge/users/Avatar/Index.jsx b/src/forge/users/Avatar/Index.jsx new file mode 100644 index 000000000..aa7669a6e --- /dev/null +++ b/src/forge/users/Avatar/Index.jsx @@ -0,0 +1,102 @@ +import React ,{useEffect, useRef, useState} from 'react'; +import './Index.scss'; +import { Modal , message } from 'antd'; +import Cropper from 'react-cropper'; +import 'cropperjs/dist/cropper.css'; +import axios from 'axios'; + +function Index({onCancel,avatarImg,login}){ + + const [ avatarPhoto , setAvatarPhoto ] = useState(avatarImg); + + useEffect(()=>{ + if(avatarImg){ + setAvatarPhoto(avatarImg); + } + },[avatarImg]) + + const cropper = useRef(); + + const saveAvatar = async () => { + const imgUrl = cropper.current.cropper.getCroppedCanvas().toDataURL("image/jpeg"); + if (!imgUrl) { + message.info('请先上传图片'); + } + const url = `/users/${login}/update_image.json`; + axios.put(url,{ + image:imgUrl + }).then(result=>{ + if(result){ + message.success("头像修改成功!"); + onCancel(true); + } + }).catch(error=>{}) + } + + function onChange(e){ + + let files; + if (e.dataTransfer) { + files = e.dataTransfer.files; + } else if (e.target) { + files = e.target.files; + } + + if (!files || (files && files.length===0)) { + return; + } + const file = files[0]; + if (!/^image\/\w+/.test(file.type)) { + message.info('请选择一个图片格式的文件'); + return; + } + + if (file.size > 2 * 1024 * 1024) { + message.info('仅支持文件大小小于2M的文件'); + return; + } + + const reader = new FileReader(); + reader.onload = () => { + setAvatarPhoto(reader.result); + }; + reader.readAsDataURL(files[0]); + } + + return( + onCancel(false)} + className="avatarBox" + > +
      +
      + + {/* 仅支持JPG、GIF、PNG,且文件小于2M */} +
      +
      +
      +
      + + 保存头像 +
      +
      +
      +
      + ) +} +export default Index; \ No newline at end of file diff --git a/src/forge/users/Avatar/Index.scss b/src/forge/users/Avatar/Index.scss new file mode 100644 index 000000000..8b7c1b17c --- /dev/null +++ b/src/forge/users/Avatar/Index.scss @@ -0,0 +1,66 @@ +.avatarBox{ + position: relative; + .ant-modal-header{ + background-color: rgba(242, 242, 255, 1); + .ant-modal-title{ + text-align: left; + } + } + + .ant-modal-body{ + position: relative; + } + .avatarDiv{ + display: flex; + .previewBox{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + margin-left: 30px; + .uploadBtn{ + margin-bottom: 30px; + display: flex; + a,label{ + cursor: pointer; + display: block; + height: 32px; + line-height: 30px; + margin:0px 10px; + width: 100px; + border:1px solid rgba(208, 208, 208, 1); + background-color: white; + border-radius:4px; + text-align: center; + color:#666666; + span{ + display: block; + .ant-upload.ant-upload-select { + width: 100%; + height: 32px; + } + .ant-upload-list{ + display: none; + } + } + } + a{ + background-color: rgba(65, 84, 241, 1); + color: white; + border-color: rgba(65, 84, 241, 1); + &:hover{ + color: white!important; + } + } + } + } + .previewImg{ + overflow: hidden; + background-color: #fff; + border-radius: 50%; + text-align: center; + width: 100px!important; + height: 100px!important; + } + } +} \ No newline at end of file diff --git a/src/forge/users/Index.css b/src/forge/users/Index.css index 280cce1b3..8cad4168d 100644 --- a/src/forge/users/Index.css +++ b/src/forge/users/Index.css @@ -137,7 +137,6 @@ .infoBox span { margin-left: 10px; } - .headimg { position: relative; display: block; @@ -150,8 +149,7 @@ .headimg span { position: absolute; bottom: -6px; - right: 0px; - left: 65px; + right: -16px; } .headimg span i { font-size: 25px !important; diff --git a/src/forge/users/Index.scss b/src/forge/users/Index.scss index 8139ebaec..e523323d2 100644 --- a/src/forge/users/Index.scss +++ b/src/forge/users/Index.scss @@ -133,6 +133,32 @@ $flex:flex; margin-left:10px ; } } + +.headimg-div{ + width: 110px; + height: 110px; + margin:0px auto; + position: relative; + .updateAvatar{ + cursor: pointer; + position: absolute; + width: 100%; + height: 100%; + border-radius: 50%; + content: ""; + left: 0px; + top:0px; + background-color: rgba(0,0,0,0.2); + display: none; + align-items: center; + justify-content: center; + color: white; + transition: 1s; + } + &:hover .updateAvatar{ + display: flex; + } +} .headimg{ position: relative; display: block; @@ -145,7 +171,7 @@ $flex:flex; position: absolute; bottom: -6px; right: 0px; - left: 65px; + z-index: 11; i{ font-size: 25px!important; border-radius: 50%; diff --git a/src/forge/users/Infos.js b/src/forge/users/Infos.js index 00b03310b..47844f1ee 100644 --- a/src/forge/users/Infos.js +++ b/src/forge/users/Infos.js @@ -1,11 +1,12 @@ import React, { Component } from "react"; import { Link } from "react-router-dom"; -import { Button, Spin , Menu } from "antd"; +import { Spin , Menu } from "antd"; import FocusButton from "../UsersList/focus_button"; import axios from "axios"; import { getImageUrl } from "educoder"; import { Route, Switch } from "react-router-dom"; +import Avatar from './Avatar/Index'; import "./new_user.css"; import "../css/index.scss"; @@ -14,10 +15,10 @@ import './Index.scss'; import Loadable from "react-loadable"; import Loading from "../../Loading"; -const UpdateInfo = Loadable({ - loader: () => import("./Material/Index"), - loading: Loading, -}); +// const UpdateInfo = Loadable({ +// loader: () => import("./Material/Index"), +// loading: Loading, +// }); const InfosDevOps = Loadable({ loader: () => import("./devOpsCI"), loading: Loading, @@ -66,7 +67,8 @@ class Infos extends Component { project_type: undefined, route_type: undefined, undo_events:0, - menuKey:"0" + menuKey:"0", + avatarVisible:false }; } @@ -193,29 +195,59 @@ class Infos extends Component { }) } + onCancelAvatar=(reset)=>{ + this.setState({ + avatarVisible:false + }) + if(reset){ + const { menuKey } = this.state; + if(menuKey === "2" || menuKey === "3"){ + window.location.reload(); + }else{ + this.fetchUser(); + const { resetUserInfo } = this.props; + resetUserInfo && resetUserInfo(); + } + } + } + render() { const { current_user } = this.props; const { username } = this.props.match.params; - const { user, isSpin, route_type , undo_events , menuKey } = this.state; + const { user, isSpin, route_type , undo_events , menuKey , avatarVisible } = this.state; return (
      + { + avatarVisible && + + }
      - - - - { - user && user.gender===1? - - : - - } +
      + + + + { + user && user.gender===1? + + : + + } + - - + { + current_user && current_user.login && current_user.login === username ? + {this.setState({avatarVisible:true})}}>修改头像 + :"" + } +
      {user && user.username}