From f68842beff7db287cbe39c0557d7009f4149cd1a Mon Sep 17 00:00:00 2001 From: caishi <1149225589@qq.com> Date: Thu, 28 Jan 2021 11:48:21 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E7=BB=87-=E5=9F=BA=E6=9C=AC=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 3 +- src/forge/Component/Cards.jsx | 8 +- src/forge/Component/Component.scss | 4 + src/forge/Component/Search.jsx | 1 + src/forge/Index.js | 8 +- src/forge/Merge/merge_footer.js | 2 +- src/forge/New/Index.js | 68 +++++-- src/forge/Team/Component/UploadImage.jsx | 1 - src/forge/Team/Group/GroupDetailSetting.jsx | 36 +++- .../Team/Group/Setting/GroupMemberSetting.jsx | 9 + .../Group/Setting/GroupProjectSetting.jsx | 9 + src/forge/Team/Index.jsx | 42 ++-- src/forge/Team/Index.scss | 2 + src/forge/Team/List.jsx | 190 ++++++++---------- src/forge/Team/ListItem.jsx | 25 +++ src/forge/Team/New.jsx | 8 +- src/forge/Team/RightBox.jsx | 113 +++++++++++ src/forge/Team/Setting/TeamSettingCommon.jsx | 85 ++++++-- src/forge/Team/Setting/TeamSettingIndex.jsx | 24 +-- src/forge/Team/Sub/Detail.jsx | 70 ++++--- src/forge/Team/Sub/SubDetail.jsx | 23 ++- 21 files changed, 510 insertions(+), 221 deletions(-) create mode 100644 src/forge/Team/Group/Setting/GroupMemberSetting.jsx create mode 100644 src/forge/Team/Group/Setting/GroupProjectSetting.jsx create mode 100644 src/forge/Team/ListItem.jsx create mode 100644 src/forge/Team/RightBox.jsx diff --git a/src/App.js b/src/App.js index 13623f56..789c0af2 100644 --- a/src/App.js +++ b/src/App.js @@ -236,7 +236,6 @@ class App extends Component { path="/register" render={ (props) => { - return () } } @@ -248,7 +247,7 @@ class App extends Component { { - return () + return () } }> diff --git a/src/forge/Component/Cards.jsx b/src/forge/Component/Cards.jsx index f35d890a..f98a834e 100644 --- a/src/forge/Component/Cards.jsx +++ b/src/forge/Component/Cards.jsx @@ -1,10 +1,11 @@ import React from 'react'; +import { getImageUrl } from 'educoder'; import './Component.scss'; -export default (({img , title, desc , rightBtn})=>{ +function Cards({img , title, desc , rightBtn}){ return(
-
+

{title} @@ -16,4 +17,5 @@ export default (({img , title, desc , rightBtn})=>{

) -}) \ No newline at end of file +} +export default Cards; \ No newline at end of file diff --git a/src/forge/Component/Component.scss b/src/forge/Component/Component.scss index 202cdd0d..c846cf2c 100644 --- a/src/forge/Component/Component.scss +++ b/src/forge/Component/Component.scss @@ -27,6 +27,7 @@ li.ant-menu-item{ display: flex; justify-content: center; align-items: center; + overflow: hidden; img{ max-width: 100%; } @@ -39,6 +40,8 @@ li.ant-menu-item{ justify-content: space-between; margin-bottom: 10px!important; align-items: center; + height: 22px; + line-height: 22px;; &>span{ font-size:18px ; color: #333; @@ -50,6 +53,7 @@ li.ant-menu-item{ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; + line-height: 20px; } } } diff --git a/src/forge/Component/Search.jsx b/src/forge/Component/Search.jsx index 676006c5..2f192509 100644 --- a/src/forge/Component/Search.jsx +++ b/src/forge/Component/Search.jsx @@ -5,6 +5,7 @@ const { Search } = Input; export default ({ placeholder , onSearch }) => { return ( import("./users/Infos"), loading: Loading, }); - class Index extends Component { - render() { return (
+ ( + + )} + > ( diff --git a/src/forge/Merge/merge_footer.js b/src/forge/Merge/merge_footer.js index 44043414..6502827c 100644 --- a/src/forge/Merge/merge_footer.js +++ b/src/forge/Merge/merge_footer.js @@ -153,7 +153,7 @@ class MergeFooter extends Component { } { filesData && filesData.files && filesData.files.length>0 && - 文件 {filesCount > 0 && {filesCount}} diff --git a/src/forge/New/Index.js b/src/forge/New/Index.js index 7d7a5ee0..33ea9b0b 100644 --- a/src/forge/New/Index.js +++ b/src/forge/New/Index.js @@ -23,18 +23,22 @@ class Index extends Component { LanguageList: undefined, GitignoreList: undefined, LicensesList: undefined, + OwnerList: undefined, isSpin: false, project_language_id: undefined, project_category_id: undefined, license_id: undefined, ignore_id: undefined, + owners_id:undefined, project_language_list: undefined, project_category_list: undefined, license_list: undefined, ignore_list: undefined, + owners_list:undefined, + project_language_name: undefined, project_category_name: undefined, license_name: undefined, @@ -42,6 +46,8 @@ class Index extends Component { } } componentDidMount = () => { + // 获取拥有者列表 + this.getOwner(); // 获取项目类别 this.getCategory(); // 获取项目语言 @@ -50,7 +56,6 @@ class Index extends Component { this.getGitignore(); // 获取开源许可证 this.getLicenses(); - } componentDidUpdate=(prevPros)=>{ if(prevPros && this.props && !this.props.checkIfLogin()){ @@ -58,6 +63,30 @@ class Index extends Component { return } } + + getOwner=()=>{ + const { OIdentifier } = this.props.match.params; + const url = `/owners.json`; + axios.get(url).then(result=>{ + if(result && result.data){ + let owner = result.data.owners; + if(OIdentifier){ + owner = owner.filter(item=>item.name === OIdentifier); + this.props.form.setFieldsValue({ + user_id:OIdentifier + }) + this.setState({ + owners_id:owner && owner[0].id + }) + } + this.setOptionsList(owner, 'owners'); + this.setState({ + OwnerList: owner, + }) + } + }).catch(error=>{}) + } + getCategory = () => { const url = `/project_categories.json` axios.get(url).then((result) => { @@ -131,8 +160,8 @@ class Index extends Component { isSpin: true }) const { current_user } = this.props; - const { projectsType } = this.props.match.params; - const { project_language_id, project_category_id, license_id, ignore_id } = this.state; + const { projectsType , OIdentifier } = this.props.match.params; + const { project_language_id, project_category_id, license_id, ignore_id , owners_id } = this.state; const decoderPass = Base64.encode(values.password); const url = projectsType === "deposit" ? "/projects.json" : "/projects/migrate.json"; axios.post(url, { @@ -142,7 +171,7 @@ class Index extends Component { project_category_id, license_id, ignore_id, - user_id: current_user.user_id + user_id:owners_id }).then((result) => { if (result) { if (result.data.id) { @@ -150,7 +179,7 @@ class Index extends Component { isSpin: false }) this.props.showNotification(`${projectsType === "deposit" ? "托管" : "镜像"}项目创建成功!`); - this.props.history.push(`/projects/${current_user && current_user.login}/${result.data.identifier}`); + this.props.history.push(`/projects/${OIdentifier ? OIdentifier :(current_user && current_user.login)}/${result.data.identifier}`); } } }).catch((error) => { @@ -230,20 +259,13 @@ class Index extends Component { const { projectsType } = this.props.match.params; const { - preType, - languageValue, - gitignoreType, - LicensesType, - CategoryList, LanguageList, GitignoreList, LicensesList, isSpin, - project_language_name, - project_category_name, - license_name, - ignore_name, + owners_list, + OwnerList, project_language_list, project_category_list, @@ -259,6 +281,24 @@ class Index extends Component {
+ + {getFieldDecorator('user_id', { + rules: [{ + required: true, message: '请选择拥有者' + }], + })( + this.ChangePlatform(value, e, 'owners', OwnerList)} + className="plateAutoComplete" + onBlur={(value) => this.blurCategory(value, OwnerList, "owners")} + > + {owners_list} + + )} + { projectsType !== "deposit" && diff --git a/src/forge/Team/Component/UploadImage.jsx b/src/forge/Team/Component/UploadImage.jsx index 4338a09d..3161f8a1 100644 --- a/src/forge/Team/Component/UploadImage.jsx +++ b/src/forge/Team/Component/UploadImage.jsx @@ -4,7 +4,6 @@ import { getUploadActionUrl } from 'educoder'; function UploadImage({ getImage , url }){ const [ imageUrl , setImageUrl ] = useState(undefined); - useEffect(()=>{ if(url){ setImageUrl(url); diff --git a/src/forge/Team/Group/GroupDetailSetting.jsx b/src/forge/Team/Group/GroupDetailSetting.jsx index 2503370e..8535f623 100644 --- a/src/forge/Team/Group/GroupDetailSetting.jsx +++ b/src/forge/Team/Group/GroupDetailSetting.jsx @@ -11,25 +11,33 @@ const Common = Loadable({ loader: () => import("./SettingCommon"), loading: Loading, }); +const Member = Loadable({ + loader: () => import("./Setting/GroupMemberSetting"), + loading: Loading, +}); +const Project = Loadable({ + loader: () => import("./Setting/GroupProjectSetting"), + loading: Loading, +}); export default (props)=>{ const pathname = props.location.pathname; - const organizeId = props.match.params.organizeId; - const memberId = props.match.params.memberId; + const OIdentifier = props.match.params.OIdentifier; + const groupId = props.match.params.groupId; function returnActive (pathname){ let a = 0; - if(pathname === `/organize/${organizeId}/member/${memberId}/setting/member`){ + if(pathname === `/organize/${OIdentifier}/group/${groupId}/setting/member`){ a = 1; - }else if(pathname === `/organize/${organizeId}/member/${memberId}/setting/project`){ + }else if(pathname === `/organize/${OIdentifier}/group/${groupId}/setting/project`){ a = 2; } return a; } const active = returnActive(pathname); const array = {list:[ - {name:'基本设置',icon:"icon-base",href:`/organize/${organizeId}/member/${memberId}/setting`}, - {name:'团队成员管理',icon:"icon-zuzhichengyuan",href:`/organize/${organizeId}/member/${memberId}/setting/member`}, - {name:'团队项目管理',icon:"icon-zuzhixiangmu",href:`/organize/${organizeId}/member/${memberId}/setting/project`}, + {name:'基本设置',icon:"icon-base",href:`/organize/${OIdentifier}/group/${groupId}/setting`}, + {name:'团队成员管理',icon:"icon-zuzhichengyuan",href:`/organize/${OIdentifier}/group/${groupId}/setting/member`}, + {name:'团队项目管理',icon:"icon-zuzhixiangmu",href:`/organize/${OIdentifier}/group/${groupId}/setting/project`}, ], active } @@ -43,7 +51,19 @@ export default (props)=>{ ( + + )} + > + ( + + )} + > + ( )} diff --git a/src/forge/Team/Group/Setting/GroupMemberSetting.jsx b/src/forge/Team/Group/Setting/GroupMemberSetting.jsx new file mode 100644 index 00000000..98381112 --- /dev/null +++ b/src/forge/Team/Group/Setting/GroupMemberSetting.jsx @@ -0,0 +1,9 @@ +import React , { useState } from 'react'; + + +function GroupMemberSetting() { + return( +
团队成员管理
+ ) +} +export default GroupMemberSetting; \ No newline at end of file diff --git a/src/forge/Team/Group/Setting/GroupProjectSetting.jsx b/src/forge/Team/Group/Setting/GroupProjectSetting.jsx new file mode 100644 index 00000000..18257cc5 --- /dev/null +++ b/src/forge/Team/Group/Setting/GroupProjectSetting.jsx @@ -0,0 +1,9 @@ +import React , { useState } from 'react'; + + +function GroupProjectSetting() { + return( +
团队项目管理
+ ) +} +export default GroupProjectSetting; \ No newline at end of file diff --git a/src/forge/Team/Index.jsx b/src/forge/Team/Index.jsx index 4cd5c9ee..197dbc66 100644 --- a/src/forge/Team/Index.jsx +++ b/src/forge/Team/Index.jsx @@ -3,7 +3,7 @@ import React from 'react'; import { Route, Switch } from "react-router-dom"; import Loadable from "react-loadable"; import Loading from "../../Loading"; - +import { withRouter } from "react-router"; import { SnackbarHOC } from "educoder"; import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC"; import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC"; @@ -23,43 +23,41 @@ const SubDetail = Loadable({ loader: () => import("./Sub/SubDetail"), loading: Loading, }); -export default CNotificationHOC()(SnackbarHOC()(TPMIndexHOC( +export default withRouter(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC( ((props)=>{ return (
- + + {/* 组织团队 */} { - return + path="/organize/:OIdentifier/group" + render={(p) => { + return }} > + {/* 组织成员 */} { - return - }} - > - { - return + path="/organize/:OIdentifier/member" + render={(p) => { + return }} > + {/* 新建组织 */} { - return + render={(p) => { + return }} > + {/* 组织详情(包含组织设置) */} { - return - }} + path="/organize/:OIdentifier" + render={(p) => ( + + )} >
) }) -))) \ No newline at end of file +)))) \ No newline at end of file diff --git a/src/forge/Team/Index.scss b/src/forge/Team/Index.scss index 1cfc0132..3f6be659 100644 --- a/src/forge/Team/Index.scss +++ b/src/forge/Team/Index.scss @@ -47,6 +47,7 @@ background-color: #fff; max-width: 860px; width: 72%; + margin-bottom: 30px; .head{ padding:16px 32px; border-bottom: 1px solid #eee; @@ -102,6 +103,7 @@ max-width: 340px; padding-left: 20px; box-sizing: border-box; + margin-bottom: 30px; } .box{ background:rgba(255,255,255,1); diff --git a/src/forge/Team/List.jsx b/src/forge/Team/List.jsx index 0dfcea9f..7f85a7c5 100644 --- a/src/forge/Team/List.jsx +++ b/src/forge/Team/List.jsx @@ -1,124 +1,110 @@ -import React from 'react'; -import { Button } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; import Search from '../Component/Search'; import Sort from '../Component/Sort'; -import ListCount from '../Component/ListCount'; -import Box from './Box'; import './Index.scss'; -import styled from 'styled-components'; +import Item from './ListItem'; +import Right from './RightBox'; +import NoData from '../Nodata'; -import { Menu } from 'antd'; +import { Menu , Pagination , Dropdown , Spin } from 'antd'; +import axios from 'axios'; -const Span = styled.span`{ - color:#888; - font-size:12px; - margin-right:10px; -}` -const Align = styled.div`{ - display:flex; - aligin:center; -}` -const ListName = styled.div`{ - font-size:14px; - color:#333; - margin-bottom:8px; - height:18px; - line-height:18px; -}`; -const ColorListName = styled.div`{ - color:#5091FF; - font-size:14px; - margin-bottom:8px; - height:18px; - line-height:18px; -}` -const Img = styled.img`{ - border-radius:50%; - width:45px; - height:45px; - margin-right:12px; -}` -export default (()=>{ - function onSearch(value){ +const limit = 15; +function List(props){ + const [ list , setList ] = useState(undefined); + const [ isSpin , setIsSpin ] = useState(false); + const [ totalCount , setTotalCount ] = useState(undefined); + const [ search , setSearch ] = useState(undefined); + const [ page , setPage ] = useState(1); + const [ sortBy , setSortBy ] = useState("updated_on"); + const [ sortDirection , setSortDirection ] = useState("asc"); + const OIdentifier = props.match.params.OIdentifier; + + useEffect(()=>{ + if(OIdentifier){ + setIsSpin(true); + getProject(); + } + },[OIdentifier,sortBy,page,search]) + + function getProject(){ + const url = `/organizations/${OIdentifier}/projects.json`; + axios.get(url,{ + params:{ + search,page,limit, + sort_by:sortBy, + sort_direction:sortDirection + } + }).then(result=>{ + if(result && result.data){ + setList(result.data.projects); + setTotalCount(result.data.total_count); + } + setIsSpin(false); + }).catch(error=>{setIsSpin(false);}) } + + function onSearch(value){ + setSearch(value); + } + const menu=( - + setSortBy(e.key)}> 更新时间排序 - 项目数排序 + 创建时间排序 + 点赞数排序 + fork数排序 ) const menu_new=( - 新建托管项目 - 新建镜像项目 + 新建托管项目 + 新建镜像项目 ) - const leftList = ( -
-

- - react项目react项目react项目react项目 - - - - - -

-
- 用于构建用户界面的 JavaScript 库 -
-
- 更新于1天前 -
-
- ) + return(
-
-
-
- -
-

- - + 新建项目 - - - 排序 - -

-
-
- {leftList} -
-
-
- -
- -
- 陈Professer - 加入时间:2020-04-28 +
+
+
+
+ +
+

+ + + 新建项目 + + + 排序 + +

+ +
+ { + list && list.length>0 ? list.map((item,key)=>{ + return( + + ) + }) + : + + } +
+
- - 新建团队} - > -
-
- 陈Professer - - 2名成员 - 1个仓库 - + { + totalCount > limit && +
+ setPage(page)}/>
-
- -
+ } +
+
) -}) \ No newline at end of file +} +export default List; \ No newline at end of file diff --git a/src/forge/Team/ListItem.jsx b/src/forge/Team/ListItem.jsx new file mode 100644 index 00000000..8dd8fd36 --- /dev/null +++ b/src/forge/Team/ListItem.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import ListCount from '../Component/ListCount'; +import { Link } from 'react-router-dom'; +function ListItem({item,key,OIdentifier}) { + return( +
+

+ + {item.identifier} + + + + + +

+
+ {item.description} +
+
+ 更新于{item.time_ago} +
+
+ ) +} +export default ListItem; \ No newline at end of file diff --git a/src/forge/Team/New.jsx b/src/forge/Team/New.jsx index bf854ab2..098db460 100644 --- a/src/forge/Team/New.jsx +++ b/src/forge/Team/New.jsx @@ -1,14 +1,13 @@ -import React,{ forwardRef , useCallback, useEffect, useState } from 'react'; +import React,{ forwardRef , useCallback , useState } from 'react'; import './Index.scss'; import { Form , Input , Radio , Checkbox , Button, InputNumber } from "antd"; import UploadImage from './Component/UploadImage'; import axios from 'axios'; export default Form.create()( - forwardRef(({form , showNotification ,history})=>{ + forwardRef(({ form , showNotification , history })=>{ const [ image , setImage ] = useState(undefined); - - const { getFieldDecorator, validateFields , setFieldsValue } = form; + const { getFieldDecorator, validateFields } = form; const radioStyle = { display: 'block', @@ -42,6 +41,7 @@ export default Form.create()( }).then(result=>{ if(result && result.data){ showNotification("组织创建成功!"); + history.push(`/organize/${result.data.name}`); } }).catch(error=>{}) } diff --git a/src/forge/Team/RightBox.jsx b/src/forge/Team/RightBox.jsx new file mode 100644 index 00000000..f535fa95 --- /dev/null +++ b/src/forge/Team/RightBox.jsx @@ -0,0 +1,113 @@ +import React , { useEffect , useState } from 'react'; +import { Button } from 'antd'; +import styled from 'styled-components'; +import Box from './Box'; +import axios from 'axios'; +import { getImageUrl } from 'educoder'; + +const Span = styled.span`{ + color:#888; + font-size:12px; + margin-right:10px; +}` +const Align = styled.div`{ + display:flex; + aligin:center; +}` +const ListName = styled.div`{ + font-size:14px; + color:#333; + margin-bottom:8px; + height:18px; + line-height:18px; +}`; +const ColorListName = styled.div`{ + color:#5091FF; + font-size:14px; + margin-bottom:8px; + height:18px; + line-height:18px; +}` +const Img = styled.img`{ + border-radius:50%; + width:45px; + height:45px; + margin-right:12px; +}` +function RightBox({ OIdentifier }) { + const [ memberData, setMemberData ] = useState(undefined); + const [ groupData, setGroupData ] = useState(undefined); + + useEffect(()=>{ + if(OIdentifier){ + getMember(OIdentifier); + getGroup(OIdentifier); + } + },[OIdentifier]) + + function getMember(iden){ + const url = `/organizations/${iden}/organization_users.json`; + axios.get(url).then(result=>{ + if(result && result.data){ + setMemberData(result.data); + } + }).catch(error=>{}) + } + function getGroup(iden){ + const url = `/organizations/${iden}/teams.json`; + axios.get(url).then(result=>{ + if(result && result.data){ + setGroupData(result.data); + } + }).catch(error=>{}) + } + return( +
+ { + memberData && memberData.organization_users && memberData.organization_users.length>0 ? + + { + memberData.organization_users.map((item,key)=>{ + return( +
+ +
+ {item.user && item.user.name} + 加入时间:{item.created_at} +
+
+ ) + }) + } +
+ :"" + } + { + groupData && groupData.teams && groupData.teams.length>0? + 新建团队} + > + { + groupData.teams.map((item,key)=>{ + return( +
+
+ {item.name} + + {item.num_users}名成员 + {item.num_projects}个仓库 + +
+
+ ) + }) + } +
+ :"" + } +
+ ) +} +export default RightBox; \ No newline at end of file diff --git a/src/forge/Team/Setting/TeamSettingCommon.jsx b/src/forge/Team/Setting/TeamSettingCommon.jsx index 1fa3eb64..2e302a37 100644 --- a/src/forge/Team/Setting/TeamSettingCommon.jsx +++ b/src/forge/Team/Setting/TeamSettingCommon.jsx @@ -1,9 +1,11 @@ -import React, { forwardRef , useCallback } from 'react'; -import { Form , Input , Cascader , Radio ,Checkbox , Divider , Button } from 'antd'; +import React, { forwardRef , useCallback , useEffect, useState } from 'react'; +import { Form , Input , Radio ,Checkbox , Divider , Button } from 'antd'; import { WhiteBack , FlexAJ } from '../../Component/layout'; import Title from '../../Component/Title'; import styled from 'styled-components'; -import { locData } from "../../Utils/locData"; +import UploadImage from '../Component/UploadImage'; +import axios from 'axios'; +import { getImageUrl } from 'educoder'; const TextArea = Input.TextArea; const Div = styled.div`{ @@ -15,19 +17,58 @@ const radioStyle = { lineHeight: '30px', }; export default Form.create()( - forwardRef(({ form })=>{ - const { getFieldDecorator } = form; + forwardRef(({ form , organizeDetail , showNotification , history })=>{ + const [ image , setImage ] = useState(undefined); + const [ imageFlag , setImageFlag ] = useState(false); + const [ password , setPassword ] = useState(undefined); + const { getFieldDecorator , validateFields, setFieldsValue} = form; + + useEffect(()=>{ + if(organizeDetail){ + setFieldsValue({ + ...organizeDetail + }) + setImage(organizeDetail.avatar_url); + } + },[organizeDetail]) + const helper = useCallback( - (label, name, rules, widget , isRequired ) => ( + (label, name, rules, widget , isRequired , flag ) => (
{label} - {getFieldDecorator(name, { rules, validateFirst: true })(widget)} + {getFieldDecorator(name, { rules, validateFirst: true , valuePropName:flag ? "checked":"value" })(widget)}
), [] ); + + // 更新 + function updateDetail(){ + validateFields((error,values)=>{ + if(!error){ + const url = `/organizations/${organizeDetail.id}.json`; + axios.patch(url,{ + ...values,image:imageFlag ? image : undefined + }).then(result=>{ + if(result && result.data){ + showNotification("组织信息更新成功!"); + history.push(`/organize/${values.name}/setting`); + } + }).catch(error=>{}) + } + }) + } + function getImage(image){ + setImageFlag(true); + setImage(image); + } + + // 删除组织 + function deleteOrganize(){ + + } return(
@@ -42,46 +83,48 @@ export default Form.create()( )} {helper( "组织描述:", - "desc", + "description", [],