Merge branch 'develop'

This commit is contained in:
caishi 2021-07-22 17:44:30 +08:00
commit 6679fe8a48
28 changed files with 631 additions and 116 deletions

View File

@ -1753,7 +1753,7 @@ a.decoration {
}
.mb15 {
margin-bottom: 15px;
margin-bottom: 15px!important;
}
.mb16 {
@ -6685,4 +6685,10 @@ p{
right: 0px;
top:4px;
color: #999;
}
.ant-input, .ant-input .ant-input-suffix{
background-color: #fff!important;
}
.has-error .ant-input{
background-color: #FEF1F0!important;
}

View File

@ -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

View File

@ -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.

View File

@ -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"}

View File

@ -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';

View File

@ -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="gitAddressClone">
{/* <p className="addressTips"><span>版本库地址已变更,请基于新地址提交代码</span></p> */}
{
http_url && <span>HTTP</span>
}
<input type="text" id="copy_rep_content" value={http_url} />
<Tooltip title="复制链接">
<span className="color-blue" onClick={() => this.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>
}
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">
<input type="text" id="copy_rep_content" value={key==="HTTP" ? http_url:ssh_url} />
<Tooltip title="复制链接">
<span className="color-blue" onClick={jsCopy}><i className="iconfont icon-fuzhi"></i></span>
</Tooltip>
</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;

View File

@ -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';

View File

@ -81,4 +81,25 @@
.listTips{
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 ;
}
}
}

View File

@ -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">

View File

@ -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;

View File

@ -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}
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>
<CloneAddress
http_url={projectDetail && projectDetail.clone_url}
ssh_url = {projectDetail && projectDetail.ssh_url}
zip_url={zip_url}
tar_url={tar_url}
showNotification={props.showNotification}/>
)
// website
function okUpdate(d,w,l){

View File

@ -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{

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import "../Branch/branch.css"
import "../Branch/branch.scss"

View File

@ -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))))
);

View File

@ -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

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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 ?