forked from Gitlink/forgeplus-react
设置模块-测试版第一版
This commit is contained in:
parent
f90243243f
commit
4f9f5649a9
|
@ -1753,7 +1753,7 @@ a.decoration {
|
|||
}
|
||||
|
||||
.mb15 {
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 15px!important;
|
||||
}
|
||||
|
||||
.mb16 {
|
||||
|
@ -6686,3 +6686,9 @@ p{
|
|||
top:4px;
|
||||
color: #999;
|
||||
}
|
||||
.ant-input, .ant-input .ant-input-suffix{
|
||||
background-color: #fff!important;
|
||||
}
|
||||
.has-error .ant-input{
|
||||
background-color: #FEF1F0!important;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2340181 */
|
||||
src: url('iconfont.woff2?t=1625800786751') format('woff2'),
|
||||
url('iconfont.woff?t=1625800786751') format('woff'),
|
||||
url('iconfont.ttf?t=1625800786751') format('truetype');
|
||||
src: url('iconfont.woff2?t=1626838578464') format('woff2'),
|
||||
url('iconfont.woff?t=1626838578464') format('woff'),
|
||||
url('iconfont.ttf?t=1626838578464') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -13,6 +13,34 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-erciqueren_icon:before {
|
||||
content: "\e867";
|
||||
}
|
||||
|
||||
.icon-xuanzhongssh_icon:before {
|
||||
content: "\e868";
|
||||
}
|
||||
|
||||
.icon-weixuanzhonganquanshezhi_icon:before {
|
||||
content: "\e869";
|
||||
}
|
||||
|
||||
.icon-weixuanzhongssh_icon:before {
|
||||
content: "\e86a";
|
||||
}
|
||||
|
||||
.icon-xuanzhonganquanshezhi_icon:before {
|
||||
content: "\e86b";
|
||||
}
|
||||
|
||||
.icon-shanchu_icon:before {
|
||||
content: "\e86c";
|
||||
}
|
||||
|
||||
.icon-liebiaossh_icon:before {
|
||||
content: "\e86e";
|
||||
}
|
||||
|
||||
.icon-file-submodule:before {
|
||||
content: "\e866";
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,55 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "22906287",
|
||||
"name": "二次确认_icon",
|
||||
"font_class": "erciqueren_icon",
|
||||
"unicode": "e867",
|
||||
"unicode_decimal": 59495
|
||||
},
|
||||
{
|
||||
"icon_id": "22906288",
|
||||
"name": "选中ssh_icon",
|
||||
"font_class": "xuanzhongssh_icon",
|
||||
"unicode": "e868",
|
||||
"unicode_decimal": 59496
|
||||
},
|
||||
{
|
||||
"icon_id": "22906289",
|
||||
"name": "未选中安全设置_icon",
|
||||
"font_class": "weixuanzhonganquanshezhi_icon",
|
||||
"unicode": "e869",
|
||||
"unicode_decimal": 59497
|
||||
},
|
||||
{
|
||||
"icon_id": "22906290",
|
||||
"name": "未选中ssh_icon",
|
||||
"font_class": "weixuanzhongssh_icon",
|
||||
"unicode": "e86a",
|
||||
"unicode_decimal": 59498
|
||||
},
|
||||
{
|
||||
"icon_id": "22906291",
|
||||
"name": "选中安全设置_icon",
|
||||
"font_class": "xuanzhonganquanshezhi_icon",
|
||||
"unicode": "e86b",
|
||||
"unicode_decimal": 59499
|
||||
},
|
||||
{
|
||||
"icon_id": "22906292",
|
||||
"name": "删除_icon",
|
||||
"font_class": "shanchu_icon",
|
||||
"unicode": "e86c",
|
||||
"unicode_decimal": 59500
|
||||
},
|
||||
{
|
||||
"icon_id": "22906293",
|
||||
"name": "列表ssh_icon",
|
||||
"font_class": "liebiaossh_icon",
|
||||
"unicode": "e86e",
|
||||
"unicode_decimal": 59502
|
||||
},
|
||||
{
|
||||
"icon_id": "17575494",
|
||||
"name": "file-submodule",
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
src/App.js
13
src/App.js
|
@ -39,6 +39,11 @@ const Projects = Loadable({
|
|||
loader: () => import('./forge/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
//forge安全设置
|
||||
const Security = Loadable({
|
||||
loader: () => import('./forge/SecuritySetting/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
//forge项目-devOps详情
|
||||
const OpsDetail = Loadable({
|
||||
loader: () => import('./forge/DevOps/opsDetail'),
|
||||
|
@ -206,6 +211,14 @@ class App extends Component {
|
|||
}
|
||||
}>
|
||||
</Route>
|
||||
<Route
|
||||
path={"/settings"}
|
||||
render={
|
||||
(props) => {
|
||||
return (<Security {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
}>
|
||||
</Route>
|
||||
{/*项目*/}
|
||||
<Route
|
||||
path={"/projects"}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React , { Component } from 'react';
|
||||
import { Dropdown , Menu , Icon , Pagination , Spin } from 'antd';
|
||||
import '../css/index.scss';
|
||||
import '../Branch/branch.css';
|
||||
import '../Branch/branch.scss';
|
||||
import './activity.css';
|
||||
import NoneData from '../Nodata';
|
||||
|
||||
|
|
|
@ -1,39 +1,35 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Dropdown, Icon, Tooltip } from 'antd';
|
||||
import "./branch.css";
|
||||
import React, { useState } from 'react';
|
||||
import { Dropdown, Menu, Tooltip } from 'antd';
|
||||
import "./branch.scss";
|
||||
|
||||
class CloneAddress extends Component {
|
||||
function CloneAddress({http_url , ssh_url , zip_url , tar_url}) {
|
||||
const [ key , setKey ] = useState("HTTP");
|
||||
// 点击按钮复制功能
|
||||
jsCopy = () => {
|
||||
function jsCopy(){
|
||||
var e = document.getElementById("copy_rep_content");
|
||||
e.select();
|
||||
document.execCommand("Copy");
|
||||
}
|
||||
|
||||
render() {
|
||||
const { http_url, downloadUrl } = this.props;
|
||||
return (
|
||||
<div className="downMenu">
|
||||
<div style={{padding:"10px 20px 20px 20px",borderBottom:"1px solid #eee"}}>
|
||||
<Menu className="urlMenu" selectedKeys={[key]} mode={"horizontal"}>
|
||||
<Menu.Item key="HTTP" onClick={(e)=>{setKey(e.key)}}>HTTP</Menu.Item>
|
||||
<Menu.Item key="SSH" onClick={(e)=>{setKey(e.key)}}>SSH</Menu.Item>
|
||||
</Menu>
|
||||
<div className="gitAddressClone">
|
||||
{/* <p className="addressTips"><span>版本库地址已变更,请基于新地址提交代码</span></p> */}
|
||||
{
|
||||
http_url && <span>HTTP</span>
|
||||
}
|
||||
<input type="text" id="copy_rep_content" value={http_url} />
|
||||
<input type="text" id="copy_rep_content" value={key==="HTTP" ? http_url:ssh_url} />
|
||||
<Tooltip title="复制链接">
|
||||
<span className="color-blue" onClick={() => this.jsCopy()}><i className="iconfont icon-fuzhi"></i></span>
|
||||
<span className="color-blue" onClick={jsCopy}><i className="iconfont icon-fuzhi"></i></span>
|
||||
</Tooltip>
|
||||
{
|
||||
downloadUrl &&
|
||||
<span>
|
||||
<Dropdown overlay={downloadUrl} trigger={['click']} placement="bottomRight">
|
||||
<a className="ant-dropdown-link">
|
||||
<Icon type="cloud-download" className="font-18 fl color-blue" />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default CloneAddress;
|
|
@ -1,6 +1,6 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Popover , Input , Spin } from 'antd';
|
||||
import './branch.css';
|
||||
import './branch.scss';
|
||||
import { getBranch , getTag } from '../GetData/getData';
|
||||
|
||||
|
||||
|
|
|
@ -82,3 +82,24 @@
|
|||
padding:20px 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.urlMenu{
|
||||
line-height: 30px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: none;
|
||||
li.ant-menu-item{
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding:0px 5px;
|
||||
margin-right: 20px!important;
|
||||
&.ant-menu-item-selected,&.ant-menu-item-active{
|
||||
color: #333;
|
||||
}
|
||||
&.ant-menu-item-selected{
|
||||
border-color:#1890ff!important;
|
||||
}
|
||||
&.ant-menu-item-active{
|
||||
border-color:transparent ;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ function Footer(){
|
|||
|
||||
return(
|
||||
<div>
|
||||
<div style={{height:"483px"}}></div>
|
||||
<div style={{height:"497px"}}></div>
|
||||
<div className="newFooter edu-txt-center">
|
||||
{value && showhtml(value)}
|
||||
{/* <div className="footerInfos">
|
||||
|
|
|
@ -3,9 +3,9 @@ import AccountProfile from "../../modules/user/AccountProfile";
|
|||
import { getImageUrl } from 'educoder'
|
||||
import axios from 'axios';
|
||||
import { Input , notification , Dropdown , Menu } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import LoginDialog from '../../modules/login/LoginDialog';
|
||||
import GotoQQgroup from '../../modal/GotoQQgroup';
|
||||
import HeadSearch from '../Component/HeadSearch';
|
||||
|
||||
import AddProjectModal from './AddProjectModal';
|
||||
|
@ -79,36 +79,6 @@ class NewHeader extends Component {
|
|||
} 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: "提示",
|
||||
|
@ -117,8 +87,6 @@ class NewHeader extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
componentWillReceiveProps(newProps, oldProps) {
|
||||
this.setState({
|
||||
user: newProps.user
|
||||
|
@ -128,7 +96,6 @@ class NewHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
educoderlogin = () => {
|
||||
//登录账号
|
||||
this.setState({
|
||||
|
@ -166,13 +133,11 @@ class NewHeader extends Component {
|
|||
})
|
||||
};
|
||||
|
||||
|
||||
// 关闭
|
||||
cancelModulationModels = () => {
|
||||
this.setState({ isRenders: false })
|
||||
}
|
||||
|
||||
|
||||
setevaluatinghides = () => {
|
||||
this.setState({
|
||||
setevaluatinghides: true
|
||||
|
@ -289,6 +254,7 @@ class NewHeader extends Component {
|
|||
)
|
||||
})
|
||||
}
|
||||
<li><Link to={`/settings/SSH`}>设置</Link></li>
|
||||
<Menu.Item><a onClick={() => this.educoderloginysl()}>退出</a></Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
|
@ -297,17 +263,13 @@ class NewHeader extends Component {
|
|||
render() {
|
||||
const { match} = this.props;
|
||||
let current_user = this.props.user;
|
||||
let { Addcoursestypes,
|
||||
tojoinitemtype,
|
||||
tojoinclasstitle,
|
||||
code_notice,
|
||||
let {
|
||||
AccountProfiletype,
|
||||
user,
|
||||
isRender,
|
||||
headtypesonClickbool,
|
||||
headtypess,
|
||||
settings,
|
||||
openSearch,
|
||||
} = this.state;
|
||||
/*用户名称 用户头像url*/
|
||||
let activeIndex = false;
|
||||
|
|
|
@ -253,17 +253,12 @@ function CoderDepot(props){
|
|||
}
|
||||
|
||||
const downloadMenu = (
|
||||
<div className="downMenu">
|
||||
<div style={{padding:"20px",borderBottom:"1px solid #eee"}}>
|
||||
<CloneAddress
|
||||
http_url={projectDetail && projectDetail.clone_url}
|
||||
ssh_url = {projectDetail && projectDetail.ssh_url}
|
||||
zip_url={zip_url}
|
||||
tar_url={tar_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){
|
||||
|
|
|
@ -319,6 +319,7 @@
|
|||
}
|
||||
}
|
||||
.downMenu{
|
||||
width: 330px;
|
||||
box-shadow: 0px 0px 9px rgba(134, 134, 134,0.4);
|
||||
background-color: #fff;
|
||||
.ant-menu-vertical .ant-menu-item:hover{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { Component } from 'react';
|
||||
import "../Branch/branch.css"
|
||||
import "../Branch/branch.scss"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import React from "react";
|
||||
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import { SnackbarHOC } from "educoder";
|
||||
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
|
||||
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
|
||||
import Loadable from "react-loadable";
|
||||
import Loading from "../../Loading";
|
||||
import { Box , Gap , LongWidth } from '../Component/layout';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import './Index.scss';
|
||||
|
||||
const SSHNew = Loadable({
|
||||
loader: () => import("./sub/New"),
|
||||
loading: Loading,
|
||||
});
|
||||
const SSHIndex = Loadable({
|
||||
loader: () => import("./sub/SSH"),
|
||||
loading: Loading,
|
||||
});
|
||||
function Index(props){
|
||||
const { current_user } = props;
|
||||
const { pathname } = props.location;
|
||||
|
||||
return(
|
||||
<div className="newMain clearfix whiteBack">
|
||||
<div className="boies">
|
||||
<Box>
|
||||
<div className="shortW">
|
||||
<div className="userDetail">
|
||||
<img src={getImageUrl(`/${current_user && current_user.image_url}`)} alt=""/>
|
||||
<span>{current_user && current_user.username}</span>
|
||||
</div>
|
||||
<ul className="securityUl">
|
||||
<li>安全设置</li>
|
||||
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i>SSH密钥</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
<LongWidth>
|
||||
<Gap>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/settings/SSH/new"
|
||||
render={(p) => (
|
||||
<SSHNew {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/settings/SSH"
|
||||
render={(p) => (
|
||||
<SSHIndex {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</Gap>
|
||||
</LongWidth>
|
||||
</Box>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default withRouter((CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index))))
|
||||
);
|
|
@ -0,0 +1,192 @@
|
|||
.whiteBack{
|
||||
background-color: #fff;
|
||||
.boies{
|
||||
width: 1200px;
|
||||
margin:0px auto;
|
||||
padding:30px 0px 10px;
|
||||
.shortW{
|
||||
width: 198px;
|
||||
border: 1px solid rgba(153, 153, 153, 0.22);
|
||||
border-radius: 4px;
|
||||
min-height: 400px;
|
||||
margin-bottom: 30px;
|
||||
.userDetail{
|
||||
background: rgba(153, 153, 153, 0.05);
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
padding:20px 25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
img{
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
}
|
||||
span{
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
max-width: 90px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.securityUl{
|
||||
padding:20px 16px;
|
||||
color: #333;
|
||||
li{
|
||||
margin-bottom: 10px;
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
a{
|
||||
color: #666;
|
||||
&:hover{
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
&.active a{
|
||||
color: #333;
|
||||
}
|
||||
&:first-child{
|
||||
font-size: 16px;
|
||||
}
|
||||
&.active::before{
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
top:0px;
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
content: "";
|
||||
background-color: #2A61FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sshHead{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding:15px 20px;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
&>span{
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.ant-list-item{
|
||||
padding:20px;
|
||||
border-bottom: 1px solid #eee!important;
|
||||
&>img{
|
||||
margin-right: 24px;
|
||||
}
|
||||
&>div{
|
||||
flex:1;
|
||||
width: 0;
|
||||
margin-right: 20px;
|
||||
p{
|
||||
margin-bottom: 8px!important;
|
||||
}
|
||||
span{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.ant-btn.ant-btn-danger{
|
||||
background-color: #fff;
|
||||
border-color: #D0D0D0;
|
||||
color: #DF0002;
|
||||
&:hover{
|
||||
background-color: #DF0002;
|
||||
color: #fff;
|
||||
border-color: #DF0002;
|
||||
}
|
||||
}
|
||||
}
|
||||
.questionLink{
|
||||
padding:15px 20px;
|
||||
a{
|
||||
color: #4B7AFF;
|
||||
&:hover{
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sshForm{
|
||||
padding:15px 20px;
|
||||
.ant-col.ant-form-item-label{
|
||||
font-size: 16px;
|
||||
color:#333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.deleteBox{
|
||||
.ant-modal-header{
|
||||
background-color: rgba(223, 0, 2, 0.06);
|
||||
border-bottom: none;
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.ant-modal-body{
|
||||
padding:30px 50px;
|
||||
p{
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
color:#666
|
||||
}
|
||||
.desc{
|
||||
.descMain{
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
i{
|
||||
font-size: 38px!important;
|
||||
color:#DF0002
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-modal-footer{
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
padding-bottom: 40px;
|
||||
button{
|
||||
width: 120px;
|
||||
margin:0px 20px;
|
||||
&.ant-btn-danger{
|
||||
background-color: #fff;
|
||||
color: #DF0002;
|
||||
border-color: #D0D0D0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.descModal{
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
font-size: 20px;
|
||||
}
|
||||
.keyContent{
|
||||
border:1px solid #eee;
|
||||
border-radius: 4px;
|
||||
padding:10px 15px;
|
||||
margin-top: 10px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.keysTitle{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
span:last-child{
|
||||
word-break: break-all;
|
||||
flex:1;
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 900 B |
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { AlignCenter } from '../../Component/layout';
|
||||
import { Modal , Button } from 'antd';
|
||||
|
||||
function DeleteBox({ visible , onCancel ,onSuccess }) {
|
||||
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
title="删除SSH密钥"
|
||||
width="600px"
|
||||
className="deleteBox"
|
||||
footer={
|
||||
<div>
|
||||
<Button size={'large'} onClick={onCancel}>取消</Button>
|
||||
<Button type={"danger"} size={"large"} onClick={onSuccess}>确认删除</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="desc">
|
||||
<AlignCenter className="descMain"><i className="iconfont icon-jinggao1 mr10"></i>您确定要删除此 SSH 密钥吗?</AlignCenter>
|
||||
<p>此操作将永久删除该SSH密钥,且不可恢复。如果您想再次使用该密钥,则需要您重新上传。</p>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default DeleteBox;
|
|
@ -0,0 +1,59 @@
|
|||
import React , { forwardRef, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Form , Input } from 'antd';
|
||||
import Axios from 'axios';
|
||||
|
||||
const { TextArea } = Input;
|
||||
function New({ form , showNotification , history }) {
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [ msg , setMsg ] = useState(undefined);
|
||||
|
||||
function submit() {
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
const url = `/public_keys.json`;
|
||||
Axios.post(url,{
|
||||
...values
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
if(result.data.status === 0){
|
||||
history.push(`/settings/SSH`);
|
||||
showNotification("密钥创建成功!");
|
||||
}
|
||||
let s = {
|
||||
status:result.data.status,
|
||||
message:result.data.message
|
||||
}
|
||||
setMsg(s);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div className="sshHead">
|
||||
<span><Link to={`/settings/SSH`} className="color-blue">SSH密钥</Link><i className="iconfont icon-youjiantou ml5 mr5 font-12"></i>添加SSH密钥</span>
|
||||
</div>
|
||||
<Form className="sshForm">
|
||||
<Form.Item label="标题" validateStatus={msg && msg.status === 10001 ? "error":undefined} help={msg && msg.status === 10001 ? msg.message:undefined}>
|
||||
{getFieldDecorator("title",{
|
||||
rules:[{required:true,message:"请输入密钥标题"}]
|
||||
})(
|
||||
<Input placeholder="请输入密钥标题" size="large" maxLength="200"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="密钥" validateStatus={msg && msg.status === 10002 ? "error":undefined} help={msg && msg.status === 10002 ? msg.message:undefined}>
|
||||
{getFieldDecorator("key",{
|
||||
rules:[{required:true,message:"请输入密钥"}]
|
||||
})(
|
||||
<TextArea placeholder="支持以'ssh-rsa','ssh-dss','ssh-ed25519','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'开头" autoSize={{ minRows: 6, maxRows: 6 }}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Button type="primary" style={{width:"100px"}} onClick={submit}>确定</Button>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(New));
|
|
@ -0,0 +1,92 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Button , List , Pagination , Skeleton } from 'antd';
|
||||
import miyao from '../img/miyao_middle_icon.png';
|
||||
import axios from 'axios';
|
||||
import DeleteBox from './DeleteBox';
|
||||
import SSHDetail from './SSHDetail';
|
||||
|
||||
const limit = 10;
|
||||
function SSH(props) {
|
||||
const [ list ,setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
const [ id , setId ] = useState(undefined);
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const [ visibleDesc , setVisibleDesc ] = useState(false);
|
||||
const [ content , setContent ] = useState(undefined);
|
||||
|
||||
useState(()=>{
|
||||
Init();
|
||||
},[])
|
||||
|
||||
function Init() {
|
||||
const url = `/public_keys.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
page,limit
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setList(result.data.public_keys);
|
||||
setTotal(result.data.total_count);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function onSuccess() {
|
||||
if(id){
|
||||
const url = `/public_keys/${id}.json`;
|
||||
axios.delete(url).then(result=>{
|
||||
if(result && result.data){
|
||||
props.showNotification("密钥删除成功!");
|
||||
setVisible(false);
|
||||
Init();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<DeleteBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/>
|
||||
<SSHDetail visible={visibleDesc} onCancel={()=>setVisibleDesc(false)} desc={content}/>
|
||||
<div className="sshHead">
|
||||
<span>SSH密钥</span>
|
||||
<Button type="primary" size="large" onClick={()=>props.history.push('/settings/SSH/new')}>添加SSH密钥</Button>
|
||||
</div>
|
||||
{
|
||||
list && list.length > 0 &&
|
||||
<List>
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<List.Item>
|
||||
<img src={miyao} alt=""/>
|
||||
<div>
|
||||
<p className="color-grey-3"><a className="task-hide" style={{display:"block",fontWeight:"500"}} onClick={()=>{setContent(i);setVisibleDesc(true)}}>{i.name}</a></p>
|
||||
<p className="task-hide color-grey-6">{i.fingerprint}</p>
|
||||
<span className="color-grey-6">添加时间:{i.created_time}</span>
|
||||
</div>
|
||||
<Button type="danger" onClick={()=>{setId(i.id);setVisible(true)}}>删除</Button>
|
||||
</List.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
</List>
|
||||
}
|
||||
{
|
||||
!list && <Skeleton />
|
||||
}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center mt15">
|
||||
<Pagination simple current={page} onChange={(p)=>{setPage(p)}} pageSize={limit} total={total}/>
|
||||
</div>
|
||||
}
|
||||
{ (!list || (list && list.length === 0)) && <p className="mt30 pl20">您还没有添加任何SSH密钥</p> }
|
||||
<p className="questionLink"><a href={`https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent`} target="_blank">如何生成SSH密钥?</a></p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default SSH;
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Button, Modal } from 'antd';
|
||||
|
||||
function SSHDetail({visible , onCancel , desc }) {
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
title={"SSH密钥详情"}
|
||||
width="600px"
|
||||
footer={null}
|
||||
className={"descModal"}
|
||||
>
|
||||
<div>
|
||||
<p className="color-grey-3 font-16 keysTitle">
|
||||
<span style={{fontWeight:"600"}}>SSH Key:</span>
|
||||
<span>{desc && desc.name}</span>
|
||||
</p>
|
||||
<p className="mt5">添加时间:{desc && desc.created_time}</p>
|
||||
<p className="color-grey-3 mt30 keysTitle mb15">
|
||||
<span style={{fontWeight:"600"}}>公钥指纹:</span>
|
||||
<span>{desc && desc.fingerprint}</span>
|
||||
</p>
|
||||
<div className="keyContent">{desc && desc.content}</div>
|
||||
<div className="edu-txt-center mt30 mb20"><Button type={"primary"} size="large" style={{width:"100px"}} onClick={onCancel}>关闭</Button></div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default SSHDetail;
|
|
@ -477,9 +477,6 @@ class NewShixunModel extends Component{
|
|||
<style>
|
||||
{
|
||||
`
|
||||
.ant-input, .ant-input .ant-input-suffix{
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.packinput .ant-input{
|
||||
border: 1px solid rgba(217,217,217,1) !important;
|
||||
}
|
||||
|
|
|
@ -325,9 +325,6 @@ class OneSelfOrderModal extends Component{
|
|||
<style>
|
||||
{
|
||||
`
|
||||
.ant-input, .ant-input .ant-input-suffix{
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.width300{
|
||||
width:300px;
|
||||
display: inline-block;
|
||||
|
|
|
@ -1158,10 +1158,6 @@ body #root {
|
|||
position: absolute;
|
||||
}
|
||||
|
||||
.ant-input,
|
||||
.ant-input .ant-input-suffix {
|
||||
background-color: #fafafa !important;
|
||||
}
|
||||
|
||||
.ant-input:focus,
|
||||
.ant-input:focus .ant-input-suffix {
|
||||
|
@ -1479,9 +1475,6 @@ samp {
|
|||
margin-left: 0px !important;
|
||||
}
|
||||
|
||||
.ant-form-explain {
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.exercise .ant-form-item-label {
|
||||
margin-left: 20px;
|
||||
|
|
|
@ -51,47 +51,34 @@ class Header extends Component {
|
|||
open={challengesDrawerOpen}
|
||||
onClose={() => onChallengesDrawerClose()}
|
||||
>
|
||||
|
||||
<TaskListContainer challenges={challenges} taskListLoading={taskListLoading} shixun={shixun}
|
||||
challenge={challenge} onStarChange={onStarChange} saveChallengeStar={saveChallengeStar}
|
||||
onChallengesDrawerClose={() => onChallengesDrawerClose()}
|
||||
showSnackbar={showSnackbar} closeTaskResultLayer={closeTaskResultLayer}
|
||||
myshixun_manager={myshixun_manager}
|
||||
>
|
||||
|
||||
</TaskListContainer>
|
||||
|
||||
</Drawer>
|
||||
|
||||
<div className="headerLeft">
|
||||
|
||||
|
||||
<div className="userInfo">
|
||||
<a href={user.user_url} target="_blank">
|
||||
{user.image_url && <img alt="0?1442652658" width="35" height="35" src={`../images/${user.image_url}`} />}
|
||||
</a>
|
||||
<a href={user.user_url} className="userInfoName" target="_blank">{user.username}</a>
|
||||
</div>
|
||||
|
||||
{(grade || grade == 0) && <div className="-header-right clearfix">
|
||||
<span style={{ width: '20px', height: '20px', background: '#FFD633', borderRadius: '10px', marginTop: '3px' }} className="fl"></span>
|
||||
<span className="ml5 color-white fl" id="user_grade">
|
||||
<span>{grade === 0 ? grade : (grade || '')}</span>
|
||||
</span>
|
||||
</div>}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div className="-layout-h ml10 headerCenter">
|
||||
<h2 className="-header-title task-hide color-white">{shixun ? shixun.name || '' : ''}</h2>
|
||||
<Tooltip title="本关通关耗时">
|
||||
<div className="timeRecord">{game && hhmmss(game.cost_time)}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className="headerRight">
|
||||
<div className="fr">
|
||||
{shixun ?
|
||||
|
|
Loading…
Reference in New Issue