forked from Gitlink/forgeplus-react
merge conflict
This commit is contained in:
commit
fe681a07d9
|
@ -2,14 +2,210 @@
|
|||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="9830c5da-176a-4c72-a301-9f6ce98c82fe" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/AddGroup.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/AddMember.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/Contributors.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/DrawerPanel.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/EAccount.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/EducoderAccount.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/LanguagePower.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Component/Releases.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/Choosen.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/Editors.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/Init.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/List.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/PipelineName.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/Stage.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/StageItem.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/Sure.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/head.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/menus.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/menusAdd.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose/menusRename.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Manage/Params.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Manage/ParamsNew.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Manage/manage.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/Mould.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/MouldNew.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/ServiceModal.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/disposePipeline.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/ssh/Index.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/ssh/XmlPanel.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/DevOps/ssh/mediator.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Head/Footer.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Head/Header.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Head/header.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/CoderDepot.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/CoderDepotCatalogue.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/CoderDepotPath.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/CoderDepotReadme.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/sub/Contribute.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/sub/DetailBanner.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/sub/UpdateDescModal.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Main/sub/sub.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Settings/CollaboratorGroup.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Settings/CollaboratorMember.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Source/AddTag.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Source/Index.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Source/Index.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Source/Upload.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Source/UploadSource.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Component/LeaveTeam.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Component/Modals.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Component/UploadImage.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupForm.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Group/Setting/GroupMemberSetting.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Group/Setting/GroupProjectSetting.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/Group/Setting/setting.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/ListItem.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/RightBox.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/Team/TeamGroupItems.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/forge/users/Service.jsx" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/modules/login/educoder.png" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/edu-purge.css" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/edu-purge.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.css" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.eot" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.eot" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.js" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.json" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.svg" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.svg" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.ttf" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.ttf" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.woff" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.woff" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/public/css/iconfont.woff2" beforeDir="false" afterPath="$PROJECT_DIR$/public/css/iconfont.woff2" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/App.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/AppConfig.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/AppConfig.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/common/UrlTool.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/common/UrlTool.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/common/components/comment/CommentItem.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/common/components/comment/CommentItem.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/About/Index.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/About/Index.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/About/index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/About/index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Activity/ActivityItem.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Activity/ActivityItem.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Branch/Select.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Branch/Select.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Branch/branch.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Branch/branch.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/Cards.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/Cards.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/Component.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/Component.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/Handbook.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/Handbook.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/ListCount.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/ListCount.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/MemberCards.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/MemberCards.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/Search.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/Search.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/SearchUser.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/SearchUser.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/User.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/User.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Component/layout.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/layout.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/About.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/About.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/Dispose.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/Dispose.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/Index.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/Index.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/Infos.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/Infos.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/OpsDetailRightpanel.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/OpsDetailRightpanel.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/Structure.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/Structure.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/ops.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/ops.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/DevOps/opsDetail.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/DevOps/opsDetail.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/GetData/getData.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/GetData/getData.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootBranch.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/CoderRootBranch.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootCommit.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/CoderRootCommit.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootDirectory.js" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootFileDetail.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/CoderRootFileDetail.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootIndex.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/CoderRootIndex.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/CoderRootTag.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/CoderRootTag.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/Detail.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/Detail.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/Diff.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/Diff.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/Index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/Index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/Index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/Index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/IndexItem.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/IndexItem.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Main/list.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/list.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/Commits.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/Commits.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/Files.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/Files.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/MergeItem.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/MergeItem.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/MergeSubmit.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/MergeSubmit.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/MessageCount.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/MessageCount.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/merge.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/merge.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Merge/merge_footer.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Merge/merge_footer.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/New/Index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/New/Index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Newfile/UserSubmitComponent.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Newfile/UserSubmitComponent.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Newfile/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Newfile/index.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Newfile/m_editor.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Newfile/m_editor.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Nodata.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Nodata.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/Detail.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/Detail.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/order_form.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/order_form.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/Milepost.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/Milepost.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/MilepostDetail.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/MilepostDetail.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/order.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/order.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Order/order.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Order/order.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/BranchRule.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/BranchRule.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/Collaborator.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/Collaborator.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/Index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/Index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/Setting.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/Setting.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/new_tags.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/new_tags.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Settings/setting.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Settings/setting.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Box.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Box.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailMember.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailMember.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailProject.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailProject.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailSetting.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetailSetting.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetails.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupDetails.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/GroupNew.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/GroupNew.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Group/SettingCommon.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Group/SettingCommon.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Index.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Index.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/List.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/List.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/New.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/New.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingCommon.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingCommon.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingGroup.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingGroup.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingIndex.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingIndex.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingMember.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Setting/TeamSettingMember.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Sub/Detail.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Sub/Detail.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/Sub/SubDetail.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/Sub/SubDetail.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/TeamGroup.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/TeamGroup.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Team/TeamMember.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Team/TeamMember.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Upload/Index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Upload/Index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/UsersList/common_users.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/UsersList/common_users.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/UsersList/focus_button.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/UsersList/focus_button.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/UsersList/fork_users.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/UsersList/fork_users.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/UsersList/list.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/UsersList/list.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/UsersList/user_list.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/UsersList/user_list.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/Version/version.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Version/version.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/comments/children_comments.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/comments/children_comments.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/comments/children_journals.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/comments/children_journals.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/comments/comments.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/comments/comments.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/css/index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/css/index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/CIList.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/CIList.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/CIdispose.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/CIdispose.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/Index.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/Index.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/Index.scss" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/Index.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/Infos.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/Infos.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/Team-item.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/Team-item.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/Team.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/Team.jsx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/common_lists.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/common_lists.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/new_user.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/new_user.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/forge/users/watch_users.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/users/watch_users.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/indexEduplus2RequestProxy.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/indexEduplus2RequestProxy.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/404/Shixunnopage.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/404/Shixunnopage.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/common/CNotificationHOC.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/common/CNotificationHOC.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/competitions/Competitimain/CompetitionsIndex.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/competitions/Competitimain/CompetitionsIndex.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/competitions/Competitioncommon/CompetitionCommon.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/competitions/Competitioncommon/CompetitionCommon.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/competitions/Competitioncommon/CompetitionCommonChild/CompetitionContentsChart.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/competitions/Competitioncommon/CompetitionCommonChild/CompetitionContentsChart.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/coursesHome/CoursesHome.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/coursesHome/CoursesHome.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/css/Courses.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/css/Courses.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/shixunHomework/Listofworksstudentone.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/shixunHomework/Listofworksstudentone.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/shixunHomework/ShixunWorkReport.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/shixunHomework/ShixunWorkReport.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunWorkModal.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/courses/shixunHomework/Shixunworkdetails/ShixunWorkModal.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/login/EducoderLogin.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/login/EducoderLogin.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/login/LoginDialog.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/login/LoginDialog.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/message/js/Messagerouting.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/message/js/Messagerouting.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/modals/Bottomsubmit.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/modals/Bottomsubmit.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/question/Paperreview.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/question/Paperreview.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/question/Questionitem_banks.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/question/Questionitem_banks.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/testpaper/Intecomponents.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/testpaper/Intecomponents.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/testpaper/Paperlibraryeditid.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/testpaper/Paperlibraryeditid.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/testpaper/Paperlibraryseeid.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/testpaper/Paperlibraryseeid.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/tpm/NewFooter.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/tpm/NewFooter.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/tpm/NewHeader.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/tpm/NewHeader.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/tpm/TPMIndex.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/tpm/TPMIndex.css" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/tpm/TPMIndexHOC.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/tpm/TPMIndexHOC.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/user/usersInfo/InfosProject.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/user/usersInfo/InfosProject.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/modules/user/usersInfo/Projects.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/modules/user/usersInfo/Projects.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/search/SearchPage.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/search/SearchPage.js" afterDir="false" />
|
||||
</list>
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
|
@ -37,7 +233,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainTransfer.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="146">
|
||||
<caret line="94" column="46" lean-forward="true" selection-start-line="94" selection-start-column="20" selection-end-line="94" selection-end-column="46" />
|
||||
<caret line="94" column="46" selection-start-line="94" selection-start-column="20" selection-end-line="94" selection-end-column="46" />
|
||||
<folding>
|
||||
<element signature="e#0#80#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -49,7 +245,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/New/Index.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="274">
|
||||
<caret line="503" column="12" lean-forward="true" selection-start-line="503" selection-start-column="12" selection-end-line="503" selection-end-column="12" />
|
||||
<caret line="546" column="12" selection-start-line="546" selection-start-column="12" selection-end-line="546" selection-end-column="12" />
|
||||
<folding>
|
||||
<element signature="e#0#41#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -57,11 +253,23 @@
|
|||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<file pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/Merge/MessageCount.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="263">
|
||||
<caret line="510" column="35" selection-start-line="510" selection-start-column="35" selection-end-line="510" selection-end-column="35" />
|
||||
<state relative-caret-position="89">
|
||||
<caret line="501" column="35" selection-start-line="501" selection-start-column="35" selection-end-line="501" selection-end-column="35" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/src/AppConfig.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="43">
|
||||
<caret line="30" column="46" selection-start-line="30" selection-start-column="46" selection-end-line="30" selection-end-column="46" />
|
||||
<folding>
|
||||
<element signature="e#0#26#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
|
@ -70,7 +278,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExMenu.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="381">
|
||||
<caret line="33" column="7" lean-forward="true" selection-start-line="33" selection-start-column="7" selection-end-line="33" selection-end-column="7" />
|
||||
<caret line="33" column="7" selection-start-line="33" selection-start-column="7" selection-end-line="33" selection-end-column="7" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -82,7 +290,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExHistory.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="177">
|
||||
<caret line="20" column="4" lean-forward="true" selection-start-line="20" selection-start-column="4" selection-end-line="30" selection-end-column="6" />
|
||||
<caret line="20" column="4" selection-start-line="20" selection-start-column="4" selection-end-line="30" selection-end-column="6" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -94,7 +302,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/Order/order.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="251">
|
||||
<caret line="352" column="13" lean-forward="true" selection-start-line="352" selection-start-column="13" selection-end-line="352" selection-end-column="13" />
|
||||
<caret line="352" column="13" selection-start-line="352" selection-start-column="13" selection-end-line="352" selection-end-column="13" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
|
@ -103,7 +311,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/Order/order_form.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="294">
|
||||
<caret line="176" column="27" lean-forward="true" selection-start-line="176" selection-start-column="27" selection-end-line="176" selection-end-column="27" />
|
||||
<caret line="176" column="27" selection-start-line="176" selection-start-column="27" selection-end-line="176" selection-end-column="27" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
|
@ -112,7 +320,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/Order/Detail.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="320">
|
||||
<caret line="363" column="18" lean-forward="true" selection-start-line="363" selection-start-column="18" selection-end-line="363" selection-end-column="18" />
|
||||
<caret line="363" column="18" selection-start-line="363" selection-start-column="18" selection-end-line="363" selection-end-column="18" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
|
@ -121,7 +329,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExCre.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="124">
|
||||
<caret line="90" column="57" lean-forward="true" selection-start-line="90" selection-start-column="57" selection-end-line="91" selection-end-column="24" />
|
||||
<caret line="90" column="57" selection-start-line="90" selection-start-column="57" selection-end-line="91" selection-end-column="24" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -140,9 +348,6 @@
|
|||
</component>
|
||||
<component name="FindInProjectRecents">
|
||||
<findStrings>
|
||||
<find>projectId</find>
|
||||
<find>params</find>
|
||||
<find>token_num</find>
|
||||
<find>getElementById</find>
|
||||
<find>number</find>
|
||||
<find>this.</find>
|
||||
|
@ -170,6 +375,9 @@
|
|||
<find>menu</find>
|
||||
<find>transfer_login</find>
|
||||
<find>fix_issue_id</find>
|
||||
<find>合并描述</find>
|
||||
<find>initOnlineOfflineListener</find>
|
||||
<find>proxy</find>
|
||||
</findStrings>
|
||||
<replaceStrings>
|
||||
<replace>任务</replace>
|
||||
|
@ -219,7 +427,6 @@
|
|||
<option value="$PROJECT_DIR$/src/forge/Order/New.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Order/UpdateDetail.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Merge/MergeSubmit.js" />
|
||||
<option value="$PROJECT_DIR$/src/AppConfig.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Main/Index.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Blockchain/Trade.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/users/Blockchain.js" />
|
||||
|
@ -242,10 +449,11 @@
|
|||
<option value="$PROJECT_DIR$/src/forge/Order/order_form.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Order/Detail.js" />
|
||||
<option value="$PROJECT_DIR$/src/forge/Merge/MessageCount.js" />
|
||||
<option value="$PROJECT_DIR$/src/AppConfig.js" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds" fullScreen="true">
|
||||
<component name="ProjectFrameBounds" extendedState="6" fullScreen="true">
|
||||
<option name="width" value="1280" />
|
||||
<option name="height" value="800" />
|
||||
</component>
|
||||
|
@ -410,12 +618,12 @@
|
|||
<workItem from="1609305703508" duration="8077000" />
|
||||
<workItem from="1618303807868" duration="8543000" />
|
||||
<workItem from="1618381961400" duration="14929000" />
|
||||
<workItem from="1618715059222" duration="57638000" />
|
||||
<workItem from="1618715059222" duration="58734000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TimeTrackingManager">
|
||||
<option name="totallyTimeSpent" value="295522000" />
|
||||
<option name="totallyTimeSpent" value="296618000" />
|
||||
</component>
|
||||
<component name="TodoView">
|
||||
<todo-panel id="selected-file">
|
||||
|
@ -427,10 +635,10 @@
|
|||
</todo-panel>
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="0" y="0" width="1280" height="800" extended-state="0" />
|
||||
<frame x="0" y="0" width="1280" height="800" extended-state="6" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.24702145" />
|
||||
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.24702145" />
|
||||
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
|
||||
<window_info id="Favorites" order="2" side_tool="true" />
|
||||
<window_info id="npm" order="3" side_tool="true" />
|
||||
|
@ -444,7 +652,7 @@
|
|||
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
|
||||
<window_info anchor="bottom" id="Database Changes" order="8" />
|
||||
<window_info anchor="bottom" id="Version Control" order="9" sideWeight="0.49928468" weight="0.5638554" />
|
||||
<window_info active="true" anchor="bottom" id="Terminal" order="10" sideWeight="0.49928468" visible="true" weight="0.3" />
|
||||
<window_info anchor="bottom" id="Terminal" order="10" sideWeight="0.49928468" visible="true" weight="0.45753425" />
|
||||
<window_info anchor="bottom" id="Event Log" order="11" sideWeight="0.5007153" side_tool="true" weight="0.713253" />
|
||||
<window_info anchor="bottom" id="TypeScript" order="12" weight="0.32876712" />
|
||||
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
|
||||
|
@ -865,16 +1073,6 @@
|
|||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/AppConfig.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="119">
|
||||
<caret line="69" column="6" selection-start-line="69" selection-start-column="6" selection-end-line="69" selection-end-column="6" />
|
||||
<folding>
|
||||
<element signature="e#0#26#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/users/CIList.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="77">
|
||||
|
@ -933,7 +1131,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExCre.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="124">
|
||||
<caret line="90" column="57" lean-forward="true" selection-start-line="90" selection-start-column="57" selection-end-line="91" selection-end-column="24" />
|
||||
<caret line="90" column="57" selection-start-line="90" selection-start-column="57" selection-end-line="91" selection-end-column="24" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -950,7 +1148,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExMenu.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="381">
|
||||
<caret line="33" column="7" lean-forward="true" selection-start-line="33" selection-start-column="7" selection-end-line="33" selection-end-column="7" />
|
||||
<caret line="33" column="7" selection-start-line="33" selection-start-column="7" selection-end-line="33" selection-end-column="7" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -963,7 +1161,7 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainExHistory.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="177">
|
||||
<caret line="20" column="4" lean-forward="true" selection-start-line="20" selection-start-column="4" selection-end-line="30" selection-end-column="6" />
|
||||
<caret line="20" column="4" selection-start-line="20" selection-start-column="4" selection-end-line="30" selection-end-column="6" />
|
||||
<folding>
|
||||
<element signature="e#0#53#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -973,21 +1171,21 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/Order/order_form.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="294">
|
||||
<caret line="176" column="27" lean-forward="true" selection-start-line="176" selection-start-column="27" selection-end-line="176" selection-end-column="27" />
|
||||
<caret line="176" column="27" selection-start-line="176" selection-start-column="27" selection-end-line="176" selection-end-column="27" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/Order/order.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="251">
|
||||
<caret line="352" column="13" lean-forward="true" selection-start-line="352" selection-start-column="13" selection-end-line="352" selection-end-column="13" />
|
||||
<caret line="352" column="13" selection-start-line="352" selection-start-column="13" selection-end-line="352" selection-end-column="13" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/users/BlockchainTransfer.jsx">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="146">
|
||||
<caret line="94" column="46" lean-forward="true" selection-start-line="94" selection-start-column="20" selection-end-line="94" selection-end-column="46" />
|
||||
<caret line="94" column="46" selection-start-line="94" selection-start-column="20" selection-end-line="94" selection-end-column="46" />
|
||||
<folding>
|
||||
<element signature="e#0#80#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -997,14 +1195,14 @@
|
|||
<entry file="file://$PROJECT_DIR$/src/forge/Order/Detail.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="320">
|
||||
<caret line="363" column="18" lean-forward="true" selection-start-line="363" selection-start-column="18" selection-end-line="363" selection-end-column="18" />
|
||||
<caret line="363" column="18" selection-start-line="363" selection-start-column="18" selection-end-line="363" selection-end-column="18" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/New/Index.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="274">
|
||||
<caret line="503" column="12" lean-forward="true" selection-start-line="503" selection-start-column="12" selection-end-line="503" selection-end-column="12" />
|
||||
<caret line="546" column="12" selection-start-line="546" selection-start-column="12" selection-end-line="546" selection-end-column="12" />
|
||||
<folding>
|
||||
<element signature="e#0#41#0" expanded="true" />
|
||||
</folding>
|
||||
|
@ -1020,8 +1218,18 @@
|
|||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/forge/Merge/MessageCount.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="263">
|
||||
<caret line="510" column="35" selection-start-line="510" selection-start-column="35" selection-end-line="510" selection-end-column="35" />
|
||||
<state relative-caret-position="89">
|
||||
<caret line="501" column="35" selection-start-line="501" selection-start-column="35" selection-end-line="501" selection-end-column="35" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/AppConfig.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="43">
|
||||
<caret line="30" column="46" selection-start-line="30" selection-start-column="46" selection-end-line="30" selection-end-column="46" />
|
||||
<folding>
|
||||
<element signature="e#0#26#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "educoder",
|
||||
"name": "forge",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
|
@ -3888,11 +3888,6 @@
|
|||
"randomfill": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/crypto-js/download/crypto-js-4.0.0.tgz",
|
||||
"integrity": "sha1-KQSrJnep0EKFai6i74DekuSjbcw="
|
||||
},
|
||||
"crypto-random-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
|
||||
|
@ -20480,6 +20475,16 @@
|
|||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"xterm": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npm.taobao.org/xterm/download/xterm-4.8.1.tgz",
|
||||
"integrity": "sha1-FVoXKaQ+Gom0BlJOIsVjQznjnKE="
|
||||
},
|
||||
"xterm-addon-fit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npm.taobao.org/xterm-addon-fit/download/xterm-addon-fit-0.4.0.tgz",
|
||||
"integrity": "sha1-BuDF0KaqrPsAnvVl76HIHpPZAZM="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
|
|
|
@ -111,7 +111,9 @@
|
|||
"webpack-dev-server": "^3.10.3",
|
||||
"webpack-manifest-plugin": "^2.2.0",
|
||||
"whatwg-fetch": "2.0.3",
|
||||
"wrap-md-editor": "^0.2.20"
|
||||
"wrap-md-editor": "^0.2.20",
|
||||
"xterm": "4.8.1",
|
||||
"xterm-addon-fit": "0.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node --max_old_space_size=15360 scripts/start.js",
|
||||
|
|
|
@ -2346,7 +2346,6 @@ input::-ms-clear {
|
|||
/*中间部分宽度固定为1200*/
|
||||
.newMain {
|
||||
margin: 0 auto;
|
||||
padding-bottom: 110px;
|
||||
min-width: 1200px;
|
||||
}
|
||||
|
||||
|
@ -4108,21 +4107,6 @@ em.vertical-line {
|
|||
|
||||
/* 右侧内容宽度变化的话,需要调整posi-search right的值*/
|
||||
|
||||
/*底部*/
|
||||
.newFooter {
|
||||
max-height: 110px;
|
||||
}
|
||||
|
||||
.newFooter {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: #323232;
|
||||
clear: both;
|
||||
min-width: 1200px;
|
||||
z-index: 8;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.footercon {
|
||||
border-bottom: 1px solid #47494d;
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 584 KiB After Width: | Height: | Size: 733 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -236,7 +236,6 @@ class App extends Component {
|
|||
path="/register"
|
||||
render={
|
||||
(props) => {
|
||||
|
||||
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +247,7 @@ class App extends Component {
|
|||
<Route path={"/organize"}
|
||||
render={
|
||||
(props) => {
|
||||
return (<OrganizeIndex {...this.props} {...props} {...this.state} />)
|
||||
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
|
||||
}
|
||||
}>
|
||||
</Route>
|
||||
|
|
|
@ -2,19 +2,17 @@ import axios from 'axios';
|
|||
import { requestProxy } from "./indexEduplus2RequestProxy";
|
||||
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
|
||||
import { notification } from 'antd';
|
||||
import cookie from 'react-cookies';
|
||||
import './index.css';
|
||||
let message501 = false;
|
||||
|
||||
import './index.css';
|
||||
|
||||
let message501 = false;
|
||||
broadcastChannelOnmessage('refreshPage', () => {
|
||||
window.location.reload()
|
||||
window.location.reload();
|
||||
})
|
||||
|
||||
function locationurl(list) {
|
||||
if (window.location.port === "3007") {
|
||||
|
||||
} else {
|
||||
window.location.href = list
|
||||
if (window.location.port !== "3007") {
|
||||
window.location.href = list
|
||||
}
|
||||
}
|
||||
// TODO 开发期多个身份切换
|
||||
|
@ -26,55 +24,23 @@ if (isDev) {
|
|||
parsed = queryString.parse(_search);
|
||||
}
|
||||
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
|
||||
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
|
||||
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
|
||||
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
|
||||
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
|
||||
}
|
||||
function clearAllCookie() {
|
||||
cookie.remove('_educoder_session', { path: '/' });
|
||||
cookie.remove('autologin_trustie', { path: '/' });
|
||||
setpostcookie()
|
||||
}
|
||||
clearAllCookie();
|
||||
function setpostcookie() {
|
||||
const str = window.location.pathname;
|
||||
if (str.indexOf("/wxcode") !== -1) {
|
||||
cookie.remove('_educoder_session', { path: '/' });
|
||||
cookie.remove('autologin_trustie', { path: '/' });
|
||||
const _params = window.location.search;
|
||||
if (_params) {
|
||||
let _search = _params.split('?')[1];
|
||||
let _educoder_sessions = _search.split('&')[0].split('=');
|
||||
cookie.save('_educoder_session', _educoder_sessions[1], { domain: '.educoder.net', path: '/' });
|
||||
let autologin_trusties = _search.split('&')[1].split('=');
|
||||
cookie.save('autologin_trustie', autologin_trusties[1], { domain: '.educoder.net', path: '/' });
|
||||
}
|
||||
}
|
||||
}
|
||||
setpostcookie();
|
||||
|
||||
window._debugType = debugType;
|
||||
export function initAxiosInterceptors(props) {
|
||||
initOnlineOfflineListener()
|
||||
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
|
||||
var
|
||||
proxy = "http://localhost:3001";
|
||||
// proxy = "https://testforgeplus.trustie.net";
|
||||
// 判断网络是否连接
|
||||
initOnlineOfflineListener();
|
||||
|
||||
const requestMap = {};
|
||||
window.setfalseInRequestMap = function (keyName) {
|
||||
requestMap[keyName] = false;
|
||||
}
|
||||
// var proxy = "https://testforgeplus.trustie.net";
|
||||
var proxy = "http://localhost:3001";
|
||||
//响应前的设置
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
setpostcookie()
|
||||
clearAllCookie()
|
||||
|
||||
if (config.url.indexOf(proxy) !== -1) {
|
||||
if(config.url.indexOf("http") !== -1) {
|
||||
return config
|
||||
}
|
||||
requestProxy(config)
|
||||
|
||||
requestProxy(config);
|
||||
let url = `/api${config.url}`;
|
||||
|
||||
if (`${config[0]}` !== `true`) {
|
||||
|
@ -88,12 +54,6 @@ export function initAxiosInterceptors(props) {
|
|||
} else {
|
||||
config.url = url;
|
||||
}
|
||||
setpostcookie();
|
||||
}
|
||||
if (config.url.indexOf('update_file') === -1) {
|
||||
requestMap[config.url] = true;
|
||||
|
||||
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
@ -148,8 +108,6 @@ export function initAxiosInterceptors(props) {
|
|||
message501 = false
|
||||
}, 2000);
|
||||
}
|
||||
requestMap[response.config.url] = false;
|
||||
setpostcookie();
|
||||
return response;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
|
|
|
@ -15,7 +15,7 @@ export function getImageUrl(path) {
|
|||
if (isDev) {
|
||||
return `${local}/${path}`
|
||||
}
|
||||
return `/${path}`;
|
||||
return `${path}`;
|
||||
}
|
||||
|
||||
export function getImage(path) {
|
||||
|
@ -162,28 +162,28 @@ export function getmyUrl(geturl) {
|
|||
}
|
||||
|
||||
export function getUploadActionUrl(path, goTest) {
|
||||
return `${getUrl()}/api/attachments.json?debug=${window._debugType || 'admin'}`;
|
||||
return `${getUrl()}/api/attachments.json${isDev ?`${isDev ?`?debug=${window._debugType || 'admin'}` : ""}` : ""}`;
|
||||
}
|
||||
|
||||
export function getUploadLogoActionUrl() {
|
||||
return `${getUrl()}/api/resumes/logo.json?debug=${window._debugType || 'admin'}`;
|
||||
return `${getUrl()}/api/resumes/logo.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`;
|
||||
}
|
||||
|
||||
export function getUploadActionUrltwo(id) {
|
||||
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json?debug=${window._debugType || 'admin'}`
|
||||
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
||||
}
|
||||
|
||||
export function getUploadActionUrlthree() {
|
||||
return `${getUrlmys()}/api/jupyters/import_with_tpm.json?debug=${window._debugType || 'admin'}`
|
||||
return `${getUrlmys()}/api/jupyters/import_with_tpm.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
||||
}
|
||||
|
||||
export function getupload_git_file(id) {
|
||||
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json?debug=${window._debugType || 'admin'}`
|
||||
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
||||
}
|
||||
|
||||
|
||||
export function getUploadActionUrlOfAuth(id) {
|
||||
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json?debug=${window._debugType || 'admin'}`
|
||||
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
|
||||
}
|
||||
|
||||
export function getRandomNumber(type) {
|
||||
|
|
|
@ -64,7 +64,7 @@ function CommentItem({
|
|||
const commentAvatar = (author) => (
|
||||
<img
|
||||
className="item-flex flex-image"
|
||||
src={author.image_url ? getImageUrl(`images/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
|
||||
src={author.image_url ? getImageUrl(`/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -67,15 +67,14 @@ function Index(props){
|
|||
}
|
||||
return(
|
||||
<div className="aboutPanels">
|
||||
|
||||
<div className="aboutContent">
|
||||
<AlignCenterBetween style={{padding:"14px 0px"}}>
|
||||
<AlignCenterBetween style={{padding:"14px 20px"}}>
|
||||
<span className="font-16"><i className="iconfont icon-xiangmujianjie mr5 font-16 color-blue"></i>项目简介</span>
|
||||
{ editOpration && !edit && <a onClick={editContent} className="color-blue">编辑</a> }
|
||||
</AlignCenterBetween>
|
||||
{
|
||||
edit ?
|
||||
<div>
|
||||
<div className="padding20">
|
||||
<MDEditor
|
||||
placeholder={"请输入描述信息"}
|
||||
height={500}
|
||||
|
@ -114,7 +113,7 @@ function Index(props){
|
|||
</div>
|
||||
</div>
|
||||
:
|
||||
<div style={{padding:"20px 0px"}}>
|
||||
<div className="padding20">
|
||||
{content ?
|
||||
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={props.history.location}/>
|
||||
:
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
.aboutContent{
|
||||
border-radius: 2px;
|
||||
border: 1px solid #EEEEEE;
|
||||
padding:0px 30px;
|
||||
width:100%;
|
||||
background-color: #fff;
|
||||
margin-top:20px;
|
||||
|
|
|
@ -33,7 +33,7 @@ class ActivityItem extends Component {
|
|||
}
|
||||
<p className="itemLine mt10">
|
||||
<Link to={`/users/${item && item.user_login}`} className="show-user-link">
|
||||
<img alt="" src={getImageUrl(`images/${item.user_avatar}`)} className="createImage" />
|
||||
<img alt="" src={getImageUrl(`/${item.user_avatar}`)} className="createImage" />
|
||||
<span className="mr20">{item.user_name}</span>
|
||||
</Link>
|
||||
{item.created_at && <span className="color-grey-9">创建于<span className="ml2 color-grey-6">{item.created_at}</span></span>}
|
||||
|
|
|
@ -102,9 +102,10 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
|
|||
</div>
|
||||
);
|
||||
return(
|
||||
<Popover placement="bottom" visible={flag} content={menu} onClick={()=>setFlag(!flag)} overlayClassName="branch-tagBox-list">
|
||||
<Popover placement='bottomLeft' visible={flag} content={menu} onClick={()=>setFlag(!flag)} overlayClassName="branch-tagBox-list">
|
||||
<div className="branch-tagBox">
|
||||
<span className="color-grey-9 mr3 ml8">{nav === 0 ?"分支":"标签"}:</span>
|
||||
{/* {nav === 0 ?"分支":"标签"} */}
|
||||
<span className="color-grey-9 mr3 ml8"><i className="iconfont icon-fenzhi2 font-18"></i></span>
|
||||
<a className="ant-dropdown-link">
|
||||
{showValue}
|
||||
</a>
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
min-width: 240px;
|
||||
min-width: 140px;
|
||||
}
|
||||
.branch-tagBox-list .ant-popover-arrow{
|
||||
display: none;
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { AutoComplete , Button , Icon } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const { Option } = AutoComplete;
|
||||
function AddGroup({organizeId,getGroupID}){
|
||||
const [ id , setID ] = useState(undefined);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ searchKey , setSearchKey ] = useState("");
|
||||
|
||||
useEffect(()=>{
|
||||
getUserList();
|
||||
},[searchKey])
|
||||
|
||||
function getUserList(e){
|
||||
const url = `/organizations/${organizeId}/teams/search.json`;
|
||||
axios.get(url, {
|
||||
params: {
|
||||
search: searchKey,
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
sourceOptions(result.data.teams);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
function sourceOptions(userDataSource){
|
||||
const s = userDataSource && userDataSource.map((item, key) => {
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
value={`${item.id}`}
|
||||
name={item.name}
|
||||
>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
setSource(s);
|
||||
}
|
||||
|
||||
function changeInputUser(e){
|
||||
setSearchKey(e || "");
|
||||
};
|
||||
|
||||
// 选择用户
|
||||
function selectInputUser(e, option){
|
||||
setID(e);
|
||||
setSearchKey(option.props.name);
|
||||
};
|
||||
|
||||
function addCollaborator(){
|
||||
getGroupID && getGroupID(id);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="addPanel">
|
||||
<AutoComplete
|
||||
dataSource={source}
|
||||
value={searchKey}
|
||||
style={{ width: 300 }}
|
||||
onChange={changeInputUser}
|
||||
onSelect={selectInputUser}
|
||||
placeholder="搜索需要添加的团队..."
|
||||
allowClear
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
onClick={addCollaborator}
|
||||
className="ml15"
|
||||
>
|
||||
<Icon type="plus" size="16"></Icon>
|
||||
添加团队
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default AddGroup;
|
|
@ -0,0 +1,95 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { AutoComplete , Button , Icon } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
const { Option } = AutoComplete;
|
||||
function AddMember({getID,login}){
|
||||
const [ id , setID ] = useState(undefined);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
getUserList();
|
||||
},[searchKey])
|
||||
|
||||
function getUserList(e){
|
||||
const url = `/users/list.json`;
|
||||
axios.get(url, {
|
||||
params: {
|
||||
search: searchKey,
|
||||
},
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
sourceOptions(result.data.users);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
function sourceOptions(userDataSource){
|
||||
const s = userDataSource && userDataSource.map((item, key) => {
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
value={`${item.user_id}`}
|
||||
login={`${item.login}`}
|
||||
name={item.username}
|
||||
>
|
||||
<img
|
||||
className="user_img radius"
|
||||
width="28"
|
||||
height="28"
|
||||
src={getImageUrl(`/${item && item.image_url}`)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
||||
{item.username}
|
||||
<span className="color-grey ml10">({item.login})</span>
|
||||
</span>
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
setSource(s);
|
||||
}
|
||||
|
||||
function changeInputUser(e){
|
||||
setSearchKey(e);
|
||||
};
|
||||
|
||||
// 选择用户
|
||||
function selectInputUser(e, option){
|
||||
setID(login ? e : option.props.login);
|
||||
setSearchKey(option.props.name);
|
||||
};
|
||||
|
||||
function addCollaborator(){
|
||||
getID && getID(id);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="addPanel">
|
||||
<AutoComplete
|
||||
dataSource={source}
|
||||
value={searchKey}
|
||||
style={{ width: 300 }}
|
||||
onChange={changeInputUser}
|
||||
onSelect={selectInputUser}
|
||||
placeholder="搜索需要添加的用户..."
|
||||
allowClear
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
ghost
|
||||
onClick={addCollaborator}
|
||||
className="ml15"
|
||||
>
|
||||
<Icon type="plus" size="16"></Icon>
|
||||
添加成员
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default AddMember;
|
|
@ -1,13 +1,15 @@
|
|||
import React from 'react';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './Component.scss';
|
||||
|
||||
export default (({img , title, desc , rightBtn})=>{
|
||||
function Cards({img , title, desc , rightBtn , src}){
|
||||
return(
|
||||
<div className="cards">
|
||||
<div className="img"><img src={img} alt=""/></div>
|
||||
{img &&<div className="img"><img src={getImageUrl(`/${img}`)} alt=""/></div>}
|
||||
<div className="content">
|
||||
<p className="titles">
|
||||
<span>{title}</span>
|
||||
<Link to={src}>{title}</Link>
|
||||
{rightBtn}
|
||||
</p>
|
||||
<div className="desc">
|
||||
|
@ -16,4 +18,5 @@ export default (({img , title, desc , rightBtn})=>{
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
export default Cards;
|
|
@ -19,6 +19,8 @@ li.ant-menu-item{
|
|||
padding:20px 34px;
|
||||
background-color: #fff;
|
||||
margin-bottom:18px;
|
||||
min-height: 130px;
|
||||
border:1px solid #eee;
|
||||
.img{
|
||||
margin-right: 20px;
|
||||
width: 190px;
|
||||
|
@ -27,8 +29,10 @@ li.ant-menu-item{
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
img{
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
.content{
|
||||
|
@ -39,7 +43,9 @@ li.ant-menu-item{
|
|||
justify-content: space-between;
|
||||
margin-bottom: 10px!important;
|
||||
align-items: center;
|
||||
&>span{
|
||||
height: 22px;
|
||||
line-height: 22px;;
|
||||
&>a{
|
||||
font-size:18px ;
|
||||
color: #333;
|
||||
}
|
||||
|
@ -50,11 +56,13 @@ li.ant-menu-item{
|
|||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tabs
|
||||
.tabsStyle{
|
||||
border:1px solid #eee;
|
||||
.ant-tabs-bar.ant-tabs-top-bar{
|
||||
padding-left: 35px;
|
||||
margin-bottom: 0px;
|
||||
|
@ -104,6 +112,9 @@ li.ant-menu-item{
|
|||
right:240px;
|
||||
z-index: 10000;
|
||||
}
|
||||
.laterest{
|
||||
color: #05690d;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1800px){
|
||||
.handleBox{
|
||||
|
@ -129,4 +140,56 @@ li.ant-menu-item{
|
|||
.handleBox{
|
||||
right:0px;
|
||||
}
|
||||
}
|
||||
.ant-drawer{
|
||||
z-index: 10000!important;
|
||||
}
|
||||
.ant-drawer-body{
|
||||
padding:0px!important;
|
||||
.drawerHead{
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding:15px 20px;
|
||||
}
|
||||
.ant-tree{
|
||||
margin:0px 20px!important;
|
||||
}
|
||||
}
|
||||
|
||||
.menuPanels{
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
.ant-popover-content,.ant-popover-inner{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.halfs{
|
||||
margin-top: 24px;
|
||||
padding:24px 0px 0px 0px;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
.attrPerson{
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
}
|
||||
.menuinfos{
|
||||
padding:15px 0px;
|
||||
&>a{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-right: 1px solid #eee;
|
||||
flex: 1;
|
||||
& >span:first-child{
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
}
|
||||
& >span:last-child{
|
||||
color: #666;
|
||||
}
|
||||
&:last-child{
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { AlignCenter , FlexAJ } from '../Component/layout';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Popover , Spin } from 'antd';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import './Component.scss';
|
||||
import { getUser } from '../GetData/getData';
|
||||
import axios from 'axios';
|
||||
|
||||
function Contributors({contributors,owner,projectsId}){
|
||||
const [ menuList ,setMenuList ]= useState([]);
|
||||
const [ list , setList ]= useState(undefined);
|
||||
const [ total , setTotal ]= useState(0);
|
||||
const [ menu , setMenu ] = useState("");
|
||||
const [ login , setLogin ] = useState(undefined);
|
||||
const [ isSpin , setIsSpin ] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
if(contributors && contributors.total_count>0){
|
||||
setTotal(contributors.total_count);
|
||||
setList(contributors.list);
|
||||
}
|
||||
},[contributors])
|
||||
|
||||
useEffect(()=>{
|
||||
if(login){
|
||||
getUsers(login);
|
||||
}else{
|
||||
setMenu(undefined);
|
||||
}
|
||||
},[login])
|
||||
|
||||
async function getUsers(login){
|
||||
setIsSpin(true);
|
||||
let a = menuList && menuList.filter(i=>i.login === login);
|
||||
if(a.length === 0){
|
||||
let result = await getUser(login);
|
||||
let arr = menuList;
|
||||
arr.push({...result});
|
||||
setMenuList(arr);
|
||||
setMenusFunc(result);
|
||||
setIsSpin(false);
|
||||
}else{
|
||||
setMenusFunc(a[0]);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}
|
||||
|
||||
function setMenusFunc(data){
|
||||
if(data){
|
||||
let ele = (
|
||||
<Spin spinning={isSpin}>
|
||||
<FlexAJ>
|
||||
<AlignCenter>
|
||||
<Link to={`/users/${data.login}`}><img src={getImageUrl(`/${data.image_url}`)} alt="" className="radius" width="38px" height="38px"/></Link>
|
||||
<Link to={`/users/${data.login}`} className="ml10">{data.name}</Link>
|
||||
</AlignCenter>
|
||||
{
|
||||
data.is_watch ? <a className="color-grey-9" onClick={()=>FocusFunc(false,data.login)}>取消关注</a>:<a className="color-blue" onClick={()=>FocusFunc(true,data.login)}>关注</a>
|
||||
}
|
||||
</FlexAJ>
|
||||
<AlignCenter className="menuinfos">
|
||||
<a href={data.projects_url}>
|
||||
<span>{data.projects_count}</span>
|
||||
<span>项目数</span>
|
||||
</a>
|
||||
<a href={data.followers_url}>
|
||||
<span>{data.followers_count}</span>
|
||||
<span>粉丝数</span>
|
||||
</a>
|
||||
<a href={data.following_url}>
|
||||
<span>{data.following_count}</span>
|
||||
<span>关注数</span>
|
||||
</a>
|
||||
</AlignCenter>
|
||||
{
|
||||
data.organizations && data.organizations.length > 0 ?
|
||||
<AlignCenter className="font-12 pt4 pb4">
|
||||
<span>所属组织:</span>
|
||||
<div className="task-hide flex1">
|
||||
{renderArray(data.organizations)}
|
||||
</div>
|
||||
</AlignCenter>
|
||||
:""
|
||||
}
|
||||
{
|
||||
data.location && <AlignCenter className="font-12 pt4 pb4"><span>所在地址:</span><span className="ml5">{data.location}</span></AlignCenter>
|
||||
}
|
||||
</Spin>
|
||||
)
|
||||
setMenu(ele);
|
||||
}
|
||||
}
|
||||
|
||||
function FocusFunc(flag,login){
|
||||
axios({
|
||||
method: flag ? 'post' : 'delete',
|
||||
url: `/watchers/${flag ? 'follow' : 'unfollow'}.json`,
|
||||
params: {target_type: "user",id:login}
|
||||
}).then(result => {
|
||||
if (result && (result.data.status === 0 || result.data.status === 2)) {
|
||||
let a = menuList && menuList.filter(i=>i.login === login);
|
||||
if(a){
|
||||
a[0].is_watch = flag;
|
||||
}
|
||||
setMenusFunc(a[0]);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function renderArray(array){
|
||||
let str = "";
|
||||
for(var i = 0;i<array.length;i++){
|
||||
str += array[i].name +"、";
|
||||
}
|
||||
let substr = str.substr(0,str.length-1);
|
||||
return (<span title={substr}>{substr}</span>)
|
||||
}
|
||||
|
||||
function setVisibleFunc(flag,l,index){
|
||||
if(l !== login){
|
||||
setLogin(l);
|
||||
}
|
||||
var lx = list.concat();
|
||||
lx.map(i=>i.visible =false);
|
||||
if(flag){
|
||||
lx[index].visible = flag;
|
||||
}
|
||||
lx.splice();
|
||||
setList(lx);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="halfs">
|
||||
<FlexAJ>
|
||||
<AlignCenter><span className="font-16 color-grey-6">贡献者</span>{ contributors && contributors.total_count > 0 && <span className="infoCount">{contributors.total_count}</span>}</AlignCenter>
|
||||
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/contribute`}>全部</Link>
|
||||
</FlexAJ>
|
||||
<div className="attrPerson" onMouseLeave={()=>setVisibleFunc(false)}>
|
||||
{
|
||||
total > 0 ?
|
||||
list.map((item,key)=>{
|
||||
return(
|
||||
<Popover content={menu} visible={item.visible} overlayClassName="menuPanels" placement="top">
|
||||
<Link key={key} to={`/users/${item.login}`}>
|
||||
<img src={getImageUrl(`/${item.image_url}`)} alt="" onMouseOver={()=>setVisibleFunc(true,item.login,key)}/>
|
||||
</Link>
|
||||
</Popover>
|
||||
)
|
||||
})
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Contributors;
|
|
@ -0,0 +1,99 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Drawer , Tree , Spin } from 'antd';
|
||||
import './Component.scss';
|
||||
import axios from 'axios';
|
||||
const { TreeNode , DirectoryTree } = Tree;
|
||||
|
||||
function DrawerPanel({visible,onClose,branch,owner,projectsId,history, name , list}){
|
||||
const [ treeData , setTreeData ] = useState(undefined);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
const [first , setFirst ] = useState(true);
|
||||
useEffect(()=>{
|
||||
if(visible && first){
|
||||
if(list){
|
||||
setTreeData(list);
|
||||
setIsSpin(false);
|
||||
}else{
|
||||
getMenulist();
|
||||
}
|
||||
setFirst(false);
|
||||
}
|
||||
},[visible])
|
||||
|
||||
function getMenulist(){
|
||||
const url = `/${owner}/${projectsId}/entries.json`;
|
||||
axios.get(url,{ params: { ref: branch } }).then(result=>{
|
||||
if(result){
|
||||
setTreeData(result.data.entries);
|
||||
}
|
||||
setIsSpin(false);
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function renderTreeNodes(data) {
|
||||
return data && data.length > 0 && data.map((item) => {
|
||||
return (
|
||||
<TreeNode title={item.name} key={item.key} dataRef={item} isLeaf={item.type === "file"}>
|
||||
{renderTreeNodes(item.children)}
|
||||
</TreeNode>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function onLoadData(tr){
|
||||
return new Promise((resolve) => {
|
||||
if (tr.props.children) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
let en = [];
|
||||
const url = `/${owner}/${projectsId}/sub_entries.json`;
|
||||
axios.get(url, {
|
||||
params:{
|
||||
filepath:tr.props.dataRef.path,
|
||||
ref:branch,
|
||||
type:"dir"
|
||||
}
|
||||
}).then((result) => {
|
||||
if(result){
|
||||
en = result.data.entries;
|
||||
}
|
||||
}).catch(error=>{})
|
||||
setTimeout(() => {
|
||||
tr.props.dataRef.children = en;
|
||||
setTreeData([...treeData]);
|
||||
resolve();
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function selectTree(keys,event){
|
||||
let dataref = event.node.props.dataRef;
|
||||
if(dataref.type==="file"){
|
||||
onClose();
|
||||
history.push(`/projects/${owner}/${projectsId}/tree/${branch}/${dataref.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<Drawer
|
||||
placement="left"
|
||||
visible={visible}
|
||||
closable={false}
|
||||
onClose={onClose}
|
||||
width={"320px"}
|
||||
maskStyle={{backgroundColor:'rgba(0,0,0,0.09)'}}
|
||||
>
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="drawerHead">
|
||||
<p className="font-20">{name}</p>
|
||||
<p><i class="iconfont icon-fenzhi2 font-18 color-grey-9 mr3"></i>{branch}</p>
|
||||
</div>
|
||||
<DirectoryTree loadData={onLoadData} onSelect={selectTree}>
|
||||
{treeData && renderTreeNodes(treeData)}
|
||||
</DirectoryTree>
|
||||
</Spin>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
export default DrawerPanel;
|
|
@ -0,0 +1,9 @@
|
|||
.ant-modal-mask{
|
||||
z-index: 10000;
|
||||
}
|
||||
.ant-modal-wrap{
|
||||
z-index: 10001;
|
||||
.ant-form-explain{
|
||||
position: absolute;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
import React , {forwardRef, useEffect} from 'react';
|
||||
import { Modal , Form , Input , Button } from 'antd';
|
||||
import './EAccount.scss';
|
||||
|
||||
function EducoderAccount({form , visible , onOk , email}){
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
|
||||
useEffect(()=>{
|
||||
if(email){
|
||||
setFieldsValue({email})
|
||||
}
|
||||
},[email])
|
||||
|
||||
function onSure(){
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
onOk(values);
|
||||
}
|
||||
})
|
||||
}
|
||||
const layout = {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="提示"
|
||||
width="500px"
|
||||
closable={false}
|
||||
footer={
|
||||
<Button type="primary" onClick={onSure}>确定</Button>
|
||||
}
|
||||
centered
|
||||
>
|
||||
<div>
|
||||
<p className="mb15 edu-txt-center" style={{maxWidth:"350px",margin:"0px auto"}}>
|
||||
为确保您能正常使用平台功能,请确认以下信息:
|
||||
</p>
|
||||
<Form {...layout}>
|
||||
<Form.Item label="邮箱">
|
||||
{getFieldDecorator("email",{
|
||||
rules:[{required:true,message:"请输入邮箱账号"}]
|
||||
})(
|
||||
<Input placeholder="请输入您的邮箱账号" width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="密码">
|
||||
{getFieldDecorator("password",{
|
||||
rules:[{required:true,message:"请输入邮箱密码"}]
|
||||
})(
|
||||
<Input.Password placeholder="请输入您的邮箱密码" width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(EducoderAccount));
|
|
@ -5,7 +5,7 @@ import './Component.scss';
|
|||
export default (()=>{
|
||||
return(
|
||||
<div className="handleBox">
|
||||
<a href="https://www.trustie.net/forums/82/memos/3075" target="_blank" >
|
||||
<a href="https://forum.trustie.net/forums/3075/detail" target="_blank" >
|
||||
<img src={Handbook} alt=""/>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
|
||||
function LanguagePower({languages}){
|
||||
const [ array , setArray ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(languages){
|
||||
let arr = [];
|
||||
Object.keys(languages).map((item,key)=>{
|
||||
arr.push({name:item,percent:languages[item],color:getColor()});
|
||||
})
|
||||
setArray(arr);
|
||||
}
|
||||
},[languages])
|
||||
|
||||
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);
|
||||
str+=arr[num];
|
||||
}
|
||||
return str;
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<p className="font-16 color-grey-6">开发语言</p>
|
||||
<div className="progress">
|
||||
{
|
||||
array && array.map((item,key)=>{
|
||||
return(
|
||||
<span style={{width:item.percent,backgroundColor:item.color}}></span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
{
|
||||
array && array.length > 0 &&
|
||||
<FlexAJ className="progresstip">
|
||||
{
|
||||
array.map((item,key)=>{
|
||||
return(
|
||||
<span><i className="zero" style={{backgroundColor:`${item.color}`}}></i><span>{item.name}</span><span>{item.percent}</span></span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</FlexAJ>
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default LanguagePower;
|
|
@ -19,7 +19,7 @@ export default (({fork,parise})=>{
|
|||
}`;
|
||||
const SpanStyleparise = styled.span`{
|
||||
display:flex;
|
||||
align-item:center;
|
||||
align-items:center;
|
||||
margin-left:30px;
|
||||
padding:0px 12px;
|
||||
border-radius:13px;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import React from 'react';
|
||||
import './Component.scss';
|
||||
import { Button } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
import FocusButton from "../UsersList/focus_button";
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Img = styled.img`{
|
||||
border-radius:50%;
|
||||
|
@ -28,26 +32,8 @@ const I = styled.i`{
|
|||
font-size:13px!important;
|
||||
color:#60B25E;
|
||||
margin-right:2px;
|
||||
}`
|
||||
const FocusBtn = styled.a`{
|
||||
display:inline-block;
|
||||
height:30px;
|
||||
line-height:26px;
|
||||
padding:0px 12px;
|
||||
background-color:#fafafa;
|
||||
border:1px solid #eee;
|
||||
border-radius:2px;
|
||||
color:#888!important;
|
||||
}`
|
||||
const Ifocused = styled.i`{
|
||||
font-size:16px!important;
|
||||
color:#FFA802;
|
||||
margin-right:4px;
|
||||
}`
|
||||
const Ifocus = styled.i`{
|
||||
font-size:16px!important;
|
||||
color:#BBBBBB;
|
||||
margin-right:4px;
|
||||
height:17px;
|
||||
line-height:17px;
|
||||
}`
|
||||
const Div = styled.div`{
|
||||
margin-bottom: 18px;
|
||||
|
@ -56,17 +42,18 @@ const Div = styled.div`{
|
|||
align-items: center;
|
||||
border:1px solid #eee;
|
||||
}`
|
||||
export default (({img,name,time, focusStatus})=>{
|
||||
return(
|
||||
export default (({ user , img, name, time, focusStatus, is_current_user, login , successFunc }) => {
|
||||
return (
|
||||
<Div>
|
||||
<Img src={img}/>
|
||||
<Link to={`/users/${user && user.login}`}><Img src={getImageUrl(`/${img}`)} /></Link>
|
||||
<div className="m-infos">
|
||||
<Name>{name}</Name>
|
||||
<Link to={`/users/${user && user.login}`}><Name>{name}</Name></Link>
|
||||
<Time><I className="iconfont icon-shijian"></I>加入时间:{time}</Time>
|
||||
{
|
||||
focusStatus ?
|
||||
<FocusBtn><Ifocused className="iconfont icon-shixing"></Ifocused>已关注</FocusBtn> :
|
||||
<FocusBtn><Ifocus className="iconfont icon-kongxing"></Ifocus>关注</FocusBtn>
|
||||
is_current_user ?
|
||||
<Button type="default">当前用户</Button>
|
||||
:
|
||||
<FocusButton is_watch={focusStatus} id={login} successFunc={successFunc}/>
|
||||
}
|
||||
</div>
|
||||
</Div>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import { AlignCenter , AlignTop , FlexAJ } from '../Component/layout';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function Releases({owner,projectsId,releaseVersions}){
|
||||
|
||||
return(
|
||||
<div>
|
||||
<FlexAJ>
|
||||
<AlignCenter><span className="font-16 color-grey-6">发行版</span>
|
||||
{ releaseVersions && releaseVersions.total_count > 0 && <span className="infoCount">{releaseVersions.total_count}</span>}
|
||||
</AlignCenter>
|
||||
{ releaseVersions && releaseVersions.total_count > 0 ?
|
||||
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/releases`}>全部</Link>
|
||||
:
|
||||
<Link className="font-12 color-blue" to={`/projects/${owner}/${projectsId}/releases/new`}>新建</Link>
|
||||
}
|
||||
</FlexAJ>
|
||||
{
|
||||
releaseVersions && releaseVersions.total_count>0 ?
|
||||
releaseVersions.list.map((item,key)=>{
|
||||
return(
|
||||
key === 0 &&<AlignTop className="mt10">
|
||||
<i className="iconfont icon-biaoqian3 color-grey-6 font-18 mr10"></i>
|
||||
<div>
|
||||
<p className="font-16 color-grey-6">
|
||||
<Link to={`/projects/${owner}/${projectsId}/releases/8/update`}>{item.name}</Link>
|
||||
<span className="font-12 laterest ml5">最新</span>
|
||||
</p>
|
||||
<p className="color-grey-9 font-13">{item.created_at}</p>
|
||||
</div>
|
||||
</AlignTop>
|
||||
)
|
||||
})
|
||||
:""
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Releases;
|
|
@ -2,13 +2,15 @@ import React from "react";
|
|||
import { Input } from "antd";
|
||||
|
||||
const { Search } = Input;
|
||||
export default ({ placeholder , onSearch }) => {
|
||||
export default ({ placeholder , onSearch , onChange }) => {
|
||||
return (
|
||||
<Search
|
||||
allowClear
|
||||
placeholder={placeholder}
|
||||
enterButton={'搜索'}
|
||||
onSearch={onSearch}
|
||||
width="300px"
|
||||
onChange={onChange}
|
||||
></Search>
|
||||
)
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import React , { useState } from 'react';
|
||||
import { AutoComplete } from 'antd';
|
||||
import { getImageUrl } from "educoder";
|
||||
import axios from 'axios';
|
||||
|
||||
const Option = AutoComplete.Option;
|
||||
|
||||
export default ({ getUser })=>{
|
||||
|
@ -31,23 +32,19 @@ export default ({ getUser })=>{
|
|||
}
|
||||
|
||||
function selectInputUser(id, option){
|
||||
setSearchKey(option.props.searchValue);
|
||||
getUserList(option.props.searchValue);
|
||||
setSearchKey(option.props.value);
|
||||
getUserList(option.props.value);
|
||||
getUser && getUser(id);
|
||||
}
|
||||
const source =
|
||||
userDataSource && userDataSource.map((item, key) => {
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
value={`${item.user_id}`}
|
||||
searchValue={`${item.username}`}
|
||||
>
|
||||
<Option key={key} value={`${item.login}`}>
|
||||
<img
|
||||
className="user_img radius"
|
||||
width="28"
|
||||
height="28"
|
||||
src={getImageUrl(`images/${item && item.image_url}`)}
|
||||
src={getImageUrl(`/${item && item.image_url}`)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
export default ({ url , name , column })=>{
|
||||
export default ({ url , name , column , id , login })=>{
|
||||
const Img = styled.span`
|
||||
display:flex;
|
||||
${column && "flex-direction: column;text-align:center;"}
|
||||
|
@ -19,8 +19,16 @@ export default ({ url , name , column })=>{
|
|||
}
|
||||
`;
|
||||
return(
|
||||
id?
|
||||
<Link to={`/users/${login}`}>
|
||||
<Img>
|
||||
{ url && <img src={url} alt=""/> }
|
||||
<span>{name}</span>
|
||||
</Img>
|
||||
</Link>
|
||||
:
|
||||
<Img>
|
||||
<img src={url} alt=""/>
|
||||
{ url && <img src={url} alt=""/> }
|
||||
<span>{name}</span>
|
||||
</Img>
|
||||
)
|
||||
|
|
|
@ -24,18 +24,33 @@ export const AlignCenter = styled.div`{
|
|||
display:flex;
|
||||
align-items: center;
|
||||
}`
|
||||
export const AlignTop = styled.div`{
|
||||
display:flex;
|
||||
align-items: flex-start;
|
||||
}`
|
||||
// 左右结构
|
||||
export const Box = styled.div`{
|
||||
display:flex;
|
||||
align-item:flex-start;
|
||||
align-items:flex-start;
|
||||
}`
|
||||
export const LongWidth = styled.div`{
|
||||
flex:1;
|
||||
width:0;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px;
|
||||
}`
|
||||
export const Long = styled.div`{
|
||||
width:72%;
|
||||
width:78%;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px;
|
||||
}`
|
||||
export const ShortWidth = styled.div`{
|
||||
width:300px;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px;
|
||||
}`
|
||||
export const Short = styled.div`{
|
||||
width:28%;
|
||||
flex:1;
|
||||
border-radius:5px;
|
||||
margin-bottom:30px;
|
||||
}`
|
||||
|
@ -61,22 +76,24 @@ export const Redline = styled.a`{
|
|||
line-height:28px;
|
||||
border-radius:2px;
|
||||
border:1px solid #F73030;
|
||||
color:#F73030;
|
||||
color:${props => (props.bold ? "#fff" : "#F73030")} !important;
|
||||
padding:0px 12px;
|
||||
display:inline-block;
|
||||
min-width:80px;
|
||||
text-align:center;
|
||||
background:${props => (props.bold ? "#F73030" : "#fff")};
|
||||
}`
|
||||
export const Greenline = styled.a`{
|
||||
height:30px;
|
||||
line-height:28px;
|
||||
border-radius:2px;
|
||||
border:1px solid #28BD6C;
|
||||
color:#28BD6C;
|
||||
color:${props => (props.bold ? "#fff" : "#28BD6C")} !important;
|
||||
padding:0px 12px;
|
||||
display:inline-block;
|
||||
min-width:80px;
|
||||
text-align:center;
|
||||
background:${props => (props.bold ? "#28BD6C" : "#fff")};
|
||||
}`
|
||||
export const Greenback = styled.a`{
|
||||
height:30px;
|
||||
|
@ -94,7 +111,7 @@ export const Blueback = styled.a`{
|
|||
line-height:30px;
|
||||
border-radius:2px;
|
||||
background-color:rgba(80,145,255,1);
|
||||
color:#fff;
|
||||
color:#fff!important;
|
||||
padding:0px 12px;
|
||||
display:inline-block;
|
||||
min-width:80px;
|
||||
|
@ -154,3 +171,9 @@ export const Content = styled.div`{
|
|||
background-color:#fff;
|
||||
justify-content: center;
|
||||
}`
|
||||
export const GroupProjectBackgroup = styled.div`{
|
||||
background:#fafafa;
|
||||
padding:20px 30px;
|
||||
width:100%;
|
||||
}`
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React, { forwardRef, useCallback, useState , useEffect } from "react";
|
||||
import activate from "../Images/activate.png";
|
||||
import { Blueback , AlignCenter } from "../Component/layout";
|
||||
import { AlignCenter, Blueback } from "../Component/layout";
|
||||
import PasswordAuthority from "../Component/PasswordAuthority";
|
||||
import styled from "styled-components";
|
||||
import { Form, Input , Spin , Modal } from "antd";
|
||||
import { Form, Input , Spin , Button } from "antd";
|
||||
import ServiceModal from './ServiceModal';
|
||||
import axios from "axios";
|
||||
|
||||
const P = styled.p`
|
||||
|
@ -20,18 +21,22 @@ const P = styled.p`
|
|||
function About(props, ref) {
|
||||
const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props;
|
||||
const [isSpining, setIsSpining] = useState(true);
|
||||
const [ authorityValBox , setAuthorityValBox ] = useState(false);
|
||||
|
||||
// 认证密码
|
||||
const [ authorityVal , setAuthorityVal ] = useState(undefined);
|
||||
const [ authorityValFlag , setAuthorityValFlag ] = useState(false);
|
||||
|
||||
//0: 标识未开启devops
|
||||
//1: 标识用户已填写了云服务器相关信息,但并未开启认证
|
||||
const [step, setStep] = useState(undefined);
|
||||
// step大于1时:为true,不能再修改服务器信息
|
||||
const [step, setStep] = useState(0);
|
||||
|
||||
const owner = props.match.params.owner;
|
||||
const { user } = props;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const [ disabled, setDisabled ] = useState(false);
|
||||
const [ typeFlag, setTypeFlag] = useState(false);
|
||||
|
||||
|
||||
const AuthorLogin = props.author && props.author.login;
|
||||
const AuthorLogin = props.projectDetail && props.projectDetail.author && props.projectDetail.author.login;
|
||||
const CurrentLogin = props.current_user && props.current_user.login;
|
||||
useEffect(()=>{
|
||||
if(CurrentLogin === AuthorLogin){
|
||||
|
@ -47,12 +52,12 @@ function About(props, ref) {
|
|||
method:`${type}`,
|
||||
url
|
||||
}).then(result=>{
|
||||
setIsSpining(false);
|
||||
if(result && result.data ){
|
||||
setIsSpining(false);
|
||||
setStep(result.data.step);
|
||||
setFieldsValue({...result.data.cloud_account});
|
||||
}
|
||||
}).catch(error=>{
|
||||
setIsSpining(false);
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
@ -72,8 +77,8 @@ function About(props, ref) {
|
|||
function goStep() {
|
||||
validateFields((error, values) => {
|
||||
if (!error) {
|
||||
if(step > 0){
|
||||
setAuthorityValBox(true);
|
||||
if(disabled){
|
||||
setStep(1);
|
||||
}else{
|
||||
setIsSpining(true);
|
||||
const url = `/${owner}/${projectsId}/cloud_accounts.json`;
|
||||
|
@ -82,7 +87,6 @@ function About(props, ref) {
|
|||
if (result && result.data.redirect_url) {
|
||||
props.showNotification("服务器信息配置完成!");
|
||||
setStep(1);
|
||||
setAuthorityValBox(true);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -94,6 +98,50 @@ function About(props, ref) {
|
|||
});
|
||||
}
|
||||
|
||||
// 选择服务器后调用接口,点击下一步(1是自有服务器,2是trustie服务器)
|
||||
function sureModal(type){
|
||||
if(type === 2){
|
||||
setTypeFlag(false);
|
||||
setIsSpining(true);
|
||||
const url = `/users/ci/cloud_account/trustie_bind.json`;
|
||||
axios.post(url,{
|
||||
account:user && user.login
|
||||
}).then(result=>{
|
||||
setIsSpining(false);
|
||||
if(result && result.data){
|
||||
setStep(result.data.step);
|
||||
}
|
||||
}).catch(error=>{setIsSpining(false)})
|
||||
}else{
|
||||
setTypeFlag(true);
|
||||
}
|
||||
}
|
||||
|
||||
// 上一步
|
||||
function goUpStep(step){
|
||||
setTypeFlag(false);
|
||||
setStep(step);
|
||||
}
|
||||
|
||||
// 输入用户密码后下一步
|
||||
function authStep(){
|
||||
if(!authorityVal){
|
||||
setAuthorityValFlag(true);
|
||||
return;
|
||||
}
|
||||
setAuthorityValFlag(false);
|
||||
setIsSpining(true);
|
||||
const url = `/users/ci/oauth_grant.json`;
|
||||
axios.get(url,{
|
||||
params:{password:authorityVal}
|
||||
}).then(result=>{
|
||||
setIsSpining(false);
|
||||
if(result){
|
||||
setStep(result.data.step);
|
||||
}
|
||||
}).catch(error=>{setIsSpining(false)});
|
||||
}
|
||||
|
||||
// 开始激活
|
||||
function startActive(){
|
||||
setIsSpining(true);
|
||||
|
@ -111,73 +159,89 @@ function About(props, ref) {
|
|||
setIsSpining(false);
|
||||
})
|
||||
}
|
||||
// 取消授权-登录密码的输入
|
||||
function cancelAuthority(){
|
||||
setAuthorityValBox(false);
|
||||
}
|
||||
// 确认授权
|
||||
function okAuthority(step){
|
||||
setAuthorityValBox(false);
|
||||
setStep(step);
|
||||
}
|
||||
|
||||
return (
|
||||
<Spin spinning={isSpining}>
|
||||
<PasswordAuthority authorityValBox={authorityValBox} successFunc={okAuthority} cancelFunc={cancelAuthority}></PasswordAuthority>
|
||||
{/* <PasswordAuthority authorityValBox={authorityValBox} successFunc={okAuthority} cancelFunc={cancelAuthority}></PasswordAuthority> */}
|
||||
<div className="activatePanel">
|
||||
<img src={activate} alt="" width="250px" />
|
||||
<P>定义DevOps工作流,帮助您检测bug、发布代码…</P>
|
||||
{
|
||||
CurrentLogin !== AuthorLogin && (step === undefined || (step && step < 1)) &&
|
||||
<div className="noOperation">DevOps开启功能暂未对项目创建者以外的角色开放,可以联系项目创建者进行开启,开启后便可查看构建信息。</div>
|
||||
CurrentLogin !== AuthorLogin ?
|
||||
<div className="noOperation">DevOps开启功能暂未对项目创建者以外的角色开放,可以联系项目创建者进行开启,开启后便可查看构建信息。</div>:""
|
||||
}
|
||||
<a href={"https://forum.trustie.net/forums/3080/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
||||
<a href={"https://forum.trustie.net/forums/3110/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
||||
了解什么是DevOps?
|
||||
</a>
|
||||
<a href={"https://forum.trustie.net/forums/3110/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
||||
<a href={"https://forum.trustie.net/forums/3080/detail"} target="_blank" style={{ color: "#5091FF"}}>
|
||||
如何使用DevOps?
|
||||
</a>
|
||||
{
|
||||
AuthorLogin === CurrentLogin ?
|
||||
<React.Fragment>
|
||||
{ step <=1 ?
|
||||
<React.Fragment>
|
||||
<Input.Password style={{display:'none'}} size="large" />
|
||||
<Form style={{marginTop:"20px"}}>
|
||||
<p className="mb20" style={{width:"370px"}}>请仔细核对您的服务器信息,一旦确认提交将无法修改</p>
|
||||
{helper(
|
||||
"服务器IP地址:",
|
||||
"ip",
|
||||
[{ required: true, message: "请输入服务器IP地址" }],
|
||||
<Input
|
||||
placeholder="请输入服务器IP地址"
|
||||
style={{ width: "368px" }}
|
||||
size="large"
|
||||
disabled={step > 0}
|
||||
/>,
|
||||
true
|
||||
)}
|
||||
{helper(
|
||||
"服务器用户名:",
|
||||
"account",
|
||||
[{ required: true, message: "请输入服务器用户名" }],
|
||||
<Input placeholder="请输入服务器用户名" size="large" disabled={step > 0}/>,
|
||||
true
|
||||
)}
|
||||
{helper(
|
||||
"服务器密码:",
|
||||
"secret",
|
||||
[{ required: true, message: "请输入服务器密码" }],
|
||||
<Input.Password placeholder="请输入服务器密码" size="large" disabled={step > 0}/>,
|
||||
true
|
||||
)}
|
||||
</Form>
|
||||
<Blueback onClick={goStep}>下一步</Blueback>
|
||||
</React.Fragment>
|
||||
:""}
|
||||
{step > 1 &&
|
||||
{
|
||||
step === 0 && !typeFlag ?
|
||||
<ServiceModal sureModal={sureModal}></ServiceModal>
|
||||
:""
|
||||
}
|
||||
{ step === 0 && typeFlag ?
|
||||
<React.Fragment>
|
||||
<Input.Password style={{display:'none'}} size="large" />
|
||||
<Form style={{marginTop:"20px"}}>
|
||||
<p className="mb20" style={{width:"370px"}}>请仔细核对您的服务器信息,一旦确认提交将无法修改</p>
|
||||
{helper(
|
||||
"服务器IP地址:",
|
||||
"ip",
|
||||
[{ required: true, message: "请输入服务器IP地址" }],
|
||||
<Input
|
||||
placeholder="请输入服务器IP地址"
|
||||
style={{ width: "368px" }}
|
||||
size="large"
|
||||
disabled={disabled}
|
||||
/>,
|
||||
true
|
||||
)}
|
||||
{helper(
|
||||
"服务器用户名:",
|
||||
"account",
|
||||
[{ required: true, message: "请输入服务器用户名" }],
|
||||
<Input placeholder="请输入服务器用户名" size="large" disabled={disabled}/>,
|
||||
true
|
||||
)}
|
||||
{helper(
|
||||
"服务器密码:",
|
||||
"secret",
|
||||
[{ required: true, message: "请输入服务器密码" }],
|
||||
<Input.Password placeholder="请输入服务器密码" size="large" disabled={disabled}/>,
|
||||
true
|
||||
)}
|
||||
</Form>
|
||||
<AlignCenter>
|
||||
{ !disabled && <Button className="mr20" onClick={()=>goUpStep(0)}>上一步</Button>}
|
||||
<Blueback onClick={goStep}>下一步</Blueback>
|
||||
</AlignCenter>
|
||||
</React.Fragment>
|
||||
:""
|
||||
}
|
||||
{
|
||||
step === 1 ?
|
||||
<div>
|
||||
<AlignCenter style={{justifyContent:"center",marginTop:"20px"}}>
|
||||
<span style={{marginBottom:"42px"}}>密码:</span>
|
||||
<div>
|
||||
<Input.Password value={authorityVal} className={authorityValFlag===true && "flags"} onChange={(e)=>setAuthorityVal(e.target.value)} style={{width:"220px"}}></Input.Password>
|
||||
<p className="color-grey-9" style={{textAlign:"left",lineHeight:'21px'}}>您已保存相关服务器信息,请输入密码,<br/>确认授权DevOps应用</p>
|
||||
</div>
|
||||
</AlignCenter>
|
||||
<AlignCenter style={{justifyContent:'center'}}>
|
||||
<Blueback onClick={authStep} className="mt20">下一步</Blueback>
|
||||
</AlignCenter>
|
||||
</div>:""
|
||||
}
|
||||
{ step === 2 ?
|
||||
<div style={{textAlign:'center',marginTop:"20px"}}>
|
||||
<Blueback onClick={startActive} className="mt20">开始激活</Blueback>
|
||||
</div>
|
||||
</div>:""
|
||||
}
|
||||
</React.Fragment>
|
||||
:""
|
||||
|
|
|
@ -1,118 +1,166 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import { Spin , Pagination } from 'antd';
|
||||
import { Blueback } from '../Component/layout';
|
||||
import Editor from "react-monaco-editor";
|
||||
import Modals from './DisposeModal';
|
||||
import FileLanguage from '../Component/OpsFileLanguage';
|
||||
import List from './Dispose/List';
|
||||
import Head from './Dispose/head';
|
||||
import axios from 'axios';
|
||||
import PipelineName from './Dispose/PipelineName';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Div = styled.div`{
|
||||
padding:24px 30px;
|
||||
}`;
|
||||
const limit = 15;
|
||||
function Dispose(props){
|
||||
const [ spining , setSpining ] = useState(true);
|
||||
const [ info , setInfo ] = useState('.trustie-pipeline.yml');
|
||||
const [ spining , setSpining ] = useState(true);
|
||||
const [ updateInfo , setUpdateInfo ] = useState(undefined);
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ permission , setPermission ] = useState(undefined);
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ ymlValue , setYmlValue ] = useState("");
|
||||
const [ sha , setSha ] = useState(undefined);
|
||||
const [ fileLanguage , setFileLanguage ] = useState(undefined);
|
||||
const [ first , setFirst ] = useState(false);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ totalCount , setTotalCount ] = useState(0);
|
||||
const [ branchList , setBranchList ] =useState(undefined);
|
||||
|
||||
|
||||
const projectDetail = props.projectDetail;
|
||||
const current_user = props.current_user;
|
||||
let projectsId = props.match.params.projectsId;
|
||||
let owner = props.match.params.owner;
|
||||
|
||||
// 获取用户的身份角色
|
||||
useEffect(()=>{
|
||||
if(projectsId){
|
||||
const url = `/${owner}/${projectsId}/get_trustie_pipeline.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
project_id:projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data.content){
|
||||
setInfo(result.data.name);
|
||||
setYmlValue(result.data.content);
|
||||
setFirst(true);
|
||||
setSha(result.data.sha);
|
||||
}else{
|
||||
setFirst(false);
|
||||
}
|
||||
setSpining(false);
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
if(projectDetail){
|
||||
setPermission(props.projectDetail.permission);
|
||||
}
|
||||
},[projectsId])
|
||||
},[projectDetail])
|
||||
|
||||
// 修改文件内容
|
||||
function changeEditor(value){
|
||||
setYmlValue(value);
|
||||
function Init(){
|
||||
const url = `/ci/pipelines/list.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
identifier:projectsId,owner,
|
||||
page,limit
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setList(result.data.pipelines);
|
||||
}
|
||||
setSpining(false);
|
||||
}).catch(error=>{setSpining(false);})
|
||||
}
|
||||
|
||||
// 切换语言
|
||||
function select_language(value,array){
|
||||
setFileLanguage(value);
|
||||
setYmlValue( array && array.content);
|
||||
}
|
||||
useEffect(()=>{
|
||||
Init();
|
||||
},[page])
|
||||
|
||||
// 确定提交
|
||||
function submit(){
|
||||
let url = '';
|
||||
const { defaultBranch } = props;
|
||||
let params = {
|
||||
branch: defaultBranch,
|
||||
content:ymlValue,
|
||||
filepath:info,
|
||||
message:''
|
||||
}
|
||||
if(first){
|
||||
// 为true,则是编辑否则是新建
|
||||
url = `/${owner}/${projectsId}/update_trustie_pipeline.json`;
|
||||
axios.put(url,{
|
||||
...params,
|
||||
sha
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setVisible(true);
|
||||
useEffect(()=>{
|
||||
if(owner && projectsId){
|
||||
const url = `/${owner}/${projectsId}/branches.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setBranchList(result.data);
|
||||
}
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
}).catch(error=>{})
|
||||
}
|
||||
},[owner,projectsId])
|
||||
|
||||
// 新增/编辑流水线
|
||||
function addNew(pipeline_name,id,branch,event){
|
||||
setVisible(true);
|
||||
setUpdateInfo(undefined);
|
||||
if(pipeline_name){
|
||||
let eventA = event.split(",");
|
||||
let l = {pipeline_name,id,branch,event:eventA}
|
||||
setUpdateInfo(l);
|
||||
}else{
|
||||
url = `/${owner}/${projectsId}/create_file.json`;
|
||||
axios.post(url,params).then(result=>{
|
||||
if(result){
|
||||
setVisible(true);
|
||||
}
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
})
|
||||
setUpdateInfo(undefined);
|
||||
}
|
||||
}
|
||||
function suresubmit(){
|
||||
setVisible(false);
|
||||
props.history.push(`/projects/${owner}/${projectsId}/devops/list`);
|
||||
|
||||
function onOk(pipeline_name,updateId,branch,event){
|
||||
if(pipeline_name){
|
||||
let eventStr = "";
|
||||
for(var i = 0;i<event.length;i++){
|
||||
eventStr +=event[i]+",";
|
||||
}
|
||||
eventStr = eventStr.substring(0,eventStr.length-1);
|
||||
if(!updateId){
|
||||
// 新增
|
||||
const url = `/ci/pipelines.json`;
|
||||
axios.post(url,{
|
||||
pipeline_name,
|
||||
file_name:".trustie-pipeline.yml",
|
||||
repo:projectsId,branch,event:eventStr,owner
|
||||
}).then(result=>{
|
||||
setVisible(false);
|
||||
if(result && result.data){
|
||||
props.showNotification("流水线新增成功,请进行工作流配置!");
|
||||
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose/${result.data.id}`);
|
||||
}else{
|
||||
props.showNotification("流水线新增失败,请稍后再试!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
// 修改
|
||||
const url = `/ci/pipelines/${updateId}.json`;
|
||||
axios.put(url,{
|
||||
pipeline_name,repo:projectsId,branch,event:eventStr,owner
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setVisible(false);
|
||||
Init();
|
||||
props.showNotification("流水线名称更新成功!");
|
||||
}else{
|
||||
props.showNotification("流水线名称更新失败,请稍后再试!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}else{
|
||||
props.showNotification("请输入流水线名称!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 删除
|
||||
function deleteFunc(id){
|
||||
const url = `/ci/pipelines/${id}.json`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result && result.data){
|
||||
props.showNotification("流水线删除成功!");
|
||||
Init();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 模板管理
|
||||
function toModalManage(){
|
||||
props.history.push(`/projects/${owner}/${projectsId}/devops/mould`);
|
||||
}
|
||||
|
||||
// 参数管理
|
||||
function toparameter(){
|
||||
props.history.push(`/projects/${owner}/${projectsId}/devops/params`);
|
||||
}
|
||||
|
||||
const operate = current_user && (permission && permission !== "Reporter");
|
||||
return(
|
||||
<Spin spinning={spining}>
|
||||
<Modals visible={visible} closeFunc={(flag)=>setVisible(flag)} sureFunc={suresubmit}></Modals>
|
||||
<p>编程语言:</p>
|
||||
|
||||
<div className="mt20 mb20">
|
||||
<FileLanguage language={fileLanguage} select_language={select_language}/>
|
||||
<PipelineName branchList={branchList} visible={visible} value={updateInfo} onCancel={()=>setVisible(false)} onOk={onOk}/>
|
||||
<div className="disposePanel">
|
||||
<Head manager={ operate ? toModalManage : undefined} parameter={operate ? toparameter :undefined}/>
|
||||
<Div>
|
||||
{ operate && <Blueback onClick={()=>addNew(undefined,undefined)}>新增流水线</Blueback> }
|
||||
<div className="mt20 disposeList">
|
||||
<List list={list} operate={operate} projectsId={projectsId} owner={owner} showModal={addNew} deleteFunc={deleteFunc}/>
|
||||
{
|
||||
totalCount > limit &&
|
||||
<div className="mt20 pb20" style={{textAlign:'center'}}>
|
||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Div>
|
||||
</div>
|
||||
<p>配置脚本:</p>
|
||||
<div className="editorBody">
|
||||
<p className="editorHead">{info}</p>
|
||||
<Editor
|
||||
height="300px"
|
||||
language={"yml"}
|
||||
theme={"vs-grey"}
|
||||
defaultValue="请输入内容"
|
||||
value={ymlValue}
|
||||
options={"editor_options"}
|
||||
onChange={changeEditor}
|
||||
></Editor>
|
||||
</div>
|
||||
<Blueback onClick={submit}>确定提交</Blueback>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
|
||||
function Choosen({ chooseFunc, temp , templateId , category }){
|
||||
const [ tempId, setTemId ] = useState(undefined);
|
||||
const [ cate , setCate ]= useState(undefined);
|
||||
const [ templates , setTemplates ] = useState(undefined);
|
||||
const [ categories , setCategories ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(templateId){
|
||||
setTemId(templateId);
|
||||
}
|
||||
},[templateId])
|
||||
|
||||
useEffect(()=>{
|
||||
if(category){
|
||||
setCate(category);
|
||||
}
|
||||
},[category])
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(temp && temp.length > 0){
|
||||
if(temp[0].category !== "初始化"){
|
||||
setCategories(temp);
|
||||
}else{
|
||||
setCategories(undefined);
|
||||
}
|
||||
if(category && temp[0].category !== "初始化" && category !== "初始化"){
|
||||
let c = temp.filter(item=>item.category === category);
|
||||
let t = c && c.length > 0 && c[0].templates;
|
||||
setTemplates(t);
|
||||
setCate(category);
|
||||
}else{
|
||||
setTemplates(temp[0].templates);
|
||||
setCate(temp[0].category);
|
||||
}
|
||||
}else{
|
||||
setTemplates(undefined);
|
||||
setCate(undefined);
|
||||
setCategories(undefined);
|
||||
}
|
||||
},[temp,category])
|
||||
|
||||
// 选择类别
|
||||
function changeCate(cate){
|
||||
setCate(cate);
|
||||
let c = categories && categories.filter(item=>item.category === cate);
|
||||
let t = c && c[0].templates;
|
||||
setTemplates(t);
|
||||
let m_t_id = t && t.length>0 && t[0].id;
|
||||
let m_t_content = t && t.length>0 && t[0].content;
|
||||
setTemId(m_t_id);
|
||||
chooseFunc && chooseFunc(m_t_content,m_t_id,cate);
|
||||
}
|
||||
|
||||
|
||||
// 选择模板
|
||||
function chooseOption(id){
|
||||
let item = templates.filter(item=>item.id === id);
|
||||
let content = item && item.length >0 && item[0].content;
|
||||
chooseFunc && chooseFunc(content,id,cate);
|
||||
setTemId(id);
|
||||
}
|
||||
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
categories && categories.length > 0 &&
|
||||
<div className="choosenList">
|
||||
<span>模板类别:</span>
|
||||
<ul>
|
||||
{
|
||||
categories.map((item,key)=>{
|
||||
return(
|
||||
<li className={cate === item.category ?"active":""} onClick={()=>changeCate(item.category)}>{item.category}</li>)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
templates && templates.length> 0 &&
|
||||
<div className="choosenList">
|
||||
<span>模板选择:</span>
|
||||
<ul>
|
||||
{
|
||||
templates.map((item,key)=>{
|
||||
return(
|
||||
<li className={tempId === item.id ? "active":""} onClick={()=>chooseOption(item.id)}>{item.template_name}</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
export default Choosen;
|
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
import Editor from 'react-monaco-editor';
|
||||
|
||||
function Editors({value,onChange,theme,height,visible,width="100%",Numbers="on"}){
|
||||
const editor_options = {
|
||||
lineNumbers: Numbers,
|
||||
wordWrap: true, //强制换行
|
||||
selectOnLineNumbers: true,
|
||||
lineHeight: 24,
|
||||
renderLineHighlight: "line",
|
||||
revealHorizontalRightPadding: 5,
|
||||
placeholder:"请输入内容",
|
||||
readOnly: visible,
|
||||
cursorStyle: visible ? "underline-thin" : "line",
|
||||
folding: true,
|
||||
foldingStrategy: "indentation", // 代码可分小段折叠
|
||||
automaticLayout: true, // 自适应布局
|
||||
minimap: {
|
||||
// 不要小地图
|
||||
enabled: false,
|
||||
},
|
||||
}
|
||||
return(
|
||||
<Editor
|
||||
height={height}
|
||||
width={width}
|
||||
language={"yaml"}
|
||||
theme={theme}
|
||||
placeholder="请输入内容"
|
||||
value={value}
|
||||
options={editor_options}
|
||||
onChange={(value)=>onChange(value)}
|
||||
disabled={true}
|
||||
></Editor>
|
||||
)
|
||||
}
|
||||
export default Editors;
|
|
@ -0,0 +1,66 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import Choosen from './Choosen';
|
||||
import Editors from './Editors';
|
||||
|
||||
function Init({ datas , templates , saveFunc , saveDatas}){
|
||||
const [ templateId , setTemplateId ] = useState(undefined);
|
||||
const [ ymlValue , setYmlValue ] = useState(undefined);
|
||||
const [ temp , setTemp ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(templates && templates.length > 0){
|
||||
setTemp(templates)
|
||||
}
|
||||
},[templates])
|
||||
|
||||
useEffect(()=>{
|
||||
if(datas && datas.length > 0){
|
||||
setTemplateId(datas[0].template_id);
|
||||
setYmlValue(datas[0].content);
|
||||
}
|
||||
},[datas])
|
||||
|
||||
// 选择模板
|
||||
function chooseFunc(content,id,cate){
|
||||
setTemplateId(id);
|
||||
setYmlValue(content);
|
||||
recieveData(id,content);
|
||||
}
|
||||
|
||||
function recieveData(id,content){
|
||||
let steps = datas;
|
||||
if(datas && datas.length>0){
|
||||
steps[0].content = content || ymlValue;
|
||||
steps[0].template_id = id || templateId ;
|
||||
}else{
|
||||
steps =
|
||||
[{
|
||||
step_name:"初始化",
|
||||
show_index:1,
|
||||
content:content || ymlValue,
|
||||
template_id:id || templateId
|
||||
}]
|
||||
}
|
||||
saveDatas(steps);
|
||||
}
|
||||
|
||||
// 点击下一步时
|
||||
function nextStep(){
|
||||
recieveData();
|
||||
saveFunc(undefined,undefined,undefined,undefined,"next");
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<Choosen chooseFunc={chooseFunc} templateId={templateId} temp={temp}/>
|
||||
<div className="mt15">
|
||||
<Editors value={ymlValue} onChange={setYmlValue} theme={"vs-dark"} height={"400px"}/>
|
||||
</div>
|
||||
<div className="mt20">
|
||||
<Button type={"primary"} onClick={nextStep}>下一步</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Init;
|
|
@ -0,0 +1,131 @@
|
|||
import React from 'react';
|
||||
import { Table , Popconfirm } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// const STATUS = {
|
||||
// running:"运行中",
|
||||
// failure:"未通过",
|
||||
// error:"未通过",
|
||||
// success:"已通过",
|
||||
// killed:"已撤销",
|
||||
// pending:"准备中"
|
||||
// }
|
||||
function renderTableStatus(status) {
|
||||
switch (status) {
|
||||
case "running":
|
||||
return (
|
||||
<span className="statusTag running">
|
||||
<i className="iconfont icon-yunhangzhong"></i>运行中
|
||||
</span>
|
||||
);
|
||||
case "failure": case 'error':
|
||||
return (
|
||||
<span className="statusTag failed">
|
||||
<i className="iconfont icon-weitongguo"></i>未通过
|
||||
</span>
|
||||
);
|
||||
case "success":
|
||||
return (
|
||||
<span className="statusTag pass">
|
||||
<i className="iconfont icon-yitongguo"></i>已通过
|
||||
</span>
|
||||
);
|
||||
case 'killed':
|
||||
return (
|
||||
<span className="statusTag killed">
|
||||
<i className="iconfont icon-weitongguo"></i>已撤销
|
||||
</span>
|
||||
);
|
||||
default :
|
||||
return (
|
||||
<span className="statusTag Preparing">
|
||||
<i className="iconfont icon-zhunbeizhong"></i>准备中
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
function List({ list, operate , projectsId , owner , showModal , deleteFunc }){
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title:"流水线名称",
|
||||
dataIndex:"pipeline_name",
|
||||
key:1,
|
||||
ellipsis:true,
|
||||
render:(txt,item)=>{
|
||||
return(
|
||||
<span onDoubleClick={()=>showModal(txt,item.id,item.branch,item.event)} style={{display:"block",cursor:"pointer"}}>{txt}</span>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title:"文件名称",
|
||||
dataIndex:"file_name",
|
||||
key:1,
|
||||
width:"15%",
|
||||
ellipsis:true,
|
||||
render:(value,item)=>{
|
||||
return(
|
||||
<Link to={`/projects/${owner}/${projectsId}/tree/${item.branch}/${value}`} className="color-blue">{value}</Link>
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title:"触发分支",
|
||||
dataIndex:"branch",
|
||||
key:1,
|
||||
width:"10%",
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"触发事件",
|
||||
dataIndex:"event",
|
||||
key:1,
|
||||
width:"10%",
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"最近构建时间",
|
||||
dataIndex:"last_build_time",
|
||||
key:1,
|
||||
width:"15%",
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"最近构建状态",
|
||||
dataIndex:"pipeline_status",
|
||||
key:1,
|
||||
width:"12%",
|
||||
ellipsis:true,
|
||||
render:(txt)=>{
|
||||
return renderTableStatus(txt)
|
||||
}
|
||||
},
|
||||
{
|
||||
title:"操作",
|
||||
dataIndex:"operation",
|
||||
key:1,
|
||||
width:"21%",
|
||||
render:(txt,item)=>{
|
||||
return(
|
||||
<span>
|
||||
{ operate ?
|
||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose/${item.id}`} className="mr10 color-grey-6">
|
||||
<i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</Link> :""
|
||||
}
|
||||
{ operate ?
|
||||
<Popconfirm title={"确定要删除此流水线?"} onConfirm={()=>deleteFunc(item.id)} okText="确定" cancelText={"取消"}>
|
||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
||||
</Popconfirm>:""
|
||||
}
|
||||
<Link to={`/projects/${owner}/${projectsId}/devops/list/${item.branch}`} className="color-grey-6"><i className="iconfont icon-yunhang font-13 mr3"></i>查看运行记录</Link>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
return(
|
||||
<Table size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
||||
)
|
||||
}
|
||||
export default List;
|
|
@ -0,0 +1,67 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Modal , Input , Select } from 'antd';
|
||||
const { Option }= Select;
|
||||
|
||||
const EVENT = ["push","pull_request","tag","cron","custom","promote","rollback"]
|
||||
function PipelineName({visible,onCancel,onOk,value ,branchList}){
|
||||
const [ name , setName ] = useState(undefined);
|
||||
const [ branchValue , setBranchValue ] = useState(undefined);
|
||||
const [ eventValue , setEventValue ] = useState([EVENT[0]]);
|
||||
|
||||
useEffect(()=>{
|
||||
if(branchList && branchList.length>0){
|
||||
setBranchValue(branchList[0].name);
|
||||
}
|
||||
},[branchList])
|
||||
|
||||
useEffect(()=>{
|
||||
if(value){
|
||||
setName(value.pipeline_name);
|
||||
setBranchValue(value.branch);
|
||||
setEventValue(value.event);
|
||||
}else{
|
||||
setName(undefined);
|
||||
}
|
||||
},[value])
|
||||
|
||||
function onSure(){
|
||||
onOk(name,value && value.id,branchValue,eventValue);
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="流水线名称"
|
||||
width={"500px"}
|
||||
onCancel={onCancel}
|
||||
onOk={onSure}
|
||||
centered={true}
|
||||
>
|
||||
<div className="choosenList">
|
||||
<span>流水线名称:</span>
|
||||
<Input value={name} onChange={(e)=>setName(e.target.value)} placeholder="请输入名称" style={{width:"340px",margin:"6px 0px"}}/>
|
||||
</div>
|
||||
<div className="choosenList mt20">
|
||||
<span>触发条件:</span>
|
||||
<Select value={branchValue} style={{width:"150px"}} onChange={(e)=>setBranchValue(e)}>
|
||||
{
|
||||
branchList && branchList.length>0 && branchList.map((item,key)=>{
|
||||
return(
|
||||
<Option value={item.name} key={key}>{item.name}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
<Select mode="multiple" allowClear value={eventValue} style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{console.log(e);setEventValue(e)}}>
|
||||
{
|
||||
EVENT.map((item,key)=>{
|
||||
return(
|
||||
<Option value={item} key={key}>{item}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default PipelineName;
|
|
@ -0,0 +1,102 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Button , Popconfirm } from 'antd';
|
||||
import { Cancel } from '../../Component/layout';
|
||||
import Item from './StageItem';
|
||||
|
||||
function Stage({
|
||||
templates ,
|
||||
datas ,
|
||||
saveDatas ,
|
||||
saveFunc ,
|
||||
stepName ,
|
||||
deleteStep ,
|
||||
deleteFunc ,
|
||||
deleteFlag
|
||||
}){
|
||||
const [ stepList , setStepList ] = useState(undefined);
|
||||
const [ temp , setTemp ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(templates && templates.length > 0){
|
||||
setTemp(templates);
|
||||
}
|
||||
},[templates])
|
||||
|
||||
useEffect(()=>{
|
||||
if(datas){
|
||||
if(datas.length > 0 && stepList !== datas){
|
||||
setStepList(datas);
|
||||
}else if(datas.length === 0){
|
||||
let list = [];
|
||||
setStepList(list);
|
||||
}
|
||||
}
|
||||
},[datas])
|
||||
|
||||
// 添加步骤
|
||||
function addFunc(){
|
||||
let list = stepList;
|
||||
let length = list ? list.length : 0;
|
||||
let pre = temp && temp.length > 0 && temp[0];
|
||||
let c = pre && pre.category;
|
||||
let child = pre && pre.templates && pre.templates.length > 0 && pre.templates[0];
|
||||
let step =
|
||||
{
|
||||
"category":c,
|
||||
"step_name": stepName+`${length + 1}`,
|
||||
"show_index": length + 1,
|
||||
"content":child.content,
|
||||
"template_id":child.id,
|
||||
"hide":false
|
||||
}
|
||||
list.push(step);
|
||||
saveDatas(list);
|
||||
}
|
||||
|
||||
// 将修改的对应项保存在数组中
|
||||
function saveItems(content,id,cate,key){
|
||||
let item = stepList;
|
||||
item[key].content = content;
|
||||
item[key].template_id = id;
|
||||
item[key].category = cate;
|
||||
saveDatas([...item]);
|
||||
}
|
||||
|
||||
function slideItems(key,hide){
|
||||
let item = stepList;
|
||||
item[key].hide = !hide;
|
||||
setStepList([...item]);
|
||||
saveDatas(item);
|
||||
}
|
||||
|
||||
function deleteItem(id,key){
|
||||
deleteStep(id,key);
|
||||
}
|
||||
// 点击下一步时
|
||||
function nextStep(btn){
|
||||
saveFunc(undefined,undefined,undefined,undefined,btn);
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
{
|
||||
stepList && stepList.length > 0 && stepList.map((item,key)=>{
|
||||
return(
|
||||
<Item item={item} templates={temp} k={key} saveItems={saveItems} slideItems={slideItems} deleteStep={deleteItem}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
<a className="addStageBtn" onClick={addFunc}>+ 添加步骤</a>
|
||||
<div className="mt20">
|
||||
<Button type="primary" onClick={()=>nextStep("last")}>上一步</Button>
|
||||
<Button className="ml20" type="primary" onClick={()=>nextStep("next")}>下一步</Button>
|
||||
{!deleteFlag &&
|
||||
<Popconfirm title={'确定要删除当前阶段吗'} okText="是" cancelText="否" onConfirm={deleteFunc}>
|
||||
<Cancel className="ml20">删除</Cancel>
|
||||
</Popconfirm>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Stage;
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import { FlexAJ } from '../../Component/layout';
|
||||
import Editors from './Editors';
|
||||
import Choosen from './Choosen';
|
||||
import { Popconfirm } from 'antd';
|
||||
|
||||
|
||||
function StageItem({item, templates,saveItems,k, slideItems , deleteStep}){
|
||||
|
||||
function onChangevalue(value){
|
||||
saveItems(value,item.template_id,item.category,k);
|
||||
}
|
||||
// 选择模板
|
||||
function chooseFunc(content,id,cate){
|
||||
saveItems(content,id,cate,k);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="stepsItem">
|
||||
<FlexAJ className="stepsHead">
|
||||
<span>{item.step_name}</span>
|
||||
<span className="color-grey-9">
|
||||
<Popconfirm
|
||||
title={"确定要删除这个步骤吗?"}
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
onConfirm={() => deleteStep(item.id,k)}
|
||||
><a>
|
||||
<i className="iconfont icon-lajitong1 font-14"></i></a>
|
||||
</Popconfirm>
|
||||
<a onClick={()=>slideItems(k,item.hide)}><i className={ (!item.hide || item.hide === false) ? "iconfont icon-sanjiaoxing-down font-14" :"iconfont icon-triangle font-14"}></i></a>
|
||||
</span>
|
||||
</FlexAJ>
|
||||
<div className={(!item.hide || item.hide === false) ? "stepsBody active" : "stepsBody"}>
|
||||
<Choosen chooseFunc={chooseFunc} category={item.category} templateId={item.template_id} temp={templates}/>
|
||||
<Editors value={item.content} onChange={onChangevalue} theme="vs-dark" height="270px" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default StageItem;
|
|
@ -0,0 +1,32 @@
|
|||
import React ,{useEffect , useState} from 'react';
|
||||
import { Button } from 'antd';
|
||||
import Editors from './Editors';
|
||||
|
||||
function Sure({datas , name , saveFunc , sureSubmit , loading}){
|
||||
const [ value , setValue ] = useState(undefined);
|
||||
useEffect(()=>{
|
||||
if(datas && datas.content){
|
||||
setValue(datas.content)
|
||||
}
|
||||
},[datas]);
|
||||
|
||||
function sure(){
|
||||
sureSubmit();
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div style={{padding:"0px 15px 15px 15px"}}>
|
||||
工作流名称:{name}
|
||||
</div>
|
||||
<div className="editorBody" style={{marginTop:"0px"}}>
|
||||
<Editors value={value} theme={"vs-grey"} height={"600px"} visible/>
|
||||
</div>
|
||||
<div className="mt20">
|
||||
<Button type={"primary"} onClick={()=>saveFunc(undefined,undefined,undefined,undefined,"last")}>上一步</Button>
|
||||
{ value && <Button type={"primary"} loading={loading} className="ml20" onClick={sure}>确定提交</Button> }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Sure;
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { AlignCenterBetween , Blueline , FlexAJ } from '../../Component/layout';
|
||||
|
||||
|
||||
function head({manager , parameter}){
|
||||
return(
|
||||
<AlignCenterBetween>
|
||||
<span className="font-20">工作流配置</span>
|
||||
<FlexAJ>
|
||||
{
|
||||
parameter && <Blueline onClick={parameter}>参数管理</Blueline>
|
||||
}
|
||||
{
|
||||
manager && <Blueline style={{marginLeft:"20px"}} onClick={manager}>模板管理</Blueline>
|
||||
}
|
||||
<a href={`https://forum.trustie.net/forums/3111/detail`} target="_blank" className="color-grey-6 ml20"><i className="iconfont icon-tishi1 font-14 mr3"></i>模板使用说明</a>
|
||||
</FlexAJ>
|
||||
</AlignCenterBetween>
|
||||
)
|
||||
}
|
||||
export default head;
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import MenusRename from './menusRename';
|
||||
import MenusAdd from './menusAdd';
|
||||
|
||||
const typeIcon = {
|
||||
init:"icon-initialize",build:"icon-structure",deploy:"icon-arrange",customize:"icon-newStage",confirm:'icon-sure'
|
||||
}
|
||||
|
||||
function Menus({step,changeStep, menuList , renameFunc , checkDatas , addFunc }){
|
||||
|
||||
function InitActive(key,stage_type,stage_id,stage_name){
|
||||
changeStep(key,stage_type,stage_id,stage_name);
|
||||
}
|
||||
|
||||
// 新增阶段
|
||||
function getName(name,index){
|
||||
addFunc && addFunc(name,index);
|
||||
}
|
||||
|
||||
return(
|
||||
<ul className="menus">
|
||||
{
|
||||
menuList && menuList.length > 0 && menuList.map((item,key)=>{
|
||||
return(
|
||||
<React.Fragment key={item.id} >
|
||||
<li onClick={()=>InitActive(item.show_index,item.stage_type,item.id,item.stage_name)} className={item.show_index === step ?"active":""}>
|
||||
<i className={`iconfont ${typeIcon[`${item.stage_type}`]}`}></i>
|
||||
<MenusRename renameFunc={renameFunc} id={item.id} name={item.stage_name} edit={item.stage_type !== "init" && item.stage_type !== "confirm"}/>
|
||||
</li>
|
||||
{ key !== (menuList.length-1) && menuList.length < 7 ?
|
||||
<MenusAdd checkDatas={checkDatas} k={key+2} getName={getName}/>:""
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
export default Menus;
|
|
@ -0,0 +1,55 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Input } from 'antd';
|
||||
|
||||
function menusAdd ({ getName , checkDatas , k }){
|
||||
const [ show, setShow ] = useState(false);
|
||||
const [ value , setValue ] = useState(undefined);
|
||||
const [ index , setIndex ] = useState(undefined);
|
||||
const [ref, setRef ] = useState(undefined);
|
||||
const [put, setPut ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (put && ref) {
|
||||
ref.focus();
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
if (k) {
|
||||
setIndex(k);
|
||||
}
|
||||
},[k])
|
||||
|
||||
|
||||
function blurInput(){
|
||||
if(value){
|
||||
getName(value , index);
|
||||
}
|
||||
setValue(undefined);
|
||||
setShow(false);
|
||||
setPut(false);
|
||||
}
|
||||
|
||||
function showInput(){
|
||||
let c = checkDatas();
|
||||
if(c || c === "" ){
|
||||
setShow(true);
|
||||
setPut(true);
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<li className="menuAdd">
|
||||
{ !show && <i className="iconfont icon-tianjia" onClick={showInput}></i> }
|
||||
<Input
|
||||
ref={(el) => setRef(el)}
|
||||
size={"small"}
|
||||
maxLength={8}
|
||||
style={{width:"75px",display : `${show?"block":'none'}`}}
|
||||
placeholder="新阶段名称"
|
||||
value={value}
|
||||
onChange={(e)=>setValue(e.target.value)} onBlur={blurInput}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default menusAdd;
|
|
@ -0,0 +1,53 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Input } from 'antd';
|
||||
|
||||
function menusRename({ name , edit , id , renameFunc }){
|
||||
const [ n , setN ] = useState(undefined);
|
||||
const [ show , setShow ] = useState(false);
|
||||
const [ref, setRef ] = useState(undefined);
|
||||
const [put, setPut ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (edit && ref) {
|
||||
ref.focus();
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(()=>{
|
||||
if(name){
|
||||
setN(name)
|
||||
}
|
||||
},[name])
|
||||
|
||||
// 显示input框编辑
|
||||
function changeShow(e){
|
||||
e.stopPropagation();
|
||||
setShow(true);
|
||||
setPut(true);
|
||||
}
|
||||
|
||||
function blurInput(e){
|
||||
renameFunc(e.target.value,id);
|
||||
setPut(false);
|
||||
setShow(false);
|
||||
}
|
||||
return(
|
||||
<div className="aboutEdit">
|
||||
<span className="operateName">
|
||||
{ !show && n }
|
||||
<Input
|
||||
ref={(el) => setRef(el)}
|
||||
value={n}
|
||||
size="small"
|
||||
maxLength={8}
|
||||
onClick={(e)=>e.stopPropagation()}
|
||||
onBlur={blurInput}
|
||||
style={{width:"75px",display : `${show?"block":'none'}`}}
|
||||
onChange={(e)=>setN(e.target.value)}
|
||||
/>
|
||||
{ !show && edit && <i className="iconfont icon-editUnder font-16 color-grey-9" onClick={changeShow}></i> }
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default menusRename
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React , { useEffect } from 'react';
|
||||
import { WhiteBack } from '../Component/layout';
|
||||
import './ops.scss';
|
||||
|
||||
|
@ -10,27 +10,65 @@ const About = Loadable({
|
|||
loader: () => import('./About'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Infos = Loadable({
|
||||
loader: () => import('./Infos'),
|
||||
const New = Loadable({
|
||||
loader: () => import('./disposePipeline'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Dispose = Loadable({
|
||||
loader: () => import('./Dispose'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Stucture = Loadable({
|
||||
loader: () => import('./Structure'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Mould = Loadable({
|
||||
loader: () => import('./Mould'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Params = Loadable({
|
||||
loader: () => import('./Manage/Params'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
export default ((props)=>{
|
||||
|
||||
return(
|
||||
<WhiteBack className="opsPanel">
|
||||
<Switch {...props}>
|
||||
<Route path="/projects/:owner/:projectsId/devops/dispose"
|
||||
<Route path="/projects/:owner/:projectsId/devops/dispose/:disposeId"
|
||||
render={
|
||||
() => (<Infos {...props} />)
|
||||
(p) => (<New {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops/list"
|
||||
<Route path="/projects/:owner/:projectsId/devops/params"
|
||||
render={
|
||||
() => (<Infos {...props} />)
|
||||
(p) => (<Params {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops/mould"
|
||||
render={
|
||||
(p) => (<Mould {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops/dispose/new"
|
||||
render={
|
||||
(p) => (<New {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops/dispose"
|
||||
render={
|
||||
(p) => (<Dispose {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops/list/:branch"
|
||||
render={
|
||||
(p) => (<Stucture {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/devops"
|
||||
render={
|
||||
() => (<About {...props} />)
|
||||
(p) => (<About {...props} {...p}/>)
|
||||
}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React , { useEffect , useState , useRef } from 'react';
|
||||
import React , { useEffect , useState } from 'react';
|
||||
import { Banner } from '../Component/layout';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Dispost from './Dispose';
|
||||
import Structure from './Structure';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
@ -11,22 +10,11 @@ const Div = styled.div`{
|
|||
padding:24px 30px;
|
||||
}`;
|
||||
export default ((props)=>{
|
||||
const [ menu , setMenu ] = useState(false);
|
||||
const [ permission , setPermission ] = useState("");
|
||||
const childRef = useRef();
|
||||
|
||||
const path = props.location.pathname;
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
|
||||
const projectDetail = props.projectDetail;
|
||||
useEffect(()=>{
|
||||
if(path === `/projects/${owner}/${projectsId}/devops/list`){
|
||||
setMenu(true);
|
||||
}else{
|
||||
setMenu(false);
|
||||
}
|
||||
},[path])
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
|
@ -34,23 +22,14 @@ export default ((props)=>{
|
|||
}
|
||||
},[projectDetail])
|
||||
|
||||
const updateChildState = () => {
|
||||
// changeVal就是子组件暴露给父组件的方法
|
||||
if (childRef.current) {
|
||||
childRef.current.changeVal();
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="disposePanel">
|
||||
<Banner>
|
||||
{ permission !=="Reporter" && <Link to={`/projects/${owner}/${props.match.params.projectsId}/devops/dispose`} className={menu===false && "color-blue"} style={{ marginRight:"66px"}}>工作流配置</Link>}
|
||||
<Link to={`/projects/${owner}/${props.match.params.projectsId}/devops/list`}className={menu===true && "color-blue"}>构建列表</Link>
|
||||
{ menu===true && <a onClick={updateChildState} style={{float:"right",fontSize:"14px",color:"#FF6E21",marginTop:"5px"}}>刷新</a>}
|
||||
{ permission !=="Reporter" && <Link to={`/projects/${owner}/${props.match.params.projectsId}/devops/dispose`}>工作流配置</Link>}
|
||||
</Banner>
|
||||
<Div>
|
||||
{ menu === true && <Structure {...props} wrappedComponentRef={(form) => childRef.current = form} ref={childRef}/> }
|
||||
{ menu === false && permission !=="Reporter" && <Dispost {...props}/> }
|
||||
<Dispost {...props}/>
|
||||
</Div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
import React , { useEffect , useState , useRef } from 'react';
|
||||
import { Banner , Blueback , FlexAJ , AlignCenter } from '../../Component/layout';
|
||||
import { Input , Table , Popconfirm , Pagination } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import New from './ParamsNew';
|
||||
import "./manage.scss";
|
||||
import axios from 'axios';
|
||||
import { result } from 'lodash';
|
||||
|
||||
const Div = styled.div`{
|
||||
padding:24px 30px;
|
||||
min-height:420px;
|
||||
}`;
|
||||
function Params(props){
|
||||
const [ list ,setList ] = useState(undefined);
|
||||
const [ editList ,setEditList ] = useState(undefined);
|
||||
const [ visible ,setVisible ] = useState(false);
|
||||
|
||||
let projectsId = props.match.params.projectsId;
|
||||
let owner = props.match.params.owner;
|
||||
|
||||
useEffect(()=>{
|
||||
Init()
|
||||
},[])
|
||||
|
||||
function Init(){
|
||||
const url = `/ci/secrets.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner,repo:projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setList(result.data);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
const columns=[
|
||||
{
|
||||
title:"参数名",
|
||||
dataIndex:"name",
|
||||
key:1,
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"操作",
|
||||
dataIndex:"operation",
|
||||
key:4,
|
||||
render:(txt,item)=>{
|
||||
return(
|
||||
<React.Fragment>
|
||||
<a className="mr10 color-grey-6" onClick={()=>editMouldFunc(item)}><i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</a>
|
||||
<Popconfirm title={"确定要删除此模板?"} onConfirm={()=>deleteMouldFunc(item.id,item.name)} okText="确定" cancelText={"取消"}>
|
||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
||||
</Popconfirm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 编辑
|
||||
function editMouldFunc(item){
|
||||
setEditList(item);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
// 删除
|
||||
function deleteMouldFunc(id,name){
|
||||
if(id && name){
|
||||
const url = `/ci/secrets/${id}.json`;
|
||||
axios.delete(url,{
|
||||
params:{owner,repo:projectsId,name}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
Init();
|
||||
props.showNotification(`参数删除成功!`);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
function successFunc(values,id){
|
||||
const url = `/ci/secrets.json?owner=${owner}&repo=${projectsId}`;
|
||||
axios.post(url,{
|
||||
...values,id
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
props.showNotification(`${id ? '参数编辑':"新增参数"}成功!`);
|
||||
Init();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function CancelFunc(){
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<New visble={visible} successFunc={successFunc} CancelFunc={CancelFunc} editList={editList}/>
|
||||
<Banner>
|
||||
<FlexAJ>
|
||||
<span className="font-18">工作流 - 参数管理</span>
|
||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-14 color-grey-9 ml20">返回</Link>
|
||||
</FlexAJ>
|
||||
</Banner>
|
||||
<Div className="disposeList">
|
||||
<div style={{textAlign:"right"}}>
|
||||
<Blueback onClick={()=>setVisible(true)}>新建</Blueback>
|
||||
</div>
|
||||
<Table className="mt20" size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
||||
</Div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Params;
|
|
@ -0,0 +1,73 @@
|
|||
import React , { useEffect , useState , useRef , forwardRef } from 'react';
|
||||
import { Modal , Input , Form } from 'antd';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
function ParamsNew({ form , visble,successFunc,CancelFunc ,editList }){
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const layout = {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(editList && editList.id){
|
||||
setFieldsValue({
|
||||
name:editList.name,
|
||||
data:editList.data
|
||||
})
|
||||
}else{
|
||||
setFieldsValue({
|
||||
name:undefined,
|
||||
data:undefined
|
||||
})
|
||||
}
|
||||
},[editList])
|
||||
|
||||
// 确定
|
||||
function onConfirmFunc(){
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
successFunc(values,editList && editList.id);
|
||||
onCancelFunc();
|
||||
}
|
||||
})
|
||||
}
|
||||
function onCancelFunc(){
|
||||
setFieldsValue({
|
||||
name:undefined,
|
||||
data:undefined
|
||||
})
|
||||
CancelFunc();
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
visible={visble}
|
||||
okText={"确定"}
|
||||
cancelText={"取消"}
|
||||
onCancel={onCancelFunc}
|
||||
onOk={onConfirmFunc}
|
||||
title={"新建"}
|
||||
closable={false}
|
||||
width="500px"
|
||||
>
|
||||
<Form {...layout}>
|
||||
<Form.Item label="参数名称">
|
||||
{getFieldDecorator("name",{
|
||||
rules:[{required:true,message:"请输入参数名称"}]
|
||||
})(
|
||||
<Input placeholder="请输入参数名称" width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="参数值">
|
||||
{getFieldDecorator("data",{
|
||||
rules:[{required:true,message:"请输入参数值"}]
|
||||
})(
|
||||
<TextArea placeholder="请输入参数值" width="220px" autoSize={{ minRows: 4, maxRows: 4 }}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(ParamsNew));
|
|
@ -0,0 +1,4 @@
|
|||
.ant-form-explain{
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
import React , { useEffect , useState , useRef } from 'react';
|
||||
import { Banner , Blueback , FlexAJ } from '../Component/layout';
|
||||
import { Input , Table ,Pagination , Select , Popconfirm } from 'antd';
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components';
|
||||
import axios from 'axios';
|
||||
import New from './MouldNew';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const Div = styled.div`{
|
||||
padding:24px 30px;
|
||||
min-height:420px;
|
||||
}`;
|
||||
const STAGE = [
|
||||
{stage_name:"所有",stage_type:"all"},
|
||||
{stage_name:"初始化",stage_type:"init"},
|
||||
{stage_name:"编译构建",stage_type:"build"},
|
||||
{stage_name:"部署",stage_type:"deploy"},
|
||||
{stage_name:"其他",stage_type:"customize"}
|
||||
]
|
||||
const limit = 15;
|
||||
function Mould(props){
|
||||
const [ visible ,setVisible ] = useState(false);
|
||||
const [ list ,setList ] = useState(undefined);
|
||||
const [ page ,setPage ] = useState(1);
|
||||
const [ totalCount ,setTotalCount ] = useState(0);
|
||||
const [ stageType ,setStageType ] = useState("all");
|
||||
const [ search ,setSearch ] = useState(undefined);
|
||||
const childRef = useRef();
|
||||
let projectsId = props.match.params.projectsId;
|
||||
let owner = props.match.params.owner;
|
||||
useEffect(()=>{
|
||||
Init(page,stageType);
|
||||
},[page,stageType])
|
||||
|
||||
function Init(page,stageType,searchvalue){
|
||||
const url = `/ci/templates/list.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
page,limit,stage_type:stageType,name:searchvalue
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setList(result.data.templates);
|
||||
setTotalCount(result.data.total_count);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
const columns=[
|
||||
{
|
||||
title:"名称",
|
||||
dataIndex:"template_name",
|
||||
key:1,
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"所属阶段",
|
||||
dataIndex:"stage_type",
|
||||
key:2,
|
||||
ellipsis:true,
|
||||
render:(txt,item)=>{
|
||||
let i = STAGE.filter(item=>item.stage_type === txt);
|
||||
return i && i.length>0 && i[0].stage_name
|
||||
}
|
||||
},
|
||||
{
|
||||
title:"模板类型",
|
||||
dataIndex:"category",
|
||||
key:3,
|
||||
ellipsis:true
|
||||
},
|
||||
{
|
||||
title:"操作",
|
||||
dataIndex:"operation",
|
||||
key:4,
|
||||
ellipsis:true,
|
||||
render:(txt,item)=>{
|
||||
return(
|
||||
<span>
|
||||
<a className="mr10 color-grey-6" onClick={()=>editMouldFunc(item)}><i className="iconfont icon-zaibianji font-13 mr3"></i>编辑</a>
|
||||
<Popconfirm title={"确定要删除此模板?"} onConfirm={()=>deleteMouldFunc(item.id)} okText="确定" cancelText={"取消"}>
|
||||
<a className="mr10 color-grey-6"><i className="iconfont icon-lajitong font-13 mr3"></i>删除</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 编辑模板
|
||||
function editMouldFunc(item){
|
||||
if (childRef.current) {
|
||||
childRef.current.setEditInfo(item);
|
||||
}
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
// 删除模板
|
||||
function deleteMouldFunc(id){
|
||||
const url = `/ci/templates/${id}.json`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result && result.data){
|
||||
props.showNotification("模板删除成功!");
|
||||
Init(page,stageType,search);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function searchValue(){
|
||||
Init(page,stageType,search);
|
||||
}
|
||||
|
||||
function newMouldFunc(){
|
||||
if (childRef.current) {
|
||||
childRef.current.setEditInfo(undefined);
|
||||
}
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
function onOk(){
|
||||
Init(page,stageType);
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<New wrappedComponentRef={(f) => childRef.current = f} ref={childRef} visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}></New>
|
||||
<Banner>
|
||||
<FlexAJ><span>工作流 - 模板管理</span><Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-14 color-grey-9">返回</Link></FlexAJ>
|
||||
</Banner>
|
||||
<Div className="disposeList">
|
||||
<FlexAJ>
|
||||
<Blueback onClick={newMouldFunc}>新建模板</Blueback>
|
||||
<FlexAJ>
|
||||
<span className="mr10">阶段:</span>
|
||||
<Select onChange={e=>setStageType(e)} value={stageType} style={{width:"180px"}}>
|
||||
{
|
||||
STAGE.map((item,key)=>{
|
||||
return(
|
||||
<Option value={item.stage_type}>{item.stage_name}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>
|
||||
<Input placeholder="请输入模板名称" value={search} onChange={(e)=>setSearch(e.target.value)} allowClear style={{width:"160px",marginLeft:"15px"}}/>
|
||||
<Blueback className="ml15" onClick={searchValue}>搜索</Blueback>
|
||||
</FlexAJ>
|
||||
</FlexAJ>
|
||||
<Table className="mt20" size="small" columns={columns} dataSource={list} rowKey={(row)=>row.id} pagination={false}></Table>
|
||||
{
|
||||
totalCount > limit &&
|
||||
<div className="mt20 pb20" style={{textAlign:'center'}}>
|
||||
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
|
||||
</div>
|
||||
}
|
||||
</Div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Mould;
|
|
@ -0,0 +1,166 @@
|
|||
import React , { useImperativeHandle , useState , forwardRef , useCallback } from 'react';
|
||||
import { Form , Modal , Input , Select , Spin } from 'antd';
|
||||
import Editor from './Dispose/Editors';
|
||||
import axios from 'axios';
|
||||
|
||||
const { Option } = Select;
|
||||
const TYPE = ["Java","C","C++","Python","Go","Ruby","R","PHP",
|
||||
"Perl","Node","Docker","Rust","Swift","Erlang","Other"]
|
||||
|
||||
function MouldNew({ form , visible , onCancel , onOk }, ref){
|
||||
const [value , setValue ] = useState(undefined);
|
||||
const [isSpin , setIsSpin ] = useState(false);
|
||||
const [valueFlag , setValueFlag ] = useState(false);
|
||||
const [ id , setId ] = useState(false);
|
||||
const [ buildFlag , setBuildFlag ] = useState(false);
|
||||
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
setEditInfo: (info) => {
|
||||
if(info){
|
||||
setFieldsValue({
|
||||
...info
|
||||
})
|
||||
if(info.stage_type === "build"){
|
||||
setBuildFlag(true);
|
||||
setFieldsValue({
|
||||
category:TYPE[0]
|
||||
})
|
||||
}else{
|
||||
removeCate();
|
||||
}
|
||||
setValue(info.content);
|
||||
setId(info.id);
|
||||
}else{
|
||||
removeCate();
|
||||
clear();
|
||||
setId(undefined);
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const helper = useCallback(
|
||||
(label, name, rules, widget, className, isRequired,flag) => (
|
||||
<Form.Item label={label} className={className}>
|
||||
{getFieldDecorator(name, { rules, validateFirst: true , valuePropName:flag ? "checked":"value" })(widget)}
|
||||
</Form.Item>
|
||||
),
|
||||
[]
|
||||
);
|
||||
function changeContent(v){
|
||||
if(v){
|
||||
setValue(v);
|
||||
setValueFlag(false);
|
||||
}
|
||||
}
|
||||
function cancel(){
|
||||
clear();
|
||||
onCancel();
|
||||
}
|
||||
function sure(){
|
||||
if(!value){
|
||||
setValueFlag(true);
|
||||
return;
|
||||
}
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
setIsSpin(true);
|
||||
const url = `/ci/templates.json`;
|
||||
axios.post(url,{
|
||||
...values,id,content:value,category:buildFlag ? values.category:""
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setIsSpin(false);
|
||||
cancel();
|
||||
onOk();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
function clear(){
|
||||
setFieldsValue({
|
||||
stage_type:"init",
|
||||
template_name:undefined,
|
||||
category:"Java",
|
||||
})
|
||||
setValue("");
|
||||
setValueFlag(false);
|
||||
}
|
||||
|
||||
function changeStage(e){
|
||||
if(e === "build"){
|
||||
setBuildFlag(true);
|
||||
setFieldsValue({
|
||||
category:TYPE[0]
|
||||
})
|
||||
}else{
|
||||
removeCate();
|
||||
}
|
||||
}
|
||||
|
||||
function removeCate(){
|
||||
setBuildFlag(false);
|
||||
setFieldsValue({
|
||||
category:""
|
||||
})
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
width="500px"
|
||||
title={"新建/编辑模板"}
|
||||
onCancel={cancel}
|
||||
onOk={sure}
|
||||
centered={true}
|
||||
>
|
||||
<Spin spinning={isSpin}>
|
||||
<Form layout={"inline"}>
|
||||
{helper(
|
||||
"所属阶段",
|
||||
"stage_type",
|
||||
[{required:true,message:"请选择所属阶段"}],
|
||||
<Select placeholder="请选择所属阶段" style={{width:"350px"}} onChange={(e)=>{changeStage(e)}}>
|
||||
<Option value="init">初始化</Option>
|
||||
<Option value="build">编译构建</Option>
|
||||
<Option value="deploy">部署</Option>
|
||||
<Option value="customize">其他</Option>
|
||||
</Select>
|
||||
)}
|
||||
{helper(
|
||||
"模板名称",
|
||||
"template_name",
|
||||
[{required:true,message:"请输入模板名称"}],
|
||||
<Input placeholder="请输入模板名称" style={{width:"350px"}}/>
|
||||
)}
|
||||
{helper(
|
||||
"模板分类",
|
||||
"category",
|
||||
[{required:buildFlag,message:"请选择模板分类"}],
|
||||
<Select placeholder="请选择模板分类" style={{width:"350px"}}>
|
||||
{
|
||||
TYPE.map((item,key)=>{
|
||||
return(
|
||||
<Option value={item}>{item}</Option>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Select>,buildFlag===true ? "" :"hide"
|
||||
)}
|
||||
<div style={{display:'flex',justifyContent:"flex-start"}}>
|
||||
<span><span className="color-red">* </span>模板内容:</span>
|
||||
<div>
|
||||
<div className="editorPanel">
|
||||
<Editor Numbers={"off"} width={"350px"} value={value} height="200px" theme="vs-grey" onChange={changeContent}/>
|
||||
</div>
|
||||
{ valueFlag && <span className="color-red">请输入模板内容</span>}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(MouldNew));
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Spin } from "antd";
|
||||
import { Spin , Menu } from "antd";
|
||||
import { FlexAJ, AlignCenter } from "../Component/layout";
|
||||
import axios from "axios";
|
||||
import CodeSSH from './ssh/Index';
|
||||
|
||||
export default ({
|
||||
data,
|
||||
|
@ -17,6 +18,8 @@ export default ({
|
|||
const [spining, setSpining] = useState(true);
|
||||
const [stage, setStage] = useState(undefined);
|
||||
const [step, setStep] = useState(undefined);
|
||||
const [nav, setNav] = useState("0");
|
||||
|
||||
useEffect(() => {
|
||||
setSpining(rightSpin);
|
||||
}, [rightSpin]);
|
||||
|
@ -35,6 +38,7 @@ export default ({
|
|||
: p[0];
|
||||
|
||||
setStep(sub);
|
||||
setNav("0");
|
||||
if (sub && sub.status !== "skipped") {
|
||||
getStep(pre.number, sub.number);
|
||||
}
|
||||
|
@ -65,43 +69,55 @@ export default ({
|
|||
}
|
||||
}
|
||||
return (
|
||||
<Spin spinning={spining}>
|
||||
<div className="rightMainContent">
|
||||
{data && data.status !== "error" ? (
|
||||
<div>
|
||||
<FlexAJ className="items">
|
||||
<span>{step && step.name}</span>
|
||||
<AlignCenter>
|
||||
{step && step.duration_time}
|
||||
<i className="iconfont icon-sanjiaoxing-down"></i>
|
||||
</AlignCenter>
|
||||
</FlexAJ>
|
||||
<div>
|
||||
{coders && coders.length > 0 ? (
|
||||
coders.map((item, key) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{/* <Menu className="devopsNav" onClick={(e)=>{setNav(e.key)}} selectedKeys={[nav]} mode="horizontal">
|
||||
<Menu.Item key={'0'} value="0">开发流水线</Menu.Item>
|
||||
<Menu.Item key={'1'} value="1">命令行</Menu.Item>
|
||||
</Menu> */}
|
||||
{
|
||||
nav === "0" &&
|
||||
<Spin spinning={spining}>
|
||||
<div className="rightMainContent">
|
||||
{data && data.status !== "error" ? (
|
||||
<div>
|
||||
<FlexAJ className="items">
|
||||
<span>{step && step.name}</span>
|
||||
<AlignCenter>
|
||||
{step && step.duration_time}
|
||||
<i className="iconfont icon-sanjiaoxing-down"></i>
|
||||
</AlignCenter>
|
||||
</FlexAJ>
|
||||
<div>
|
||||
{coders && coders.length > 0 ? (
|
||||
coders.map((item, key) => {
|
||||
return (
|
||||
<div className="opsDetailOut">
|
||||
<span>{key + 1}</span>
|
||||
<p>{item.out}</p>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : empty ? (
|
||||
<div className="opsDetailOut">
|
||||
<span>{key + 1}</span>
|
||||
<p>{item.out}</p>
|
||||
<span>1</span>
|
||||
<p>
|
||||
{stage && stage.name} – {step && step.name}: Skipped
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
) : empty ? (
|
||||
<div className="opsDetailOut">
|
||||
<span>1</span>
|
||||
<p>
|
||||
{stage && stage.name} – {step && step.name}: Skipped
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ color: "red" }}>error:{data && data.error}</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ color: "red" }}>error:{data && data.error}</div>
|
||||
)}
|
||||
</div>
|
||||
</Spin>
|
||||
</Spin>
|
||||
}
|
||||
{
|
||||
nav === "1" && <CodeSSH />
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Radio , Button } from 'antd';
|
||||
|
||||
function ServiceModal({sureModal}){
|
||||
const [ type , setType ] = useState(1);
|
||||
|
||||
function changeType(e){
|
||||
setType(e.target.value);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="mt30" style={{textAlign:"center"}}>
|
||||
<Radio.Group value={type} onChange={changeType}>
|
||||
<Radio value={1}>自有服务器</Radio>
|
||||
<Radio value={2}>Trustie服务器</Radio>
|
||||
</Radio.Group>
|
||||
<p className="mt30"><Button type="primary" onClick={()=>sureModal(type)}>下一步</Button></p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ServiceModal;
|
|
@ -1,10 +1,16 @@
|
|||
import React, { useState, useEffect , useImperativeHandle ,forwardRef } from "react";
|
||||
import { FlexAJ, AlignCenter } from "../Component/layout";
|
||||
import { FlexAJ, AlignCenter , Banner } from "../Component/layout";
|
||||
import { Table, Pagination, Popconfirm } from "antd";
|
||||
import { truncateCommitId } from "../common/util";
|
||||
import {getUrl} from 'educoder';
|
||||
import axios from "axios";
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Div = styled.div`{
|
||||
padding:24px 30px;
|
||||
}`;
|
||||
const STATUS = [
|
||||
{ name: "所有"},
|
||||
{ name: "运行中", value: "running" },
|
||||
|
@ -22,6 +28,7 @@ function Structure(props,ref){
|
|||
|
||||
let projectsId = props.match.params.projectsId;
|
||||
let owner = props.match.params.owner;
|
||||
let branch = props.match.params.branch;
|
||||
const permission = props.projectDetail && props.projectDetail.permission;
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
|
@ -43,7 +50,7 @@ function Structure(props,ref){
|
|||
axios.get(url,{
|
||||
params:{
|
||||
search:status,
|
||||
page,limit:LIMIT
|
||||
page,limit:LIMIT,branch
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result && result.data) {
|
||||
|
@ -51,6 +58,7 @@ function Structure(props,ref){
|
|||
return {
|
||||
...item,
|
||||
author:item.author && item.author.name,
|
||||
image_url:item.author && item.author.image_url,
|
||||
message: {
|
||||
branch: item.branch_target,
|
||||
message: item.message,
|
||||
|
@ -180,7 +188,7 @@ function Structure(props,ref){
|
|||
<i className="iconfont icon-weitongguo"></i>已撤销
|
||||
</span>
|
||||
);
|
||||
case 'pending':
|
||||
default:
|
||||
return (
|
||||
<span className="statusTag Preparing">
|
||||
<i className="iconfont icon-zhunbeizhong"></i>准备中
|
||||
|
@ -237,7 +245,7 @@ function Structure(props,ref){
|
|||
{meg.sha && <span className="color-orange">{meg.sha}</span>}
|
||||
</div>
|
||||
<AlignCenter>
|
||||
<img style={{borderRadius:"50%",marginRight:"10px",width:"25px",height:"25px"}} src={`${current_user && getUrl(`/images/${current_user.image_url}`)}`} />
|
||||
<img style={{borderRadius:"50%",marginRight:"10px",width:"25px",height:"25px"}} alt="" src={`${item.image_url && getUrl(`/images/${item.image_url}`)}`} />
|
||||
<div className="task-hide ml5" style={{ maxWidth: "300px" }}>
|
||||
{meg.message}
|
||||
</div>
|
||||
|
@ -278,42 +286,45 @@ function Structure(props,ref){
|
|||
},
|
||||
];
|
||||
return (
|
||||
<div className="listPart">
|
||||
<FlexAJ>
|
||||
{renderStatus()}
|
||||
{/* <Blueback>手动创建</Blueback> */}
|
||||
{/* <span className="mr30">
|
||||
<i className="iconfont icon-fenzhi1 font-16 mr5 color-blue"></i>分支
|
||||
</span>
|
||||
<span>
|
||||
<i className="iconfont icon-biaoqian3 font-16 mr5 color-blue"></i>
|
||||
标签
|
||||
</span> */}
|
||||
</FlexAJ>
|
||||
<Table
|
||||
onRow={(record,index)=>{
|
||||
return{
|
||||
onClick:(event)=>clickRows(event,record)
|
||||
}
|
||||
}}
|
||||
columns={column}
|
||||
className="normalTable"
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
loading={tableLoading}
|
||||
></Table>
|
||||
{total > LIMIT ?
|
||||
<div style={{ textAlign: "center", margin: "30px 50px" }}>
|
||||
<Pagination
|
||||
showQuickJumper
|
||||
defaultCurrent={page}
|
||||
total={total}
|
||||
pageSize={LIMIT}
|
||||
onChange={ChangePage}
|
||||
></Pagination>
|
||||
</div>
|
||||
:
|
||||
"" }
|
||||
<div className="disposePanel">
|
||||
<Banner>
|
||||
<FlexAJ>
|
||||
<span>构建列表</span>
|
||||
<Link to={`/projects/${owner}/${projectsId}/devops/dispose`} className="font-15 color-grey-9">返回</Link>
|
||||
</FlexAJ>
|
||||
</Banner>
|
||||
<Div>
|
||||
<div className="listPart">
|
||||
<FlexAJ>
|
||||
{renderStatus()}
|
||||
<a onClick={()=>Init(status)} className="color-red font-16">刷新</a>
|
||||
</FlexAJ>
|
||||
<Table
|
||||
onRow={(record,index)=>{
|
||||
return{
|
||||
onClick:(event)=>clickRows(event,record)
|
||||
}
|
||||
}}
|
||||
columns={column}
|
||||
className="normalTable"
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
loading={tableLoading}
|
||||
></Table>
|
||||
{total > LIMIT ?
|
||||
<div style={{ textAlign: "center", margin: "30px 50px" }}>
|
||||
<Pagination
|
||||
showQuickJumper
|
||||
defaultCurrent={page}
|
||||
total={total}
|
||||
pageSize={LIMIT}
|
||||
onChange={ChangePage}
|
||||
></Pagination>
|
||||
</div>
|
||||
:
|
||||
"" }
|
||||
</div>
|
||||
</Div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { WhiteBack } from '../Component/layout';
|
||||
import { Spin } from 'antd';
|
||||
import Head from './Dispose/head';
|
||||
import Menus from './Dispose/menus';
|
||||
import Init from './Dispose/Init';
|
||||
import Sure from './Dispose/Sure';
|
||||
import Stage from './Dispose/Stage';
|
||||
import axios from 'axios';
|
||||
|
||||
function disposePipeline(props){
|
||||
const [ spining , setSpining ] = useState(true);
|
||||
const [ stage , setStage ] = useState(1);
|
||||
const [ pipeLineName , setPipeLineName ] = useState(undefined);
|
||||
const [ stepName , setStepName ] = useState(undefined);
|
||||
const [ stageId , setStageId ] =useState(undefined);
|
||||
const [ menuList , setMenuList ] = useState(undefined);
|
||||
const [ stageType , setStageType ] = useState("init");
|
||||
const [ datas , setDatas ] = useState(undefined);
|
||||
const [ templates , setTemplates] = useState(undefined);
|
||||
const [ datasUpdataFlag , setDatasUpdataFlag ] = useState(false);
|
||||
const [ loading ,setLoading ] = useState(false);
|
||||
|
||||
const { disposeId } = props.match.params;
|
||||
let projectsId = props.match.params.projectsId;
|
||||
let owner = props.match.params.owner;
|
||||
useEffect(()=>{
|
||||
if(stageType && stageType !=="confirm"){
|
||||
InitTemplates();
|
||||
}
|
||||
},[stageType])
|
||||
|
||||
// 获取模板
|
||||
function InitTemplates(){
|
||||
const url = `/ci/templates/templates_by_stage.json`;
|
||||
axios.get(url,{
|
||||
params:{ stage_type:stageType, id:disposeId }
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setTemplates(result.data);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(disposeId && (stageType && stageType !=="confirm")){
|
||||
getData(0);
|
||||
}
|
||||
},[disposeId])
|
||||
// 获取所有阶段默认第一个阶段的数据
|
||||
function getData(index){
|
||||
const url = `/ci/pipelines/${disposeId}/stages.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setMenuList(result.data.stages);
|
||||
if(index || index === 0){
|
||||
let first = result.data.stages[index];
|
||||
setStage(first.show_index);
|
||||
setStageId(first.id);
|
||||
setPipeLineName(`${first.pipeline_name}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(stageId){
|
||||
getStageStep(stageId);
|
||||
}
|
||||
},[stageId])
|
||||
// 切换阶段时获取阶段步骤数据(第一次默认查询”初始化“阶段的数据)
|
||||
function getStageStep(stageId){
|
||||
let url = "";
|
||||
if(stageType && stageType === "confirm"){
|
||||
url = `/ci/pipelines/${disposeId}/content.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
owner,repo:projectsId
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setDatas(result.data);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
url = `/ci/pipelines/${disposeId}/${stageId}/steps.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
let step = result.data.steps;
|
||||
setDatas(step);
|
||||
let flag = !step || (step && step.length === 0);
|
||||
setDatasUpdataFlag(flag);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
setSpining(false);
|
||||
}
|
||||
|
||||
// 切换阶段:s:show_index,stage_type:stage_type
|
||||
function changestage(show_index,stage_type,stage_id,stage_name){
|
||||
if(show_index !== stage){
|
||||
saveFunc(show_index,stage_type,stage_id,stage_name);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取子组件传过来的数据
|
||||
function saveDatas(steps){
|
||||
setDatas([...steps]);
|
||||
setDatasUpdataFlag(true);
|
||||
}
|
||||
|
||||
// 检查数组里面是否有空数据
|
||||
function checkDatas(){
|
||||
if(datas && datas.length > 0){
|
||||
for(let i= 0;i< datas.length;i++){
|
||||
if(datas[i] && (!datas[i].content || !datas[i].template_id)){
|
||||
props.showNotification("请先选择模板!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(stageType === "init" ){
|
||||
props.showNotification("请先选择模板!");
|
||||
return false;
|
||||
}else if(stageType === "confirm" ){
|
||||
return true;
|
||||
}else{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 保存步骤
|
||||
function saveFunc(show_index,stage_type,stage_id,stageName,btn){
|
||||
setSpining(true);
|
||||
// 判断数据是否有过更新
|
||||
if(datasUpdataFlag && stageType !== "confirm"){
|
||||
// 先判断子组件传过来的数据是否有undefined --datas
|
||||
let f = checkDatas();
|
||||
if(f && (datas && datas.length !== 0)){
|
||||
// 数据没问题后先保存数据再切换阶段
|
||||
saveDataFunc(btn,show_index,stage_type,stage_id,stageName);
|
||||
}
|
||||
else{
|
||||
setSpining(false);
|
||||
f === "" && elseFunc(btn,show_index,stage_type,stage_id,stageName);
|
||||
}
|
||||
}else{
|
||||
elseFunc(btn,show_index,stage_type,stage_id,stageName);
|
||||
}
|
||||
}
|
||||
|
||||
function saveDataFunc(btn,show_index,stage_type,stage_id,stageName){
|
||||
const url = `/ci/pipelines/${disposeId}/${stageId}/stage_step.json`;
|
||||
axios.post(url,{
|
||||
steps:datas
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setDatasUpdataFlag(false);
|
||||
if(!btn){
|
||||
setStage(show_index);
|
||||
setStageType(stage_type);
|
||||
setStageId(stage_id);
|
||||
setStepName(pipeLineName+`-`+stageName);
|
||||
}else{
|
||||
enters(btn);
|
||||
}
|
||||
}else{
|
||||
props.showNotification("阶段更新失败,请稍微重试!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function elseFunc(btn,show_index,stage_type,stage_id,stageName){
|
||||
if(btn){
|
||||
enters(btn);
|
||||
}else{
|
||||
setStage(show_index);
|
||||
setStageType(stage_type);
|
||||
setStageId(stage_id);
|
||||
setStepName(pipeLineName+`-`+stageName);
|
||||
}
|
||||
}
|
||||
|
||||
// 上一步、下一步
|
||||
function enters(btn){
|
||||
let s = stage;
|
||||
if(btn === "next"){
|
||||
// 点击阶段里面的下一步
|
||||
s = s+1;
|
||||
}else{
|
||||
// 上一步
|
||||
s = s-1;
|
||||
}
|
||||
let item = menuList && menuList.filter(i=>i.show_index === (s));
|
||||
setStage(s);
|
||||
setStageType(item[0].stage_type);
|
||||
setStageId(item[0].id);
|
||||
setStepName(pipeLineName+`-`+item[0].stage_name);
|
||||
}
|
||||
// 删除步骤
|
||||
function deleteStep(id,index){
|
||||
if(id){
|
||||
const url = `/ci/pipelines/${disposeId}/${stageId}/${id}/delete_step.json`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getStageStep(stageId);
|
||||
props.showNotification("阶段步骤删除成功!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
let d = datas.filter(s=>s.show_index !== (index+1));
|
||||
setDatas(d);
|
||||
}
|
||||
}
|
||||
function renameFunc(value,id){
|
||||
const url = `/ci/pipelines/${disposeId}/${id}/update_stage.json`;
|
||||
axios.put(url,{
|
||||
stage_name:value
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
getData();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 新增阶段
|
||||
function addMenuFunc(name,index){
|
||||
const url = `/ci/pipelines/${disposeId}/create_stage.json`;
|
||||
axios.post(url,{
|
||||
show_index:index,
|
||||
stage_name:name
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
getData(index-1);
|
||||
setStageType("customize");
|
||||
}else{
|
||||
props.showNotification("阶段新增失败!");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除阶段
|
||||
function deleteStageFunc(){
|
||||
let next = menuList && menuList.filter(item=>item.show_index === (stage+1));
|
||||
let nextStageType = next && next.length>0 && next[0].stage_type;
|
||||
const url = `/ci/pipelines/${disposeId}/${stageId}/delete_stage.json`;
|
||||
axios.delete(url,{
|
||||
params:{show_index:stage}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
getData(stage-1);
|
||||
setStageType(nextStageType);
|
||||
}else{
|
||||
props.showNotification("阶段删除失败!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 确认提交
|
||||
function sureSubmit(){
|
||||
setLoading(true);
|
||||
let params = {
|
||||
branch: datas.branch,
|
||||
content:datas.content,
|
||||
filepath:'.trustie-pipeline.yml',
|
||||
message:'',
|
||||
sha:datas.sha || undefined,
|
||||
owner:owner,
|
||||
repo:projectsId
|
||||
}
|
||||
let url = `/${owner}/${projectsId}/update_trustie_pipeline.json`;
|
||||
axios.put(url,{
|
||||
...params
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`);
|
||||
}
|
||||
setLoading(false);
|
||||
}).catch(error=>{
|
||||
console.log(error);
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="disposePanel">
|
||||
<Head />
|
||||
<WhiteBack style={{padding:"24px 30px"}}>
|
||||
<Spin spinning={spining}>
|
||||
<div style={{minHeight:"450px"}}>
|
||||
<Menus step={stage} checkDatas={checkDatas} changeStep={changestage} menuList={menuList} renameFunc={renameFunc} addFunc={addMenuFunc}/>
|
||||
{
|
||||
stageType === "init" ? <Init stage_type={stageType} templates={templates} datas={datas} saveDatas={saveDatas} saveFunc={saveFunc}/>
|
||||
:
|
||||
stageType === "confirm" ? <Sure sureSubmit={sureSubmit} name={pipeLineName} datas={datas} saveFunc={saveFunc} loading={loading}/>
|
||||
:
|
||||
<Stage {...props}
|
||||
stepName={stepName}
|
||||
deleteStep={deleteStep}
|
||||
stage_type={stageType}
|
||||
templates={templates}
|
||||
datas={datas}
|
||||
deleteFunc={deleteStageFunc}
|
||||
saveDatas={saveDatas}
|
||||
saveFunc={saveFunc}
|
||||
deleteFlag={menuList && menuList.length === 3}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</WhiteBack>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default disposePipeline;
|
|
@ -10,6 +10,7 @@
|
|||
padding:60px 0px;
|
||||
}
|
||||
.disposePanel{
|
||||
border:1px solid #eee;
|
||||
.language{
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
@ -81,48 +82,7 @@
|
|||
.ant-modal-close{
|
||||
top:7px;
|
||||
}
|
||||
// 列表
|
||||
.listPart{
|
||||
.statusTag{
|
||||
display: flex;
|
||||
padding:0px 16px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
border-radius:20px;
|
||||
float: left;
|
||||
i{
|
||||
font-size: 20px!important;
|
||||
margin-right: 7px;
|
||||
}
|
||||
&.running{
|
||||
background:#F1F8FF;
|
||||
border:1px solid #5091FF;
|
||||
color: #5091FF;
|
||||
}
|
||||
&.Preparing{
|
||||
background:rgba(255,248,244,1);
|
||||
border:1px solid rgba(255,110,33,1);
|
||||
color:rgba(255,110,33,1) ;
|
||||
}
|
||||
&.pass{
|
||||
background:#EEFDF5;
|
||||
border:1px solid #28BD6C;
|
||||
color:#28BD6C ;
|
||||
}
|
||||
&.failed{
|
||||
background:#FCEEEE;
|
||||
border:1px solid #F73030;
|
||||
color:#F73030 ;
|
||||
}
|
||||
&.killed{
|
||||
background:#eee;
|
||||
border:1px solid #999;
|
||||
color:#999 ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// ops详情
|
||||
.opsDetailPanel{
|
||||
|
@ -234,6 +194,18 @@
|
|||
&.rightSection{
|
||||
width:100%;
|
||||
background-color: #081930;
|
||||
.devopsNav{
|
||||
background-color: #111c24;
|
||||
border-bottom: none;
|
||||
.ant-menu-item{
|
||||
color: #ccc;
|
||||
padding:0px;
|
||||
margin:0px 20px!important;
|
||||
}
|
||||
.ant-menu-item.ant-menu-item-selected{
|
||||
color: #1890ff;
|
||||
}
|
||||
}
|
||||
.rightMainContent{
|
||||
padding:24px 30px;
|
||||
height:100vh;
|
||||
|
@ -313,4 +285,237 @@
|
|||
text-align:center;
|
||||
line-height:22px;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.disposeList{
|
||||
min-height: 450px;
|
||||
.ant-table-body{
|
||||
margin:0px!important;
|
||||
.ant-table-thead{
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
||||
.txtright{
|
||||
text-align: right;
|
||||
}
|
||||
.menus{
|
||||
display: flex;
|
||||
box-shadow: 0px 0px 6px rgba(0,3,8,0.1);
|
||||
padding:20px 0px 10px 0px;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
& > li{
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items:center;
|
||||
justify-content: flex-start;
|
||||
text-align: center;
|
||||
color: #787878;
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
max-width: 122px;
|
||||
flex:2;
|
||||
&>i{
|
||||
font-size: 30px!important;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
&:hover,&.active{
|
||||
& > i{
|
||||
color: #1890FF;
|
||||
}
|
||||
}
|
||||
&.active::after{
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -25px;
|
||||
bottom:-10px;
|
||||
width: 50px;
|
||||
height: 2px;
|
||||
background-color: #1890FF;
|
||||
content: "";
|
||||
}
|
||||
.aboutEdit{
|
||||
width: 100%;
|
||||
.operateName{
|
||||
position: relative;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 15px;
|
||||
min-height: 40px;
|
||||
& > i{
|
||||
position: absolute;
|
||||
right:0px;
|
||||
top:10px;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&:hover i{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > li.menuAdd{
|
||||
position: relative;
|
||||
flex: 1;
|
||||
&:hover{
|
||||
& > i{
|
||||
color: #787878;
|
||||
}
|
||||
}
|
||||
& > i{
|
||||
font-size: 18px!important;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top:0px;
|
||||
}
|
||||
& > input{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top:3px;
|
||||
}
|
||||
&::before{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
border-bottom: 1px dashed #eee;
|
||||
content: "";
|
||||
top:15px;
|
||||
margin-top: -1px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.choosenList{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
line-height: 35px;
|
||||
align-items: center;
|
||||
& > span{
|
||||
display: block;
|
||||
min-width: 75px;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
height: 35px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
& > ul{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
li{
|
||||
padding:0px 12px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
border:1px solid #e1e4e8;
|
||||
background-color: #fff;
|
||||
margin:6px 0px;
|
||||
margin-right: 12px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
color: #333;
|
||||
}
|
||||
li.active{
|
||||
color: #fff;
|
||||
background-color: #1890FF;
|
||||
}
|
||||
}
|
||||
.ant-select-selection.ant-select-selection--multiple{
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
.addStageBtn{
|
||||
display: block;
|
||||
width: 100%;
|
||||
border:1px solid #e1e4e8;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
color: #1890FF;
|
||||
font-size: 16px;
|
||||
border-radius: 3px;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
.stepsItem{
|
||||
border:1px solid #e1e4e8;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
.stepsHead{
|
||||
padding:8px 15px;
|
||||
span > a > i{
|
||||
margin-left: 8px;
|
||||
color: #666!important;
|
||||
}
|
||||
}
|
||||
.stepsBody{
|
||||
padding:10px 15px;
|
||||
border-top: 1px solid #e1e4e8;
|
||||
background-color: rgb(251, 251, 251);
|
||||
display: none;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.stepsBody.active{
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.editorPanel{
|
||||
border:1px solid #eee;
|
||||
.margin{
|
||||
width: 0px;
|
||||
}
|
||||
.monaco-scrollable-element.editor-scrollable{
|
||||
left: 0px!important;
|
||||
width: 100%!important;
|
||||
.view-lines{
|
||||
width: 336px!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hide{
|
||||
display: none!important;
|
||||
}
|
||||
.statusTag{
|
||||
display: flex;
|
||||
padding:0px 16px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
border-radius:20px;
|
||||
float: left;
|
||||
i{
|
||||
font-size: 20px!important;
|
||||
margin-right: 7px;
|
||||
}
|
||||
&.running{
|
||||
background:#F1F8FF;
|
||||
border:1px solid #5091FF;
|
||||
color: #5091FF;
|
||||
}
|
||||
&.Preparing{
|
||||
background:rgba(255,248,244,1);
|
||||
border:1px solid rgba(255,110,33,1);
|
||||
color:rgba(255,110,33,1) ;
|
||||
}
|
||||
&.pass{
|
||||
background:#EEFDF5;
|
||||
border:1px solid #28BD6C;
|
||||
color:#28BD6C ;
|
||||
}
|
||||
&.failed{
|
||||
background:#FCEEEE;
|
||||
border:1px solid #F73030;
|
||||
color:#F73030 ;
|
||||
}
|
||||
&.killed{
|
||||
background:#eee;
|
||||
border:1px solid #999;
|
||||
color:#999 ;
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ export default (props) => {
|
|||
</AlignCenter>
|
||||
<Link
|
||||
style={{ color: "#ddd" }}
|
||||
to={`/projects/${owner}/${projectId}/devops/list`}
|
||||
to={`/projects/${owner}/${projectId}/devops/dispose`}
|
||||
>
|
||||
<i className="iconfont icon-yiguanbi font-15 mr5"></i>退出
|
||||
</Link>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import XmlPanel from "./XmlPanel";
|
||||
import mediator from "./mediator";
|
||||
import axios from "axios";
|
||||
|
||||
// const defaulturl = `http://47.111.130.18:48088`;
|
||||
const defaultValue = {
|
||||
host: "106.75.231.63",
|
||||
port: "2021",
|
||||
ws_url: "wss://pre-webssh.educoder.net/ws",
|
||||
username: "root",
|
||||
secret: "Dron_123123",
|
||||
};
|
||||
function Index() {
|
||||
const [sshConfigData, setSshConfigData] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sshConfigData) {
|
||||
init();
|
||||
}
|
||||
setTimeout(() => {
|
||||
mediator.publish("create-socket", 1);
|
||||
}, 300);
|
||||
}, [sshConfigData]);
|
||||
|
||||
// 获取服务器连接信息
|
||||
function init() {
|
||||
const url = `/api/ci/pipelines/ssh_server.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setSshConfigData({...result.data})
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return (
|
||||
<XmlPanel
|
||||
sshConfigData={sshConfigData||{}}
|
||||
sid={1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default Index;
|
|
@ -0,0 +1,219 @@
|
|||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
import { Terminal } from 'xterm';
|
||||
import 'xterm/css/xterm.css';
|
||||
import mediator from './mediator';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
function getColsAndRows(width, height, term) {
|
||||
let w = term._core._renderService.dimensions.actualCellWidth || 9.5;
|
||||
let h = term._core._renderService.dimensions.actualCellHeight || 18;
|
||||
const rows = Math.floor(height / h);
|
||||
const cols = Math.floor(width / w);
|
||||
return [cols, rows];
|
||||
}
|
||||
|
||||
function onLayout(term, el) {
|
||||
const ro = new ResizeObserver(entries => {
|
||||
console.log(entries);
|
||||
for (let entry of entries) {
|
||||
if (entry.target.offsetHeight > 0 || entry.target.offsetWidth > 0) {
|
||||
const [cols, rows] = getColsAndRows(
|
||||
entry.target.offsetWidth,
|
||||
entry.target.offsetHeight,
|
||||
term,
|
||||
);
|
||||
console.log('cols, rows', cols, rows);
|
||||
term.resize(cols, rows);
|
||||
mediator.publish('ssh-xterm-resize', {
|
||||
columns: cols,
|
||||
rows: rows,
|
||||
width: entry.target.offsetWidth,
|
||||
height: entry.target.offsetHeight,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
ro.observe(el);
|
||||
return ro;
|
||||
}
|
||||
|
||||
const TimeTicket = 30000;
|
||||
|
||||
//建立 websockt 来交互
|
||||
//根据容器大小计算行数和列数并做到自适应
|
||||
//socket 与 term 需要分开初始化 因为socket 可能重置连接
|
||||
//mediator 监听消息,如果和id匹配,则建立连接,重置,或关闭连接
|
||||
|
||||
export default ({ sshConfigData, sid }) => {
|
||||
const [term, setTerm] = useState(null);
|
||||
|
||||
const { ws_url, password, port, secret } = sshConfigData;
|
||||
const el = useRef();
|
||||
const socket = useRef();
|
||||
const isFirstConnected = useRef(false);
|
||||
|
||||
//term init
|
||||
useEffect(() => {
|
||||
if (el.current && ws_url) {
|
||||
const term = new Terminal({ fontSize: 16, rendererType: 'dom' });
|
||||
term.open(el.current);
|
||||
|
||||
term.onData(data => {
|
||||
if (socket.current) {
|
||||
if (socket.current.readyState === 1) {
|
||||
socket.current.send(JSON.stringify({ tp: 'client', data: data }));
|
||||
mediator.publish('on-operating-ssh'); //有操作则自动延时
|
||||
} else {
|
||||
//断开连接后重连
|
||||
// socket.current = null
|
||||
// mediator.publish('create-socket', sid)
|
||||
}
|
||||
}
|
||||
});
|
||||
term.write('Connecting...');
|
||||
setTerm(term);
|
||||
const ro = onLayout(term, el.current);
|
||||
return () => {
|
||||
term.dispose();
|
||||
ro.unobserve(el.current);
|
||||
};
|
||||
}
|
||||
}, [ws_url, el.current]);
|
||||
|
||||
useEffect(() => {
|
||||
if (term && ws_url) {
|
||||
function createSocket() {
|
||||
const socketInstance = new WebSocket(ws_url);
|
||||
socket.current = socketInstance;
|
||||
|
||||
socketInstance.onopen = () => {
|
||||
let container = term.element.parentElement;
|
||||
if (container) {
|
||||
let width = container.offsetWidth;
|
||||
let height = container.offsetHeight;
|
||||
console.log('init', {
|
||||
tp: 'init',
|
||||
data: {
|
||||
...sshConfigData,
|
||||
secret: secret,
|
||||
width,
|
||||
height,
|
||||
rows: term.rows,
|
||||
columns: term.cols,
|
||||
},
|
||||
});
|
||||
socketInstance.send(
|
||||
JSON.stringify({
|
||||
tp: 'init',
|
||||
data: {
|
||||
...sshConfigData,
|
||||
secret: secret,
|
||||
width,
|
||||
height,
|
||||
rows: term.rows,
|
||||
columns: term.cols,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
term.focus();
|
||||
};
|
||||
socketInstance.onerror = error => {
|
||||
console.log(
|
||||
'------in socket error----',
|
||||
error,
|
||||
socketInstance,
|
||||
ws_url,
|
||||
);
|
||||
//连接报错后,重新请求资源
|
||||
// mediator.publish('on-recreate-socket')
|
||||
};
|
||||
socketInstance.onmessage = event => {
|
||||
if (!isFirstConnected.current) {
|
||||
term.write('\r');
|
||||
// term.focus()
|
||||
setTimeout(() => {
|
||||
// term.clear();
|
||||
}, 1000);
|
||||
}
|
||||
isFirstConnected.current = true;
|
||||
console.log('event:', event);
|
||||
|
||||
const data = Base64.decode(event.data.toString());
|
||||
let w = term._core._renderService.dimensions.actualCellWidth || 9.5;
|
||||
|
||||
console.log('data:', data, w, term);
|
||||
term.write(data);
|
||||
};
|
||||
|
||||
socketInstance.onclose = evt => {
|
||||
if (tid) {
|
||||
clearInterval(tid);
|
||||
}
|
||||
term.write('\r\nconnection closed');
|
||||
};
|
||||
}
|
||||
|
||||
const tid = setInterval(() => {
|
||||
if (socket.current) {
|
||||
socket.current.send(JSON.stringify({ tp: 'h' }));
|
||||
}
|
||||
}, TimeTicket);
|
||||
|
||||
const unSubCreate = mediator.subscribe('create-socket', id => {
|
||||
if (sid === id) {
|
||||
if (socket.current && socket.current.readyState === 1) {
|
||||
term.focus();
|
||||
} else {
|
||||
createSocket();
|
||||
}
|
||||
term.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const unSubClose = mediator.subscribe('close-socket', id => {
|
||||
if (sid === id) {
|
||||
if (socket.current) {
|
||||
socket.current.close();
|
||||
isFirstConnected.current = false;
|
||||
term.clear();
|
||||
}
|
||||
socket.current = null;
|
||||
}
|
||||
});
|
||||
|
||||
const unSubResize = mediator.subscribe('ssh-xterm-resize', option => {
|
||||
if (socket.current && socket.current.readyState === 1) {
|
||||
socket.current.send(
|
||||
JSON.stringify({ tp: 'resize', data: { ...option } }),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const unSubAddTime = mediator.subscribe('ssh-add-connect-time', () => {
|
||||
if (socket.current && socket.current.readyState === 1) {
|
||||
socket.current.send(JSON.stringify({ tp: 'overtime' }));
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unSubClose();
|
||||
unSubCreate();
|
||||
unSubResize();
|
||||
unSubAddTime();
|
||||
if (socket.current) {
|
||||
socket.current.close();
|
||||
isFirstConnected.current = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [term, ws_url, port]);
|
||||
|
||||
return (
|
||||
<div ref={el} className="xterm-panel" style={{height:"100%"}}>
|
||||
{!ws_url ? <p style={{ color: '#fff' }}>正在连接命令行服务...</p> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
function Mediator(obj) {
|
||||
const channels = {};
|
||||
|
||||
const mediator = {
|
||||
subscribe: function(channel, cb) {
|
||||
if (!channels[channel]) {
|
||||
channels[channel] = [];
|
||||
}
|
||||
channels[channel].push(cb);
|
||||
return this.unsubscribe.bind(null, channel, cb);
|
||||
},
|
||||
|
||||
unsubscribe: function(channel, cb) {
|
||||
let rs = channels[channel];
|
||||
let index = -1;
|
||||
if (rs) {
|
||||
for (let i = 0; i < rs.length; i++) {
|
||||
if (rs[i].name === cb.name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
channels[channel].splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
publish: function(channel) {
|
||||
if (!channels[channel]) {
|
||||
return false;
|
||||
}
|
||||
const args = Array.prototype.slice.call(arguments, 1);
|
||||
channels[channel].forEach(subscription => {
|
||||
subscription.apply(null, args);
|
||||
});
|
||||
return this;
|
||||
},
|
||||
};
|
||||
|
||||
return obj ? Object.assign(obj, mediator) : mediator;
|
||||
}
|
||||
const mediator = new Mediator();
|
||||
export default mediator;
|
|
@ -10,4 +10,12 @@ export const getTag = async (id,owner)=>{
|
|||
// 获取hooks(仓库设置-管理web钩子)列表
|
||||
export const getHooks = async (id,params)=>{
|
||||
return (await axios.get(`/projects/${id}/hooks.json`,{params})).data;
|
||||
}
|
||||
// 获取子目录列表
|
||||
export const getSubEntries = async (owner,projectsId,params)=>{
|
||||
return (await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params})).data;
|
||||
}
|
||||
// 获取用户信息
|
||||
export const getUser = async (login)=>{
|
||||
return (await axios.get(`/users/${login}/hovercard.json`)).data;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import React, { useEffect , useState } from 'react';
|
||||
import './header.scss';
|
||||
|
||||
function Footer(){
|
||||
const [ value , setValue ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
try {
|
||||
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
||||
setValue(chromesettingArray.footer);
|
||||
} catch (e) {
|
||||
}
|
||||
},[])
|
||||
|
||||
function showhtml(htmlString){
|
||||
var html = {__html:htmlString};
|
||||
return <div dangerouslySetInnerHTML={html}></div> ;
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div style={{height:"483px"}}></div>
|
||||
<div className="newFooter edu-txt-center">
|
||||
{value && showhtml(value)}
|
||||
{/* <div className="footerInfos">
|
||||
<ul>
|
||||
<li>社区</li>
|
||||
<li><a href={`/`} target="_blank">网站首页</a></li>
|
||||
<li><a href={`https://www.trustie.net/agreement`} target="_blank">服务协议</a></li>
|
||||
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
||||
<li><a href={`https://forum.trustie.net/`} target="_blank">问吧交流</a></li>
|
||||
<li><a href={`https://www.trustie.net/cooperation`} target="_blank">合作伙伴</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>支持与服务</li>
|
||||
<li><a href={`https://forgeplus.trustie.net/docs/api`} target="_blank">API文档</a></li>
|
||||
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
|
||||
<li><a href={`https://git-scm.com`} target="_blank">Git常用命令</a></li>
|
||||
<li><a href={`https://forum.trustie.net/forums/3080/detail`} target="_blank">DevOps使用文档</a></li>
|
||||
<li><a href={`https://forgeplus.trustie.net/projects/jasder/forgeplus/tree/master/CHANGELOG.md`} target="_blank">日志更新</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>合作伙伴</li>
|
||||
<li><a href={`http://www.sei.pku.edu.cn`} target="_blank">北京大学</a></li>
|
||||
<li><a href={`http://scse.buaa.edu.cn`} target="_blank">北京航空航天大学</a></li>
|
||||
<li><a href={`https://www.nju.edu.cn`} target="_blank">南京大学</a></li>
|
||||
<li><a href={`https://www.xtu.edu.cn`} target="_blank">湘潭大学</a></li>
|
||||
<li><a href={`http://www.iscas.ac.cn`} target="_blank">ISCAS</a></li>
|
||||
<li><a href={`https://www.ucloud.cn`} target="_blank">UCloud优刻得</a></li>
|
||||
<li><a href={`http://www.inforbus.com`} target="_blank">中创软件</a></li>
|
||||
<li><a href={`https://www.inspur.com`} target="_blank">浪潮集团</a></li>
|
||||
<li><a href={`http://www.copu.org.cn`} target="_blank">中国开源软件推进联盟</a></li>
|
||||
<li><a href={`https://www.sjtu.edu.cn`} target="_blank">上海交通大学</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>合作伙伴</li>
|
||||
<li><span>热线:</span></li>
|
||||
<li><span>QQ群:1071514693</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p className="footerCopy">© Copyright 2007~2021 国防科技大学Trustie团队 & IntelliDE <a href="https://beian.miit.gov.cn">湘ICP备 17009477号</a></p> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Footer;
|
|
@ -0,0 +1,730 @@
|
|||
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 ,Divider } from 'antd';
|
||||
|
||||
import LoginDialog from '../../modules/login/LoginDialog';
|
||||
import GotoQQgroup from '../../modal/GotoQQgroup'
|
||||
// import 'antd/lib/modal/style/index.css';
|
||||
// import 'antd/lib/checkbox/style/index.css';
|
||||
// import 'antd/lib/radio/style/index.css';
|
||||
// import 'antd/lib/input/style/index.css';
|
||||
import '../../modules/tpm/TPMIndex.css';
|
||||
import logo from '../../modules/tpm/images/logo.png';
|
||||
import './header.scss';
|
||||
const $ = window.$
|
||||
// TODO 这部分脚本从公共脚本中直接调用
|
||||
const { Search } = Input;
|
||||
let old_url;
|
||||
|
||||
window._header_componentHandler = null;
|
||||
// 非trustie链接则新开页跳转
|
||||
const str = ['www.trustie.net','forgeplus.trustie.net','forum.trustie.net','testforgeplus.trustie.net']
|
||||
class NewHeader extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
Addcoursestypes: false,
|
||||
tojoinitemtype: false,
|
||||
tojoinclasstitle: undefined,
|
||||
rolearr: ["", ""],
|
||||
Checkboxteacherchecked: false,
|
||||
Checkboxstudentchecked: false,
|
||||
Checkboxteachingchecked: false,
|
||||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
RadioGroupvalue: undefined,
|
||||
submitapplications: false,
|
||||
isRender: false,
|
||||
showSearchOpentype: false,
|
||||
showTrial: false,
|
||||
setevaluatinghides: false,
|
||||
occupation: 0,
|
||||
mydisplay: false,
|
||||
headtypesonClickbool: false,
|
||||
headtypess: "/",
|
||||
settings: null,
|
||||
goshowqqgtounp: false,
|
||||
visiblemyss: false,
|
||||
openSearch:false,
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
// this.getAppdata();
|
||||
this.geturlsdata();
|
||||
window._header_componentHandler = this;
|
||||
|
||||
//下拉框的显示隐藏
|
||||
var hoverTimeout;
|
||||
var hoveredPanel;
|
||||
$(".edu-menu-panel").hover(function () {
|
||||
if (hoverTimeout) { // 一次只显示一个panel
|
||||
if (hoveredPanel && hoveredPanel !== this) {
|
||||
$(hoveredPanel).find(".edu-menu-list").hide()
|
||||
}
|
||||
clearTimeout(hoverTimeout);
|
||||
hoverTimeout = null;
|
||||
}
|
||||
hoveredPanel = this;
|
||||
$(this).find(".edu-menu-list").show();
|
||||
}, function () {
|
||||
var that = this;
|
||||
// 延迟hide
|
||||
hoverTimeout = setTimeout(function () {
|
||||
$(that).find(".edu-menu-list").hide();
|
||||
}, 800)
|
||||
|
||||
});
|
||||
//获取游览器地址
|
||||
try {
|
||||
window.sessionStorage.setItem("yslgeturls", JSON.stringify(window.location.href))
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
SearchInput = (open,item)=>{
|
||||
if(open){
|
||||
return(
|
||||
<div
|
||||
onBlur={() => {
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
openSearch:false
|
||||
})
|
||||
}, 300)
|
||||
}}
|
||||
>
|
||||
<Search placeholder="实践课程/教学课堂/实践项目/交流问答"
|
||||
className={`search-input mr20`}
|
||||
onSearch={(value)=>this.onGlobalSearch(value,item)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}else{
|
||||
return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
|
||||
this.setState({openSearch:true})
|
||||
}} />
|
||||
}
|
||||
}
|
||||
|
||||
onGlobalSearch=(value,item)=>{
|
||||
window.location.href=`${item}?value=` + value;
|
||||
}
|
||||
|
||||
openNotification = (messge) => {
|
||||
notification.open({
|
||||
message: "提示",
|
||||
description:
|
||||
messge,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
componentWillReceiveProps(newProps, oldProps) {
|
||||
this.setState({
|
||||
user: newProps.user
|
||||
})
|
||||
if (newProps.Headertop !== undefined) {
|
||||
old_url = newProps.Headertop.old_url
|
||||
}
|
||||
}
|
||||
getCookie = (key) => {
|
||||
var arr, reg = RegExp('(^| )' + key + '=([^;]+)(;|$)');
|
||||
if (arr === document.cookie.match(reg))
|
||||
return decodeURIComponent(arr[2]);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
delCookie = (name) => {
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() - 1);
|
||||
var cval = this.getCookie(name);
|
||||
if (cval != null) {
|
||||
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
|
||||
}
|
||||
}
|
||||
onLogout = () => {
|
||||
const url = `/accounts/logout.json`
|
||||
this.delCookie("autologin_trustie")
|
||||
axios.get(url, {
|
||||
}).then((response) => {
|
||||
if (response.data.status === 1) {
|
||||
this.setState({
|
||||
user: undefined
|
||||
})
|
||||
window.location.href = "/login"
|
||||
message.success('退出成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tojoinclass = () => {
|
||||
let { user } = this.state;
|
||||
if (user === undefined) {
|
||||
this.setState({
|
||||
isRender: true
|
||||
})
|
||||
return
|
||||
}
|
||||
if (user && user.login === "") {
|
||||
this.setState({
|
||||
isRender: true
|
||||
})
|
||||
return;
|
||||
}
|
||||
if (user && user.profile_completed === false) {
|
||||
this.setState({
|
||||
AccountProfiletype: true
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
Addcoursestypes: true,
|
||||
})
|
||||
}
|
||||
|
||||
tojoinitem = () => {
|
||||
if (this.props.user && this.props.user.email === undefined || this.props.user && this.props.user.email === null || this.props.user && this.props.user.email === "") {
|
||||
this.openNotification("请先绑定邮箱,谢谢");
|
||||
return
|
||||
}
|
||||
let { user } = this.state;
|
||||
if (user === undefined) {
|
||||
this.setState({
|
||||
isRender: true
|
||||
})
|
||||
return
|
||||
}
|
||||
if (user && user.login === "") {
|
||||
this.setState({
|
||||
isRender: true
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
if (user && user.profile_completed === false) {
|
||||
this.setState({
|
||||
AccountProfiletype: true
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
tojoinitemtype: true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
submitstatevalue = (sum, value, data) => {
|
||||
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,
|
||||
submitapplicationssum: sum,
|
||||
submitapplications: true,
|
||||
submitapplicationsvalue: value,
|
||||
submitapplicationsvaluedata: data,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
}
|
||||
|
||||
onChangeRadioGroup = (e) => {
|
||||
this.setState({
|
||||
RadioGroupvalue: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
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({
|
||||
isRender: true
|
||||
})
|
||||
}
|
||||
educoderloginysl = () => {
|
||||
//退出账号
|
||||
var url = `/accounts/logout.json`;
|
||||
axios.get((url)).then((result) => {
|
||||
if (result !== undefined) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
hideAddcoursestypes = () => {
|
||||
this.setState({
|
||||
Addcoursestypes: false
|
||||
})
|
||||
};
|
||||
HideAddcoursestypess = (i) => {
|
||||
console.log("调用了");
|
||||
this.setState({
|
||||
Addcoursestypes: false,
|
||||
mydisplay: true,
|
||||
occupation: i,
|
||||
})
|
||||
};
|
||||
ModalCancelsy = () => {
|
||||
this.setState({
|
||||
mydisplay: false,
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
|
||||
setevaluatinghides = () => {
|
||||
this.setState({
|
||||
setevaluatinghides: true
|
||||
})
|
||||
}
|
||||
//修改登录方法
|
||||
Modifyloginvalue = () => {
|
||||
this.setState({
|
||||
isRender: false,
|
||||
})
|
||||
}
|
||||
|
||||
hideAccountProfile = () => {
|
||||
this.setState({
|
||||
AccountProfiletype: false
|
||||
})
|
||||
};
|
||||
headtypesonClick = (url, bool) => {
|
||||
this.setState({
|
||||
headtypess: url,
|
||||
headtypesonClickbool: bool,
|
||||
})
|
||||
}
|
||||
//获取数据为空的时候
|
||||
gettablogourlnull = () => {
|
||||
this.setState({
|
||||
settings: undefined
|
||||
});
|
||||
var link = document.createElement('link'),
|
||||
oldLink = document.getElementById('dynamic-favicon');
|
||||
link.id = 'dynamic-favicon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = "/react/build/./favicon.ico";
|
||||
if (oldLink) {
|
||||
document.head.removeChild(oldLink);
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
};
|
||||
|
||||
//获取数据的时候
|
||||
gettablogourldata = (response) => {
|
||||
document.title = response.data.setting.name;
|
||||
var link = document.createElement('link'),
|
||||
oldLink = document.getElementById('dynamic-favicon');
|
||||
link.id = 'dynamic-favicon';
|
||||
link.rel = 'shortcut icon';
|
||||
link.href = '/' + response.data.setting.tab_logo_url;
|
||||
if (oldLink) {
|
||||
document.head.removeChild(oldLink);
|
||||
}
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
handleVisibleChanges = (boll) => {
|
||||
this.setState({
|
||||
visiblemyss: boll,
|
||||
})
|
||||
}
|
||||
|
||||
getAppdata = () => {
|
||||
try {
|
||||
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
|
||||
var chromesettingresponseArray = JSON.parse(localStorage.getItem('chromesettingresponse'));
|
||||
this.setState({
|
||||
settings: chromesettingArray
|
||||
});
|
||||
if (chromesettingArray.tab_logo_url) {
|
||||
this.gettablogourldata(chromesettingresponseArray);
|
||||
} else {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
} catch (e) {
|
||||
this.geturlsdata();
|
||||
}
|
||||
};
|
||||
|
||||
geturlsdata = () => {
|
||||
let url = "/setting.json";
|
||||
axios.get(url).then((response) => {
|
||||
if (response && response.data) {
|
||||
this.setState({ settings: response.data.setting });
|
||||
// localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
|
||||
// localStorage.setItem('chromesettingresponse', JSON.stringify(response));
|
||||
try {
|
||||
if (response.data.setting.tab_logo_url) {
|
||||
this.gettablogourldata(response);
|
||||
} else {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
} catch (e) {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
} else {
|
||||
this.gettablogourlnull();
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.gettablogourlnull();
|
||||
});
|
||||
}
|
||||
|
||||
matchpaths = (url) => {
|
||||
const { match } = this.props;
|
||||
if(url){
|
||||
if (match.path.indexOf(url) > -1) {
|
||||
return true
|
||||
}else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理弹框
|
||||
setgoshowqqgtounp = (bool) => {
|
||||
this.setState({
|
||||
goshowqqgtounp: bool
|
||||
})
|
||||
}
|
||||
|
||||
addMenu=(list)=>{
|
||||
return(
|
||||
list && list.length >0 &&
|
||||
<div className="dropdownFlex">
|
||||
<Menu>
|
||||
{
|
||||
list.map((item,key)=>{
|
||||
return(
|
||||
(item.name !=="加入课堂" && item.name !=="加入开发项目") && <Menu.Item><a href={item.url}>{item.name}</a></Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match} = this.props;
|
||||
let current_user = this.props.user;
|
||||
let { Addcoursestypes,
|
||||
tojoinitemtype,
|
||||
tojoinclasstitle,
|
||||
code_notice,
|
||||
checked_notice,
|
||||
AccountProfiletype,
|
||||
submitapplications,
|
||||
submitapplicationsvalue,
|
||||
user,
|
||||
isRender,
|
||||
showSearchOpentype,
|
||||
headtypesonClickbool,
|
||||
headtypess,
|
||||
settings,
|
||||
goshowqqgtounp,
|
||||
openSearch,
|
||||
} = this.state;
|
||||
/*用户名称 用户头像url*/
|
||||
let activeIndex = false;
|
||||
let activeForums = false;
|
||||
let activeShixuns = false;
|
||||
let activePaths = false;
|
||||
let coursestype = false;
|
||||
let activePackages = false;
|
||||
let activeMoopCases = false;
|
||||
let activeCompetitions = false;
|
||||
|
||||
if (match.path === '/forums') {
|
||||
activeForums = true;
|
||||
} else if (match.path.startsWith('/shixuns')) {
|
||||
activeShixuns = true;
|
||||
} else if (match.path.startsWith('/paths')) {
|
||||
activePaths = true;
|
||||
} else if (match.path.startsWith('/courses')) {
|
||||
coursestype = true;
|
||||
} else if (match.path.startsWith('/crowdsourcing')) {
|
||||
activePackages = true;
|
||||
} else if (match.path.startsWith('/moop_cases')) {
|
||||
activeMoopCases = true;
|
||||
} else if (match.path.startsWith('/competitions')) {
|
||||
activeCompetitions = true;
|
||||
} else {
|
||||
activeIndex = true;
|
||||
}
|
||||
|
||||
let headtypes = '/';
|
||||
if (settings) {
|
||||
if (settings.navbar) {
|
||||
if (settings.navbar.length > 0) {
|
||||
if (match.path === '/') {
|
||||
if (headtypesonClickbool === false) {
|
||||
headtypes = undefined;
|
||||
} else {
|
||||
headtypes = headtypess;
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < settings.navbar.length; i++) {
|
||||
if (match.path === settings.navbar[i].link) {
|
||||
headtypes = settings.navbar[i].link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let shixuntype = false;
|
||||
let pathstype = false;
|
||||
let coursestypes = false;
|
||||
if (this.props && this.props.mygetHelmetapi != null) {
|
||||
let shixun = "/shixuns";
|
||||
let paths = "/paths";
|
||||
let courses = "/courses";
|
||||
this.props.mygetHelmetapi.navbar.map((item, key) => {
|
||||
var reg = RegExp(item.link);
|
||||
if (shixun.match(reg)) {
|
||||
if (item.hidden === true) {
|
||||
shixuntype = true
|
||||
}
|
||||
}
|
||||
if (paths.match(reg)) {
|
||||
if (item.hidden === true) {
|
||||
pathstype = true
|
||||
}
|
||||
}
|
||||
if (courses.match(reg)) {
|
||||
if (item.hidden === true) {
|
||||
coursestypes = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let search_url = settings && settings.common && settings.common.search;
|
||||
let notice_url = settings && settings.common && settings.common.notice;
|
||||
return (
|
||||
<div className="newHeaders" id="nHeader">
|
||||
<div className="headerContent">
|
||||
{isRender === true ?
|
||||
<LoginDialog
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
Modifyloginvalue={() => this.Modifyloginvalue()}
|
||||
/> : ""}
|
||||
|
||||
{AccountProfiletype === true ?
|
||||
<AccountProfile
|
||||
hideAccountProfile={() => this.hideAccountProfile()}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
/> : ""}
|
||||
{
|
||||
goshowqqgtounp === true ?
|
||||
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
|
||||
:""
|
||||
}
|
||||
<a href={settings && settings.new_course.default_url} className={"fl mr30"} style={{minWidth:"45px"}}>
|
||||
{
|
||||
settings && settings.nav_logo_url ?
|
||||
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={getImageUrl(`/${settings.nav_logo_url}`)}></img>
|
||||
:
|
||||
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={logo}></img>
|
||||
}
|
||||
</a>
|
||||
<div className="head-nav pr" id={"head-navpre1"}>
|
||||
{
|
||||
settings && settings.navbar && settings.navbar.length > 0 ?
|
||||
<ul id="header-nav">
|
||||
{
|
||||
settings.navbar && settings.navbar.map((item, key) => {
|
||||
var new_link = item.link;
|
||||
var user_login = this.props.user && this.props.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, "users/" + user_login + "/courses")
|
||||
} else if (new_link.indexOf("contests") > -1) {
|
||||
new_link = new_link.replace(/contests/g, "users/" + user_login + "/contests")
|
||||
}
|
||||
} else {
|
||||
is_hidden = true
|
||||
}
|
||||
}
|
||||
if (user_login && (new_link && new_link.indexOf("homes") > -1)) {
|
||||
new_link = new_link.replace(/homes/g, "users/" + user_login + "/user_activities")
|
||||
}
|
||||
|
||||
var waiLian = (new_link && str.filter(item=>new_link.indexOf(item)>-1) );
|
||||
var wl = waiLian && waiLian.length>0;
|
||||
return (
|
||||
<li key={key} onClick={() => this.headtypesonClick(item.link, true)} className={`${this.matchpaths(item.link) === true ? 'pr active' : 'pr'}`} style={!is_hidden ? { display: 'flex' } : { display: 'none' }}>
|
||||
<a href={new_link} target={wl ? "_self":"_blank"}>{item.name}</a>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
<div className="head-right">
|
||||
{search_url ? this.SearchInput(openSearch,search_url):""}
|
||||
{
|
||||
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)?
|
||||
<Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">
|
||||
<i className="iconfont icon-tianjiafangda color-grey-6 ml30"></i>
|
||||
</Dropdown>:""
|
||||
}
|
||||
|
||||
{this.props.user && this.props.user.login && notice_url ?
|
||||
<div className="ml30 edu-menu-panel">
|
||||
{user && user.login &&
|
||||
<a href={`${notice_url}`} style={{ position: 'relative' }}>
|
||||
<i className="iconfont icon-xiaoxilingdang color-grey-6"></i>
|
||||
<span className="newslight" style={{ display: this.props.Headertop === undefined ? "none" : this.props.Headertop.new_message === true ? "block" : "none" }}>
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
</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">
|
||||
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
|
||||
{
|
||||
settings && settings.common && settings.common.register &&
|
||||
<span><em className="vertical-line"></em><a className="ml5 color-grey-6" href={`${settings.common.register}`} target="_blank">注册</a></span>
|
||||
}
|
||||
</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>
|
||||
</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>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewHeader;
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
.dropdownFlex{
|
||||
display:flex;
|
||||
padding:5px;
|
||||
background:#fff;
|
||||
border-radius: 3px;
|
||||
.ant-menu-vertical > .ant-menu-item{
|
||||
border:none
|
||||
}
|
||||
}
|
||||
|
||||
.newFooter {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: #323232;
|
||||
clear: both;
|
||||
min-width: 1200px;
|
||||
z-index: 8;
|
||||
left: 0px;
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom:0px !important;
|
||||
}
|
||||
.footerInfos{
|
||||
display: flex;
|
||||
max-width: 1200px;
|
||||
margin:0px auto;
|
||||
justify-content: space-around;
|
||||
padding:60px 0px;
|
||||
& >ul{
|
||||
padding:0px 40px;
|
||||
box-sizing: border-box;
|
||||
max-width: 25%;
|
||||
text-align: left;
|
||||
li{
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
&:first-child{
|
||||
font-size: 17px;
|
||||
}
|
||||
&>a,&>span{
|
||||
color: #bbb;
|
||||
}
|
||||
&>a:hover{
|
||||
color: #4cacff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.footerCopy{
|
||||
color: #bbb;
|
||||
border-top: 1px solid #4e4e4e;
|
||||
padding:10px 0px;
|
||||
a{
|
||||
color: #bbb;
|
||||
&:hover{
|
||||
color: #4cacff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import { withRouter } from "react-router";
|
|||
import { SnackbarHOC } from "educoder";
|
||||
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
|
||||
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
|
||||
import Handbook from './Component/Handbook';
|
||||
import "./css/index.scss";
|
||||
|
||||
import Loadable from "react-loadable";
|
||||
|
@ -31,20 +30,29 @@ const Infos = Loadable({
|
|||
loader: () => import("./users/Infos"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
class Index extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<Handbook />
|
||||
<Switch {...this.props}>
|
||||
<Route
|
||||
path="/projects/:projectsType/new/:OIdentifier"
|
||||
render={(props) => (
|
||||
<ProjectNew {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/projects/:projectsType/new"
|
||||
render={(props) => (
|
||||
<ProjectNew {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/projects/new"
|
||||
render={(props) => (
|
||||
<ProjectNew {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/projects/:owner/:projectsId"
|
||||
render={(props) => (
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { WhiteBack , Box , LongWidth , ShortWidth , Gap , AlignCenter , FlexAJ } from '../Component/layout';
|
||||
import { Dropdown , Menu , Divider , Spin, Button } from 'antd';
|
||||
import { getImageUrl } from "educoder";
|
||||
import { Link } from 'react-router-dom';
|
||||
import CloneAddress from '../Branch/CloneAddress';
|
||||
|
||||
import SelectBranch from '../Branch/Select';
|
||||
import User from '../Component/User';
|
||||
import axios from 'axios';
|
||||
import Path from './CoderDepotPath';
|
||||
import Catalogue from './CoderDepotCatalogue';
|
||||
import ReadMe from './CoderDepotReadme';
|
||||
import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
import './Index.scss';
|
||||
import Releases from '../Component/Releases';
|
||||
import Contributors from '../Component/Contributors';
|
||||
import LanguagePower from '../Component/LanguagePower';
|
||||
import DrawerPanel from '../Component/DrawerPanel';
|
||||
import UpdateDescModal from './sub/UpdateDescModal';
|
||||
import Nodata from '../Nodata';
|
||||
|
||||
/**
|
||||
* projectDetail.type:0是托管项目,1是镜像项目,2是同步镜像项目(为2时不支持在线创建、在线上传、在线修改、在线删除、创建合并请求等功能)
|
||||
*/
|
||||
function CoderDepot(props){
|
||||
const [ projectDetail , setProjectDetail ]= useState(undefined);
|
||||
const [ treeValue , setTreeValue ] = useState(undefined);
|
||||
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
|
||||
const [ lastCommit,setLastCommit ] = useState(undefined);
|
||||
const [ lastCommitAuthor,setLastCommitAuthor ] = useState(undefined);
|
||||
const [ type ,setType ] = useState('dir');
|
||||
const [ hide , setHide ] = useState(true);
|
||||
const [ hideBtn , setHideBtn ] = useState(false);
|
||||
const [ commitCount ,setCommitCount ] = useState(0);
|
||||
const [ dirInfo ,setDirInfo ] = useState(undefined);//文件夹目录列表
|
||||
const [ fileInfo ,setFileInfo ] = useState(undefined);//文件内容信息
|
||||
const [ zip_url , setZip_url ] = useState(undefined);
|
||||
const [ tar_url , setTar_url ] = useState(undefined);
|
||||
const [ readOnly , setReadOnly] = useState(true);
|
||||
const [ isSpin , setIsSpin] = useState(true);
|
||||
const [ visible ,setVisible ] = useState(false);
|
||||
const [ mainFlag ,setMainFlag ] = useState(false);
|
||||
const [ openModal , setOpenModal ] = useState(false);
|
||||
const [ desc , setDesc ] = useState(undefined);
|
||||
const [ website , setWebsite ] = useState(undefined);
|
||||
const [ lesson_url , setLessonUrl ] = useState(undefined);
|
||||
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const branchName = props.match.params.branchName;
|
||||
let pathname = props.history.location.pathname;
|
||||
useEffect(()=>{
|
||||
if(props.projectDetail){
|
||||
setProjectDetail(props.projectDetail);
|
||||
setDesc(props.projectDetail.description);
|
||||
setWebsite(props.projectDetail.website);
|
||||
setLessonUrl(props.projectDetail.lesson_url);
|
||||
}
|
||||
},[props])
|
||||
|
||||
useEffect(()=>{
|
||||
if(treeValue){
|
||||
setTreeValuePath(treeValue.split('/'));
|
||||
}else{
|
||||
setTreeValuePath(undefined);
|
||||
}
|
||||
},[treeValue])
|
||||
|
||||
useEffect(()=>{
|
||||
if (pathname && projectDetail){
|
||||
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${branchName}/`) > -1) {
|
||||
let url = pathname.split(`/tree/${branchName}/`)[1];
|
||||
setTreeValue(url);
|
||||
getFileInfo(url,branchName);
|
||||
}else{
|
||||
setTreeValue(undefined);
|
||||
getDirInfo(branchName ||projectDetail.default_branch);
|
||||
}
|
||||
}
|
||||
},[pathname,projectDetail])
|
||||
|
||||
// 获取主目录列表
|
||||
function getDirInfo(branch){
|
||||
setIsSpin(true);
|
||||
const url = `/${owner}/${projectsId}/entries.json`;
|
||||
axios.get(url, {
|
||||
params: { ref: branch }
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
setCommitCount(result.data.commits_count);
|
||||
setDirInfo(result.data.entries);
|
||||
setFileInfo(undefined);
|
||||
setTar_url(result.data.tar_url);
|
||||
setZip_url(result.data.zip_url);
|
||||
let c = result.data.last_commit
|
||||
setLastCommit(c && c.commit);
|
||||
setLastCommitAuthor(c && c.committer);
|
||||
setMainFlag(true);
|
||||
}
|
||||
setTimeout(function(){setIsSpin(false);},500);
|
||||
}).catch(error=>{setIsSpin(false);})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail && lastCommit)
|
||||
{
|
||||
let ele = document.getElementById("ptxt");
|
||||
if(ele){
|
||||
let h = ele.offsetHeight;
|
||||
if( h > 18 ) setHideBtn(true)
|
||||
}
|
||||
}
|
||||
},[projectDetail,lastCommit])
|
||||
// 获取子目录列表
|
||||
function getFileInfo(path, ref){
|
||||
setIsSpin(true);
|
||||
const url = `/${owner}/${projectsId}/sub_entries.json`;
|
||||
axios.get(url, {
|
||||
params:{
|
||||
filepath:path,
|
||||
ref:ref || branchName,
|
||||
type
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
let en = result.data.entries;
|
||||
if(en.type){
|
||||
setDirInfo(undefined);
|
||||
setFileInfo(en);
|
||||
setType(en.type);
|
||||
}else{
|
||||
setFileInfo(undefined);
|
||||
setDirInfo(en);
|
||||
setType("dir");
|
||||
}
|
||||
let c = result.data.last_commit
|
||||
setLastCommit(c && c.commit);
|
||||
setLastCommitAuthor(c && c.committer);
|
||||
setMainFlag(false);
|
||||
}
|
||||
setTimeout(function(){setIsSpin(false);},500)
|
||||
}).catch(error=>{setIsSpin(false);})
|
||||
}
|
||||
|
||||
// 切换分支或者标签
|
||||
function changeBranch(value){
|
||||
let url = `/projects/${owner}/${projectsId}${value && `/tree/${value}`}${treeValue ? `/${treeValue}`:""}`;
|
||||
props.history.push(url);
|
||||
}
|
||||
|
||||
// 文件相关的下拉项
|
||||
const fileMenu =(
|
||||
<Menu>
|
||||
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
|
||||
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
|
||||
function getPathUrl(array,index){
|
||||
if(array && array.length>0 && index){
|
||||
let str = "";
|
||||
for(let i=0;i<index;i++){
|
||||
str += `/${array[i]}`;
|
||||
}
|
||||
return str.substr(1);
|
||||
}
|
||||
}
|
||||
// 页面地址返回到主目录
|
||||
function returnMain(){
|
||||
setTreeValue(undefined);
|
||||
let branch = branchName || (projectDetail && projectDetail.default_branch);
|
||||
props.history.push(`/projects/${owner}/${projectsId}/tree/${branch}`);
|
||||
};
|
||||
// 子目录路径返回链接
|
||||
function returnUlr(url){
|
||||
props.history.push(`/projects/${owner}/${projectsId}/tree${branchName?`/${branchName}`:""}/${url}`);
|
||||
}
|
||||
// 点击跳转到子目录
|
||||
function goToSubRoot(path,type,filename){
|
||||
setType(type);
|
||||
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${branchName || (projectDetail && projectDetail.default_branch)}`}${path?`/${path}`:""}`);
|
||||
}
|
||||
|
||||
function onEdit(readOnly){
|
||||
setReadOnly(readOnly);
|
||||
}
|
||||
function ChangeFile(path, readOnly){
|
||||
//点击直接跳转页面 加载一次路由
|
||||
props.history.push(`/projects/${owner}/${projectsId}/tree/${branchName || (projectDetail && projectDetail.default_branch)}/${path}`);
|
||||
setType("file");
|
||||
setReadOnly(readOnly);
|
||||
};
|
||||
|
||||
function changeHide(hide){
|
||||
setHide(!hide);
|
||||
}
|
||||
|
||||
function urlLink(link){
|
||||
if(props.checkIfLogin()===false){
|
||||
props.showLoginDialog()
|
||||
return false;
|
||||
}
|
||||
props.history.push(link);
|
||||
}
|
||||
|
||||
const downloadMenu = (
|
||||
<div className="downMenu">
|
||||
<div style={{padding:"20px",borderBottom:"1px solid #eee"}}>
|
||||
<CloneAddress
|
||||
http_url={projectDetail && projectDetail.clone_url}
|
||||
showNotification={props.showNotification}/>
|
||||
</div>
|
||||
<Menu className="edu-txt-center">
|
||||
<Menu.Item><a href={zip_url}>下载 ZIP</a></Menu.Item>
|
||||
<Menu.Item><a href={tar_url}>下载 TAR.GZ</a></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
// 确认修改简介、website、实践课程链接
|
||||
function okUpdate(d,w,l){
|
||||
const url = `/${owner}/${projectsId}.json`;
|
||||
axios.put(url,{
|
||||
description:d,website:w,lesson_url:l
|
||||
}).then(result=>{
|
||||
if(result && result.data && result.data.id){
|
||||
setDesc(result.data.description);
|
||||
setWebsite(result.data.website);
|
||||
setLessonUrl(result.data.lesson_url);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let n = fileInfo && fileInfo.name;
|
||||
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
|
||||
|
||||
return(
|
||||
<WhiteBack>
|
||||
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
|
||||
<Spin spinning={isSpin}>
|
||||
{
|
||||
(dirInfo || fileInfo) &&
|
||||
<React.Fragment>
|
||||
<DrawerPanel
|
||||
history={props.history}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
name={projectDetail && projectDetail.name}
|
||||
branch={branchName || (projectDetail && projectDetail.default_branch)}
|
||||
visible={visible}
|
||||
onClose={()=>setVisible(false)}
|
||||
list = {mainFlag ? dirInfo : undefined}
|
||||
/>
|
||||
<div className="drawerBtn" onClick={()=>setVisible(true)}>
|
||||
<i className="iconfont icon-youjiantou font-16"></i>
|
||||
<span>目录</span>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
<div style={{minHeight:"500px"}}>
|
||||
{
|
||||
projectDetail &&
|
||||
<Box className="Panels">
|
||||
<LongWidth>
|
||||
<div className="panelmenu">
|
||||
<FlexAJ>
|
||||
<AlignCenter>
|
||||
<div className="mr20">
|
||||
{
|
||||
props && props.platform ?
|
||||
<SelectBranch
|
||||
repo_id={projectDetail && projectDetail.repo_id}
|
||||
projectsId={projectsId}
|
||||
branch={branchName || (projectDetail && projectDetail.default_branch)}
|
||||
changeBranch={changeBranch}
|
||||
owner={owner}
|
||||
history={props.history}
|
||||
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
|
||||
></SelectBranch>
|
||||
:
|
||||
<span>分支:<span className="color-grey-6">{branchName || (projectDetail && projectDetail.default_branch)}</span></span>
|
||||
}
|
||||
</div>
|
||||
<AlignCenter className="mr20">
|
||||
<Link to={`/projects/${owner}/${projectsId}/branchs`} className="color-grey-9">
|
||||
<i className="iconfont icon-fenzhi2 font-18 color-grey-9 mr3"></i>
|
||||
<span className="color-grey-6 mr3">{projectDetail && projectDetail.branches && projectDetail.branches.total_count}个</span>分支
|
||||
</Link>
|
||||
</AlignCenter>
|
||||
<AlignCenter className="mr20">
|
||||
<Link to={`/projects/${owner}/${projectsId}/tag`} className="color-grey-9">
|
||||
<i className="iconfont icon-biaoqian3 font-16 color-grey-9 mr3"></i>
|
||||
<span className="color-grey-6 mr3">{projectDetail && projectDetail.tags && projectDetail.tags.total_count}个</span>标签
|
||||
</Link>
|
||||
</AlignCenter>
|
||||
</AlignCenter>
|
||||
<AlignCenter>
|
||||
<div className="mr20 addOptionBtn">
|
||||
{
|
||||
projectDetail.type !== 2 &&
|
||||
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</a>
|
||||
}
|
||||
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a>
|
||||
</div>
|
||||
{ type === "dir" && projectDetail.type !== 2 &&
|
||||
<Dropdown overlay={fileMenu} className="mr20">
|
||||
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button>
|
||||
</Dropdown>
|
||||
}
|
||||
|
||||
<Dropdown overlay={downloadMenu} placement="bottomRight">
|
||||
<Button type={'primary'}>下载 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-white"></i></Button>
|
||||
</Dropdown>
|
||||
</AlignCenter>
|
||||
</FlexAJ>
|
||||
{
|
||||
dirInfo || fileInfo ?
|
||||
<div className="listtable">
|
||||
{
|
||||
lastCommit &&
|
||||
<div className="listtablehead">
|
||||
<User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/>
|
||||
<div className={hideBtn && hide ? "ellipsistxt hide" :"ellipsistxt"}><p id="ptxt">{lastCommit && lastCommit.message}</p></div>
|
||||
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
|
||||
|
||||
<span className="ml12 color-grey-9 mt3">{lastCommit && lastCommit.time_from_now}</span>
|
||||
{ commitCount ? <Link to={`/projects/${owner}/${projectsId}/commits`} className="ml12 color-grey-9"><i className="iconfont icon-tijiao mr3 font-17 color-grey-9"></i>{commitCount}次提交</Link>:"" }
|
||||
</div>
|
||||
}
|
||||
<ul className="listtablebody">
|
||||
{
|
||||
treeValuePath && treeValuePath.length > 0 &&
|
||||
<Path
|
||||
identifier={projectDetail && projectDetail.identifier}
|
||||
treeValuePath={treeValuePath}
|
||||
returnUlr={returnUlr}
|
||||
returnMain={returnMain}
|
||||
getPathUrl={getPathUrl}
|
||||
/>
|
||||
}
|
||||
{
|
||||
dirInfo && dirInfo.length > 0 &&
|
||||
dirInfo.map((item,key)=>{
|
||||
return(
|
||||
<Catalogue
|
||||
owner={owner}
|
||||
item={item}
|
||||
projectsId={projectsId}
|
||||
goToSubRoot={goToSubRoot}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
fileInfo &&
|
||||
<CoderRootFileDetail
|
||||
{...props}
|
||||
detail={fileInfo}
|
||||
readOnly={readOnly}
|
||||
md={mdFlag}
|
||||
onEdit={onEdit}
|
||||
currentBranch={branchName || (projectDetail && projectDetail.default_branch)}
|
||||
type={projectDetail.type}
|
||||
></CoderRootFileDetail>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
: ""
|
||||
}
|
||||
{
|
||||
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
|
||||
}
|
||||
{/* readme文件显示(显示文件详情时不显示readme文件) */}
|
||||
{ dirInfo && (projectDetail && projectDetail.readme) ? <ReadMe ChangeFile={ChangeFile} readme={projectDetail && projectDetail.readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
|
||||
</div>
|
||||
</LongWidth>
|
||||
{
|
||||
!fileInfo &&
|
||||
<ShortWidth>
|
||||
<Gap style={{paddingLeft:"30px"}}>
|
||||
<div className="panelmenu">
|
||||
<FlexAJ className="font-18 color-grey-6 mb20" style={{lineHeight:"28px"}}>简介
|
||||
{projectDetail.permission && (projectDetail.permission==="Admin" || projectDetail.permission==="Owner") && <i onClick={()=>setOpenModal(true)} className="iconfont icon-anquanshezhi color-grey-9 font-15"></i>}
|
||||
</FlexAJ>
|
||||
{desc && <p className="font-14 color-grey-9 mb15 task-hide-2" style={{lineHeight:"22px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}}>{desc}</p>}
|
||||
{
|
||||
website &&
|
||||
<p className="color-grey-6 df">
|
||||
<i className="iconfont icon-lianjie2 font-15 mr10 color-grey-9"></i>
|
||||
<a href={website} target="_blank" style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px",textDecoration:"underline"}}>{website}</a>
|
||||
</p>
|
||||
}
|
||||
<p>
|
||||
<i className="iconfont icon-wenjian4 font-15 mr10 color-grey-9"></i>
|
||||
<a href="#readme" className="color-grey-6">README.md</a>
|
||||
</p>
|
||||
<p className="color-grey-6">
|
||||
<i className="iconfont icon-dataBase font-15 mr10 color-grey-9"></i>
|
||||
<span>{projectDetail && projectDetail.size}</span>
|
||||
</p>
|
||||
{
|
||||
projectDetail && projectDetail.license_name &&
|
||||
<p className="color-grey-6">
|
||||
<i className="iconfont icon-tianping font-16 mr10 color-grey-9"></i>
|
||||
<span>{projectDetail.license_name}</span>
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
lesson_url &&
|
||||
<div>
|
||||
<Divider />
|
||||
<p className="font-16 color-grey-6">实践课程</p>
|
||||
<a href={lesson_url} target="_blank" className="color-grey-6" style={{textDecoration:"underline"}}>{lesson_url}</a>
|
||||
</div>
|
||||
}
|
||||
{/* 发布 */}
|
||||
{
|
||||
projectDetail && projectDetail.release_versions &&
|
||||
<React.Fragment>
|
||||
<Divider />
|
||||
<Releases owner={owner} projectsId={projectsId} releaseVersions={projectDetail.release_versions} history={props.history}/>
|
||||
</React.Fragment>
|
||||
}
|
||||
{/* 贡献者 */}
|
||||
{
|
||||
projectDetail && projectDetail.contributors &&
|
||||
<Contributors contributors={projectDetail && projectDetail.contributors} owner={owner} projectsId={projectsId} />
|
||||
}
|
||||
{/* 语言 */}
|
||||
{ projectDetail && projectDetail.languages &&
|
||||
<React.Fragment>
|
||||
<Divider />
|
||||
<LanguagePower languages={projectDetail.languages}/>
|
||||
</React.Fragment>
|
||||
}
|
||||
</Gap>
|
||||
</ShortWidth>
|
||||
}
|
||||
</Box>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</WhiteBack>
|
||||
)
|
||||
}
|
||||
export default CoderDepot;
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { truncateCommitId } from '../common/util';
|
||||
|
||||
function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId }){
|
||||
return(
|
||||
<li>
|
||||
<span>
|
||||
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)}>
|
||||
<i className={item.type === 'dir' ? "iconfont icon-wenjianjia1 color-green-file font-15 mr5":"iconfont icon-wenjia color-green-file font-15 mr5"}></i>{item.name}
|
||||
</a>
|
||||
</span>
|
||||
<span title="init project">
|
||||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.commit && item.commit.sha}`)}`} title={item.commit && item.commit.message}>
|
||||
{item.commit && item.commit.message}
|
||||
</Link>
|
||||
</span>
|
||||
<span>{item.commit && item.commit.time_from_now}</span>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default CoderDepotCatalogue;
|
|
@ -0,0 +1,30 @@
|
|||
import React from 'react';
|
||||
|
||||
|
||||
function CoderDepotPath({treeValuePath , returnUlr , returnMain , getPathUrl , identifier}){
|
||||
return(
|
||||
<li className="listtablepath">
|
||||
<p>
|
||||
<a
|
||||
onClick={returnMain}
|
||||
className="color-blue"
|
||||
>
|
||||
{identifier}
|
||||
</a>
|
||||
{treeValuePath.map((item, key) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{
|
||||
key === treeValuePath.length-1 ?
|
||||
<span className="color-grey-6 subFileName" key={key}>{item}</span>
|
||||
:
|
||||
<a onClick={()=>returnUlr(`${getPathUrl(treeValuePath,key+1)}`)} className="color-blue subFileName">{item}</a>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
export default CoderDepotPath;
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import RenderHtml from '../../components/render-html';
|
||||
import { Dropdown , Menu , Spin } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
const $ = window.$;
|
||||
|
||||
function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
||||
const [ menuList ,setMenuList ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(readme && readme.content){
|
||||
let path = history.location.pathname;
|
||||
const items = $.map($("#readme").find("h1,h2,h3,h4,h5,h6"), function (el, _) {
|
||||
const anchor = el.id;
|
||||
const level = el.tagName.replace("H", "");
|
||||
const href = `#${anchor}`;
|
||||
return { href:`${path}${href}`,text:el.textContent , level:level }
|
||||
});
|
||||
setMenuList(items);
|
||||
}
|
||||
},[readme])
|
||||
|
||||
function menu(){
|
||||
if(menuList && menuList.length > 0){
|
||||
let hash = history.location.hash;
|
||||
return(
|
||||
<Menu className="menuslist">
|
||||
{
|
||||
menuList.map((item,key)=>{
|
||||
return(
|
||||
<Menu.Item key={item.id} className={decodeURI(hash).indexOf(item.text)>-1 ?"active":""}><Link to={`${item.href}`} style={{paddingLeft:`${item.level *10}px`}} title={item.text}>{item.text}</Link></Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
)
|
||||
}else{
|
||||
return <Spin />
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div className="commonBox" id="readme">
|
||||
<div className="commonBox-title">
|
||||
<Dropdown overlay={menu()}>
|
||||
<i className="iconfont icon-zhangjie1 font-16 color-grey-3 mr10"></i>
|
||||
</Dropdown>
|
||||
<span className="commonBox-title-read">README.md</span>
|
||||
{
|
||||
operate ?
|
||||
<a className="ml20 pull-right" onClick={() =>ChangeFile(readme && readme.path, false)}>
|
||||
<i className="iconfont icon-bianji6 font-16 color-blue"></i>
|
||||
</a>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
<div className="commonBox-info">
|
||||
<RenderHtml className="break_word_comments imageLayerParent" value={readme && readme.content} url={history.location}/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default CoderDepotReadme;
|
|
@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
|
|||
import { Dropdown , Menu , Icon , Tooltip , Spin } from 'antd';
|
||||
import { truncateCommitId } from '../common/util';
|
||||
import { getBranch } from '../GetData/getData';
|
||||
|
||||
import Nodata from '../Nodata';
|
||||
import './list.css';
|
||||
|
||||
export default ((props)=>{
|
||||
|
@ -32,7 +32,7 @@ export default ((props)=>{
|
|||
return(
|
||||
<li key={key}>
|
||||
<div>
|
||||
<Link to={`/projects/${owner}/${projectsId}/branch/${item.name}`} className="color-blue font-15" style={{"maxWidth":"100px"}}>{item.name}</Link>
|
||||
<Link to={`/projects/${owner}/${projectsId}/tree/${item.name}`} className="color-blue font-15" style={{"maxWidth":"100px"}}>{item.name}</Link>
|
||||
<p className="f-wrap-alignCenter mt15">
|
||||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.last_commit.sha}`)}`} className="mr5 commitKey" style={{marginLeft:0}}>{item.last_commit && truncateCommitId(item.last_commit.sha)}</Link>
|
||||
<span className="color-grey-3 hide-1 messages leftPoint">{item.last_commit && item.last_commit.message}</span>
|
||||
|
@ -54,6 +54,8 @@ export default ((props)=>{
|
|||
</ul>
|
||||
</React.Fragment>
|
||||
)
|
||||
}else if(data && data.length === 0){
|
||||
return ( <Nodata _html="暂无数据"/>)
|
||||
}
|
||||
}
|
||||
const menu =(zip_url,tar_url)=> (
|
||||
|
@ -68,7 +70,7 @@ export default ((props)=>{
|
|||
<Spin spinning={isSpin}>
|
||||
<div className="branchTable">
|
||||
<p className="branchTitle bor-bottom-greyE">分支列表</p>
|
||||
{list()}
|
||||
<div style={{minHeight:"400px"}}>{list()}</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,6 @@ import { getImageUrl } from 'educoder';
|
|||
import { truncateCommitId } from '../common/util';
|
||||
import SelectBranch from '../Branch/Select';
|
||||
import Nodata from '../Nodata';
|
||||
import { getBranch } from '../GetData/getData';
|
||||
|
||||
import axios from 'axios';
|
||||
import {Link} from "react-router-dom";
|
||||
|
@ -75,6 +74,7 @@ class CoderRootCommit extends Component{
|
|||
array.push({
|
||||
name:item.author && item.author.name,
|
||||
login: item.author && item.author.login,
|
||||
id: item.author && item.author.id,
|
||||
image_url:item.author && item.author.image_url,
|
||||
sha:item.sha,
|
||||
time_from_now:item.time_from_now,
|
||||
|
@ -108,7 +108,7 @@ class CoderRootCommit extends Component{
|
|||
let branch = branchName || defaultBranch;
|
||||
return(
|
||||
<React.Fragment>
|
||||
<div className={commit_class}>
|
||||
<div className={"main"}>
|
||||
<div className="f-wrap-between">
|
||||
<SelectBranch
|
||||
repo_id={projectDetail && projectDetail.repo_id}
|
||||
|
@ -129,7 +129,7 @@ class CoderRootCommit extends Component{
|
|||
</div>
|
||||
<div className="commitList">
|
||||
{
|
||||
commitDatas && commitDatas.length > 0 ? commitDatas.map((item,k)=>{
|
||||
commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{
|
||||
return(
|
||||
<div key={k}>
|
||||
<p className="f-wrap-alignCenter">
|
||||
|
@ -137,15 +137,24 @@ class CoderRootCommit extends Component{
|
|||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="flex1 ml20 font-16 color-grey-3">{item.message}</Link>
|
||||
</p>
|
||||
<p className="f-wrap-alignCenter mt15">
|
||||
<Link to={`/users/${item.login}`} className="show-user-link">
|
||||
{item.image_url?<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
|
||||
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
|
||||
</Link>
|
||||
{
|
||||
item.id ?
|
||||
<Link to={`/users/${item.login}`} className="show-user-link">
|
||||
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
|
||||
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
|
||||
</Link>:
|
||||
<span className="show-user-link">
|
||||
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
|
||||
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
|
||||
</span>
|
||||
}
|
||||
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}):<Nodata _html="暂无数据"/>
|
||||
})
|
||||
}
|
||||
{commitDatas && commitDatas.length === 0 && <Nodata _html="暂无数据"/>}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
|
|
|
@ -1,555 +0,0 @@
|
|||
import React, { Component } from "react";
|
||||
import { Menu, Spin } from "antd";
|
||||
import { getImageUrl } from "educoder";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import './list.css';
|
||||
import SelectBranch from '../Branch/Select';
|
||||
import CloneAddress from '../Branch/CloneAddress';
|
||||
import RootTable from './RootTable';
|
||||
import CoderRootFileDetail from './CoderRootFileDetail';
|
||||
import { truncateCommitId } from '../common/util';
|
||||
import RenderHtml from '../../components/render-html';
|
||||
import Nodata from '../Nodata';
|
||||
import { getBranch } from '../GetData/getData';
|
||||
|
||||
import axios from "axios";
|
||||
/**
|
||||
* address:http和SSH,http_url(对应git地址)
|
||||
* filePath:点击目录时当前目录的路径
|
||||
* subfileType:保存当前点击目录的文件类型(显示目录列表时才显示新建文件,如果点击的是文件就不显示新建文件按钮)
|
||||
* readMeContent:根目录下面的readme文件内容
|
||||
*/
|
||||
function getPathUrl(array,index){
|
||||
if(array && array.length>0 && index){
|
||||
let str = "";
|
||||
for(let i=0;i<index;i++){
|
||||
str += `/${array[i]}`;
|
||||
}
|
||||
return str.substr(1);
|
||||
}
|
||||
}
|
||||
class CoderRootDirectory extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
address: "http",
|
||||
filePath: undefined,
|
||||
subFileType: undefined,
|
||||
readMeContent: undefined,
|
||||
readMeFile: undefined,
|
||||
|
||||
isSpin: true,
|
||||
|
||||
branchList: undefined,
|
||||
fileDetail: undefined,
|
||||
branchLastCommit: undefined,
|
||||
lastCommitAuthor: undefined,
|
||||
|
||||
rootList: undefined,
|
||||
readOnly: true,
|
||||
zip_url:undefined,
|
||||
tar_url:undefined,
|
||||
chooseType:undefined,
|
||||
|
||||
md:false
|
||||
}
|
||||
}
|
||||
changeAddress = (address) => {
|
||||
this.setState({
|
||||
address,
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.Init();
|
||||
this.getBranchs();
|
||||
};
|
||||
// 获取分支列表
|
||||
getBranchs=()=>{
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
axios.get(`/${owner}/${projectsId}/branches.json`).then(result=>{
|
||||
this.setState({
|
||||
branchList:result.data
|
||||
})
|
||||
}).catch((error)=>{})
|
||||
}
|
||||
|
||||
componentDidUpdate = (prevState) => {
|
||||
const { location } = this.props;
|
||||
const prevlocation = prevState && prevState.location;
|
||||
if (location !== prevlocation) {
|
||||
this.setState({
|
||||
isSpin: true,
|
||||
});
|
||||
this.Init();
|
||||
}
|
||||
};
|
||||
|
||||
Init = () => {
|
||||
let { search } = this.props.history.location;
|
||||
const { branchName } = this.props.match.params;
|
||||
const { defaultBranch } = this.props;
|
||||
let branch = branchName || defaultBranch;
|
||||
if (search && (search.indexOf("?url=") > -1 || search.indexOf("&url=") > -1)) {
|
||||
let url = search.split("url=")[1];
|
||||
if(url && decodeURI(url).indexOf("&")){
|
||||
url=decodeURI(url).split("&")[0];
|
||||
}
|
||||
this.getFileDetail(decodeURI(url),branch);
|
||||
} else {
|
||||
this.getProjectRoot(branch);
|
||||
}
|
||||
};
|
||||
|
||||
// 页面地址返回到主目录
|
||||
returnMain = (branch) => {
|
||||
const { projectsId , owner , branchName } = this.props.match.params;
|
||||
this.setState({
|
||||
readOnly:true
|
||||
})
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}${branchName?`/branch/${branchName}`:""}`);
|
||||
this.getProjectRoot(branch);
|
||||
};
|
||||
|
||||
// 获取根目录
|
||||
getProjectRoot = (branch) => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/entries.json`;
|
||||
axios.get(url, { params: { ref: branch } })
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
let last_commit = result.data && result.data.last_commit;
|
||||
let entries = result.data && result.data.entries;
|
||||
this.setState({
|
||||
filePath: undefined,
|
||||
fileDetail: undefined,
|
||||
isSpin: false,
|
||||
branchLastCommit: last_commit && last_commit.commit,
|
||||
lastCommitAuthor:
|
||||
last_commit && (last_commit.author || (last_commit.commit && last_commit.commit.author)),
|
||||
zip_url: result.data.zip_url,
|
||||
tar_url: result.data.tar_url
|
||||
});
|
||||
if (entries && entries.length > 0) {
|
||||
this.renderData(entries);
|
||||
}
|
||||
this.setState({
|
||||
rootList: entries,
|
||||
subFileType: true,
|
||||
});
|
||||
}
|
||||
}).catch((error) => {});
|
||||
};
|
||||
|
||||
ChangeFile = (arr, readOnly) => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
//点击直接跳转页面 加载一次路由
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}?url=${arr.path}`);
|
||||
this.setState({
|
||||
readOnly: readOnly,
|
||||
chooseType:"file"
|
||||
});
|
||||
};
|
||||
|
||||
renderUrl = (name, path, type) => {
|
||||
let list = [];
|
||||
const { filePath } = this.state;
|
||||
if (path.indexOf("/")) {
|
||||
const array = path.split("/");
|
||||
let str = "";
|
||||
array.map((i, k) => {
|
||||
str += "/" + i;
|
||||
return list.push({
|
||||
key: k,
|
||||
index: k,
|
||||
name: i,
|
||||
path: str.substr(1),
|
||||
type: filePath && filePath.length > 0 ? filePath[k] ? filePath[k].type : type : type,
|
||||
});
|
||||
});
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
//点击直接跳转页面 加载一次路由
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}?url=${str.substr(1)}`);
|
||||
} else {
|
||||
list.push({
|
||||
index: 0,
|
||||
name,
|
||||
path,
|
||||
type,
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
filePath: list,
|
||||
});
|
||||
};
|
||||
|
||||
// 获取子目录
|
||||
getFileDetail = (path, ref) => {
|
||||
this.setState({
|
||||
filePath: decodeURI(path),
|
||||
});
|
||||
const { projectsId , owner , branchName } = this.props.match.params;
|
||||
const { chooseType } = this.state;
|
||||
const url = `/${owner}/${projectsId}/sub_entries.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
filepath:path,
|
||||
ref:ref || branchName,
|
||||
type:chooseType
|
||||
}
|
||||
}).then((result)=>{
|
||||
let entries = result.data && result.data.entries;
|
||||
this.setState({
|
||||
isSpin:false
|
||||
})
|
||||
if(result){
|
||||
if(entries){
|
||||
// 返回对象entries.type则是文件类型,否则是文件夹
|
||||
if(entries.type){
|
||||
this.setState({
|
||||
fileDetail:[entries],
|
||||
rootList:undefined,
|
||||
subFileType:false
|
||||
})
|
||||
}else{
|
||||
this.setState({
|
||||
fileDetail:undefined,
|
||||
rootList:entries,
|
||||
branchLastCommit:result.data.last_commit && result.data.last_commit.commit,
|
||||
lastCommitAuthor:result.data.last_commit && (result.data.last_commit.author || (result.data.last_commit.commit && result.data.last_commit.commit.author))
|
||||
})
|
||||
this.renderData(entries);
|
||||
}
|
||||
}else{
|
||||
this.setState({
|
||||
fileDetail:undefined,
|
||||
rootList:undefined,
|
||||
isSpin:false,
|
||||
subFileType:false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
isSpin:false
|
||||
})
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
renderData = (data) => {
|
||||
const rootList = [];
|
||||
const readMeContent = [];
|
||||
const readMeFile = [];
|
||||
data && data.map((item, key) => {
|
||||
rootList.push({
|
||||
key,
|
||||
message: item.commit && item.commit.message,
|
||||
...item,
|
||||
});
|
||||
if (item.is_readme_file) {
|
||||
readMeContent.push({ ...item });
|
||||
readMeFile.push({ ...item });
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
rootList: rootList,
|
||||
readMeContent,
|
||||
readMeFile,
|
||||
});
|
||||
};
|
||||
|
||||
// 点击跳转到子目录
|
||||
goToSubRoot=(path,type,filename)=>{
|
||||
this.setState({
|
||||
chooseType:type
|
||||
})
|
||||
const { projectsId, owner , branchName } = this.props.match.params;
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}${branchName?`/branch/${branchName}`:""}?url=${path}`);
|
||||
if(filename.substring(filename.length - 3) === ".md"){
|
||||
this.setState({
|
||||
md:true
|
||||
})
|
||||
}else{
|
||||
this.setState({
|
||||
md:false
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// readme文件内容
|
||||
renderReadMeContent = (readMeContent, permission) => {
|
||||
const { fileDetail, readMeFile } = this.state;
|
||||
if (fileDetail) {
|
||||
return;
|
||||
}
|
||||
if (readMeContent && readMeContent.length > 0) {
|
||||
return (
|
||||
<div className="commonBox">
|
||||
<div className="commonBox-title">
|
||||
<span className="mr10">
|
||||
<i className="iconfont icon-wenjian1 font-16 color-grey-9 fl mt3"></i>
|
||||
</span>
|
||||
<span className="commonBox-title-read">
|
||||
{readMeContent[0].name}
|
||||
</span>
|
||||
{permission ?
|
||||
<a
|
||||
onClick={() => this.ChangeFile(readMeFile[0], false)}
|
||||
className="ml20 pull-right"
|
||||
>
|
||||
<i className="iconfont icon-bianji6 font-16 color-blue"></i>
|
||||
</a>
|
||||
:
|
||||
""
|
||||
}
|
||||
</div>
|
||||
<div className="commonBox-info">
|
||||
{readMeContent[0].content ?
|
||||
<RenderHtml className="break_word_comments imageLayerParent" value={readMeContent[0].content} url={this.props.history.location}/>
|
||||
:
|
||||
<span>暂无~</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 选择分支
|
||||
changeBranch = (value) => {
|
||||
let { search } = this.props.history.location;
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
|
||||
let url = `/projects/${owner}/${projectsId}${value && `/branch/${value}`}`;
|
||||
if (search && (search.indexOf("?url=") > -1 || search.indexOf("&url=") > -1)) {
|
||||
let u = search.split("url=")[1];
|
||||
if(u && decodeURI(u).indexOf("&")){
|
||||
u=decodeURI(u).split("&")[0];
|
||||
}
|
||||
url += `?url=${u}`;
|
||||
}
|
||||
this.props.history.push(url);
|
||||
}
|
||||
|
||||
// 子目录路径返回链接
|
||||
returnUlr=(url)=>{
|
||||
this.setState({
|
||||
chooseType:"dir",
|
||||
readOnly:true
|
||||
})
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}?url=${url}`);
|
||||
}
|
||||
|
||||
onEdit=(readOnly)=>{
|
||||
this.setState({
|
||||
readOnly
|
||||
})
|
||||
}
|
||||
|
||||
downloadUrl = (zip_url,tar_url) => {
|
||||
return(
|
||||
<Menu>
|
||||
{zip_url && (
|
||||
<Menu.Item>
|
||||
<a href={zip_url}>ZIP</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{tar_url && (
|
||||
<Menu.Item>
|
||||
<a href={tar_url}>TAR.GZ</a>
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
title = (branchLastCommit,lastCommitAuthor) => {
|
||||
if (branchLastCommit) {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
return (
|
||||
<div className="f-wrap-alignCenter">
|
||||
{lastCommitAuthor ? (
|
||||
<React.Fragment>
|
||||
{lastCommitAuthor.login ? (
|
||||
<Link
|
||||
to={`/users/${lastCommitAuthor.login}/projects`}
|
||||
className="show-user-link"
|
||||
>
|
||||
<img
|
||||
src={getImageUrl(`images/${lastCommitAuthor.image_url}`)}
|
||||
className="radius mr10"
|
||||
width="32"
|
||||
height="32"
|
||||
alt=""
|
||||
/>
|
||||
<span className="mr15">{lastCommitAuthor.name}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span className="mr15">{lastCommitAuthor.name}</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${branchLastCommit.sha}`)}`} className="color-blue flex-1 hide-1">
|
||||
{branchLastCommit.message}
|
||||
</Link>
|
||||
<span>{branchLastCommit.time_from_now}</span>
|
||||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${branchLastCommit.sha}`)}`} className="commitKey">
|
||||
{truncateCommitId(branchLastCommit.sha)}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
const { branchLastCommit , lastCommitAuthor , rootList ,filePath , fileDetail , subFileType , readMeContent, isSpin , zip_url , tar_url , branchList} = this.state;
|
||||
const { isManager , isDeveloper , projectDetail , platform , defaultBranch } = this.props;
|
||||
|
||||
const { projectsId , owner , branchName } = this.props.match.params;
|
||||
let branch = branchName || defaultBranch;
|
||||
const columns = [
|
||||
{
|
||||
key:"name",
|
||||
dataIndex: 'name',
|
||||
width:"30%",
|
||||
render: (text,item) => (
|
||||
<a onClick={()=>this.goToSubRoot(item.path,item.type,text)} className="ml12 task-hide" style={{ display: "block", maxWidth: "345px" }}>
|
||||
<i className={ item.type === "file" ? "iconfont icon-wenjia font-15 color-green-file mr5" : "iconfont icon-wenjianjia1 color-green-file font-15 mr5"}></i>{text}
|
||||
</a>
|
||||
),
|
||||
},
|
||||
{
|
||||
key:"message",
|
||||
dataIndex: "message",
|
||||
width: "60%",
|
||||
render: (text, item) =>
|
||||
item.commit && item.commit.message ?
|
||||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.commit.sha}`)}`} title={item.commit.message} className="task-hide" style={{ display: "block", maxWidth: "670px" }} >
|
||||
{item.commit.message}
|
||||
</Link>
|
||||
: ""
|
||||
},
|
||||
{
|
||||
key:"time_from_now",
|
||||
dataIndex: "time_from_now",
|
||||
width: "10%",
|
||||
className: "edu-txt-right",
|
||||
render: (text, item) =>
|
||||
item.commit && item.commit.time_from_now ?
|
||||
<a title={item.commit.created_at} className="mr12" style={{ cursor: "default", color: "#888" }} >
|
||||
{item.commit.time_from_now}
|
||||
</a>
|
||||
:""
|
||||
},
|
||||
];
|
||||
|
||||
const urlRoot = filePath === undefined ? "" : `/${filePath}`;
|
||||
let array = filePath && filePath.split("/");
|
||||
return (
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="main">
|
||||
<div className="f-wrap-between mb20">
|
||||
<div className="f-wrap-alignCenter">
|
||||
{
|
||||
platform ?
|
||||
<SelectBranch
|
||||
repo_id={projectDetail && projectDetail.repo_id}
|
||||
projectsId={projectsId}
|
||||
branch={branch}
|
||||
changeBranch={this.changeBranch}
|
||||
owner={owner}
|
||||
history={this.props.history}
|
||||
branchList={branchList}
|
||||
></SelectBranch>
|
||||
:
|
||||
<span>分支:<span className="color-grey-6">master</span></span>
|
||||
}
|
||||
|
||||
|
||||
{filePath && (
|
||||
<span className="ml20 font-16">
|
||||
<a
|
||||
onClick={() => this.returnMain(branch)}
|
||||
className="color-blue"
|
||||
>
|
||||
{projectDetail && projectDetail.identifier}
|
||||
</a>
|
||||
{array &&
|
||||
array.map((item, key) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{
|
||||
key === array.length-1 ?
|
||||
<span className="color-grey-6 subFileName" key={key}>{item}</span>
|
||||
:
|
||||
<a onClick={()=>this.returnUlr(`${getPathUrl(array,key+1)}`)} className="color-blue subFileName">{item}</a>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="f-wrap-alignCenter">
|
||||
{subFileType && (projectDetail && parseInt(projectDetail.type)) !== 2 && (isManager || isDeveloper) && platform && (
|
||||
<div>
|
||||
<span>
|
||||
<Link to={`/projects/${owner}/${projectsId}/${branch}/uploadfile${urlRoot}`} >
|
||||
<span className="color-green mr30">上传文件</span>
|
||||
</Link>
|
||||
</span>
|
||||
<span className="mr30">
|
||||
<Link
|
||||
to={`/projects/${owner}/${projectsId}/${branch}/newfile${urlRoot}`}
|
||||
>
|
||||
<span className="color-blue">新建文件</span>
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{projectDetail && projectDetail.clone_url && (
|
||||
<CloneAddress
|
||||
http_url={projectDetail.clone_url}
|
||||
downloadUrl={this.downloadUrl(zip_url,tar_url)}
|
||||
showNotification={this.props.showNotification}
|
||||
></CloneAddress>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 主目录列表 */}
|
||||
{rootList && (
|
||||
<RootTable
|
||||
columns={columns}
|
||||
data={rootList}
|
||||
title={() => this.title(branchLastCommit,lastCommitAuthor)}
|
||||
></RootTable>
|
||||
)}
|
||||
{/* 子目录列表、文件 */}
|
||||
{fileDetail && fileDetail.length > 0 && (
|
||||
<CoderRootFileDetail
|
||||
detail={fileDetail[0]}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
readOnly={this.state.readOnly}
|
||||
onEdit={this.onEdit}
|
||||
currentBranch={branch}
|
||||
></CoderRootFileDetail>
|
||||
)}
|
||||
{
|
||||
!rootList && !fileDetail && <Nodata _html="暂未发现当前文件!"/>
|
||||
}
|
||||
{ rootList && this.renderReadMeContent(readMeContent, isManager || isDeveloper)}
|
||||
</div>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default CoderRootDirectory;
|
|
@ -24,13 +24,25 @@ class CoderRootFileDetail extends Component {
|
|||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
const { detail } = this.props;
|
||||
const { detail , mdFlag } = this.props;
|
||||
this.setState({
|
||||
value: detail.content,
|
||||
});
|
||||
this.languages_total();
|
||||
};
|
||||
|
||||
componentDidUpdate=(prevProps)=>{
|
||||
const { content } = this.props && this.props.detail;
|
||||
const prevcontent = prevProps.detail && prevProps.detail.content;
|
||||
if (content && prevcontent) {
|
||||
if (prevcontent !== content){
|
||||
this.setState({
|
||||
description: content
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
languages_total = () => {
|
||||
const { detail } = this.props;
|
||||
const file_name = detail.path.split("/").pop().split(".").pop();
|
||||
|
@ -164,15 +176,16 @@ class CoderRootFileDetail extends Component {
|
|||
current_user,
|
||||
isManager,
|
||||
isDeveloper,
|
||||
md,
|
||||
currentBranch,
|
||||
platform
|
||||
platform,
|
||||
md,
|
||||
type
|
||||
} = this.props;
|
||||
const { language, languages, description } = this.state;
|
||||
let flag = current_user && current_user.login && (isManager || isDeveloper);
|
||||
const Option = Select.Option;
|
||||
return (
|
||||
<div className="mb20">
|
||||
<React.Fragment>
|
||||
<div className="grid-item branchTitle">
|
||||
<div className="grid-item">
|
||||
<span className="ml20 color-grey-6 font-16">
|
||||
|
@ -185,17 +198,18 @@ class CoderRootFileDetail extends Component {
|
|||
{readOnly ? (
|
||||
<span>
|
||||
{
|
||||
detail.direct_download ?
|
||||
""
|
||||
:
|
||||
!detail.direct_download?
|
||||
<span>
|
||||
<a onClick={() => this.DownLoadFile(detail.download_url)} className="ml20">
|
||||
<i className="iconfont icon-xiazai1 font-15 color-grey-6"></i>
|
||||
</a>
|
||||
<a onClick={() => this.EditFile(false)} className="ml20">
|
||||
<i className="iconfont icon-bianji1 font-15 color-grey-6"></i>
|
||||
</a>
|
||||
</span>
|
||||
{
|
||||
type !==2 &&
|
||||
<a onClick={() => this.EditFile(false)} className="ml20">
|
||||
<i className="iconfont icon-bianji1 font-15 color-grey-6"></i>
|
||||
</a>
|
||||
}
|
||||
</span>:""
|
||||
}
|
||||
</span>
|
||||
) : (
|
||||
|
@ -226,18 +240,21 @@ class CoderRootFileDetail extends Component {
|
|||
</button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
<Popconfirm
|
||||
title="确认删除这个文件?"
|
||||
className="ml20"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
onConfirm={this.deleteFile}
|
||||
>
|
||||
<a>
|
||||
<i className="iconfont icon-shanchu font-15 color-grey-6"></i>
|
||||
</a>
|
||||
</Popconfirm>
|
||||
{
|
||||
type !==2 &&
|
||||
<Popconfirm
|
||||
title="确认删除这个文件?"
|
||||
className="ml20"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
onConfirm={this.deleteFile}
|
||||
>
|
||||
<a>
|
||||
<i className="iconfont icon-shanchu font-15 color-grey-6"></i>
|
||||
</a>
|
||||
</Popconfirm>
|
||||
}
|
||||
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
|
@ -245,11 +262,7 @@ class CoderRootFileDetail extends Component {
|
|||
<div>
|
||||
{detail.image_type ? (
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<img
|
||||
alt=""
|
||||
src={detail.download_url}
|
||||
style={{ maxWidth: "80%" }}
|
||||
/>
|
||||
<img alt="" src={detail.download_url} style={{ maxWidth: "80%" }} />
|
||||
</div>
|
||||
) : detail.direct_download ? (
|
||||
<div className="mt20 text-center">
|
||||
|
@ -268,14 +281,14 @@ class CoderRootFileDetail extends Component {
|
|||
{...this.state}
|
||||
language={language ? language : "javascript"}
|
||||
filepath={`/${detail.path}`}
|
||||
content={detail.content}
|
||||
content={description}
|
||||
readOnly={readOnly}
|
||||
editorType="update"
|
||||
currentBranch={currentBranch}
|
||||
></Meditor>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,6 @@ const UploadFile = Loadable({
|
|||
loader: () => import('../Newfile/upload_file'),
|
||||
loading: Loading,
|
||||
})
|
||||
const CoderRootDirectory = Loadable({
|
||||
loader: () => import('./CoderRootDirectory'),
|
||||
loading: Loading,
|
||||
})
|
||||
const CoderRootCommit = Loadable({
|
||||
loader: () => import('./CoderRootCommit'),
|
||||
loading: Loading,
|
||||
|
@ -71,6 +67,7 @@ class CoderRootIndex extends Component{
|
|||
this.getTopCount(branchName || defaultBranch);
|
||||
}
|
||||
|
||||
// 获取<Top />组件里要显示的数据
|
||||
getTopCount=(branch)=>{
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/top_counts.json`;
|
||||
|
@ -105,13 +102,6 @@ class CoderRootIndex extends Component{
|
|||
(props) => (<FileNew {...this.props} {...props} {...this.state} getTopCount={this.getTopCount} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
{/* diff */}
|
||||
<Route path="/projects/:owner/:projectsId/commits/branch/:branchName"
|
||||
render={
|
||||
(props) => (<CoderRootCommit {...this.props} {...props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/commits/:sha"
|
||||
render={
|
||||
(props) => (<Diff {...this.props} {...props} {...this.state}/>)
|
||||
|
@ -122,7 +112,6 @@ class CoderRootIndex extends Component{
|
|||
() => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
<Route path="/projects/:owner/:projectsId/releases/:versionId/update"
|
||||
render={
|
||||
(props) => (<CoderRootVersionUpdate {...this.props} {...this.state} {...props} />)
|
||||
|
@ -133,33 +122,21 @@ class CoderRootIndex extends Component{
|
|||
() => (<CoderRootVersionNew {...this.props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
<Route path="/projects/:owner/:projectsId/releases"
|
||||
render={
|
||||
() => (<CoderRootVersion {...this.props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
<Route path="/projects/:owner/:projectsId/tag"
|
||||
render={
|
||||
() => (<CoderRootTag {...this.props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/branch/:branchName"
|
||||
render={
|
||||
(props) => (<CoderRootDirectory {...this.props} {...this.state} getTopCount={this.getTopCount} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/branchs"
|
||||
render={
|
||||
() => (<CoderRootBranch {...this.props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId"
|
||||
render={
|
||||
() => (<CoderRootDirectory {...this.props} {...this.state} getTopCount={this.getTopCount} />)
|
||||
}
|
||||
></Route>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -28,8 +28,9 @@ export default (( props, { projectDetail }) => {
|
|||
return (
|
||||
<div className="main">
|
||||
<Spin spinning={isSpin}>
|
||||
{
|
||||
data && data.length > 0 ?
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
data && data.length > 0 &&
|
||||
<div className="div_table">
|
||||
<ul className="ul_thead">
|
||||
<li>
|
||||
|
@ -40,7 +41,7 @@ export default (( props, { projectDetail }) => {
|
|||
</ul>
|
||||
<ul className="ul_tbody">
|
||||
{
|
||||
data && data.length > 0 && data.map((item, key) => {
|
||||
data.map((item, key) => {
|
||||
return (
|
||||
<li>
|
||||
<span className="flex1">
|
||||
|
@ -60,9 +61,9 @@ export default (( props, { projectDetail }) => {
|
|||
}
|
||||
</ul>
|
||||
</div>
|
||||
:
|
||||
<Nodata _html={`暂无标签!`}/>
|
||||
}
|
||||
}
|
||||
{ data && data.length === 0 && <Nodata _html={`暂无标签!`}/> }
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
|||
import { Spin, Tooltip } from 'antd';
|
||||
import { Link, Route, Switch } from 'react-router-dom';
|
||||
import { Content } from '../Component/layout';
|
||||
import DetailBanner from './sub/DetailBanner';
|
||||
import '../css/index.scss'
|
||||
import './list.css';
|
||||
|
||||
|
@ -10,17 +11,6 @@ import Loading from '../../Loading';
|
|||
|
||||
import axios from 'axios';
|
||||
|
||||
import img_1 from '../Images/1.png';
|
||||
import img_2 from '../Images/2.png';
|
||||
import img_3 from '../Images/3.png';
|
||||
import img_6 from '../Images/6.png';
|
||||
import img_7 from '../Images/7.png';
|
||||
import img_parise from '../Images/parise.png';
|
||||
import img_focus from '../Images/focus.png';
|
||||
import img_parised from '../Images/parised.png';
|
||||
import img_focused from '../Images/focused.png';
|
||||
import img_fork from '../Images/fork.png';
|
||||
import img_milepost from '../Images/milepost.png';
|
||||
const Setting = Loadable({
|
||||
loader: () => import('../Settings/Index'),
|
||||
loading: Loading,
|
||||
|
@ -107,7 +97,19 @@ const ForkUsers = Loadable({
|
|||
loader: () => import('../UsersList/fork_users'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Contribute = Loadable({
|
||||
loader: () => import('./sub/Contribute'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const CoderRootCommit = Loadable({
|
||||
loader: () => import('./CoderRootCommit'),
|
||||
loading: Loading,
|
||||
})
|
||||
const CoderDepot = Loadable({
|
||||
loader: () => import('./CoderDepot'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
const TrendsIndex = Loadable({
|
||||
loader: () => import('../Activity/Activity'),
|
||||
|
@ -118,6 +120,10 @@ const DevAbout = Loadable({
|
|||
loader: () => import('../About/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Source = Loadable({
|
||||
loader: () => import('../Source/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const DevIndex = Loadable({
|
||||
loader: () => import('../DevOps/Index'),
|
||||
loading: Loading,
|
||||
|
@ -125,23 +131,26 @@ const DevIndex = Loadable({
|
|||
/**
|
||||
* permission:Manager:管理员,Reporter:报告人员(只有读取权限),Developer:开发人员(除不能设置仓库信息外)
|
||||
*/
|
||||
function checkPathname(pathname){
|
||||
function checkPathname(projectsId,owner,pathname){
|
||||
let name = "";
|
||||
if(pathname){
|
||||
if(pathname.indexOf("/about")>-1){
|
||||
if(pathname && pathname !== `/projects/${owner}/${projectsId}`){
|
||||
let url = pathname.split(`/projects/${owner}/${projectsId}`)[1];
|
||||
if(url.indexOf("/about")>-1){
|
||||
name="about"
|
||||
}else if(pathname.indexOf("/issues")>-1 ||pathname.indexOf("Milepost") > 0){
|
||||
}else if(url.indexOf("/issues")>-1 ||url.indexOf("Milepost") > 0){
|
||||
name = "issues";
|
||||
}else if(pathname.indexOf("/pulls")>-1){
|
||||
}else if(url.indexOf("/pulls")>-1){
|
||||
name="pulls"
|
||||
}else if(pathname.indexOf("/milestones")>-1){
|
||||
}else if(url.indexOf("/milestones")>-1){
|
||||
name="milestones"
|
||||
}else if(pathname.indexOf("/activity")>-1){
|
||||
}else if(url.indexOf("/activity")>-1){
|
||||
name="activity"
|
||||
}else if(pathname.indexOf("/setting")>-1){
|
||||
}else if(url.indexOf("/setting")>-1){
|
||||
name="setting"
|
||||
}else if(pathname.indexOf("/devops")>-1){
|
||||
}else if(url.indexOf(`/devops`)>-1){
|
||||
name="devops"
|
||||
}else if(url.indexOf(`/source`)>-1){
|
||||
name="source"
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
@ -161,7 +170,6 @@ class Detail extends Component {
|
|||
watched: false,
|
||||
praised: false,
|
||||
http_url: undefined,
|
||||
author: undefined,
|
||||
branchs: undefined,
|
||||
branchList: undefined,
|
||||
project: null,
|
||||
|
@ -199,17 +207,6 @@ class Detail extends Component {
|
|||
platform:result.data.platform && result.data.platform !== 'educoder'
|
||||
})
|
||||
|
||||
// 工作流:两种状态进入的链接不同
|
||||
const pathname = this.props.history.location.pathname;
|
||||
let p = checkPathname(pathname);
|
||||
if(p==="devops"){
|
||||
if(result.data.open_devops && pathname === `/projects/${owner}/${projectsId}/devops`){
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}/devops/list`);
|
||||
}else if(result.data.open_devops===false && pathname !== `/projects/${owner}/${projectsId}/devops`){
|
||||
this.props.history.push(`/projects/${owner}/${projectsId}/devops`);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.data.type !== 0 && result.data.mirror_status === 1) {
|
||||
console.log("--------start channel --------");
|
||||
// 是镜像项目,且未完成迁移
|
||||
|
@ -267,7 +264,8 @@ class Detail extends Component {
|
|||
|
||||
getDetail = () => {
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}.json`;
|
||||
this.getBanner();
|
||||
const url = `/${owner}/${projectsId}/detail.json`;
|
||||
axios.get(url).then((result) => {
|
||||
if (result && result.data) {
|
||||
this.setState({
|
||||
|
@ -277,7 +275,6 @@ class Detail extends Component {
|
|||
isReporter: result.data.permission && result.data.permission === "Reporter",
|
||||
isDeveloper: result.data.permission && result.data.permission === "Developer",
|
||||
http_url: result.data.clone_url,
|
||||
author: result.data.author,
|
||||
praised: result.data.praised,
|
||||
watched: result.data.watched,
|
||||
watchers_count: result.data.watchers_count,
|
||||
|
@ -289,6 +286,19 @@ class Detail extends Component {
|
|||
}).catch((error) => { })
|
||||
}
|
||||
|
||||
// 获取动态导航栏菜单
|
||||
getBanner(){
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/menu_list.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result){
|
||||
this.setState({
|
||||
bannerList:result.data
|
||||
})
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 关注和取消关注
|
||||
focusFunc = (flag) => {
|
||||
const { platform } = this.state;
|
||||
|
@ -369,6 +379,7 @@ class Detail extends Component {
|
|||
const url = `/${owner}/${projectsId}/sync_mirror.json`;
|
||||
axios.post(url).then(result => {
|
||||
if (result && result.data && result.data.status === 0) {
|
||||
this.props.showNotification("镜像同步成功!");
|
||||
this.getProject(2);
|
||||
} else {
|
||||
this.props.showNotification("镜像同步失败!");
|
||||
|
@ -384,13 +395,13 @@ class Detail extends Component {
|
|||
const { projectDetail, watchers_count, praises_count,
|
||||
forked_count, firstSync , secondSync ,
|
||||
isManager, watched, praised,
|
||||
project , open_devops , platform , defaultBranch } = this.state;
|
||||
project , open_devops , platform , defaultBranch , bannerList } = this.state;
|
||||
const url = this.props.history.location.pathname;
|
||||
const urlArr = url.split("/");
|
||||
const urlFlag = (urlArr.length === 3);
|
||||
let pathname = checkPathname(url);
|
||||
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
let pathname = checkPathname(projectsId,owner,url);
|
||||
|
||||
const { state } = this.props.history.location;
|
||||
|
||||
|
@ -416,7 +427,7 @@ class Detail extends Component {
|
|||
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
|
||||
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
|
||||
{project && project.author &&
|
||||
<Link to={`/users/${project.author.login}`} className="show-user-link">
|
||||
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
|
||||
{project.author.name}
|
||||
</Link>
|
||||
}
|
||||
|
@ -459,14 +470,15 @@ class Detail extends Component {
|
|||
<span>{watched ? '取消关注' : '关注'}</span>
|
||||
</a>
|
||||
{
|
||||
watchers_count > 0 ?
|
||||
platform ?
|
||||
<Link className="detail_tag_btn_count" style={{color:`${watched?"#2878FF":"#666"}`}} to={platform?{ pathname: `/projects/${owner}/${projectsId}/watchers`, state }:""}>
|
||||
{watchers_count}
|
||||
</Link>
|
||||
:
|
||||
<span className="detail_tag_btn_count">{watchers_count}</span>
|
||||
:""
|
||||
}
|
||||
|
||||
</span>
|
||||
<span className="detail_tag_btn">
|
||||
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.pariseFunc(praised)}>
|
||||
|
@ -474,24 +486,30 @@ class Detail extends Component {
|
|||
<span>{praised ? '取消点赞' : '点赞'}</span>
|
||||
</a>
|
||||
{
|
||||
praises_count > 0 ?
|
||||
platform ?
|
||||
<Link className="detail_tag_btn_count" style={{color:`${praised?"#2878FF":"#666"}`}} to={{ pathname: `/projects/${owner}/${projectsId}/stargazers`, state }}>
|
||||
{praises_count}
|
||||
</Link>:
|
||||
<span className="detail_tag_btn_count">{praises_count}</span>
|
||||
:""
|
||||
}
|
||||
</span>
|
||||
<span className="detail_tag_btn">
|
||||
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={this.forkFunc}>
|
||||
<i className="iconfont icon-fork color-grey-9 mr3"></i>复刻 (Fork)
|
||||
</a>
|
||||
<Tooltip title="复刻是fork的中文名,即复制代码仓库" placement="bottom">
|
||||
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={this.forkFunc}>
|
||||
<i className="iconfont icon-fork color-grey-9 mr3"></i>复刻
|
||||
</a>
|
||||
</Tooltip>
|
||||
{
|
||||
forked_count > 0 ?
|
||||
platform ?
|
||||
<Link className="detail_tag_btn_count" to={{ pathname: `/projects/${owner}/${projectsId}/fork_users`, state }}>
|
||||
{forked_count}
|
||||
</Link>
|
||||
:
|
||||
<span className="detail_tag_btn_count">{praises_count}</span>
|
||||
<span className="detail_tag_btn_count">{forked_count}</span>
|
||||
:""
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
|
@ -499,83 +517,36 @@ class Detail extends Component {
|
|||
</div>
|
||||
{
|
||||
firstSync ? "" :
|
||||
<div className="f-wrap-between mt15">
|
||||
<ul className="headerMenu-wrapper">
|
||||
<li className={pathname==="about" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/about`, state }}>
|
||||
<i className={(pathname==="" || urlFlag) ? "iconfont icon-zhuye1 color-grey-3 mr5 font-14":"iconfont icon-zhuye1 color-grey-6 font-14 mr5"}></i>
|
||||
<span>主页</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className={(pathname==="" || urlFlag) ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}`, state }}>
|
||||
<i className={(pathname==="" || urlFlag) ? "iconfont icon-daimaku color-grey-3 mr5 font-14":"iconfont icon-daimaku color-grey-6 font-14 mr5"}></i>
|
||||
<span>代码库</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className={pathname==="issues" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/issues`, state }}>
|
||||
<i className={pathname==="issues" ? "iconfont icon-renwu color-grey-3 mr5 font-14":"iconfont icon-renwu color-grey-6 font-14 mr5"}></i>
|
||||
<span>易修 (Issue)</span>
|
||||
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
|
||||
</Link>
|
||||
</li>
|
||||
{
|
||||
projectDetail && parseInt(projectDetail.type) !== 2 && platform &&
|
||||
<li className={pathname==="pulls" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/pulls`, state }}>
|
||||
<i className={pathname==="pulls" ? "iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14":"iconfont icon-hebingqingqiu1 color-grey-6 font-14 mr5"}></i>
|
||||
<span>合并请求</span>
|
||||
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{projectDetail.pull_requests_count}</span> : ""}
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
platform &&
|
||||
<li className={pathname==="devops" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/devops${open_devops ? `/list`:""}`, state }}>
|
||||
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流(beta版)
|
||||
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
|
||||
<li className={pathname==="milestones" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/milestones`, state }}>
|
||||
<i className={pathname==="milestones" ? "iconfont icon-lichengbei color-grey-3 mr5 font-14":"iconfont icon-lichengbei color-grey-6 font-14 mr5"}></i>
|
||||
<span>里程碑</span>
|
||||
{projectDetail && projectDetail.versions_count ? <span className="num">{projectDetail.versions_count}</span> :""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={pathname==="activity" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/activity`, state }}>
|
||||
<i className={pathname==="activity" ? "iconfont icon-tongzhi color-grey-3 mr5 font-14":"iconfont icon-tongzhi color-grey-6 font-14 mr5"}></i>
|
||||
<span>动态</span>
|
||||
</Link>
|
||||
</li>
|
||||
{
|
||||
isManager && platform &&
|
||||
<li className={url.indexOf("/setting") > 0 ? "active" : ""}>
|
||||
<Link to={`/projects/${owner}/${projectsId}/setting`}>
|
||||
<i className={url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
|
||||
<span>仓库设置</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<DetailBanner
|
||||
history={this.props.history}
|
||||
list={bannerList}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
pathname={pathname}
|
||||
state={state}
|
||||
projectDetail={projectDetail}
|
||||
open_devops={open_devops}
|
||||
platform={platform}
|
||||
urlFlag={urlFlag}
|
||||
isManager={isManager}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
firstSync ?
|
||||
<Content className="spincontent">
|
||||
<Spin className="spinstyle" tip={project && `正在从 ${project.mirror_url} 迁移`} size="large">
|
||||
</Spin>
|
||||
<Spin className="spinstyle" tip={project && `正在从 ${project.mirror_url} 迁移`} size="large" />
|
||||
</Content>
|
||||
:
|
||||
<Spin spinning={secondSync} className="spinstyle" tip="正在同步镜像" size="large">
|
||||
<Switch {...this.props}>
|
||||
{/* 资源 */}
|
||||
<Route path="/projects/:owner/:projectsId/source"
|
||||
render={
|
||||
() => (<Source {...this.props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* 主页 */}
|
||||
<Route path="/projects/:owner/:projectsId/about"
|
||||
render={
|
||||
|
@ -666,6 +637,7 @@ class Detail extends Component {
|
|||
(props) => (<OrderIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* 新建合并请求 */}
|
||||
<Route path="/projects/:owner/:projectsId/pulls/new"
|
||||
render={
|
||||
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
|
||||
|
@ -692,11 +664,6 @@ class Detail extends Component {
|
|||
(props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/coders/filesurl"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/watchers"
|
||||
render={
|
||||
(props) => (<WatchUsers {...this.props} {...props} {...this.state} {...common} />)
|
||||
|
@ -712,19 +679,32 @@ class Detail extends Component {
|
|||
(props) => (<ForkUsers {...this.props} {...props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/commits/branch/:branchName"
|
||||
{/* 贡献者列表 */}
|
||||
<Route path="/projects/:owner/:projectsId/contribute"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
() => (<Contribute {...this.props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/branch/:branchName"
|
||||
|
||||
{/* 代码库----详情页面 */}
|
||||
<Route path="/projects/:owner/:projectsId/commits/branch/:branchName"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
(props) => (<CoderRootCommit {...this.props} {...props} {...this.state} {...common}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/tree/:branchName"
|
||||
render={
|
||||
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId/:subIndex"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/projects/:owner/:projectsId"
|
||||
render={
|
||||
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
|
||||
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common}/>)
|
||||
}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -59,13 +59,13 @@ export default ({ match , history }) => {
|
|||
{commit && commit.message &&
|
||||
<pre className="task-hide" style={{marginBottom:"0px",height:"28px",whiteSpace:"pre-wrap"}}>{commit.message}</pre>
|
||||
}
|
||||
<Button type="primary" onClick={()=>{history.push(`/projects/${owner}/${projectsId}/branch/${truncateCommitId(sha)}`)}} className="ml30">浏览代码</Button>
|
||||
<Button type="primary" onClick={()=>{history.push(`/projects/${owner}/${projectsId}/tree/${truncateCommitId(sha)}`)}} className="ml30">浏览代码</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="f-wrap-between" style={{ alignItems: "center" }}>
|
||||
<ul className="df">
|
||||
<User
|
||||
url={(committer && getImageUrl(`images/${committer.image_url}`))|| "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
|
||||
url={(committer && getImageUrl(`/${committer.image_url}`))|| "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
|
||||
name={committer && committer.name}
|
||||
/>
|
||||
{committer && committer.time_from_now && <li className="ml20 mt2">{committer.time_from_now}</li>}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Menu, Input , Spin, Pagination , Popover , Select } from 'antd';
|
||||
import { getUrl } from 'educoder';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import '../css/index.scss'
|
||||
import './list.css';
|
||||
import './Index.scss';
|
||||
|
@ -271,7 +271,7 @@ class Index extends Component {
|
|||
return(
|
||||
<div onClick={()=>this.getoDetail(item.author && item.author.login,item.identifier)}>
|
||||
<div className="mainInfo">
|
||||
<img src={getUrl(`/images/${item.author && item.author.image_url}`)} alt=""/>
|
||||
<img src={getImageUrl(`/${item.author && item.author.image_url}`)} alt=""/>
|
||||
<p className="school">{item.name}</p>
|
||||
<p className="name">{item.author && item.author.name}</p>
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
width: 220px;
|
||||
margin-right: 25px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #eee;
|
||||
&:last-child{
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
@ -71,4 +72,241 @@
|
|||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// coderDepot
|
||||
.Panels{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
.panelmenu{
|
||||
padding-top:30px;
|
||||
}
|
||||
.addOptionBtn{
|
||||
height: 32px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
border:1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
a{
|
||||
padding:0px 13px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
cursor: pointer;
|
||||
}
|
||||
& > a:first-child{
|
||||
border-right: 1px solid #d9d9d9;
|
||||
}
|
||||
& > a:last-child{
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.infoCount{
|
||||
display: inline-block;
|
||||
padding:0px 5px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
background-color: #eee;
|
||||
color:#999;
|
||||
border-radius: 12px;
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.attrPerson{
|
||||
padding-top: 15px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
a{
|
||||
margin: 10px 10px 0px 0px;
|
||||
img{
|
||||
border-radius: 50%;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
&:nth-child(6){
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progress{
|
||||
display: flex;
|
||||
border-radius: 10px;
|
||||
height: 7px;
|
||||
margin-top: 12px;
|
||||
span{
|
||||
border-left: 1px solid #fff;
|
||||
&:first-child{
|
||||
border-left: none;
|
||||
border-radius: 10px 0px 0px 10px;
|
||||
}
|
||||
&:last-child{
|
||||
border-radius: 0px 10px 10px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.progresstip{
|
||||
margin-top: 15px;
|
||||
flex-wrap: wrap;
|
||||
i.zero{
|
||||
position: absolute;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
left: 0px;
|
||||
top:10px
|
||||
}
|
||||
&>span{
|
||||
padding-left: 15px;
|
||||
position: relative;
|
||||
min-width: 33.5%;
|
||||
span{
|
||||
color: #666;
|
||||
&:last-child{
|
||||
color: #999;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listtable{
|
||||
margin-top: 20px;
|
||||
border:1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
.listtablehead{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
padding:7px 20px;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
background-color: #FAFBFC;
|
||||
.ellipsistxt{
|
||||
margin-left: 13px;
|
||||
line-height:18px;
|
||||
margin-top:6px;
|
||||
flex:1;
|
||||
width: 0;
|
||||
color: #666;
|
||||
&>p{
|
||||
word-break:break-all;
|
||||
}
|
||||
&.hide{
|
||||
height: 18px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding-right:8px;
|
||||
}
|
||||
&.hide::after{
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
content:"...";
|
||||
|
||||
}
|
||||
}
|
||||
.ellipsis{
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
background-color: #c1c1c1;
|
||||
padding:0px 4px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
margin-top: 9px;
|
||||
i{
|
||||
font-size: 15px!important;
|
||||
color: #fff;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.listtablebody{
|
||||
li.listtablepath{
|
||||
a{color: #40a9ff;}
|
||||
}
|
||||
li{
|
||||
height: 42px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
padding:0px 20px 0px 24px;
|
||||
& > span:first-child{
|
||||
width: 30%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
& > span:nth-child(2){
|
||||
width: 60%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
& > span:nth-child(3){
|
||||
width: 10%;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
&> li:last-child{
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.drawerBtn{
|
||||
position: fixed;
|
||||
left: -13px;
|
||||
border:1px solid rgb(207,205,223);
|
||||
width: 34px;
|
||||
border-radius: 0px 12px 12px 0px;
|
||||
height: 70px;
|
||||
top:50%;
|
||||
margin-top: -35px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
&:hover{
|
||||
box-shadow: 1px 0px 7px rgba(0,0,0,0.1);
|
||||
}
|
||||
span{
|
||||
writing-mode: vertical-lr;
|
||||
color: #202429;
|
||||
width: 25px;
|
||||
}
|
||||
i{
|
||||
color: #24292e;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
.downMenu{
|
||||
box-shadow: 0px 0px 9px rgba(134, 134, 134,0.4);
|
||||
background-color: #fff;
|
||||
.ant-menu-vertical .ant-menu-item:hover{
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
||||
|
||||
.menuslist{
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding:10px 15px;
|
||||
border-radius: 4px;
|
||||
.ant-dropdown-menu-item{
|
||||
border-radius: 8px;
|
||||
text-align: left!important;
|
||||
a{
|
||||
width: 350px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.ant-dropdown-menu-item.active{
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
|
@ -16,65 +16,62 @@ class IndexItem extends Component {
|
|||
}
|
||||
render() {
|
||||
const { projects } = this.props;
|
||||
const renderList = (
|
||||
projects && projects.length > 0 ? projects.map((item, key) => {
|
||||
return (
|
||||
<div className="p-r-Item" key={key}>
|
||||
{
|
||||
item.platform === "educoder" ?
|
||||
<a href="javascript:void(0)" style={{cursor:"default"}} className="show-user-link">
|
||||
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
|
||||
</a>
|
||||
:
|
||||
<Link to={`/users/${item.author.login}`} className="show-user-link">
|
||||
<img className="p-r-photo" alt="" src={getImageUrl(`${item.author && item.author.image_url}`)} ></img>
|
||||
</Link>
|
||||
}
|
||||
<div className="p-r-Infos">
|
||||
<div className="p-r-name">
|
||||
<Link to={`/projects/${item.author.login}/${item.identifier}`} className="hide-1 color-grey-3 font-18 task-hide " style={{ whiteSpace: "wrap", display: 'flex', width: 400 }}>
|
||||
{item.author.name}/{item.name}
|
||||
{
|
||||
item.forked_from_project_id ?
|
||||
<span className="ml5">
|
||||
<i className="iconfont icon-fork font-18 color-orange" />
|
||||
</span>
|
||||
: ""
|
||||
}
|
||||
{
|
||||
item.type && item.type !== 0 ?
|
||||
item.type === 2 ?
|
||||
<Tooltip title="该项目是一个镜像" className="ml5">
|
||||
<i className="iconfont icon-banbenku font-18 color-green" />
|
||||
</Tooltip>:
|
||||
<span className="ml5">
|
||||
<i className="iconfont icon-jingxiang font-18 color-green" />
|
||||
</span>:""
|
||||
}
|
||||
</Link>
|
||||
<span className="p-r-tags">
|
||||
<span className="pariseTag"><img src={img_parise} alt="" className="pariseImg" />赞 {item.praises_count}</span>
|
||||
<span><i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
|
||||
|
||||
<div className="p-r-about">
|
||||
<span className="p-r-detail">
|
||||
{/* <span><label>浏览量:</label>{item.visits}</span> */}
|
||||
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
|
||||
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
|
||||
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}) : <Nodata _html="暂无数据~"></Nodata>
|
||||
)
|
||||
return (
|
||||
<div className="project-list minH-670">
|
||||
{renderList}
|
||||
{ projects && projects.length > 0 ? projects.map((item, key) => {
|
||||
return (
|
||||
<div className="p-r-Item" key={key}>
|
||||
{
|
||||
item.platform === "educoder" ?
|
||||
<a href="javascript:void(0)" style={{cursor:"default"}} className="show-user-link">
|
||||
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
|
||||
</a>
|
||||
:
|
||||
<Link to={item.author && (item.author.type === "Organization" ? `/organize/${item.author.login}`:`/users/${item.author.login}`)} className="show-user-link">
|
||||
<img className="p-r-photo" alt="" src={getImageUrl(`/${item.author && item.author.image_url}`)} ></img>
|
||||
</Link>
|
||||
}
|
||||
<div className="p-r-Infos">
|
||||
<div className="p-r-name">
|
||||
<Link to={`/projects/${item.author.login}/${item.identifier}`} className="hide-1 color-grey-3 font-18 task-hide " style={{ whiteSpace: "wrap", display: 'flex', width: 400 }}>
|
||||
{item.author.name}/{item.name}
|
||||
{
|
||||
item.forked_from_project_id ?
|
||||
<span className="ml5">
|
||||
<i className="iconfont icon-fork font-18 color-orange" />
|
||||
</span>
|
||||
: ""
|
||||
}
|
||||
{
|
||||
item.type && item.type !== 0 ?
|
||||
item.type === 2 ?
|
||||
<Tooltip title="该项目是一个镜像" className="ml5">
|
||||
<i className="iconfont icon-banbenku font-18 color-green" />
|
||||
</Tooltip>:
|
||||
<span className="ml5">
|
||||
<i className="iconfont icon-jingxiang font-18 color-green" />
|
||||
</span>:""
|
||||
}
|
||||
</Link>
|
||||
<span className="p-r-tags">
|
||||
<span className="pariseTag"><img src={img_parise} alt="" className="pariseImg" />赞 {item.praises_count}</span>
|
||||
<span><i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
|
||||
|
||||
<div className="p-r-about">
|
||||
<span className="p-r-detail">
|
||||
{/* <span><label>浏览量:</label>{item.visits}</span> */}
|
||||
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
|
||||
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
|
||||
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}) : <Nodata _html="暂无数据~"></Nodata>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,10 +18,17 @@
|
|||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.list-left > div{
|
||||
border:1px solid #eee;
|
||||
}
|
||||
.list-left > div.bgcF{
|
||||
border:none;
|
||||
}
|
||||
.list-right{
|
||||
width:74%;
|
||||
background: #fff;
|
||||
padding:10px;
|
||||
border:1px solid #eee;
|
||||
}
|
||||
|
||||
|
||||
|
@ -274,7 +281,6 @@
|
|||
height:100%;
|
||||
}
|
||||
.files-md{
|
||||
border:1px solid #eee;
|
||||
padding:20px;
|
||||
}
|
||||
/* 详情-代码 */
|
||||
|
@ -424,18 +430,11 @@
|
|||
max-width: 100%;
|
||||
}
|
||||
/* 分支 */
|
||||
.branchTable{
|
||||
border:1px solid #f7f7f7;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.branchTitle{
|
||||
padding:12px 10px;
|
||||
padding:8px 10px;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
background:rgba(241,248,255,1);
|
||||
}
|
||||
.branchUl{
|
||||
padding:0px 30px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
}
|
||||
.branchUl li{
|
||||
display: flex;
|
||||
|
@ -548,17 +547,19 @@
|
|||
}
|
||||
.commonBox{
|
||||
border:1px solid #ddd;
|
||||
margin-top: 25px;
|
||||
margin-top: 30px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.commonBox .commonBox-title{
|
||||
padding:0px 15px;
|
||||
padding:0px 20px;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
background: #F1F8FF;
|
||||
background: #FAFBFC;/* F1F8FF */
|
||||
font-weight: bold;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
}
|
||||
.synchronism{
|
||||
display: block;
|
||||
|
@ -575,7 +576,7 @@
|
|||
.commonBox .commonBox-info{
|
||||
padding:20px 15px;
|
||||
}
|
||||
.commonBox-title-read{vertical-align: middle;}
|
||||
.commonBox-title-read{vertical-align: middle;color: #666;}
|
||||
|
||||
@media screen and (max-width: 370px){
|
||||
.p-r-tags,.p-r-btn{
|
||||
|
@ -665,6 +666,7 @@ a.color-grey-ccc:hover{
|
|||
|
||||
.commitList{
|
||||
padding:0px 30px;
|
||||
min-height: 400px;
|
||||
}
|
||||
.commitList > div{
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { WhiteBack , AlignCenter } from '../../Component/layout';
|
||||
import "./sub.scss";
|
||||
import axios from 'axios';
|
||||
import { Pagination , Spin } from 'antd';
|
||||
import NoData from '../../Nodata';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function Contribute(props){
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
|
||||
const owner = props.match.params.owner;
|
||||
const projectsId = props.match.params.projectsId;
|
||||
const LIMIT = 20;
|
||||
useEffect(()=>{
|
||||
if(owner && projectsId){
|
||||
getData();
|
||||
}
|
||||
},[owner,projectsId,page]);
|
||||
|
||||
function getData(){
|
||||
setIsSpin(true);
|
||||
const url = `/${owner}/${projectsId}/contributors.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
limit:LIMIT,page,
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setList(result.data.contributors);
|
||||
setTotal(result.data.total_count);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<WhiteBack>
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="boxPanel">
|
||||
<p className="font-18 mb20">贡献者列表</p>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<div className="contrbuteList">
|
||||
{
|
||||
list.map((item,key)=>{
|
||||
return(
|
||||
<AlignCenter>
|
||||
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`/${item.image_url}`)} width="50px" height="50px"/>
|
||||
<div>
|
||||
<Link to={`/users/${item.login}`} className="font-16">{item.name}</Link>
|
||||
<p className="font-12 color-grey-9">提交{item.contributions}次</p>
|
||||
</div>
|
||||
</AlignCenter>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
{
|
||||
list && list.length === 0 ? <NoData _html="暂无贡献者" />:""
|
||||
}
|
||||
{
|
||||
total > LIMIT ?
|
||||
<div className="mt20 edu-txt-center">
|
||||
<Pagination simple pageSize={LIMIT} onChange={(p)=>{setPage(p)}} current={page} total={total}/>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</WhiteBack>
|
||||
)
|
||||
}
|
||||
export default Contribute;
|
|
@ -0,0 +1,127 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Skeleton , Tooltip} from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
|
||||
const [ menuName , setMenuName ] = useState(undefined);
|
||||
useEffect(()=>{
|
||||
if(list){
|
||||
// 没有资源库banner但是通过连接进资源库页面时
|
||||
if(pathname && pathname==="source"){
|
||||
let a = list.filter(item=>item.menu_name === "resources");
|
||||
if(a && a.length === 0){
|
||||
history.push(`/projects/${owner}/${projectsId}`);
|
||||
}
|
||||
}
|
||||
setMenuName(list);
|
||||
}
|
||||
},[list]);
|
||||
return(
|
||||
<div className="f-wrap-between mt15">
|
||||
{
|
||||
menuName && projectDetail ?
|
||||
<ul className="headerMenu-wrapper">
|
||||
{
|
||||
menuName.map((item,key)=>{
|
||||
return(
|
||||
<React.Fragment>
|
||||
{
|
||||
item.menu_name === "home" &&
|
||||
<li className={pathname==="about" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/about`, state }}>
|
||||
<i className={(pathname==="" || urlFlag) ? "iconfont icon-zhuye1 color-grey-3 mr5 font-14":"iconfont icon-zhuye1 color-grey-6 font-14 mr5"}></i>
|
||||
<span>主页</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "code" &&
|
||||
<li className={(pathname==="" || urlFlag) ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}`, state }}>
|
||||
<i className={(pathname==="" || urlFlag) ? "iconfont icon-daimaku color-grey-3 mr5 font-14":"iconfont icon-daimaku color-grey-6 font-14 mr5"}></i>
|
||||
<span>代码库</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "issues" &&
|
||||
<li className={pathname==="issues" ? "active" : ""}>
|
||||
<Tooltip title="易修是Issue的中文名,即问题列表" placement="bottom">
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/issues`, state }}>
|
||||
<i className={pathname==="issues" ? "iconfont icon-renwu color-grey-3 mr5 font-14":"iconfont icon-renwu color-grey-6 font-14 mr5"}></i>
|
||||
<span>易修</span>
|
||||
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "pulls" && projectDetail && parseInt(projectDetail.type) !== 2 && platform ?
|
||||
<li className={pathname==="pulls" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/pulls`, state }}>
|
||||
<i className={pathname==="pulls" ? "iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14":"iconfont icon-hebingqingqiu1 color-grey-6 font-14 mr5"}></i>
|
||||
<span>合并请求</span>
|
||||
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{projectDetail.pull_requests_count}</span> : ""}
|
||||
</Link>
|
||||
</li>:""
|
||||
}
|
||||
{
|
||||
item.menu_name === "devops" && platform ?
|
||||
<li className={pathname==="devops" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}>
|
||||
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流(beta版)
|
||||
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
||||
</Link>
|
||||
</li>
|
||||
:""
|
||||
}
|
||||
{/* {
|
||||
item.menu_name === "resources" &&
|
||||
<li className={pathname==="source" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/source`, state }}>
|
||||
<i className={pathname==="source" ? "iconfont icon-ziyuanpaihanghetuijian color-grey-3 mr5 font-14":"iconfont icon-ziyuanpaihanghetuijian color-grey-6 font-14 mr5"}></i>
|
||||
<span>资源库</span>
|
||||
{projectDetail && projectDetail.source_count ? <span className="num">{projectDetail.source_count}</span> :""}
|
||||
</Link>
|
||||
</li>
|
||||
} */}
|
||||
{
|
||||
item.menu_name === "versions" &&
|
||||
<li className={pathname==="milestones" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/milestones`, state }}>
|
||||
<i className={pathname==="milestones" ? "iconfont icon-lichengbei color-grey-3 mr5 font-14":"iconfont icon-lichengbei color-grey-6 font-14 mr5"}></i>
|
||||
<span>里程碑</span>
|
||||
{projectDetail && projectDetail.versions_count ? <span className="num">{projectDetail.versions_count}</span> :""}
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "activity" &&
|
||||
<li className={pathname==="activity" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/projects/${owner}/${projectsId}/activity`, state }}>
|
||||
<i className={pathname==="activity" ? "iconfont icon-tongzhi color-grey-3 mr5 font-14":"iconfont icon-tongzhi color-grey-6 font-14 mr5"}></i>
|
||||
<span>动态</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "setting" &&
|
||||
<li className={pathname === "setting" ? "active" : ""}>
|
||||
<Link to={`/projects/${owner}/${projectsId}/setting`}>
|
||||
<i className={url && url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
|
||||
<span>仓库设置</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
<Skeleton paragraph={false} active={true}/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default DetailBanner;
|
|
@ -0,0 +1,63 @@
|
|||
import React , { forwardRef, useEffect } from 'react';
|
||||
import {Form , Modal , Input } from 'antd';
|
||||
import "./sub.scss";
|
||||
const { TextArea } = Input;
|
||||
function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_url}){
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
|
||||
useEffect(()=>{
|
||||
if(desc || website){
|
||||
setFieldsValue({
|
||||
website,desc,lesson_url
|
||||
})
|
||||
}
|
||||
},[desc,website])
|
||||
|
||||
function onSure(){
|
||||
validateFields((err,values)=>{
|
||||
if(!err){
|
||||
onCancel();
|
||||
onOk(values.desc,values.website,values.lesson_url)
|
||||
}
|
||||
})
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
title={"修改信息"}
|
||||
closable={false}
|
||||
visible={visible}
|
||||
centered
|
||||
onCancel={onCancel}
|
||||
onOk={onSure}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
width="400px"
|
||||
className={"descmodal"}
|
||||
>
|
||||
<Form>
|
||||
<Form.Item label="仓库描述">
|
||||
{getFieldDecorator("desc",{
|
||||
rules:[]
|
||||
})(
|
||||
<TextArea placeholder="仓库描述" rows={4} maxLength={200}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="website">
|
||||
{getFieldDecorator("website",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input placeholder="website链接"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="实践课程">
|
||||
{getFieldDecorator("lesson_url",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input placeholder="实践课程链接"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(UpdateDescModal));
|
|
@ -0,0 +1,23 @@
|
|||
.boxPanel{
|
||||
width: 1200px;
|
||||
margin:0px auto;
|
||||
padding:20px 0px;
|
||||
min-height: 500px;
|
||||
.contrbuteList{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
& > div{
|
||||
width: 20%;
|
||||
}
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
.descmodal{
|
||||
.ant-row.ant-form-item{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ant-col.ant-form-item-label{
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ function Commits({ commits , projectsId , owner }){
|
|||
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(item.sha)}`} className="color-blue">浏览代码</Link>
|
||||
</FlexAJ>
|
||||
<AlignCenter className="mt15">
|
||||
<User url={getImageUrl(`images/${item.committer && item.committer.image_url}`)} name={`${item.committer && item.committer.name}`}></User><span>:提交于{item.time_from_now}</span>
|
||||
<User url={getImageUrl(`/${item.committer && item.committer.image_url}`)} name={`${item.committer && item.committer.name}`}></User><span>:提交于{item.time_from_now}</span>
|
||||
</AlignCenter>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,7 @@ function Files({data,history,owner,projectsId}){
|
|||
<span>{item.name}</span>
|
||||
</AlignCenter>
|
||||
<span>
|
||||
<Button className="mr20" onClick={()=>{history.push(`/projects/${owner}/${projectsId}${item.sha ? `/branch/${truncateCommitId(item.sha)}?`:"?"}url=${item.name}`)}}>查看文件</Button>
|
||||
<Button className="mr20" onClick={()=>{history.push(`/projects/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}/${item.name}`)}}>查看文件</Button>
|
||||
<span className="color-green">+{item.addition}</span>
|
||||
<span className="color-red ml20">-{item.deletion}</span>
|
||||
</span>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Popconfirm, Tag } from "antd";
|
||||
import { Tag } from "antd";
|
||||
import { AlignCenter } from '../Component/layout';
|
||||
import { getImageUrl } from "educoder";
|
||||
import "./merge.css";
|
||||
|
||||
|
@ -74,13 +75,13 @@ class MergeItem extends Component {
|
|||
>
|
||||
<img
|
||||
className="radius"
|
||||
src={getImageUrl(`images/${item && item.avatar_url}`)}
|
||||
src={getImageUrl(`/${item && item.avatar_url}`)}
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
</Link>
|
||||
<span>
|
||||
<AlignCenter>
|
||||
<Link
|
||||
to={`/users/${item && item.author_login}`}
|
||||
className="show-user-link color-grey-8 ml5"
|
||||
|
@ -98,7 +99,7 @@ class MergeItem extends Component {
|
|||
<span className="ml15">
|
||||
<Tag className="pr-branch-tag">
|
||||
<Link
|
||||
to={`/projects/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/branch/${item.pull_request_head}`}
|
||||
to={`/projects/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/tree/${item.pull_request_head}`}
|
||||
className="maxW200px hide-1 ver-middle"
|
||||
>
|
||||
{item.is_original
|
||||
|
@ -116,7 +117,7 @@ class MergeItem extends Component {
|
|||
</span>
|
||||
<Tag className="pr-branch-tag">
|
||||
<Link
|
||||
to={`/projects/${owner}/${projectsId}/branch/${item.pull_request_base}`}
|
||||
to={`/projects/${owner}/${projectsId}/tree/${item.pull_request_base}`}
|
||||
className="maxW200px hide-1 ver-middle"
|
||||
>
|
||||
{/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */}
|
||||
|
@ -124,7 +125,7 @@ class MergeItem extends Component {
|
|||
</Link>
|
||||
</Tag>
|
||||
</span>
|
||||
</span>
|
||||
</AlignCenter>
|
||||
</p>
|
||||
</div>
|
||||
<ul
|
||||
|
|
|
@ -101,7 +101,7 @@ class MergeSubmit extends Component{
|
|||
render: (text,item) => (
|
||||
<span className="f-wrap-alignCenter">
|
||||
<Link to={`/users/${item.login}`} className="show-user-link">
|
||||
<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>
|
||||
<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>
|
||||
<label className="hide-1" style={{maxWidth:"75px",'vertical-align':'middle'}}>{text}</label>
|
||||
</Link>
|
||||
</span>
|
||||
|
|
|
@ -40,6 +40,7 @@ class MessageCount extends Component {
|
|||
SpinMerge: false,
|
||||
edit_spin: false,
|
||||
pr_status: undefined,
|
||||
pull_request:undefined,
|
||||
|
||||
copyVisible:false,
|
||||
|
||||
|
@ -71,11 +72,12 @@ class MessageCount extends Component {
|
|||
axios
|
||||
.get(url)
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
if (result && result.data) {
|
||||
this.setState({
|
||||
data: result.data,
|
||||
SpinFlag: false,
|
||||
pr_status: result.data && result.data.pull_request && result.data.pull_request.status,
|
||||
pr_status: result.data.pull_request && result.data.pull_request.status,
|
||||
pull_request:result.data.pull_request
|
||||
});
|
||||
} else {
|
||||
this.setState({ SpinFlag: false });
|
||||
|
@ -258,7 +260,8 @@ class MessageCount extends Component {
|
|||
isSpin,
|
||||
ismesrge,
|
||||
SpinFlag,
|
||||
copyVisible
|
||||
copyVisible,
|
||||
pull_request
|
||||
} = this.state;
|
||||
const { current_user, projectDetail } = this.props;
|
||||
const menu = (
|
||||
|
@ -277,9 +280,11 @@ class MessageCount extends Component {
|
|||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const permission = projectDetail && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner" || projectDetail.permission === "Manager");
|
||||
const userLogin = current_user && current_user.login;
|
||||
const operate = userLogin && projectDetail && pr_status === 0 && permission;
|
||||
return (
|
||||
<div className="">
|
||||
<div>
|
||||
{data ? (
|
||||
<div>
|
||||
<div className="main">
|
||||
|
@ -309,7 +314,7 @@ class MessageCount extends Component {
|
|||
<div className="mt15">
|
||||
<Tag className="pr-branch-tag">
|
||||
<Link
|
||||
to={`/projects/${owner}/${data.pull_request.is_original?data.pull_request.fork_project_id:projectsId}/branch/${data.pull_request.head}`}
|
||||
to={`/projects/${owner}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${data.pull_request.head}`}
|
||||
className="ver-middle"
|
||||
>
|
||||
{data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}:{data.pull_request.head}
|
||||
|
@ -324,7 +329,7 @@ class MessageCount extends Component {
|
|||
</span>
|
||||
<Tag className="pr-branch-tag">
|
||||
<Link
|
||||
to={`/projects/${owner}/${projectsId}/branch/${data.pull_request.base}`}
|
||||
to={`/projects/${owner}/${projectsId}/tree/${data.pull_request.base}`}
|
||||
className="ver-middle"
|
||||
>
|
||||
{/* {data.pull_request.is_fork ? data.pull_request.base : `${data.pull_request.pull_request_user}:${data.pull_request.base}`} */}
|
||||
|
@ -335,26 +340,14 @@ class MessageCount extends Component {
|
|||
}
|
||||
|
||||
<div className="mt15">
|
||||
<Link
|
||||
to={`/users/${data.issue.author_login}`}
|
||||
className="show-user-link"
|
||||
>
|
||||
<img
|
||||
className="mr5"
|
||||
src={getImageUrl(
|
||||
`images/${data.issue.author_picture}`
|
||||
)}
|
||||
alt=""
|
||||
width="24"
|
||||
height="24"
|
||||
<Link to={`/users/${data.issue.author_login}`} className="show-user-link">
|
||||
<img className="mr5" src={getImageUrl(`/${data.issue.author_picture}`)}
|
||||
alt="" width="24" height="24" style={{borderRadius:"50%"}}
|
||||
/>
|
||||
</Link>
|
||||
<span className="ver-middle">
|
||||
<span className="color-grey-8 mr5">由</span>
|
||||
<Link
|
||||
to={`/users/${data.issue.author_login}`}
|
||||
className="show-user-link color-blue"
|
||||
>
|
||||
<Link to={`/users/${data.issue.author_login}`} className="show-user-link color-blue">
|
||||
{data.issue.author_name}
|
||||
</Link>
|
||||
<span className="ml5 color-grey-8">
|
||||
|
@ -410,9 +403,7 @@ class MessageCount extends Component {
|
|||
</Dropdown>
|
||||
<span>下载为<i className="iconfont icon-sanjiaoxing-down color-blue"></i></span>
|
||||
</span> */}
|
||||
{current_user && projectDetail &&
|
||||
pr_status === 0 &&
|
||||
projectDetail.permission !=="Reporter" && (
|
||||
{operate && (
|
||||
<Button
|
||||
type="green"
|
||||
ghost
|
||||
|
@ -422,7 +413,7 @@ class MessageCount extends Component {
|
|||
编辑
|
||||
</Button>
|
||||
)}
|
||||
{projectDetail && projectDetail.permission !=="Reporter" && pr_status === 0 && (
|
||||
{operate && (
|
||||
<Button
|
||||
type="danger"
|
||||
ghost
|
||||
|
|
|
@ -253,27 +253,9 @@ class merge extends Component {
|
|||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const Paginations = (
|
||||
<React.Fragment>
|
||||
{search_count > limit ? (
|
||||
<div className="mt30 mb50 edu-txt-center">
|
||||
<Pagination
|
||||
simple
|
||||
defaultCurrent={page}
|
||||
total={search_count}
|
||||
pageSize={limit}
|
||||
onChange={this.ChangePage}
|
||||
></Pagination>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
return (
|
||||
<div className="main">
|
||||
<div className="topWrapper" style={{borderBottom:"1px solid #eee"}}>
|
||||
<div className="main" style={{padding:"0px"}}>
|
||||
<div className="topWrapper" style={{borderBottom:"none",padding:"20px"}}>
|
||||
<div className="target-detail-search">
|
||||
<Search
|
||||
placeholder="输入关键字搜索合并请求"
|
||||
|
@ -405,26 +387,38 @@ class merge extends Component {
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{data && data.search_count && data.search_count > 0 ? (
|
||||
<div style={{minHeight:"470px"}}>
|
||||
<Spin spinning={isSpin}>
|
||||
<OrderItem
|
||||
issues={issues}
|
||||
search_count={search_count}
|
||||
page={select_params.page}
|
||||
limit={select_params.limit}
|
||||
project_name={data.project_name}
|
||||
project_author_name={data.project_author_name}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
></OrderItem>
|
||||
{Paginations}
|
||||
</Spin>
|
||||
</div>
|
||||
) : (
|
||||
<NoneData _html="暂时还没有相关数据!" projectsId={projectsId} owner={owner} />
|
||||
)}
|
||||
<div style={{minHeight:"470px"}}>
|
||||
<Spin spinning={isSpin}>
|
||||
{data && data.search_count && data.search_count > 0 ? (
|
||||
<div>
|
||||
<OrderItem
|
||||
issues={issues}
|
||||
search_count={search_count}
|
||||
page={select_params.page}
|
||||
limit={select_params.limit}
|
||||
project_name={data.project_name}
|
||||
project_author_name={data.project_author_name}
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
></OrderItem>
|
||||
</div>
|
||||
):""}
|
||||
{search_count > select_params.limit ? (
|
||||
<div className="mt30 mb50 edu-txt-center">
|
||||
<Pagination
|
||||
simple
|
||||
current={select_params.page}
|
||||
total={search_count}
|
||||
pageSize={select_params.limit}
|
||||
onChange={this.ChangePage}
|
||||
></Pagination>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{ data && data.issues && data.issues.length === 0 ? <NoneData _html="暂时还没有相关数据!" projectsId={projectsId} owner={owner} /> :""}
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ class MergeFooter extends Component {
|
|||
}
|
||||
{
|
||||
filesData && filesData.files && filesData.files.length>0 &&
|
||||
<TabPane tab={
|
||||
<TabPane tab={
|
||||
<span><span className="font-16">文件</span>
|
||||
{filesCount > 0 && <span className="tabNum">{filesCount}</span>}
|
||||
</span>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue