Compare commits
75 Commits
Author | SHA1 | Date |
---|---|---|
Eeeros | 40ec067139 | |
xxq250 | 801c544f65 | |
hucong | 5a1fcb1443 | |
hucong | 33297226ea | |
hucong | fd4046af04 | |
hucong | 36683ad69b | |
hucong | 2262508777 | |
hucong | 9db8455de8 | |
jasder | 7579dc8a59 | |
jasder | 89773b5036 | |
hucong | 2617831a9f | |
hucong | f0747eab05 | |
jasder | d1743b350d | |
baladiwei | 7fd790fe7d | |
谢思 | c48e3584c3 | |
谢思 | d0ca09e780 | |
baladiwei | ed3bf55f90 | |
谢思 | 7ff29fec70 | |
hucong | fbfd5f175d | |
hucong | 07101f5770 | |
谢思 | a94d02bb8a | |
谢思 | cc2dd35ff4 | |
谢思 | f003a99779 | |
谢思 | b1ead191a6 | |
谢思 | 55a6f93fe3 | |
谢思 | ac4488a907 | |
jasder | ea979b78ee | |
hucong | b1775e5158 | |
hucong | 3678b8eb72 | |
hucong | e04fb11194 | |
hucong | 010edf3cb2 | |
jasder | 6d39e25667 | |
hucong | 190200d1e6 | |
hucong | b124364203 | |
谢思 | 2078a044b6 | |
jasder | e785e9a358 | |
jasder | b3b95e9856 | |
caishi | 5be8ead356 | |
jasder | 7d78919c4a | |
jasder | d5b4c5425f | |
caishi | f259f05cdc | |
jasder | 95cd289373 | |
caishi | c79be9a597 | |
jasder | fb03565a25 | |
jasder | 56946f94e8 | |
caishi | 61ee3fe36f | |
caishi | d5f989bbe2 | |
caishi | 1c24baf3b9 | |
caishi | 3d3ef19d4a | |
jasder | 7b77c46915 | |
hucong | 746f552a21 | |
hucong | 7872d5ecab | |
hucong | 60da6bd110 | |
caishi | d37709b1a5 | |
hucong | aeff357660 | |
hucong | 0e6cb3954a | |
hucong | ae12618c0c | |
谢思 | 1c46617144 | |
谢思 | ec133a6880 | |
谢思 | bcdb631c20 | |
谢思 | 8021d96cd8 | |
谢思 | 3498390974 | |
谢思 | 4f3e385f78 | |
谢思 | 18b289b84c | |
谢思 | e3f49db5c0 | |
谢思 | 4add266047 | |
谢思 | 4a6012bfa9 | |
谢思 | 7fe88aa4d9 | |
谢思 | bc6ef583fb | |
谢思 | b268b540d8 | |
谢思 | 7e69c64c9c | |
谢思 | 85ef26f70e | |
谢思 | 8c7865d2c1 | |
谢思 | 999a15761d | |
谢思 | 5c2330512e |
|
@ -3971,9 +3971,12 @@ html>body #ajax-indicator {
|
|||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
color: #333;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
.head-right i{
|
||||
color: #fff!important;
|
||||
}
|
||||
|
||||
.head-nav ul#header-nav li a:hover,.head-nav ul#header-nav li.active a {
|
||||
color: #5091FF;
|
||||
|
|
|
@ -322,6 +322,13 @@ class App extends Component {
|
|||
{/* 查询 */}
|
||||
<Route path="/search" component={Search} />
|
||||
|
||||
<Route exact path="/explore/all"
|
||||
render={
|
||||
(props) => (
|
||||
<ProjectIndex {...this.props} {...props} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Route exact path="/explore"
|
||||
render={
|
||||
(props) => (
|
||||
|
|
|
@ -32,7 +32,7 @@ export default ({history}) => {
|
|||
/>
|
||||
</div>
|
||||
:
|
||||
<i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
|
||||
<i className="iconfont icon-sousuo font-18 ml30" onClick={() => {
|
||||
setOpenSearch(true)
|
||||
}} />
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ function Footer(){
|
|||
|
||||
return(
|
||||
<div>
|
||||
<div style={{height:"543px"}}></div>
|
||||
<div style={{height:"483px"}}></div>
|
||||
<div className="newFooter edu-txt-center">
|
||||
{value && showhtml(value)}
|
||||
{/* <div className="footerInfos">
|
||||
|
|
|
@ -433,7 +433,7 @@ class NewHeader extends Component {
|
|||
{
|
||||
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 mr15"></i>
|
||||
<i className="iconfont icon-tianjiafangda ml30 mr15"></i>
|
||||
</Dropdown>:""
|
||||
}
|
||||
|
||||
|
@ -448,7 +448,7 @@ class NewHeader extends Component {
|
|||
>
|
||||
<Link to={"/settings/notice"} className="message-icon">
|
||||
{current_user && <Badge count={current_user.message_unread_total}>
|
||||
<i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i>
|
||||
<i className="iconfont icon-xiaoxilingdang ml15 mr15"></i>
|
||||
</Badge>}
|
||||
</Link>
|
||||
</Popover>
|
||||
|
@ -457,10 +457,10 @@ class NewHeader extends Component {
|
|||
</div>
|
||||
{!user || (user && !user.login) ?
|
||||
<span className="font-15 ml30">
|
||||
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
|
||||
<a onClick={() => this.educoderlogin()} className="mr5 color-white">登录</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><em className="vertical-line"></em><a className="ml5 color-white" href={`${settings.common.register}`} target="_blank">注册</a></span>
|
||||
}
|
||||
</span>
|
||||
:
|
||||
|
|
|
@ -224,7 +224,7 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:
|
|||
<Badge color="#FA2020" />
|
||||
</span>
|
||||
<div className="noticeCont-text">
|
||||
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: "<b>" + (item.sender ? item.sender.name : '') + "</b> " + item.content + " 中@我" }}></span>
|
||||
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: item.content }}></span>
|
||||
<span className="timeSpan">{item.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
After Width: | Height: | Size: 52 KiB |
|
@ -22,6 +22,11 @@ const ProjectIndex = Loadable({
|
|||
loading: Loading,
|
||||
});
|
||||
|
||||
const ProjectHome = Loadable({
|
||||
loader: () => import("./Main/projecthome/Index"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
// 项目详情放在用户和组织下作为二级菜单存在
|
||||
// const ProjectDetail = Loadable({
|
||||
// loader: () => import("./Main/Detail"),
|
||||
|
@ -67,16 +72,22 @@ class Index extends Component {
|
|||
)}
|
||||
></Route> */}
|
||||
<Route
|
||||
path="/explore"
|
||||
path="/explore/all"
|
||||
render={(props) => (
|
||||
<ProjectIndex {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
|
||||
<Route
|
||||
path="/explore"
|
||||
render={(props) => (
|
||||
<ProjectHome {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/"
|
||||
render={(props) => (
|
||||
<ProjectIndex {...this.props} {...props} />
|
||||
<ProjectHome {...this.props} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -138,7 +138,7 @@ function CoderDepot(props){
|
|||
if(result && result.data){
|
||||
const release = {
|
||||
"list":result.data.releases,
|
||||
"total_count":result.data.releases.length
|
||||
"total_count":result.data.releases && result.data.releases.length
|
||||
}
|
||||
setReleaseVersions(release);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
|
||||
return(
|
||||
<div className="commonBox readBox" id="readme">
|
||||
<Anchor offsetTop={70} targetOffset={160}>
|
||||
<Anchor offsetTop={70}>
|
||||
<div className="commonBox-title boxTitle">
|
||||
<AlignCenter>
|
||||
<Dropdown overlay={menu()} trigger={['hover']} overlayClassName="menuslist">
|
||||
|
|
|
@ -509,11 +509,6 @@ class Detail extends Component {
|
|||
this.textFunc(projectDetail.forked_from_project_id, projectDetail.fork_info)
|
||||
: ""
|
||||
}
|
||||
{
|
||||
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
|
||||
<span className="color-grey-9">导入于 <a className="color-grey-6" target="_blank" href={projectDetail.mirror_url}>{projectDetail.mirror_url}</a></span>
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -50,7 +50,7 @@ class Index extends Component {
|
|||
|
||||
this.getCategory();
|
||||
|
||||
this.getRecommand();
|
||||
// this.getRecommand();
|
||||
|
||||
this.getLanguage();
|
||||
|
||||
|
@ -304,9 +304,9 @@ class Index extends Component {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<p className="t_project_banner">
|
||||
{/* <p className="t_project_banner">
|
||||
<img src={banner} width="100%" alt=""/>
|
||||
</p>
|
||||
</p> */}
|
||||
{/* {
|
||||
recommendOriList && recommendOriList.length>0?
|
||||
<Slider {...settings} className="recommandOri">
|
||||
|
@ -320,7 +320,7 @@ class Index extends Component {
|
|||
</Slider>
|
||||
:""
|
||||
} */}
|
||||
{
|
||||
{/* {
|
||||
recommendList && recommendList.length>0 &&
|
||||
<Slider {...setting} className={recommendList.length>5 ? "recommandProjects":"recommandProjects mb20"}>
|
||||
{
|
||||
|
@ -341,7 +341,7 @@ class Index extends Component {
|
|||
})
|
||||
}
|
||||
</Slider>
|
||||
}
|
||||
} */}
|
||||
<div className="ProjectListIndex">
|
||||
<div className="list-left">
|
||||
<ul className="list-l-Menu">
|
||||
|
|
|
@ -52,12 +52,6 @@ class IndexItem extends Component {
|
|||
<i className="iconfont icon-banbenku font-18 color-green" />
|
||||
</Tooltip>:""
|
||||
}
|
||||
{
|
||||
item.type && item.type === 1 ?
|
||||
<Tooltip title="该项目是一个导入于其他网站的仓库" className="ml5">
|
||||
<i className="iconfont icon-jingxiang font-18 color-green" />
|
||||
</Tooltip>:""
|
||||
}
|
||||
</AlignCenter>
|
||||
<span className="p-r-tags">
|
||||
{
|
||||
|
|
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 435 B |
After Width: | Height: | Size: 969 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 367 B |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,126 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import './Index.scss';
|
||||
import SubBanner from './SubBanner';
|
||||
import SubUnitBanner from './SubUnitBanner';
|
||||
import Icon from '../img/index/icon.png';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Spin } from 'antd';
|
||||
import SubList from './SubList';
|
||||
import more from '../img/index/more.png';
|
||||
import axios from 'axios';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Nodata from '../../Nodata';
|
||||
|
||||
const LIMIT = 20;
|
||||
function Index() {
|
||||
|
||||
const [ cateList , setCateList ] = useState(undefined);
|
||||
const [ projectsList , setProjectsList ] = useState(undefined);
|
||||
const [ cateID, setCateID ] = useState(undefined);
|
||||
const [ isSpin, setIsSpin ] = useState(true);
|
||||
|
||||
useEffect(()=>{
|
||||
getCate();
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
setIsSpin(true);
|
||||
getProject();
|
||||
},[cateID])
|
||||
|
||||
function getCate() {
|
||||
const url = `/project_categories/pinned_index.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setCateList(result.data.project_categories);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
function getProject() {
|
||||
const url = `/projects.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
pinned:"d",
|
||||
category_id:cateID,
|
||||
limit:LIMIT
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setProjectsList(result.data.projects);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<SubBanner />
|
||||
<SubUnitBanner />
|
||||
<div className="dataPanel">
|
||||
<div className="left">
|
||||
<ul className="leftTypes">
|
||||
<a className={cateID ? "" : "active"} onClick={()=>setCateID(undefined)}><img src={Icon} alt="" /><span>最近更新</span></a>
|
||||
{
|
||||
cateList && cateList.length>0?
|
||||
cateList.map((i,k)=>{
|
||||
return(
|
||||
<a className={cateID === i.id ?"active":""} onClick={()=>setCateID(i.id)}><img src={i.logo_url || Icon} alt="" /><span>{i.name}</span></a>
|
||||
)
|
||||
})
|
||||
:""
|
||||
}
|
||||
</ul>
|
||||
<div className="leftLists">
|
||||
<div className="leftTitles">
|
||||
<span>开源项目</span>
|
||||
<Link to={`/explore/all`}>更多<i className="iconfont icon-triangle font-12"></i></Link>
|
||||
</div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
projectsList && projectsList.length > 0 ?
|
||||
<div className="leftlistItem">
|
||||
{
|
||||
projectsList.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
|
||||
<div className="itemTitle">
|
||||
<div className="item-title-infos">
|
||||
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="infotitle">{i.author && i.author.name}/{i.name}</Link>
|
||||
{i.praises_count > 0 ? <span><i className="iconfont icon-dianzan11 mr3 font-16"></i>{i.praises_count}</span> :"" }
|
||||
{i.forked_count > 0 ? <span><i className="iconfont icon-fork2 mr3 font-13"></i>{i.forked_count}</span>:""}
|
||||
</div>
|
||||
<div className="item-desc task-hide-2">
|
||||
{i.description}
|
||||
</div>
|
||||
<div className="item-data">
|
||||
{i.category && i.category.id ? <span className="category">{i.category.name}</span> :"" }
|
||||
{i.language && i.language.id ? <span className="language mr30">{i.language.name}</span> :""}
|
||||
<span style={{lineHeight:"15px",display:"flex"}}><i className="iconfont icon-shijian font-15 mr5"></i>更新于{i.time_ago}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
{
|
||||
projectsList && projectsList.length === 0 && <Nodata _html="暂无数据" />
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
<div className="left-bottom-btn">
|
||||
<Link to={`/explore/all`}>查看更多开源项目<img src={more} alt="" /></Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SubList />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Index;
|
|
@ -0,0 +1,553 @@
|
|||
.banners{
|
||||
background: url('../img/index/banner.png') no-repeat top;
|
||||
min-height: 516px;
|
||||
background-size: cover;
|
||||
background-color: #0C2A5B;
|
||||
.bannersCenter{
|
||||
padding-top: 30px;
|
||||
max-width: 1200px;
|
||||
margin: 0px auto;
|
||||
position: relative;
|
||||
height: 516px;
|
||||
}
|
||||
.bTitle{
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 30px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 20px!important;
|
||||
text-align: center;
|
||||
span{
|
||||
background: linear-gradient(to right,#289AF6 0%, #9E84FF 40%, #FFB03B 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
}
|
||||
.bSubTitle{
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.bannerBox{
|
||||
background: url('../img/index/box.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
height: 240px;
|
||||
width: 380px;
|
||||
margin:0px auto;
|
||||
padding:25px 30px 34px;
|
||||
.bannersProject{
|
||||
position: relative;
|
||||
.slick-list{
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.slick-track{
|
||||
display: flex;
|
||||
li{
|
||||
padding:15px 20px;
|
||||
.projectinfos{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 13px;
|
||||
img{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 7px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.name{
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 250px;
|
||||
color: #25EBFF;
|
||||
a{
|
||||
color: #25EBFF!important;
|
||||
}
|
||||
}
|
||||
.company{
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 250px;
|
||||
a{
|
||||
color: #FFFFFF!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.desc{
|
||||
height: 40px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
.infoData{
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
justify-content: space-around;
|
||||
&>span{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&>span{
|
||||
color: #FFB729;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
img{
|
||||
width: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.airBubble{
|
||||
&>div{
|
||||
position: absolute;
|
||||
height: 410px;
|
||||
width: 415px;
|
||||
bottom: 0px;
|
||||
&.right{
|
||||
right: 0px;
|
||||
}
|
||||
&.left{
|
||||
left: 0px;
|
||||
}
|
||||
&.right div, &.left div{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
span{
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(124deg, rgba(255, 255, 255, 0.1) 0%, rgba(5, 200, 220, 0.04) 50%, rgba(5, 200, 220, 0.03) 100%);
|
||||
box-shadow: 0px 0px 7px 3px rgba(0, 154, 255, 0.19);
|
||||
border: 1px solid rgba(0, 154, 255, 0.19);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
word-break: break-all;
|
||||
text-align: center;
|
||||
padding:5px;
|
||||
animation: zoomin 1.3s infinite alternate;
|
||||
&:hover{
|
||||
animation:none
|
||||
}
|
||||
}
|
||||
}
|
||||
&.right span:nth-child(4) a,&.left span:nth-child(1) a,&.left span:nth-child(2) a{
|
||||
color: #25EBFF!important;
|
||||
}
|
||||
&.right span:nth-child(1) a,&.left span:nth-child(5) a{
|
||||
color: #CD8AFF!important;
|
||||
}
|
||||
&.right span:nth-child(3) a,&.right span:nth-child(5) a,&.left span:nth-child(6) a{
|
||||
color: #FF6125!important;
|
||||
}
|
||||
&.left span:nth-child(3) a{
|
||||
color: #FF8425!important;
|
||||
}
|
||||
&.right span:nth-child(2) a,&.left span:nth-child(4) a{
|
||||
color: #8CC2FF!important;
|
||||
}
|
||||
&.right span:nth-child(1){
|
||||
right: 0px;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
&.right span:nth-child(2){
|
||||
right: 160px;
|
||||
height: 72px;
|
||||
width: 72px;
|
||||
bottom: 220px;
|
||||
padding:0px;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
&.right span:nth-child(3){
|
||||
right: 300px;
|
||||
height: 94px;
|
||||
width: 94px;
|
||||
bottom: 99px;
|
||||
padding:8px;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
&.right span:nth-child(4){
|
||||
right: 156px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
bottom: 39px;
|
||||
font-size: 12px;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
&.right span:nth-child(5){
|
||||
right: 10px;
|
||||
height: 94px;
|
||||
width: 94px;
|
||||
bottom: 97px;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
&.left span:nth-child(1){
|
||||
left: 80px;
|
||||
height: 110px;
|
||||
width: 110px;
|
||||
padding:8px;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
&.left span:nth-child(2){
|
||||
right: 60px;
|
||||
height: 96px;
|
||||
width: 96px;
|
||||
top: 85px;
|
||||
font-size: 13px;
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
&.left span:nth-child(3){
|
||||
left: 0px;
|
||||
height: 86px;
|
||||
width: 86px;
|
||||
top: 145px;
|
||||
font-size: 13px;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
&.left span:nth-child(4){
|
||||
left: 164px;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
bottom: 158px;
|
||||
font-size: 12px;
|
||||
font-size: 12px;
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
&.left span:nth-child(5){
|
||||
left: 35px;
|
||||
height: 102px;
|
||||
width: 102px;
|
||||
bottom: 32px;
|
||||
font-size: 13px;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
&.left span:nth-child(6){
|
||||
right: 10px;
|
||||
height: 114px;
|
||||
width: 114px;
|
||||
bottom: 26px;
|
||||
padding:8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes zoomin{
|
||||
0%{-webkit-transform:scale(1.05);transform:scale(1.05)}
|
||||
100%{-webkit-transform:scale(0.8);transform:scale(0.8)}
|
||||
}
|
||||
@-webkit-keyframes zoomin{
|
||||
0%{-webkit-transform:scale(1.05);transform:scale(1.05)}
|
||||
100%{-webkit-transform:scale(0.8);transform:scale(0.8)}
|
||||
}
|
||||
.unitBanner{
|
||||
padding:29px 0px;
|
||||
background: #F9F9F9;
|
||||
.unitContent{
|
||||
max-width: 1200px;
|
||||
margin:0px auto;
|
||||
.unitTitle{
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #1E1E1E;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
img{
|
||||
margin-left: 9px;
|
||||
}
|
||||
}
|
||||
.unitSlider{
|
||||
padding:30px 0px 10px;
|
||||
.slick-list{
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
overflow: hidden;
|
||||
.slickline{
|
||||
display: flex!important;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dataPanel{
|
||||
width: 1200px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding:30px 0px 60px;
|
||||
margin:0px auto;
|
||||
.left{
|
||||
width: 850px;
|
||||
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
|
||||
display: flex;
|
||||
.leftTypes{
|
||||
width: 220px;
|
||||
height: 1576px;
|
||||
background: url('../img/index/typebg.png');
|
||||
background-size: 100% 100%;
|
||||
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
|
||||
padding:12px 0px;
|
||||
a{
|
||||
padding:0px 20px;
|
||||
height: 44px;
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
&.active{
|
||||
background: linear-gradient(to right,#07228F , #466AFF);
|
||||
color: #fff!important;
|
||||
}
|
||||
img{
|
||||
margin-right: 12px;
|
||||
width: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.leftLists{
|
||||
flex:1;
|
||||
.leftTitles{
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgba(153, 153, 153, 0.16);
|
||||
padding:0px 20px;
|
||||
span{
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #000000;
|
||||
}
|
||||
a{
|
||||
color: #466AFF!important;
|
||||
}
|
||||
}
|
||||
.leftlistItem{
|
||||
padding:0px 20px;
|
||||
li{
|
||||
border-bottom: 1px solid rgba(153, 153, 153, 0.16);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding:20px 0px ;
|
||||
&>a img{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.itemTitle{
|
||||
flex:1;
|
||||
.item-title-infos{
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.infotitle{
|
||||
flex:1;
|
||||
height: 21px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 21px;
|
||||
color: #333!important;
|
||||
&:hover{
|
||||
color: #466AFF!important;
|
||||
}
|
||||
}
|
||||
span{
|
||||
margin-left:20px ;
|
||||
color: #333;
|
||||
i{
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-desc{
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #414141;
|
||||
line-height: 24px;
|
||||
max-width: 548px;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
.item-data{
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #7D7D7D;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
.category{
|
||||
position: relative;
|
||||
padding-left: 11px;
|
||||
margin-right: 10px;
|
||||
&::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #466AFF;
|
||||
top: 7px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
.language{
|
||||
position: relative;
|
||||
padding-left: 11px;
|
||||
&::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
border-left: 1px solid #9e9e9e;
|
||||
bottom: 4px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.left-bottom-btn{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding:18px 0px;
|
||||
a{
|
||||
height: 38px;
|
||||
line-height: 36px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #466AFF;
|
||||
color: #466AFF;
|
||||
font-size: 16px;
|
||||
padding:0px 20px;
|
||||
img{
|
||||
height: 16px;
|
||||
margin-top: -1px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.right{
|
||||
width: 330px;
|
||||
&>div{
|
||||
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.partTitle{
|
||||
padding:15px;
|
||||
background: #F6F9FF;
|
||||
font-size: 18px;
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
img{
|
||||
width: 33px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
.righthotAuthor{
|
||||
padding-bottom: 15px;
|
||||
li{
|
||||
padding-left: 15px;
|
||||
&>div{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding:10px 15px 10px 0px;
|
||||
border-bottom: 1px solid rgba(153, 153, 153, 0.1);
|
||||
img{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
&:hover{
|
||||
background-color: #F3F3F3;
|
||||
}
|
||||
&:last-child > div{
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hotProjects{
|
||||
li{
|
||||
padding:10px 15px;
|
||||
margin-bottom: 10px;
|
||||
&:hover{
|
||||
background-color: #F3F3F3;
|
||||
}
|
||||
.mInfos{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
.num{
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: #466AFF;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.name{
|
||||
flex:1;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
.sInfos{
|
||||
background: #F7F8F9;
|
||||
padding:5px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
line-height: 22px;
|
||||
word-break: break-all;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import Slider from 'react-slick';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Eye from '../img/index/eye.png';
|
||||
import Data from '../img/index/data.png';
|
||||
import Earth from '../img/index/earth.png';
|
||||
import Imgs from '../img/tree.png';
|
||||
import axios from 'axios';
|
||||
|
||||
// const list =[
|
||||
// {img:Imgs,name:"矽璓工业物联操作系统XiUOS",company:"泛在操作系统实验室",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"openGauss-operator",company:"华为技术有限公司",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"PaddleDetection 2.0",company:"TrustieMirrors",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"skyline",company:"浪潮信息",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"BitXHub",company:"开放原子开源基金会",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"Device Model",company:"openDACS",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"Gitlink",company:"Gitlink",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"openEuler-datenlord",company:"华为技术有限公司",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"OpenAtom XuperChain",company:"开放原子开源基金会",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"xuos-web",company:"泛在操作系统实验室",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// {img:Imgs,name:"CrowdOS开源项目开发",company:"西北工业大学",desc:"此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情此处为项目介绍详情",look:432,cloud:"云原声",type:"C++"},
|
||||
// ]
|
||||
const settings={
|
||||
dots: false,
|
||||
infinite: true,
|
||||
speed: 1000,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
autoplay:true,
|
||||
arrows:false,
|
||||
adaptiveHeight:true
|
||||
}
|
||||
|
||||
function SubBanner() {
|
||||
|
||||
const [ list , setList ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
getList();
|
||||
},[])
|
||||
|
||||
function getList() {
|
||||
const url = `/projects/banner_recommend.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result && result.data){
|
||||
setList(result.data.projects);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return(
|
||||
<div className="banners">
|
||||
<div className="bannersCenter">
|
||||
<p className="bTitle"><span>新一代开源创新服务平台</span></p>
|
||||
<p className="bSubTitle">Gitlink(确实开源)-CCF官网指定的产学研融合面向软件开源创新的开源社区</p>
|
||||
<div className="bannerBox">
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<Slider {...settings} className="bannersProject">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<div className="projectinfos">
|
||||
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
|
||||
<div>
|
||||
<p className="name"><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.name}</Link></p>
|
||||
<p className="company"><Link to={`/${i.author && i.author.login}`}>{i.author && i.author.name}</Link></p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="desc">{i.description}</p>
|
||||
<div className="infoData">
|
||||
<span><img src={Eye} alt="" /><span>{i.visits}</span></span>
|
||||
{i.category && i.category.id && <span><img src={Data} alt="" /><span>{i.category.name}</span></span>}
|
||||
{i.language && i.language.id && <span><img src={Earth} alt="" /><span>{i.language.name}</span></span>}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Slider>
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
<div className="airBubble">
|
||||
<div className="left">
|
||||
<div>
|
||||
{
|
||||
list && list.length > 0 && list.map((i,k)=>{
|
||||
return(
|
||||
k%2 === 0? <span><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.author && i.author.name}/{i.name}</Link></span> : ""
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="right">
|
||||
<div>
|
||||
{
|
||||
list && list.length > 0 && list.map((i,k)=>{
|
||||
return(
|
||||
k%2 > 0 ? <span><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.author && i.author.name}/{i.name}</Link></span> : ""
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default SubBanner
|
|
@ -0,0 +1,138 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import hotAuthor from '../img/index/hotAuthor.png';
|
||||
import week from '../img/index/week.png';
|
||||
import month from '../img/index/month.png';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
|
||||
function SubList() {
|
||||
|
||||
const [ weekList ,setWeekList ] = useState(undefined);
|
||||
const [ monthList ,setMonthList ] = useState(undefined);
|
||||
const [ authorList ,setAuthorList ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
getList(7);
|
||||
getList(30);
|
||||
getAuthorList(7);
|
||||
},[])
|
||||
|
||||
function getList(time){
|
||||
const url = `/project_rank.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
time
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
time === 7 ? setWeekList(result.data.projects) : setMonthList(result.data.projects);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
function getAuthorList(time){
|
||||
const url = `/user_rank.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
time
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setAuthorList(result.data.users);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="right">
|
||||
{
|
||||
authorList && authorList.length > 0?
|
||||
<div>
|
||||
<div className="partTitle"><img src={hotAuthor} alt="" /><span>本周热门开发者</span></div>
|
||||
<div className="righthotAuthor">
|
||||
{
|
||||
authorList.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<div>
|
||||
<Link to={`/${i.login}`}><img src={getImageUrl(`/${i.avatar_url}`)} alt=""/></Link>
|
||||
<div>
|
||||
<Link to={`/${i.login}`} className="font-15">{i.name}</Link>
|
||||
<p className="task-hide" style={{maxWidth:"260px"}}>
|
||||
<Link to={`/${i.login}/${i.project && i.project.identifier}`}><i className="iconfont icon-daimakuicon1 font-14 mr8"></i>{i.project && i.project.name}</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
|
||||
{
|
||||
weekList && weekList.length > 0 ?
|
||||
<div>
|
||||
<div className="partTitle"><img src={week} alt="" /><span>本周热门项目</span></div>
|
||||
<div className="hotProjects">
|
||||
{
|
||||
weekList.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<div className="mInfos">
|
||||
<span className="num">{k+1}</span>
|
||||
<Link to={`/${i.owner && i.owner.login}/${i.identifier}`} className="name task-hide">{i.owner && i.owner.name}/{i.name}</Link>
|
||||
<span>
|
||||
<i className="iconfont icon-dianzan11 font-16 mr4"></i>{i.praises}
|
||||
</span>
|
||||
</div>
|
||||
{i.description &&
|
||||
<div className="sInfos task-hide-2">
|
||||
{i.description}
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
{
|
||||
monthList && monthList.length > 0?
|
||||
<div>
|
||||
<div className="partTitle"><img src={month} alt="" /><span>本月热门项目</span></div>
|
||||
<div className="hotProjects">
|
||||
{
|
||||
monthList.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<div className="mInfos">
|
||||
<span className="num">{k+1}</span>
|
||||
<Link to={`/${i.owner && i.owner.login}/${i.identifier}`} className="name task-hide">{i.owner && i.owner.name}/{i.name}</Link>
|
||||
<span>
|
||||
<i className="iconfont icon-dianzan11 font-16 mr4"></i>{i.praises}
|
||||
</span>
|
||||
</div>
|
||||
{i.description &&
|
||||
<div className="sInfos task-hide-2">
|
||||
{i.description}
|
||||
</div>
|
||||
}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default SubList;
|
|
@ -0,0 +1,84 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import Hot from '../img/index/hot.png';
|
||||
import Slider from "react-slick";
|
||||
import huawei from '../img/index/unit/huawei.png';
|
||||
import langchao from '../img/index/unit/langchao.png';
|
||||
import jijinhui from '../img/index/unit/jijinhui.png';
|
||||
import mulan from '../img/index/unit/mulan.png';
|
||||
import xigongye from '../img/index/unit/xigongye.png';
|
||||
import feiteng from '../img/index/unit/feiteng.png';
|
||||
import xiuos from '../img/index/unit/xiuos.png';
|
||||
import huake from '../img/index/unit/huake.png';
|
||||
import axios from 'axios';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder';
|
||||
|
||||
const settings = {
|
||||
dots: false,
|
||||
infinite: true,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
vertical: true,
|
||||
verticalSwiping: true,
|
||||
autoplay:true,
|
||||
arrows:false
|
||||
};
|
||||
const group_size = 6;
|
||||
function SubUnitBanner() {
|
||||
|
||||
const [ list , setlist ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
getRecommandOrz();
|
||||
},[])
|
||||
|
||||
function getRecommandOrz(params) {
|
||||
const url = `/organizations/recommend.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
group_size
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setlist(result.data.organizations)
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return(
|
||||
list && list.length > 0 ?
|
||||
<div className="unitBanner">
|
||||
<div className="unitContent">
|
||||
<div className="unitTitle">
|
||||
<span>精选开源组织</span>
|
||||
<img src={Hot} alt="" width="47px"/>
|
||||
</div>
|
||||
<Slider {...settings} className="unitSlider">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<div className="slickline">
|
||||
{
|
||||
i.map((j,k1)=>{
|
||||
return(
|
||||
<Link to={j.name}><img src={getImageUrl(`/${j.avatar_url}`)} alt="" height="56px" style={{maxWidth:"180px"}}/></Link>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
{/* <div className="slickline">
|
||||
<a href="https://www.nwpu.edu.cn/" target="_blank"><img src={xigongye} alt="" height="56px"/></a>
|
||||
<a href="https://www.phytium.com.cn/" target="_blank"><img src={feiteng} alt="" height="56px"/></a>
|
||||
<a href="http://xuos.io" target="_blank"><img src={xiuos} alt="" height="56px"/></a>
|
||||
<a href="https://www.hust.edu.cn/" target="_blank"><img src={huake} alt="" height="56px"/></a>
|
||||
</div> */}
|
||||
</Slider>
|
||||
</div>
|
||||
</div>
|
||||
:""
|
||||
|
||||
)
|
||||
}
|
||||
export default SubUnitBanner;
|
|
@ -48,7 +48,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
item.menu_name === "issues" &&
|
||||
<li className={pathname==="issues" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/issues`, state }}>
|
||||
<Tooltip title="易修是Issue的中文名,即问题列表" placement="bottom">
|
||||
<Tooltip placement="bottom">
|
||||
<i className={"iconfont icon-yixiuicon1 color-grey-3 mr5 font-14"}></i>
|
||||
<span>易修(Issue)</span>
|
||||
</Tooltip>
|
||||
|
@ -61,7 +61,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
<li className={pathname==="pulls" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/pulls`, state }}>
|
||||
<i className={"iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14"}></i>
|
||||
<span>合并请求</span>
|
||||
<span>合并请求(PR)</span>
|
||||
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{numFormat(projectDetail.pull_requests_count)}</span> : ""}
|
||||
</Link>
|
||||
</li>:""
|
||||
|
@ -71,7 +71,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
<li className={pathname === "wiki" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/wiki`, state }}>
|
||||
<i className={"iconfont icon-a-wikiicon1 color-grey-3 mr5 font-14"}></i>
|
||||
<span>Wiki</span>
|
||||
<span>维基(Wiki)</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
<li className={pathname==="devops" ? "active" : ""}>
|
||||
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
|
||||
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>工作流(beta版)
|
||||
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>引擎(Engine)
|
||||
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
||||
</Link>
|
||||
</li>
|
||||
|
|
|
@ -36,7 +36,7 @@ function ReadmeCatelogue({ menuList , hash }) {
|
|||
{
|
||||
menu && menu.length>0?
|
||||
<div className="anchorBox">
|
||||
<Anchor affix={false} onChange={onChange}>
|
||||
<Anchor affix={false} onChange={onChange} offsetTop={140}>
|
||||
{
|
||||
menu.map((item,key)=>{
|
||||
return(
|
||||
|
|
|
@ -18,7 +18,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){
|
|||
|
||||
useEffect(()=>{
|
||||
document.addEventListener('click',()=>{setIsOpen(false)})
|
||||
})
|
||||
},[])
|
||||
|
||||
function showDown(flag,index,isBin){
|
||||
if(!isBin){
|
||||
|
|
|
@ -112,9 +112,9 @@ class MergeItem extends Component {
|
|||
to={`/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/tree/${turnbar(item.pull_request_head)}`}
|
||||
className="maxW200px task-hide ver-middle" style={{maxWidth:"200px"}}
|
||||
>
|
||||
{item.is_original
|
||||
? item.fork_project_user
|
||||
: project_author_name}
|
||||
{item.is_fork
|
||||
? item.pull_request_base
|
||||
: item.author_name}
|
||||
:{item.pull_request_head}
|
||||
</Link>
|
||||
</Tag>
|
||||
|
@ -136,8 +136,8 @@ class MergeItem extends Component {
|
|||
to={`/${owner}/${projectsId}/tree/${turnbar(item.pull_request_base)}`}
|
||||
className="maxW200px task-hide ver-middle" style={{maxWidth:"200px"}}
|
||||
>
|
||||
{/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */}
|
||||
{project_author_name}:{item.pull_request_base}
|
||||
{item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`}
|
||||
{/* {project_author_name}:{item.pull_request_base} */}
|
||||
</Link>
|
||||
</Tag>
|
||||
}
|
||||
|
|
|
@ -365,10 +365,10 @@ class MessageCount extends Component {
|
|||
<div className="mt15">
|
||||
<Tag className="pr-branch-tag">
|
||||
<Link
|
||||
to={`/${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
|
||||
to={`/${data.pull_request.is_fork ? data.pull_request_base.fork_project_user : data.issue.author_name}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
|
||||
className="ver-middle task-hide" style={{maxWidth:"200px"}} title={`${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: ${data.pull_request && data.pull_request.head}`}
|
||||
>
|
||||
{data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: {data.pull_request && data.pull_request.head}
|
||||
{data.pull_request.is_fork ? data.pull_request_base.fork_project_user : data.issue.author_name}: {data.pull_request && data.pull_request.head}
|
||||
</Link>
|
||||
</Tag>
|
||||
<span className="mr8 ver-middle">
|
||||
|
@ -383,7 +383,7 @@ class MessageCount extends Component {
|
|||
to={`/${owner}/${projectsId}/tree/${data.pull_request.base}`}
|
||||
className="ver-middle task-hide" style={{maxWidth:"200px"}} title={`${data.issue.project_author_name}:${data.pull_request.base}`}
|
||||
>
|
||||
{data.issue.project_author_name}:{data.pull_request.base}
|
||||
{data.issue.author_name}:{data.pull_request.base}
|
||||
</Link>
|
||||
</Tag>
|
||||
</div>
|
||||
|
|
|
@ -54,7 +54,7 @@ class UpdateMerge extends Component {
|
|||
const { data, isSpin, pull, merge } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="main">
|
||||
<div className="main updateMerge">
|
||||
<Spin spinning={isSpin}>
|
||||
{" "}
|
||||
{data ? (
|
||||
|
|
|
@ -222,4 +222,8 @@ form .ant-cascader-picker, form .ant-select {
|
|||
.overlihide li{
|
||||
max-width: 450px;
|
||||
|
||||
}
|
||||
/* 距离底部加大 @列表被遮挡 */
|
||||
.updateMerge{
|
||||
margin: 30px auto 60px;
|
||||
}
|
|
@ -25,6 +25,7 @@ class MergeForm extends Component {
|
|||
issue_tags: undefined,
|
||||
issue_versions: undefined,
|
||||
issue_priories: undefined,
|
||||
atWhoLoginList:undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -146,7 +147,7 @@ class MergeForm extends Component {
|
|||
} else {
|
||||
values.issue_tag_ids = [];
|
||||
}
|
||||
const { desc } = this.state;
|
||||
const { desc , atWhoLoginList } = this.state;
|
||||
if (merge_type === "new") {
|
||||
let url = `/${owner}/${projectsId}/pulls.json`;
|
||||
axios.post(url, {
|
||||
|
@ -158,7 +159,8 @@ class MergeForm extends Component {
|
|||
fork_project_id: data && data.fork_project_id,
|
||||
merge_user_login: data && data.merge_user_login,
|
||||
files_count,
|
||||
commits_count
|
||||
commits_count,
|
||||
receivers_login:atWhoLoginList,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
|
@ -189,6 +191,7 @@ class MergeForm extends Component {
|
|||
body: desc,
|
||||
head: pull,
|
||||
base: merge,
|
||||
receivers_login:atWhoLoginList,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
|
@ -225,6 +228,13 @@ class MergeForm extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
//合并请求中at谁列表(存储:login)
|
||||
changeAtWhoLoginList = (loginList) =>{
|
||||
this.setState({
|
||||
atWhoLoginList:loginList,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { merge_type } = this.props;
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
@ -273,6 +283,10 @@ class MergeForm extends Component {
|
|||
mdID={"merge-new-description"}
|
||||
initValue={desc}
|
||||
onChange={this.onContentChange}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
<p className="clearfix mt20">
|
||||
<Button
|
||||
|
|
|
@ -35,7 +35,8 @@ class order_form extends Component {
|
|||
get_attachments: undefined,
|
||||
show_token: false,
|
||||
cannot_edit: false,
|
||||
issue_current_user: true
|
||||
issue_current_user: true,
|
||||
atWhoLoginList:undefined
|
||||
};
|
||||
}
|
||||
componentDidUpdate=(prevPros)=>{
|
||||
|
@ -152,7 +153,7 @@ class order_form extends Component {
|
|||
if (values.issue_tag_ids.length > 0) {
|
||||
values.issue_tag_ids = [values.issue_tag_ids];
|
||||
}
|
||||
const { description, start_date, due_date, issue_type } = this.state;
|
||||
const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state;
|
||||
if (form_type !== "edit") {
|
||||
const url = `/${owner}/${projectsId}/issues.json`;
|
||||
axios.post(url, {
|
||||
|
@ -162,6 +163,7 @@ class order_form extends Component {
|
|||
start_date: start_date,
|
||||
due_date: due_date,
|
||||
issue_type: issue_type,
|
||||
receivers_login:atWhoLoginList,
|
||||
}).then((result) => {
|
||||
if (result && result.data.id) {
|
||||
this.props.showNotification("任务创建成功!");
|
||||
|
@ -188,6 +190,7 @@ class order_form extends Component {
|
|||
due_date: due_date,
|
||||
issue_type: issue_type,
|
||||
...values,
|
||||
receivers_login:atWhoLoginList,
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`);
|
||||
|
@ -221,6 +224,14 @@ class order_form extends Component {
|
|||
description: value,
|
||||
});
|
||||
};
|
||||
|
||||
//issue中at谁列表(存储:login)
|
||||
changeAtWhoLoginList = (loginList) =>{
|
||||
this.setState({
|
||||
atWhoLoginList:loginList,
|
||||
});
|
||||
};
|
||||
|
||||
// 修改开始时间
|
||||
changeBeginTime = (start_date, value) => {
|
||||
this.setState({
|
||||
|
@ -329,6 +340,10 @@ class order_form extends Component {
|
|||
mdID={"order-new-description"}
|
||||
initValue={description}
|
||||
onChange={this.onContentChange}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
</div>
|
||||
{get_attachments && get_attachments.length > 0 ? (
|
||||
|
|
|
@ -17,7 +17,7 @@ function MyNotice(props) {
|
|||
const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数
|
||||
const [isBatchDelete, setIsBatchDelete] = useState(false);//@我是否批量删除
|
||||
const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@我批量删除--全选
|
||||
|
||||
const [messageType, setMessageType] = useState(undefined);
|
||||
const [noticeUnreadCount, setNoticeUnreadCount] = useState();//未读系统通知数量
|
||||
// const [letterUnreadCount, setLetterUnreadCount] = useState(0);//未读私信数量
|
||||
const [atUnreadCount, setAtUnreadCount] = useState();//未读@我数量
|
||||
|
@ -65,6 +65,7 @@ function MyNotice(props) {
|
|||
setAtUnreadCount(response.data.unread_atme);
|
||||
setMessageList(response.data.messages);
|
||||
setMessTotalCount(response.data.total_count);
|
||||
setMessageType(response.data.type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -203,7 +204,46 @@ function MyNotice(props) {
|
|||
</div>
|
||||
</div>}
|
||||
|
||||
{messageList && messageList.map(item => {
|
||||
{/* 系统消息 */}
|
||||
{messageType === "notification" && messageList && messageList.map(item =>{
|
||||
return (
|
||||
<div className="mynotice-content vertical-center-style" key={item.id}>
|
||||
<div className="mynotice-cont stretch-style">
|
||||
{item.status === 1 ? <Badge color="#FA2020" /> : <span className="system-notice-blank"></span>}
|
||||
<i className={"iconfont "+noticeSourceType[item.source]}></i>
|
||||
<span className={`sysNotice-length ${item.notification_url?'highlightSpan':''}`} onClick={() => {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}></span>
|
||||
</div>
|
||||
<div className="mynotice-cont vertical-center-style float-left-little">
|
||||
<span className={item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||
{item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* @我消息 */}
|
||||
{messageType === "atme" && messageList && messageList.map(item =>{
|
||||
return (
|
||||
<div className={`mynotice-content vertical-center-style ${isBatchDelete?'batchDel':''}`} key={item.id}>
|
||||
<div className="mynotice-cont vertical-center-style">
|
||||
<Checkbox value={item.id} className={isBatchDelete ? 'visible-checkbox' : 'invisible-checkbox'} onChange={onChange} checked={item.checkedBatch}></Checkbox>
|
||||
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
||||
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
||||
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
||||
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: item.content}}></span>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mynotice-cont vertical-center-style">
|
||||
<span className={!isBatchDelete && item.status === 1?"timeSpan":""}>{item.time_ago}</span>
|
||||
{!isBatchDelete && item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
|
||||
{!isBatchDelete && <span className="invisable-read float-left-little" onClick={()=>deleteNotice([item.id])}>删除</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{false && messageList && messageList.map(item => {
|
||||
console.log('item',item);
|
||||
// 系统消息
|
||||
if (noticeType === "0") {
|
||||
// 消息类别
|
||||
|
@ -229,7 +269,7 @@ function MyNotice(props) {
|
|||
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
|
||||
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
|
||||
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
|
||||
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content +" 中@我"}}></span>}
|
||||
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content}}></span>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mynotice-cont vertical-center-style">
|
||||
|
|
|
@ -13,9 +13,9 @@ const menu = [
|
|||
{name:"主页",index:"home"},
|
||||
{name:"代码库",index:"code"},
|
||||
{name:"易修 (Issue)",index:"issues"},
|
||||
{name:"合并请求",index:"pulls"},
|
||||
{name:"Wiki",index:"wiki"},
|
||||
{name:"工作流(beta版)",index:"devops"},
|
||||
{name:"合并请求 (PR)",index:"pulls"},
|
||||
{name:"维基 (Wiki)",index:"wiki"},
|
||||
{name:"引擎 (Engine)",index:"devops"},
|
||||
// {name:"资源库",index:"resources"},
|
||||
{name:"里程碑",index:"versions"},
|
||||
{name:"动态",index:"activity"},
|
||||
|
@ -267,6 +267,11 @@ class Setting extends Component {
|
|||
},
|
||||
],
|
||||
})(<Input placeholder="请输入项目名称" />)}
|
||||
{
|
||||
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
|
||||
<span className="color-grey-9">该项目导入于 <a className="color-grey-6" target="_blank" href={projectDetail.mirror_url}>{projectDetail.mirror_url}</a></span>
|
||||
: ""
|
||||
}
|
||||
</Form.Item>
|
||||
<div className="df" style={{ alignItems: "center" }}>
|
||||
<span className="mr20 mb15 font-16">可见性</span>
|
||||
|
|
|
@ -117,8 +117,8 @@ export default ((props) => {
|
|||
</AlignCenterBetween>
|
||||
<div className="g-desc">{group.description ? group.description : "暂无描述"}</div>
|
||||
<div className="g-tip">
|
||||
<p>管理员团队对 <span>所有仓库</span> 具有操作权限,且对组织具有 <span>管理员权限</span>。 </p>
|
||||
<p>此外,该团队拥有了 <span>创建仓库</span> 的权限:成员可以在组织中创建新的仓库。 </p>
|
||||
<p>组织的Owner团队拥有更改组织设置,在组织内新建仓库,新建组织团队等权限。</p>
|
||||
<p>此外,Owner团队成员对当前组织下所有项目均具有仓库管理员权限,组织下其他团队的权限可由Owner团队成员自由制定。</p>
|
||||
{group.is_admin ? <Button type="primary" onClick={()=>props.history.push(`/${OIdentifier}/teams/${groupId}/setting`)}><span className="color-white">团队设置</span></Button> : ""}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,14 +10,10 @@ function ListItem({item,key,OIdentifier}) {
|
|||
<Link to={`/${OIdentifier}/${item.identifier}`} className="name">{item.name}</Link>
|
||||
{ item.forked_from_project_id && <i className="iconfont icon-fork font-18 color-orange ml8" /> }
|
||||
{
|
||||
item.type && item.type !== 0 ?
|
||||
item.type === 2 ?
|
||||
item.type && item.type === 2 ?
|
||||
<Tooltip title="该项目是一个镜像" className="ml8">
|
||||
<i className="iconfont icon-banbenku font-18 color-green" />
|
||||
</Tooltip>:
|
||||
<span className="ml8">
|
||||
<i className="iconfont icon-jingxiang font-18 color-green" />
|
||||
</span>:""
|
||||
</Tooltip>:""
|
||||
}
|
||||
</span>
|
||||
<ListCount fork={item.forked_count} parise={item.praises_count}/>
|
||||
|
|
|
@ -27,7 +27,7 @@ class ForkUsers extends Component {
|
|||
});
|
||||
const { projectsId , owner } = this.props.match.params;
|
||||
|
||||
const url = `/${owner}/${projectsId}/members.json`;
|
||||
const url = `/${owner}/${projectsId}/forks.json`;
|
||||
axios
|
||||
.get(url, {
|
||||
params: {
|
||||
|
|
|
@ -30,6 +30,7 @@ class comments extends Component {
|
|||
reply_id: undefined,
|
||||
reply_content: undefined,
|
||||
new_journal_id: undefined,
|
||||
atWhoLoginList:undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -51,6 +52,7 @@ class comments extends Component {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
const {
|
||||
|
@ -60,10 +62,12 @@ class comments extends Component {
|
|||
orderId,
|
||||
reply_id,
|
||||
is_reply,
|
||||
atWhoLoginList,
|
||||
} = this.state;
|
||||
|
||||
const url = `/issues/${orderId}/journals.json`;
|
||||
|
||||
|
||||
const url = `/issues/${orderId}/journals.json`;
|
||||
axios
|
||||
.post(url, {
|
||||
...values,
|
||||
|
@ -71,6 +75,7 @@ class comments extends Component {
|
|||
issue_id: orderId,
|
||||
attachment_ids: fileList,
|
||||
parent_id: reply_id,
|
||||
receivers_login:atWhoLoginList,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result && result.data.status === 0) {
|
||||
|
@ -248,18 +253,29 @@ class comments extends Component {
|
|||
onContentChange = (value) => {
|
||||
if (value) {
|
||||
this.setState({
|
||||
content: value,
|
||||
quillFlag: false,
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
content: value,
|
||||
});
|
||||
};
|
||||
replyContentChange = (value) => {
|
||||
if (value) {
|
||||
this.setState({
|
||||
reply_content: value,
|
||||
quillFlag: false,
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
reply_content: value,
|
||||
});
|
||||
};
|
||||
|
||||
//评论中at谁列表(存储:login)
|
||||
changeAtWhoLoginList = (loginList) =>{
|
||||
this.setState({
|
||||
atWhoLoginList:loginList,
|
||||
});
|
||||
};
|
||||
|
||||
onRef = (ref) => {
|
||||
|
@ -308,6 +324,7 @@ class comments extends Component {
|
|||
new_journal_id,
|
||||
} = this.state;
|
||||
const { current_user, only_show_content } = this.props;
|
||||
const { projectsId ,owner } = this.props.match.params;
|
||||
|
||||
const new_comment = (is_reply, item_id) => {
|
||||
return (
|
||||
|
@ -339,6 +356,10 @@ class comments extends Component {
|
|||
onChange={
|
||||
is_reply ? this.replyContentChange : this.onContentChange
|
||||
}
|
||||
isCanAtme = {true}
|
||||
changeAtWhoLoginList = {this.changeAtWhoLoginList}
|
||||
owner = {owner}
|
||||
projectsId = {projectsId}
|
||||
></MDEditor>
|
||||
<p className="quillFlag">
|
||||
{quillFlag && <span className="">请输入评论内容</span>}
|
||||
|
|
|
@ -4,6 +4,13 @@ ul,ol,dl{
|
|||
.newMain{
|
||||
background-color: #fff;
|
||||
}
|
||||
.newTable{
|
||||
background-color: #F0F5FF;
|
||||
.newImages{
|
||||
background-image: url('../../forge/Images/taskbar.png');
|
||||
height: 364px;
|
||||
}
|
||||
}
|
||||
.color-black{
|
||||
color: #333;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react';
|
||||
import React, { Component, useReducer } from 'react';
|
||||
|
||||
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
|
@ -11,20 +11,29 @@ import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
|
|||
import { SnackbarHOC,getImageUrl } from 'educoder';
|
||||
|
||||
class Shixunauthority extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
console.log(this.props);
|
||||
const { current_user } = this.props;
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<div className=" edu-txt-center mt60 mb60">
|
||||
<div className="newMain newTable clearfix">
|
||||
<div className="edu-txt-center mt60 mb60">
|
||||
{/*mt100 mb100*/}
|
||||
<img src={getImageUrl("images/warn/pic_403.jpg")} />
|
||||
<p className="font-18 mt40">
|
||||
您可以稍后尝试 <a href="/"
|
||||
className="color-blue">返回首页</a>
|
||||
,或者
|
||||
<p className="font-16 mt56 newImages">
|
||||
<p>您尚未被授权访问此页面,请先获取相关权限</p>
|
||||
您可尝试
|
||||
{(!current_user || !current_user.login) && (
|
||||
<p style={{display:"inline"}}>您可尝试<a href="//testforgeplus.trustie.net/login" className="color-blue">登录</a>或</p>
|
||||
)}
|
||||
<a href="/"
|
||||
className="color-blue">返回首页</a>,也可以通过
|
||||
<a target="_blank"
|
||||
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
|
||||
className="color-blue">QQ反馈>></a>
|
||||
</p>
|
||||
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
|
||||
className="color-blue">QQ</a>向我们反馈
|
||||
</p>
|
||||
</div>
|
||||
{/*<div style="clear:both;"></div>*/}
|
||||
{/*<div id="ajax-indicator" style="display:none;"><span>载入中...</span></div>*/}
|
||||
|
@ -33,5 +42,4 @@ class Shixunauthority extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SnackbarHOC() (TPMIndexHOC ( Shixunauthority ));
|
||||
|
|
|
@ -11,19 +11,29 @@ import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
|
|||
import { SnackbarHOC,getImageUrl } from 'educoder';
|
||||
|
||||
class http500 extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
console.log(this.props);
|
||||
const { current_user } = this.props;
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<div className=" edu-txt-center mt60 mb60">
|
||||
<div className="newMain newTable clearfix">
|
||||
<div className=" edu-txt-center mt60">
|
||||
{/*mt100 mb100*/}
|
||||
<img src={getImageUrl("/images/warn/pic_404.jpg")} />
|
||||
<p className="font-18 mt40">
|
||||
您可以稍后尝试 <a href="/"
|
||||
<p className="font-16 mt56 newImages">
|
||||
<p>你访问的页面不存在</p>
|
||||
您可尝试
|
||||
{(!current_user || !current_user.login) && (
|
||||
<p style={{display:"inline"}}>您可尝试<a href="//testforgeplus.trustie.net/login" className="color-blue">登录</a>或</p>
|
||||
)}
|
||||
<a href="/"
|
||||
className="color-blue">返回首页</a>
|
||||
,或者
|
||||
,也可通过
|
||||
<a target="_blank"
|
||||
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
|
||||
className="color-blue">QQ反馈>></a>
|
||||
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
|
||||
className="color-blue">QQ</a>向我们反馈
|
||||
</p>
|
||||
</div>
|
||||
{/*<div style="clear:both;"></div>*/}
|
||||
|
|
|
@ -13,17 +13,18 @@ import { SnackbarHOC,getImageUrl } from 'educoder';
|
|||
class Shixunnopage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<div className="newMain newTable clearfix">
|
||||
<div className=" edu-txt-center mt60 mb60">
|
||||
{/*mt100 mb100*/}
|
||||
<img src={getImageUrl("images/warn/pic_500.jpg")} />
|
||||
<p className="font-18 mt40">
|
||||
您可以稍后尝试 <a href="/"
|
||||
<p className="font-16 mt56 newImages">
|
||||
<p>服务器异常,请稍后重试</p>
|
||||
您可尝试<a href="javascript:location.reload();" className="color-blue">刷新页面</a>或<a href="/"
|
||||
className="color-blue">返回首页</a>
|
||||
,或者
|
||||
,也可以通过
|
||||
<a target="_blank"
|
||||
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
|
||||
className="color-blue">QQ反馈>></a>
|
||||
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
|
||||
className="color-blue">QQ</a>向我们反馈
|
||||
</p>
|
||||
</div>
|
||||
{/*<div style="clear:both;"></div>*/}
|
||||
|
|
|
@ -153,21 +153,21 @@ class EducoderLogin extends Component {
|
|||
justifyContent: "center",
|
||||
width: "100%",
|
||||
}}>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} EduCoder<span className="ml15 mr15">湘ICP备17009477号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">Trustie</a> & IntelliDE inside.</div>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} GitLink | 确实开源<span className="ml15 mr15">京ICP备13000930号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">GitLink</a> & IntelliDE inside.</div>
|
||||
</div>:
|
||||
this.props.mygetHelmetapi===undefined||this.props.mygetHelmetapi.main_site===null|| this.props.mygetHelmetapi.main_site===undefined? <div style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
}}>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} EduCoder<span className="ml15 mr15">湘ICP备17009477号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">Trustie</a> & IntelliDE inside.</div>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} GitLink | 确实开源<span className="ml15 mr15">京ICP备13000930号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">GitLink</a> & IntelliDE inside.</div>
|
||||
</div>:this.props.mygetHelmetapi.main_site===true?
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
}}>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} EduCoder<span className="ml15 mr15">湘ICP备17009477号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">Trustie</a> & IntelliDE inside.</div>
|
||||
<div className="font-14 color-grey-9 " style={{marginTop:"20px"}}><span className="font-18">©</span> {moment().year()} GitLink | 确实开源<span className="ml15 mr15">京ICP备13000930号</span><a href="https://team.trustie.net" style={{"color":"#888"}} target="_blank">GitLink</a> & IntelliDE inside.</div>
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
|
|
|
@ -142,12 +142,12 @@ class Trialapplication extends Component {
|
|||
if (this.state.Phonenumberisnotcobool === false) {
|
||||
if (this.state.login.length === 0) {
|
||||
this.setState({
|
||||
Phonenumberisnotco: "请输入正确的手机号或邮箱",
|
||||
Phonenumberisnotco: "请输入正确的邮箱账号",
|
||||
})
|
||||
return
|
||||
} else {
|
||||
this.setState({
|
||||
Phonenumberisnotco: "请输入正确的手机号或邮箱",
|
||||
Phonenumberisnotco: "请输入正确的邮箱账号",
|
||||
})
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -158,12 +158,12 @@ class Trialapplicationysl extends Component {
|
|||
if (this.state.Phonenumberisnotcobool === false) {
|
||||
if (this.state.login.length === 0) {
|
||||
this.setState({
|
||||
Phonenumberisnotco: "请输入正确的手机号或邮箱",
|
||||
Phonenumberisnotco: "请输入正确的邮箱账号",
|
||||
})
|
||||
return
|
||||
} else {
|
||||
this.setState({
|
||||
Phonenumberisnotco: "请输入正确的手机号或邮箱",
|
||||
Phonenumberisnotco: "请输入正确的邮箱账号",
|
||||
})
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -28,13 +28,14 @@ body>.-task-title {
|
|||
height:70px;
|
||||
min-width: 1200px;
|
||||
z-index: 1000;
|
||||
background:rgba(255,255,255,1);
|
||||
box-shadow: 0px 2px 4px 0px rgb(0 0 0 / 50%);
|
||||
position: fixed;
|
||||
box-shadow: 0px 0px 14px rgb(0 0 0 / 10%);
|
||||
background-image: linear-gradient(to right, #0F141F, #0C2A5B,#101417);
|
||||
color: #fff;
|
||||
}
|
||||
.headerContent{
|
||||
width:1200px;
|
||||
margin:0px auto;
|
||||
padding:0px 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
|
|
@ -4,4 +4,39 @@
|
|||
.Permanentban{
|
||||
color:#5091FF !important;
|
||||
border-color: #5091FF !important;
|
||||
}
|
||||
|
||||
/*md编辑器中输入@弹出可选人列表样式*/
|
||||
.at_who_list{
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
width: 180px;
|
||||
max-height: 160px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5);
|
||||
border-radius: 4px;
|
||||
overflow-y: scroll;
|
||||
cursor: pointer;
|
||||
}
|
||||
.at_who{
|
||||
height: 40px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
|
||||
padding: 0 4px;
|
||||
}
|
||||
.at_who.active{
|
||||
background: #F3F4F6;
|
||||
}
|
||||
.at_who img{
|
||||
width:30px;
|
||||
height:30px;
|
||||
border-radius:50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.at_who span{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { getUploadActionUrl, getUrl } from 'educoder';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
import { getImageUrl } from 'educoder';
|
||||
import axios from 'axios';
|
||||
import '../../courses/css/Courses.css';
|
||||
import './css/TPMchallengesnew.css';
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import './css/newquestion.css';
|
||||
const $ = window.$
|
||||
|
||||
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];
|
||||
|
@ -39,7 +40,6 @@ function md_rec_data(k, mdu, id) {
|
|||
}
|
||||
window.md_rec_data = md_rec_data;
|
||||
|
||||
|
||||
function md_elocalStorage(editor, mdu, id) {
|
||||
let oc = window.sessionStorage.getItem('content' + mdu)
|
||||
if (oc !== null && oc !== editor.getValue()) {
|
||||
|
@ -74,16 +74,38 @@ function md_elocalStorage(editor, mdu, id) {
|
|||
return tid
|
||||
}
|
||||
|
||||
|
||||
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true }) => {
|
||||
|
||||
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => {
|
||||
|
||||
const editorEl = useRef();
|
||||
const resizeBarEl = useRef();
|
||||
const [editorInstance, setEditorInstance] = useState();
|
||||
const [atWhoVisible, setAtWhoVisible] = useState(false);
|
||||
const [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
|
||||
//调用member.json接口获取到的用户列表
|
||||
const [users, setUsers] = useState([]);
|
||||
//可以@的全部用户
|
||||
const [allUsers, setAllUsers] = useState([]);
|
||||
const atWhoLoginList = useRef([]);
|
||||
const atWhoVisibleRef = useRef(false);
|
||||
const containerId = `mdEditor_${mdID}`;
|
||||
const editorBodyId = `mdEditors_${mdID}`;
|
||||
const tipId = `e_tips_mdEditor_${mdID}`;
|
||||
|
||||
useEffect(()=>{
|
||||
//请求members接口获取全部可@列表
|
||||
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
|
||||
if(response.data.total_count !== 0){
|
||||
setAllUsers(response.data.users);
|
||||
setUsers(response.data.users);
|
||||
}
|
||||
})
|
||||
//点击其他地方关闭弹框
|
||||
document.addEventListener('click',()=>{
|
||||
atWhoVisibleRef.current = false;
|
||||
setAtWhoVisible(false);
|
||||
})
|
||||
},[])
|
||||
|
||||
function onLayout() {
|
||||
let ro;
|
||||
if (editorEl.current) {
|
||||
|
@ -101,6 +123,96 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
return ro;
|
||||
}
|
||||
|
||||
function selectAtWho(username){
|
||||
atWhoVisibleRef.current = false;
|
||||
setAtWhoVisible(false);
|
||||
const cm = editorInstance.cm;
|
||||
//获取鼠标所在行的行数和ch
|
||||
const cursor = cm.doc.getCursor();
|
||||
const line = cursor.line;//行
|
||||
const ch = cursor.ch;//列
|
||||
const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@");
|
||||
let selectUserLogin = undefined;
|
||||
users.map((item)=>{
|
||||
item.username === username && (selectUserLogin = item.login);
|
||||
})
|
||||
//替换内容
|
||||
cm.replaceRange("[@"+username+"]"+`(/${selectUserLogin}) `,{line,ch:startIndex},{line,ch});
|
||||
//鼠标聚焦
|
||||
cm.focus();
|
||||
//将此user的login存储到atWhoLoginList集合中
|
||||
const list = new Set(atWhoLoginList.current);
|
||||
list.add(selectUserLogin);
|
||||
atWhoLoginList.current = Array.from(list);
|
||||
setAtWhoLoginListState(Array.from(list));
|
||||
}
|
||||
|
||||
function onMouseOver(key){
|
||||
document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who");
|
||||
document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active");
|
||||
}
|
||||
|
||||
//markdown编辑器中输入的键盘监听事件
|
||||
function mdKeyDown(e){
|
||||
if (e.shiftKey && e.code === "Digit2") {
|
||||
// 输入@键后在对应的位置显示可选的项目成员
|
||||
atWhoVisibleRef.current = true;
|
||||
setAtWhoVisible(true);
|
||||
//获取光标位置
|
||||
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
||||
//设置弹框位置
|
||||
const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
|
||||
const newLeft = placeholder === "添加评论..." ? 80: 20;
|
||||
document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px";
|
||||
document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px";
|
||||
}
|
||||
//处理本来@了某人 -> 删掉 -> 撤回 的情况
|
||||
if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){
|
||||
const codemirror = editorInstance.cm;
|
||||
let value = codemirror.getValue();
|
||||
//处理初始内容就自带@谁的情况
|
||||
if(initValue){
|
||||
const del = [];
|
||||
allUsers.map(item=>{
|
||||
if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
|
||||
//初始内容中有符合@+名字的格式并且当前内容未删除初始内容
|
||||
del[del.length] = `[@${item.username}](/${item.login})`;
|
||||
}
|
||||
})
|
||||
del.length!=0 && del.map(str=>{
|
||||
value = value.replace(str,"");
|
||||
})
|
||||
}
|
||||
//判断value是否包含@符号
|
||||
value.indexOf("@") != -1 && allUsers.map(item =>{
|
||||
if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){
|
||||
//将此user的login存储到atWhoLoginList集合中
|
||||
const list = new Set(atWhoLoginList.current);
|
||||
list.add(item.login);
|
||||
atWhoLoginList.current = Array.from(list);
|
||||
setAtWhoLoginListState(Array.from(list));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
|
||||
},[atWhoLoginListState])
|
||||
|
||||
const atWhoList = (
|
||||
<div className="at_who_list" id="at_who_list" >
|
||||
{users && users.map((item,key)=>{
|
||||
return(
|
||||
<div key={key} className={`at_who ${key===0 && `active`}`} onClick={()=>{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}>
|
||||
{item.image_url && <img src={getImageUrl(`/${item.image_url}`)}></img>}
|
||||
<span>{item.username}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (editorInstance) {
|
||||
return
|
||||
|
@ -183,6 +295,69 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
|
||||
const cmEl = editorInstance && editorInstance.cm
|
||||
|
||||
useEffect(()=>{
|
||||
if(atWhoVisibleRef.current){
|
||||
// 添加上下键、enter键监听事件
|
||||
cmEl.addKeyMap({
|
||||
'Up':()=>{
|
||||
const atWhoListDiv = document.getElementById("at_who_list");
|
||||
const atWhoDivs = document.getElementsByClassName("at_who");
|
||||
let index;
|
||||
for(let i = 0; i<atWhoDivs.length;i++){
|
||||
atWhoDivs[i].className === "at_who active" && (index = i);
|
||||
}
|
||||
if(index>0){
|
||||
index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40)
|
||||
atWhoDivs[index].className = "at_who";
|
||||
atWhoDivs[index-1].className = "at_who active";
|
||||
}
|
||||
},
|
||||
'Down':()=>{
|
||||
const atWhoListDiv = document.getElementById("at_who_list");
|
||||
const atWhoDivs = document.getElementsByClassName("at_who");
|
||||
let index;
|
||||
for(let i = 0; i<atWhoDivs.length;i++){
|
||||
atWhoDivs[i].className === "at_who active" && (index = i);
|
||||
}
|
||||
if(index<atWhoDivs.length-1){
|
||||
index >=3 && (atWhoListDiv.scrollTop +=40)
|
||||
atWhoDivs[index].className = "at_who";
|
||||
atWhoDivs[index+1].className = "at_who active";
|
||||
}
|
||||
},
|
||||
'Enter':()=>{
|
||||
//找到classname为at_who active的div,执行click事件
|
||||
if(document.getElementsByClassName("at_who active")[0]){
|
||||
document.getElementsByClassName("at_who active")[0].click()
|
||||
}else{
|
||||
const cm = editorInstance.cm;
|
||||
const cursor = cm.doc.getCursor();
|
||||
const line = cursor.line;//行
|
||||
const ch = cursor.ch;//列
|
||||
//添加换行
|
||||
cm.replaceRange("\n",{line,ch},{line,ch});
|
||||
setAtWhoVisible(false);
|
||||
atWhoVisibleRef.current = false;
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
//移除上下、enter键监听
|
||||
cmEl && cmEl.removeKeyMap();
|
||||
}
|
||||
},[atWhoVisible])
|
||||
|
||||
useEffect(()=>{
|
||||
//当users数组发生变化时改变框的位置
|
||||
if(atWhoVisibleRef.current && users){
|
||||
//获取光标位置
|
||||
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
|
||||
//设置弹框位置
|
||||
const newLeft = placeholder === "添加评论..."? 80: 10;
|
||||
document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft)+"px";
|
||||
}
|
||||
},[users])
|
||||
|
||||
useEffect(() => {
|
||||
if (cmEl) {
|
||||
let tid = null
|
||||
|
@ -198,19 +373,138 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
if (!noStorage) {
|
||||
tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId)
|
||||
}
|
||||
if (onChange) {
|
||||
editorInstance.cm.on('change', (cm) => {
|
||||
// if(forMember){
|
||||
// document.onkeydown = (e) => {
|
||||
// if (e.key === "@") {
|
||||
// // 输入@键后在对应的位置显示可选的项目成员
|
||||
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
onChange(cm.getValue())
|
||||
})
|
||||
}
|
||||
//isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
|
||||
//绑定@事件
|
||||
isCanAtme && editorInstance.cm.on("focus", () => {
|
||||
document.addEventListener("keydown", mdKeyDown);
|
||||
});
|
||||
isCanAtme && editorInstance.cm.on("blur", () => {
|
||||
document.removeEventListener("keydown",mdKeyDown);
|
||||
});
|
||||
editorInstance.cm.on("change", (cm) => {
|
||||
//调用父组件的onchange方法,将输入内容传入父级组件
|
||||
onChange && onChange(cm.getValue());
|
||||
if(atWhoVisibleRef.current){
|
||||
//搜索用户(弹框之后用户输入用户名信息)
|
||||
const cur = cm.doc.getCursor();
|
||||
const line = cur.line;
|
||||
const ch = cur.ch;
|
||||
let rangeCont = cmEl.getRange({line,ch:0},{line,ch});
|
||||
//处理已经弹出列表框,但用户删除@符号
|
||||
if(rangeCont.indexOf("@")===-1){
|
||||
setAtWhoVisible(false);
|
||||
atWhoVisibleRef.current = false;
|
||||
}else{
|
||||
rangeCont = rangeCont.substring(rangeCont.lastIndexOf("@")+1);
|
||||
rangeCont ? axios.get(`/${owner}/${projectsId}/members.json`,{
|
||||
params: {
|
||||
search: rangeCont,
|
||||
},
|
||||
}).then(response=>{
|
||||
if(response && response.data && response.data.total_count !== 0){
|
||||
setUsers(response.data.users);
|
||||
}else{
|
||||
setUsers(undefined);
|
||||
}
|
||||
}):setUsers(allUsers)
|
||||
}
|
||||
}
|
||||
|
||||
//当内容发生改变并且有已@列表时
|
||||
if(atWhoLoginList.current.length != 0){
|
||||
const codemirror = editorInstance.cm;
|
||||
//startValue:触发change方法时的内容,value:处理了初始内容带@用户的情况
|
||||
let startValue = codemirror.getValue();
|
||||
let value = codemirror.getValue();
|
||||
//处理初始内容就自带@谁的情况
|
||||
if(initValue){
|
||||
const del = [];
|
||||
allUsers.map(item=>{
|
||||
if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
|
||||
//初始内容中有符合@+名字的格式并且当前内容未删除初始内容
|
||||
del[del.length] = `[@${item.username}](/${item.login})`;
|
||||
}
|
||||
})
|
||||
del.length!=0 && del.map(str=>{
|
||||
value = value.replace(str,"");
|
||||
})
|
||||
}
|
||||
//以username为主键,login为value的map集合
|
||||
let atWhoMap = new Map();
|
||||
Array.from(atWhoLoginList.current).map(item=>{
|
||||
allUsers.map(i=>{
|
||||
if(i.login === item){
|
||||
atWhoMap.set(i.username,i.login);
|
||||
}
|
||||
})
|
||||
});
|
||||
const cursor = codemirror.doc.getCursor();
|
||||
const line = cursor.line;
|
||||
const ch = cursor.ch;
|
||||
//处理全部内容中不包含“@”的情况
|
||||
if(value.indexOf("@") === -1){
|
||||
//markdown嵌套的链接删掉
|
||||
// Array.from(atWhoMap.keys()).map(username=>{
|
||||
// startValue = startValue.replaceAll(`[${username}](/${atWhoMap.get(username)}) `,username);
|
||||
// })
|
||||
//替换全部内容
|
||||
// codemirror.setValue(startValue);
|
||||
//全部内容已经有要@的列表,但是没有@符号 -> 清空@集合
|
||||
atWhoLoginList.current = [];
|
||||
setAtWhoLoginListState([]);
|
||||
}
|
||||
|
||||
//截取第一个字符到光标的内容
|
||||
const curAfterCont = codemirror.getRange({line,ch:0},{line,ch});
|
||||
const content = codemirror.getLine(line);
|
||||
//处理光标所在行 有“@”的情况
|
||||
if(content && content.indexOf("@") !== -1){
|
||||
Array.from(atWhoMap.keys()).map(username=>{
|
||||
//判断content是不是以列表中的某个username结尾
|
||||
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
|
||||
//删除空格->选中@用户区域
|
||||
if(curAfterCont.endsWith(userCont)){
|
||||
codemirror.setSelection({line,ch:curAfterCont.lastIndexOf("@")-1},{line,ch});
|
||||
}
|
||||
//处理已经有@列表但是value中不包含完整[@用户名](/login)的情况
|
||||
if(value.indexOf(userCont)===-1){
|
||||
// //markdown嵌套的链接删掉,删[]、()的情况不用处理,markdown会自动认为不是链接
|
||||
// //找到[和)的index,将区域内容替换成[]包裹的内容
|
||||
// //光标之后的内容
|
||||
// const curLeterCont = codemirror.getRange({line,ch},{line,ch:content.length});
|
||||
// console.log('光标之后的内容curLeterCont',curLeterCont);
|
||||
// //删除用户名 -> ]在curLeterCont中
|
||||
// //删除login -> ]在curAfterCont中
|
||||
// const a = curAfterCont.lastIndexOf('[');
|
||||
// const b = curLeterCont.indexOf(')')
|
||||
// const c = curLeterCont.indexOf(']') === -1 ? curAfterCont.lastIndexOf(']') : curLeterCont.indexOf(']')+curAfterCont.length;
|
||||
// console.log('[',a,')',b,']',c);
|
||||
// const newCont = codemirror.getRange({line,ch:a+1},{line,ch:c});
|
||||
// console.log('newCont',newCont);
|
||||
// codemirror.replaceRange(newCont,{line,ch:a-1},{line,ch:b+curAfterCont.length+1})
|
||||
|
||||
//符合情况->踢掉这个人 不给他发消息
|
||||
const list = new Set(atWhoLoginList.current);
|
||||
list.delete(atWhoMap.get(username));
|
||||
atWhoLoginList.current = Array.from(list);
|
||||
setAtWhoLoginListState(Array.from(list));
|
||||
}
|
||||
})
|
||||
}else{
|
||||
//处理所在行没有“@”的情况
|
||||
Array.from(atWhoMap.keys()).map(username=>{
|
||||
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
|
||||
if(value.indexOf(userCont)===-1){
|
||||
//符合情况->踢掉这个人 不给他发消息
|
||||
const list = new Set(atWhoLoginList.current);
|
||||
list.delete(atWhoMap.get(username));
|
||||
atWhoLoginList.current = Array.from(list);
|
||||
setAtWhoLoginListState(Array.from(list));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
ro = onLayout()
|
||||
return () => {
|
||||
if (!noStorage) {
|
||||
|
@ -271,7 +565,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
|
||||
return (
|
||||
<Fragment>
|
||||
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `} >
|
||||
{atWhoVisible && atWhoList}
|
||||
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
|
||||
<div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
|
||||
<textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
|
||||
<div className="CodeMirror cm-s-defualt"></div>
|
||||
|
|
|
@ -699,7 +699,7 @@ class LoginRegisterComponent extends Component {
|
|||
{/*onBlur={(e) => this.inputOnBlur(e)}*/}
|
||||
<Input style={loginInputsyl} type="text" autoComplete="off" onClick={this.changeTypey}
|
||||
className={Phonenumberisnotco && Phonenumberisnotco !== "" ?" color-grey-9 loginInputzhucheyslass bor-reds":" color-grey-9 loginInputzhuche"}
|
||||
placeholder="输入注册手机号或邮箱" value={this.state.login}
|
||||
placeholder="输入注册的邮箱账号" value={this.state.login}
|
||||
// onBlur={(e) => this.inputOnBlurzhuche(e)}
|
||||
onChange={this.loginInputonChange} style={{marginTop: '10px', height: "38px"}}></Input>
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ import axios from 'axios';
|
|||
import CheckInputysl1 from './CheckInputysl';
|
||||
import CheckInputysl2 from './CheckInputysl';
|
||||
import Notcompletedysl from './Notcompletedysl';
|
||||
import Educoder from '../login/educoder.png';
|
||||
import './common.css'
|
||||
import './commontwo.css'
|
||||
const { TabPane } = Tabs;
|
||||
|
@ -1039,7 +1040,7 @@ class LoginRegisterComponent extends Component {
|
|||
}
|
||||
</style>
|
||||
|
||||
<Input placeholder="请输入登录手机号码或邮箱" value={this.state.login}
|
||||
<Input placeholder="请输入邮箱账号" value={this.state.login}
|
||||
onChange={this.loginInputonChange}
|
||||
name="username"
|
||||
className={Phonenumberisnotco && Phonenumberisnotco !== "" ?" color-grey-9 loginInputzhucheyslass bor-reds":" color-grey-9 loginInputzhuche"}
|
||||
|
@ -1085,28 +1086,14 @@ class LoginRegisterComponent extends Component {
|
|||
|
||||
<Button className="login_btn font-16" type="primary" style={{height:"46px"}} onClick={() => this.postLogin()}
|
||||
size={"large"}>登录</Button>
|
||||
{this.props.mygetHelmetapi&&this.props.mygetHelmetapi.main_site===true?this.state.isphone===true?<p className="clearfix mb10 textcenter">
|
||||
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
<a onClick={()=>this.openweixinlogin()}>
|
||||
<img src={require('./img/WeChat.png')} alt="微信登录"/>
|
||||
</a>
|
||||
<a onClick={()=>this.openqqlogin()} className={"ml10"}>
|
||||
<img src={require('./img/qq.png')} alt="qq登录"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>:<p className="clearfix mb10 textcenter">
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
{/*<a onClick={()=>this.openweixinlogin()}>*/}
|
||||
{/*<img src={require('./WeChat.png')} alt="微信登录"/>*/}
|
||||
{/*</a>*/}
|
||||
<a onClick={()=>this.openphoneqqlogin()}>
|
||||
<img src={require('./img/qq.png')} alt="qq登录"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>:""}
|
||||
<p className="clearfix mb10 textcenter">
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
<a href="https://data.educoder.net/oauth2?call_url=/oauth/authorize?client_id=d060ea87615f6f731880857bccc73f2620b0421b6780532cdf0df33583dbab4d&redirect_uri=https%3A%2F%2Fforgeplus.trustie.net%2Fapi%2Fauth%2Feducoder%2Fcallback&response_type=code">
|
||||
<img src={Educoder} alt="educoder登录" width="46px"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -1115,7 +1102,7 @@ class LoginRegisterComponent extends Component {
|
|||
{
|
||||
weixinlogin===false&&parseInt(tab[0])==1 &&
|
||||
<div style={{width: '340px'}}>
|
||||
<Input placeholder="请使用手机号/邮箱账号进行注册"
|
||||
<Input placeholder="请使用邮箱账号进行注册"
|
||||
className={Phonenumberisnotcos && Phonenumberisnotcos !== "" ?" color-grey-9 loginInputzhucheyslass bor-reds":" color-grey-9 loginInputzhuche"}
|
||||
value={this.state.logins}
|
||||
type="text" autoComplete="off"
|
||||
|
@ -1255,34 +1242,19 @@ class LoginRegisterComponent extends Component {
|
|||
color: '#676767',
|
||||
}}>我已阅读并同意
|
||||
<span>
|
||||
<a href={'https://forge.educoder.net/help?index=4'} target="_blank" className={"color-blue"}>《服务协议条款》</a>
|
||||
<a href={'https://gitlink.org.cn/forums/5029/detail'} target="_blank" className={"color-blue"}>《服务协议条款》</a>
|
||||
</span></span></Checkbox>:""}
|
||||
<Button className=" font-16 mb20" type="primary" style={this.props.mygetHelmetapi&&this.props.mygetHelmetapi.main_site===true?{height:"46px", width: "100%",marginTop:"26px"}:{height:"46px", width: "100%"}} onClick={() => this.postregistered()}
|
||||
size={"large"}>{this.props.weixinlogin?"注册并绑定":"注册"}</Button>
|
||||
|
||||
{this.props.mygetHelmetapi&&this.props.mygetHelmetapi.main_site===true?this.state.isphone===true?<p className="clearfix mb10 textcenter">
|
||||
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
<a onClick={()=>this.openweixinlogin()}>
|
||||
<img src={require('./img/WeChat.png')} alt="微信登录"/>
|
||||
</a>
|
||||
<a onClick={()=>this.openqqlogin()} className={"ml10"}>
|
||||
<img src={require('./img/qq.png')} alt="qq登录"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>:<p className="clearfix mb10 textcenter">
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
{/*<a onClick={()=>this.openweixinlogin()}>*/}
|
||||
{/*<img src={require('./WeChat.png')} alt="微信登录"/>*/}
|
||||
{/*</a>*/}
|
||||
<a onClick={()=>this.openphoneqqlogin()}>
|
||||
<img src={require('./img/qq.png')} alt="qq登录"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>:""
|
||||
}
|
||||
<p className="clearfix mb10 textcenter">
|
||||
<span className={"startlogin"}>———————— 快速登录 ————————</span>
|
||||
<div className={"mt10"}>
|
||||
<a href="https://data.educoder.net/oauth2?call_url=/oauth/authorize?client_id=d060ea87615f6f731880857bccc73f2620b0421b6780532cdf0df33583dbab4d&redirect_uri=https%3A%2F%2Fforgeplus.trustie.net%2Fapi%2Fauth%2Feducoder%2Fcallback&response_type=code">
|
||||
<img src={Educoder} alt="educoder登录" width="46px"/>
|
||||
</a>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|