diff --git a/public/css/edu-purge.css b/public/css/edu-purge.css index 60351333..0d146204 100644 --- a/public/css/edu-purge.css +++ b/public/css/edu-purge.css @@ -1949,9 +1949,7 @@ a.decoration { } .mr20 { - margin-right: 10px; - margin-left: 10px; - float: left; + margin-right: 20px; } .mr25 { @@ -1959,7 +1957,7 @@ a.decoration { } .mr30 { - margin-right: 10px; + margin-right: 30px; } .mr35 { @@ -2458,7 +2456,7 @@ a.hoverLine:hover{ .color-grey-9 { - color: #333333 !important; + color: #999 !important; } a:hover{ diff --git a/src/App.js b/src/App.js index 442d4f32..3072aca5 100644 --- a/src/App.js +++ b/src/App.js @@ -97,10 +97,10 @@ const ProjectIndex = Loadable({ loading: Loading, }); -const CreateMerge = Loadable({ - loader: () => import('./forge/Merge/NewMerge'), - loading: Loading, -}) +// const CreateMerge = Loadable({ +// loader: () => import('./forge/Merge/NewMerge'), +// loading: Loading, +// }) // 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。 const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize"]; @@ -279,7 +279,7 @@ class App extends Component { } /> {/* 项目PR */} - <Route path="/:owner/:projectsId/pulls/new" + <Route path="/:owner/:projectsId/compare" render={ (props) => (<ProjectDetail {...this.props} {...props} {...this.state} />) } diff --git a/src/forge/Activity/Activity.js b/src/forge/Activity/Activity.js index 0272f065..4a7a1d34 100644 --- a/src/forge/Activity/Activity.js +++ b/src/forge/Activity/Activity.js @@ -10,6 +10,10 @@ import ActivityItem from './ActivityItem'; import axios from 'axios'; const LIMIT = 15; const ARRAY = [ + { + id:"", + name:'全部' + }, { id:1, name:'1天' @@ -32,10 +36,15 @@ class Activity extends Component{ constructor(props){ super(props); this.state={ - time:'30', + time:undefined, type:undefined, state:undefined, page:1, + pr_count:undefined, + new_pr_count:undefined, + close_issues_count:undefined, + open_issues_count:undefined, + pr_all_count:undefined,issues_count:undefined, data:undefined, project_trends:undefined, @@ -63,8 +72,15 @@ class Activity extends Component{ this.setState({ data:result.data, project_trends:result.data.project_trends, - isSpin:false + isSpin:false, + pr_count:result.data.pr_count, + new_pr_count:result.data.new_pr_count, + close_issues_count:result.data.close_issues_count, + open_issues_count:result.data.open_issues_count, + pr_all_count:result.data.pr_all_count, + issues_count:result.data.issues_count, }) + window.scrollTo(0,0); } }).catch(error=>{ console.log(error); @@ -74,19 +90,19 @@ class Activity extends Component{ // 切换周期 changeTime=(e)=>{ this.setState({ - time:e.key, + time:e.key ==="item_0"?undefined:e.key, isSpin:true }) const { type,status,page } = this.state; - this.getInfo(e.key,type,status,page); + this.getInfo(e.key ==="item_0"?undefined:e.key,type,status,page); } //筛选 changeTrends=(type,status)=>{ this.setState({ - type,status + type,status,page:1 }) - const {time,page}=this.state; - this.getInfo(time,type,status,page); + const {time}=this.state; + this.getInfo(time,type,status,1); } // 分页 ChangePage=(page)=>{ @@ -108,12 +124,14 @@ class Activity extends Component{ </Menu> ) render(){ - const { time , data , page , project_trends , isSpin } = this.state; + const { time , data , page , project_trends , isSpin , pr_count , new_pr_count , close_issues_count , open_issues_count , pr_all_count ,issues_count } = this.state; + let name = time ? ARRAY.filter(item=>item.id === parseInt(time)) :[{name:"全部"}]; - let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ; - const second_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%'; - const third_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%'; - const fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%'; + const first_per = pr_all_count > 0 ? `${parseFloat(pr_count/pr_all_count).toFixed(2)*100}%` :"50%"; + const second_per =pr_all_count > 0 ? `${parseFloat(new_pr_count/pr_all_count).toFixed(2)*100}%` :"50%"; + const third_per =issues_count > 0 ?`${parseFloat(close_issues_count/issues_count).toFixed(2)*100}%` :"50%"; + const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%"; + return( <div className="main"> @@ -122,7 +140,7 @@ class Activity extends Component{ <div className="orderInfo"> <div> <div className="percentLine prPercent"> - <p className="percent_purple" style={{width:'100%'}}></p> + <p className="percent_purple" style={{width:first_per}}></p> <p className="percent_green resetStyle" style={{width:`${second_per}`}}></p> </div> <span>{data && data.pr_all_count}合并请求</span> @@ -132,25 +150,25 @@ class Activity extends Component{ <p className="percent_red" style={{width:`${third_per}`}}></p> <p className="percent_green" style={{width:`${fourth_per}`}}></p> </div> - <span>{data && data.issues_count}任务</span> + <span>{data && data.issues_count}易修</span> </div> </div> <ul className="percentBox"> <li> <span className="purple">{data && data.pr_count}</span> - <span className="change" onClick={()=>this.changeTrends("PullRequest","close")}>已处理的合并请求</span> + <span className="change" onClick={()=>this.changeTrends("PullRequest","delay")}>已处理的合并请求</span> </li> <li> <span className="green">{data && data.new_pr_count}</span> - <span className="change" onClick={()=>this.changeTrends("PullRequest","create")}>未处理的合并请求</span> + <span className="change" onClick={()=>this.changeTrends("PullRequest","not_delay")}>未处理的合并请求</span> </li> <li> <span className="red">{data && data.close_issues_count}</span> - <span className="change" onClick={()=>this.changeTrends("Issue","close")}>已关闭的任务</span> + <span className="change" onClick={()=>this.changeTrends("Issue","delay")}>已关闭的易修</span> </li> <li> <span className="green">{data && data.open_issues_count}</span> - <span className="change" onClick={()=>this.changeTrends("Issue","create")}>未处理的任务</span> + <span className="change" onClick={()=>this.changeTrends("Issue","not_delay")}>未处理的易修</span> </li> </ul> </div> diff --git a/src/forge/Activity/ActivityItem.js b/src/forge/Activity/ActivityItem.js index 2b1ad3c8..48a72e79 100644 --- a/src/forge/Activity/ActivityItem.js +++ b/src/forge/Activity/ActivityItem.js @@ -27,7 +27,7 @@ class ActivityItem extends Component { : // 如果是合并请求 <p className="itemLine"> - <Link to={`/${owner}/${projectsId}/pulls/${item.trend_id}/Messagecount`} className="color-blue font-16">{item.name}</Link> + <Link to={`/${owner}/${projectsId}/pulls/${item.trend_id}`} className="color-blue font-16">{item.name}</Link> <span className="activity_type">{item.trend_type}</span> </p > } diff --git a/src/forge/Branch/SelectOverlay.jsx b/src/forge/Branch/SelectOverlay.jsx index f043a8f5..0be56732 100644 --- a/src/forge/Branch/SelectOverlay.jsx +++ b/src/forge/Branch/SelectOverlay.jsx @@ -2,7 +2,7 @@ import React , { useState , useEffect } from 'react'; import { Input , Spin , Menu } from 'antd'; import { getBranch , getTag } from '../GetData/getData'; -function SelectOverlay({ changeBranch , tagflag , branchList , projectsId , owner , visible }) { +function SelectOverlay({ changeBranch , tagflag , projectsId , owner , visible }) { const [ inputValue , setInputValue] = useState(undefined); const [ nav , setNav ] = useState(0); const [ isSpin , setIsSpin ] = useState(true); @@ -11,21 +11,13 @@ function SelectOverlay({ changeBranch , tagflag , branchList , projectsId , owne const [ datas , setDatas ] = useState(undefined); const [ keys ,setKeys] = useState("branch"); - // useEffect(()=>{ - // if(visible){ - // setKeys("branch"); - // getBranchs(projectsId,owner); - // setIsSpin(true); - // } - // },[visible]) - useEffect(()=>{ - if(branchList){ - setData(branchList); - setDatas(branchList); - setIsSpin(false); + if(visible){ + setKeys("branch"); + getBranchs(projectsId,owner); + setIsSpin(true); } - },[branchList]) + },[visible]) async function getBranchs(id,owner){ let result = await getBranch(id,owner); @@ -53,8 +45,10 @@ function SelectOverlay({ changeBranch , tagflag , branchList , projectsId , owne setIsSpin(true); if(e.key === "branch"){ getBranchs(projectsId,owner); + setNav(0); }else{ getTags(projectsId,owner); + setNav(1); } } diff --git a/src/forge/Component/NoticeModal/Index.scss b/src/forge/Component/NoticeModal/Index.scss new file mode 100644 index 00000000..18e4dcda --- /dev/null +++ b/src/forge/Component/NoticeModal/Index.scss @@ -0,0 +1,90 @@ +.systemBox{ + .ant-modal-body{ + padding:1px 0px 0px 0px; + .sysBox{ + background-image: url('./bg.png'); + background-repeat: no-repeat; + background-size: 100% 334px; + margin-top: -55px; + } + .sysnoticeBox{ + width: 100%; + padding:80px 0px 34px; + display: flex; + flex-direction: column; + width: 780px; + margin: 0px auto; + p.ntitle{ + height: 33px; + font-size: 24px; + font-weight: 500; + color: #31FFF7; + line-height: 33px; + text-align: center; + } + p.nSubtitle{ + height: 25px; + line-height: 25px; + font-size: 18px; + font-weight: 500; + color: #FFFFFF; + margin-top: 60px; + padding-left: 20px; + } + .markdown-body{ + box-shadow: 0px 0px 17px rgba(0,0,0,0.2); + border-radius: 4px; + margin-top: 17px!important; + } + .nContent{ + padding:20px 34px; + background-color: #fff; + line-height: 30px; + font-size: 15px; + font-weight: 400; + color: #333; + .realmName{ + margin-top: 20px; + display: flex; + ul{ + width: 50%; + padding-left: 0px!important; + li{ + font-size: 15px; + font-weight: 500; + line-height: 32px; + text-align: left; + color: #000; + list-style-type: none!important; + &:first-child{ + color: #E65714; + } + } + } + } + .nSubdesc{ + font-size: 15px; + font-weight: 400; + color: #000000; + line-height: 31px; + margin-top: 20px; + } + .nInfo{ + font-size: 14px; + font-weight: 400; + color: #333333; + text-align: right; + margin-top: 25px; + p{ + height: 20px; + line-height: 20px; + } + } + } + .nBtn{ + text-align: center; + margin-top: 33px; + } + } + } +} \ No newline at end of file diff --git a/src/forge/Component/NoticeModal/SystemNotice.jsx b/src/forge/Component/NoticeModal/SystemNotice.jsx new file mode 100644 index 00000000..1560c1ff --- /dev/null +++ b/src/forge/Component/NoticeModal/SystemNotice.jsx @@ -0,0 +1,76 @@ +import React , { useEffect , useState } from 'react'; +import { Modal , Button } from 'antd'; +import './Index.scss'; +import '../../css/index.scss'; +import RenderHtml from '../../../components/render-html'; +import cookie from 'react-cookies'; + +function SystemNotice({system_notification,history}){ + const [ visible , setVisible ] = useState(false); + + useEffect(()=>{ + if(system_notification && !cookie.load('notice_stage')){ + setVisible(true); + } + },[system_notification,history.location]) + + function sureContinue() { + cookie.remove('notice_stage'); + + let inFifteenMinutes = new Date(new Date().getTime() + 24 * 3600 * 1000);//一天 + // let inFifteenMinutes = new Date(new Date().getTime() + 60 * 1000);//一分钟 + cookie.save('notice_stage', true,{ expires: inFifteenMinutes,path:"/" }); + + setVisible(false); + } + + return ( + <Modal + visible = {visible} + width="1000px" + footer={false} + title={false} + centered={true} + closable={false} + wrapClassName={'systemBox'} + > + <div className="sysBox"> + <div className="sysnoticeBox"> + <p className="ntitle">{system_notification && system_notification.subject}</p> + <p className="nSubtitle">{system_notification && system_notification.sub_subject}</p> + {/* <div className="nContent"> + <div className="nMaindesc"> + 为了给用户提供更加稳定、优质的服务,我们即将对平台门户首页、平台名称、平台域名进行一次全面升级与变更。原平台名称:Trustie(中文名:确实)将于2021年10月xx日统一更改为Gitlink(中文名:确实开源)。届时平台域名将统一进行更换,更换规则如下 + </div> + <div className="realmName"> + <ul> + <li>原域名:</li> + <li>官网顶级域名https://www.trustie.net</li> + <li>版本库子域名https://forgeplus.trustie.net</li> + <li>论坛子域名https://forum.trustie.net/forums</li> + </ul> + <ul> + <li>更换后域名:</li> + <li>官网顶级域名https://www.gitlink.org.cn</li> + <li>版本库子域名https://www.git.gitlink.org.cn</li> + <li>论坛子域名https://forum.gitlink.org.cn</li> + </ul> + </div> + <div className="nSubdesc"> + 自2021年10月xx日起,旧域名将停止访问。因平台名称与域名变更给您带来的不便,我们深表歉意!非常感谢您一直以来对本平台的信任与支持,我们将一如既往地为您提供优质的服务。 特此通知! + </div> + <div className="nInfo"> + <p>Gitlink运营团队</p> + <p>2021年10月xx日</p> + </div> + </div> */} + <RenderHtml className="break_word_comments imageLayerParent" value={system_notification && system_notification.content} url={history.location}/> + <div className="nBtn"> + <Button type="primary" className="btnblue" onClick={sureContinue}>确认并继续</Button> + </div> + </div> + </div> + </Modal> + ) +} +export default SystemNotice; \ No newline at end of file diff --git a/src/forge/Component/NoticeModal/bg.png b/src/forge/Component/NoticeModal/bg.png new file mode 100644 index 00000000..625b3c31 Binary files /dev/null and b/src/forge/Component/NoticeModal/bg.png differ diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js index 59513153..ac139351 100644 --- a/src/forge/Head/Header.js +++ b/src/forge/Head/Header.js @@ -367,7 +367,6 @@ class NewHeader extends Component { } let search_url = settings && settings.common && settings.common.search; - let notice_url = settings && settings.common && settings.common.notice; return ( <div className="newHeaders" id="nHeader"> <div className="headerContent"> @@ -438,7 +437,7 @@ class NewHeader extends Component { </Dropdown>:"" } - {current_user && current_user.login ? + { (settings && settings.common && settings.common.notice) && (current_user && current_user.login)? <Popover overlayClassName="notice-popover" placement={`bottomRight`} @@ -448,9 +447,9 @@ class NewHeader extends Component { destroyTooltipOnHide > <Link to={"/settings/notice"} className="message-icon"> - <Badge count={current_user.message_unread_total}> + {current_user && <Badge count={current_user.message_unread_total}> <i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i> - </Badge> + </Badge>} </Link> </Popover> : "" diff --git a/src/forge/Main/CoderDepot.jsx b/src/forge/Main/CoderDepot.jsx index 70d1901e..f50c134a 100644 --- a/src/forge/Main/CoderDepot.jsx +++ b/src/forge/Main/CoderDepot.jsx @@ -22,6 +22,7 @@ import UpdateDescModal from './sub/UpdateDescModal'; import Nodata from '../Nodata'; import Invite from './sub/Invite'; import CheckProfile from '../Component/ProfileModal/Profile'; +import RenderHtml from '../../components/render-html'; /** * projectDetail.type:0是托管项目,1是镜像项目,2是同步镜像项目(为2时不支持在线创建、在线上传、在线修改、在线删除、创建合并请求等功能) */ @@ -196,7 +197,7 @@ function CoderDepot(props){ let ele = document.getElementById("ptxt"); if(ele){ let h = ele.offsetHeight; - if( h > 18 ) setHideBtn(true); + if( h > 35 ) setHideBtn(true); } } },[projectDetail,lastCommit]) @@ -401,22 +402,22 @@ function CoderDepot(props){ getPathUrl={getPathUrl} /> : - <div> + <React.Fragment> <AlignCenter className="mr20"> <Link to={`/${owner}/${projectsId}/branches`} className="iconBtn"> <i className="iconfont icon-master_icon font-16"></i> <span>分支</span> - <span>{projectDetail && projectDetail.branches && projectDetail.branches.total_count}</span> + <span>{projectDetail && projectDetail.branches_count}</span> </Link> </AlignCenter> <AlignCenter className="mr20"> <Link to={`/${owner}/${projectsId}/tags`} className="iconBtn"> <i className="iconfont icon-biaoqianicon font-16"></i> <span>标签</span> - <span>{projectDetail && projectDetail.tags && projectDetail.tags.total_count}</span> + <span>{projectDetail && projectDetail.tags_count}</span> </Link> </AlignCenter> - </div> + </React.Fragment> } </AlignCenter> <AlignCenter className="depotBtn"> @@ -425,7 +426,7 @@ function CoderDepot(props){ <div className="addOptionBtn"> { baseOperate && - <CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/pulls/new/${branchName || defaultBranch}`)} >+ 合并请求</CheckProfile> + <CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/compare/master...${branchName || defaultBranch}`)} >+ 合并请求</CheckProfile> } { baseOper && @@ -457,7 +458,7 @@ function CoderDepot(props){ <div className="listtablehead"> <User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/> <div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}> - <pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}>{lastCommit.message}</Link></pre> + <pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}><RenderHtml value={lastCommit.message}/></Link></pre> </div> { hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> } diff --git a/src/forge/Main/CoderRootCommit.js b/src/forge/Main/CoderRootCommit.js index 604917ba..b542c390 100644 --- a/src/forge/Main/CoderRootCommit.js +++ b/src/forge/Main/CoderRootCommit.js @@ -6,7 +6,8 @@ import { AlignTop } from '../Component/layout'; import SelectBranch from '../Branch/Select'; import Nodata from '../Nodata'; -import User from '../Component/User' +import User from '../Component/User'; +import RenderHtml from '../../components/render-html.jsx'; import Tree from './img/tree.png'; import axios from 'axios'; import {Link} from "react-router-dom"; @@ -158,7 +159,7 @@ class CoderRootCommit extends Component{ <div className="commitList-item f-wrap-between"> <div> <AlignTop> - <div className="commitDesc"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="font-14 color-grey-3 font-bd">{item.message}</Link></div> + <div className="commitDesc"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="font-14 color-grey-3 font-bd"><RenderHtml value={item.message}/></Link></div> </AlignTop> <p className="f-wrap-alignCenter mt15 pb5"> <User diff --git a/src/forge/Main/CoderRootFileDetail.js b/src/forge/Main/CoderRootFileDetail.js index c6c976f4..bdcfc98a 100644 --- a/src/forge/Main/CoderRootFileDetail.js +++ b/src/forge/Main/CoderRootFileDetail.js @@ -27,6 +27,7 @@ class CoderRootFileDetail extends Component { } componentDidMount = () => { + window.scrollTo(0, 0); const { detail , mdFlag } = this.props; this.setState({ value: detail.content, @@ -214,7 +215,7 @@ class CoderRootFileDetail extends Component { const Option = Select.Option; return ( <React.Fragment> - <Anchor className="griditemAnchor" offsetTop={70} targetOffset={160}> + <Anchor className="griditemAnchor" offsetTop={70}> <div className="griditemCate"> { md && readOnly && diff --git a/src/forge/Main/CoderRootIndex.js b/src/forge/Main/CoderRootIndex.js index b5d24196..7ff25dae 100644 --- a/src/forge/Main/CoderRootIndex.js +++ b/src/forge/Main/CoderRootIndex.js @@ -3,7 +3,6 @@ import { Route , Switch } from 'react-router-dom'; // import Top from './DetailTop'; import Loadable from 'react-loadable'; import Loading from '../../Loading'; -import axios from 'axios'; import './Index.scss'; const FileNew = Loadable({ @@ -51,37 +50,37 @@ class CoderRootIndex extends Component{ } } - componentDidMount=()=>{ - this.Init(); - } - componentDidUpdate=(prevProps)=>{ - const { location } = this.props; - const prevlocation = prevProps && prevProps.location; - if (location !== prevlocation) { - this.Init(); - } - } + // componentDidMount=()=>{ + // this.Init(); + // } + // componentDidUpdate=(prevProps)=>{ + // const { location } = this.props; + // const prevlocation = prevProps && prevProps.location; + // if (location !== prevlocation) { + // this.Init(); + // } + // } - Init=()=>{ - const { branchName } = this.props.match.params; - const { defaultBranch } = this.props; - this.getTopCount(branchName || defaultBranch); - } + // Init=()=>{ + // const { branchName } = this.props.match.params; + // const { defaultBranch } = this.props; + // this.getTopCount(branchName || defaultBranch); + // } // 获取<Top />组件里要显示的数据 - getTopCount=(branch)=>{ - const { projectsId , owner } = this.props.match.params; - const url = `/${owner}/${projectsId}/top_counts.json`; - axios.get(url,{params:{ - ref:branch - }}).then(result=>{ - if(result){ - this.setState({ - coderCount:result.data - }) - } - }).catch(error=>{console.log(error);}) - } + // getTopCount=(branch)=>{ + // const { projectsId , owner } = this.props.match.params; + // const url = `/${owner}/${projectsId}/top_counts.json`; + // axios.get(url,{params:{ + // ref:branch + // }}).then(result=>{ + // if(result){ + // this.setState({ + // coderCount:result.data + // }) + // } + // }).catch(error=>{console.log(error);}) + // } render(){ return( <div className="coderSubPage"> @@ -100,12 +99,12 @@ class CoderRootIndex extends Component{ ></Route> <Route path="/:owner/:projectsId/:branch/newfile" render={ - (props) => (<FileNew {...this.props} {...props} {...this.state} getTopCount={this.getTopCount} />) + (props) => (<FileNew {...this.props} {...props} {...this.state} />) } ></Route> <Route path="/:owner/:projectsId/commits/branch/:branchName" render={ - () => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />) + () => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" />) } ></Route> <Route path="/:owner/:projectsId/commits/:sha" @@ -115,7 +114,7 @@ class CoderRootIndex extends Component{ ></Route> <Route path="/:owner/:projectsId/commits" render={ - () => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />) + () => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" />) } ></Route> {/* <Route path="/:owner/:projectsId/releases/:versionId/update" diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index 4e1c241b..b4d17a81 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -70,7 +70,7 @@ const MergeIndexDetail = Loadable({ }) const CreateMerge = Loadable({ - loader: () => import('../Merge/NewMerge'), + loader: () => import('../Merge/CreateMerge'), loading: Loading, }) @@ -150,7 +150,9 @@ function checkPathname(projectsId, owner, pathname) { name = "about" } else if (url.indexOf("/issues") > -1 || url.indexOf("Milepost") > 0) { name = "issues"; - } else if (url.indexOf("/pulls") > -1) { + } else if (url.indexOf("/pulls") > -1 || url.indexOf("/compare") > -1) { + // /pulls,合并请求除新建合并请求外, + // /compare,新建合并请求 name = "pulls" } else if (url.indexOf("/milestones") > -1) { name = "milestones" @@ -318,6 +320,9 @@ class Detail extends Component { const url = `/${owner}/${projectsId}/detail.json`; axios.get(url).then((result) => { if (result && result.data) { + if (result.data.status === 404) { + this.props.history.push('/nopage'); + } this.setState({ projectDetail: result.data, project_id: result.data.project_id, @@ -503,11 +508,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> @@ -552,7 +552,7 @@ class Detail extends Component { <span className="detail_tag_btn" loading={forkSpin}> <Tooltip title="复刻是fork的中文名,即复制代码仓库" placement="bottom"> <a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={this.forkFunc}> - <i className="iconfont icon-fork color-grey-9 mr3 font-16"></i><span>复刻</span> + <i className="iconfont icon-fork color-grey-9 mr3 font-16"></i><span>复刻(Fork)</span> </a> </Tooltip> { @@ -713,22 +713,32 @@ class Detail extends Component { } ></Route> {/* 新建合并请求 */} - <Route path="/:owner/:projectsId/pulls/new/:branch" + {/* <Route path="/:owner/:projectsId/compare/:branch" + render={ + (props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />) + } + ></Route> */} + <Route path="/:owner/:projectsId/compare" render={ (props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />) } ></Route> - <Route path="/:owner/:projectsId/pulls/new" - render={ - (props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />) - } - ></Route> - <Route path="/:owner/:projectsId/pulls/:mergeId/UpdateMerge" + <Route path="/:owner/:projectsId/pulls/:mergeId/edit" render={ (props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />) } ></Route> - <Route path="/:owner/:projectsId/pulls/:mergeId/Messagecount" + <Route path="/:owner/:projectsId/pulls/:mergeId" + render={ + (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />) + } + ></Route> + <Route path="/:owner/:projectsId/pulls/:mergeId/commits" + render={ + (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />) + } + ></Route> + <Route path="/:owner/:projectsId/pulls/:mergeId/files" render={ (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />) } diff --git a/src/forge/Main/Diff.jsx b/src/forge/Main/Diff.jsx index 17535e43..8dad59fb 100644 --- a/src/forge/Main/Diff.jsx +++ b/src/forge/Main/Diff.jsx @@ -6,6 +6,7 @@ import { getImageUrl } from 'educoder'; import Files from '../Merge/Files'; import Tree from "./img/tree.png"; import User from "../Component/User"; +import RenderHtml from "../../components/render-html"; import axios from "axios"; import { Link } from "react-router-dom"; @@ -17,8 +18,11 @@ const Infos = styled.div` border: 1px solid rgba(42, 97, 255, 0.23); border-radius: 3px 3px 0px 0px; padding: 10px 20px 10px 16px; - & .f-wrap-between{ - align-items: center; + & .markdown-body table{ + background: #f1f8ff; + } + & .btnblue{ + margin-top: 12px; } & .task-hide{ width: 65rem; @@ -85,9 +89,9 @@ export default (props) => { <div className="f-wrap-between"> <div> {commit && commit.message && - <pre className="task-hide">{commit.message}</pre> + <RenderHtml className="task-hide" value={commit.message}/> } - <Link to={`/${owner}/${projectsId}/tree/${truncateCommitId(sha)}`}><i className="iconfont icon-fenzhi2 font-18"></i>{data.branch}</Link> + <Link to={`/${owner}/${projectsId}/tree/${data.branch}`}><i className="iconfont icon-fenzhi2 font-18"></i>{data.branch}</Link> </div> <Button type="primary" onClick={()=>{history.push(`/${owner}/${projectsId}/tree/${truncateCommitId(sha)}`)}} className="btnblue" style={{height:"36px"}}>浏览文件</Button> </div> diff --git a/src/forge/Main/Index.scss b/src/forge/Main/Index.scss index a19bd80d..5f369292 100644 --- a/src/forge/Main/Index.scss +++ b/src/forge/Main/Index.scss @@ -195,7 +195,7 @@ width: 40px; height: 40px; } - &:nth-child(5){ + &:nth-child(5n){ margin-right: 0px; } } @@ -224,7 +224,7 @@ height: 8px; width: 8px; left: 0px; - top:10px + top:8px; } &>span{ padding-left: 15px; @@ -252,7 +252,13 @@ border: 1px solid rgba(42, 97, 255, 0.23); background-color: #FAFCFF; .ellipsistxt{ - margin-top: 6px; + &:hover .markdown-body{ + color: #466AFF; + & a{ + color: #466AFF; + } + } + margin-top: 2px; // cursor: pointer; #ptxt{ margin-bottom: 0px; @@ -263,6 +269,27 @@ white-space:-pre-wrap; /* Opera 4-6 */ white-space:-o-pre-wrap; /* Opera 7 */ word-wrap:break-word; + .markdown-body{ + line-height: 10px; + font-size: 14px; + & p { + margin: 1px 0px 0px !important; + font-size: 14px !important; + } + & ol,ul{ + padding-bottom: 3px; + & li{ + min-height: 18px; + } + } + & table{ + line-height: 1; + background: #FAFCFF; + } + &:first-child { + margin-top: -1px !important; + } + } } margin-left: 13px; line-height:18px; @@ -270,7 +297,7 @@ width: 0; color: #666; &.hidetxt{ - height: 18px; + height: 24px; overflow: hidden; position: relative; padding-right:8px; @@ -453,7 +480,7 @@ } } .ant-anchor-wrapper{ - padding-left: 2px; + padding-left: 2px!important; .ant-anchor-ink::before{ background-color: #fff; } @@ -463,8 +490,8 @@ margin:0px auto; } .griditemAnchor{ - margin-left: 0px; - padding: 0px; + margin-left: 0px!important; + padding: 0px!important; border-bottom: 1px solid #ddd; .ant-anchor{ display: flex; diff --git a/src/forge/Main/IndexItem.js b/src/forge/Main/IndexItem.js index 0cba9123..4ddd7633 100644 --- a/src/forge/Main/IndexItem.js +++ b/src/forge/Main/IndexItem.js @@ -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"> { diff --git a/src/forge/Main/list.scss b/src/forge/Main/list.scss index 73b534de..59589fb6 100644 --- a/src/forge/Main/list.scss +++ b/src/forge/Main/list.scss @@ -734,7 +734,12 @@ a.color-grey-ccc:hover{ border: 1px solid rgba(42, 97, 255, 0.23); border-radius: 4px; margin-left: 16px; - align-items: center; + & .treecopy{ + margin-top: 20px; + } + & .markdown-body table{ + background: #FAFCFF; + } &:after,&:before{ content: ""; position: absolute; @@ -760,6 +765,9 @@ a.color-grey-ccc:hover{ &:before{ border-right: 10px solid rgba(42, 97, 255, 0.58); } + & .markdown-body table{ + background: #EEF6FF; + } } .treecopy-cont{ padding: 4px 15px; diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index 5e3ab944..22a5ce41 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -50,7 +50,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa <Link to={{ pathname: `/${owner}/${projectsId}/issues`, state }}> <Tooltip title="易修是Issue的中文名,即问题列表" placement="bottom"> <i className={"iconfont icon-yixiuicon1 color-grey-3 mr5 font-14"}></i> - <span>易修</span> + <span>易修(Issue)</span> </Tooltip> {projectDetail && projectDetail.issues_count ? <span className="num">{numFormat(projectDetail.issues_count)}</span> : ""} </Link> diff --git a/src/forge/Main/tag/Index.jsx b/src/forge/Main/tag/Index.jsx index 88cca13b..ff764851 100644 --- a/src/forge/Main/tag/Index.jsx +++ b/src/forge/Main/tag/Index.jsx @@ -1,17 +1,19 @@ import React,{ useEffect , useState } from 'react'; import SubMenu from '../sub/SubMenu'; -import { Table , Tooltip } from 'antd'; +import { Table , Tooltip , Spin } from 'antd'; import axios from 'axios'; import { Link } from 'react-router-dom'; import { truncateCommitId } from '../../common/util'; +import { getImageUrl } from 'educoder'; +import Nonedata from '../../Nodata'; import './Index.scss'; import Tree from '../img/tree.png' import moment from 'moment'; - function Tags(props) { - const [ source , setSource ] = useState([]); + const [ source , setSource ] = useState(undefined); + const [ isSpin , setIsSpin ] = useState(true); const { projectsId , owner } = props.match.params; @@ -21,6 +23,7 @@ function Tags(props) { axios.get(url).then((result) => { if (result) { setSource(result.data); + setIsSpin(false); } }).catch(error => {}) } @@ -32,8 +35,13 @@ function Tags(props) { dataIndex:"name", key:1, ellipsis:true, + width:"200px", render:(txt,item)=>{ - return <Link className="hover" to={`/${owner}/${projectsId}/tree/${item.name}`} >{item.name}</Link> + return( + <div className="tagBranch"> + <Link className="hover tagClass" to={`/${owner}/${projectsId}/tree/${item.name}`}>{item.name}</Link> + </div> + ) } }, { @@ -43,8 +51,22 @@ function Tags(props) { ellipsis:true, render:(txt,item)=>{ return ( - <span className="color-grey-3"> - <Link className="mr3" style={{fontWeight:"500"}} to={`/${item.commit && item.commit.login}`} >{item.commit && item.commit.name}</Link> + <span className="color-grey-3 tagModel"> + { + item.tagger && + <Tooltip placement="top" title={item.tagger.name}> + { + item.tagger.id ? + <Link className="mr3 tagModelImg" to={`/${item.tagger.login}`} > + <img src={getImageUrl(`/${item.tagger && item.tagger.image_url}`)} alt=""/> + </Link> + : + <span className="mr3 tagModelImg" style={{cursor:"default"}}> + <img src={getImageUrl(`/${item.tagger && item.tagger.image_url}`)} alt=""/> + </span> + } + </Tooltip> + } <span>创建于{txt}</span> </span> ) @@ -59,7 +81,7 @@ function Tags(props) { return ( <Tooltip placement="top" title={`最后提交日期:${item.created_at_unix ? moment(item.created_at_unix*1000).format('YYYY-MM-DD'):''}`}> <img src={Tree} alt="提交ID" width="22px" className="mr4"/> - <Link className="hover color-blue" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.commit && item.commit.sha}`)}`}>{truncateCommitId(item.commit && item.commit.sha)}</Link> + <Link className="hover color-blue" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.id}`)}`}>{truncateCommitId(item.id)}</Link> </Tooltip> ) } @@ -79,18 +101,17 @@ function Tags(props) { key:5, ellipsis:true, align:"center", - width:"181px", + width:"204px", render:(txt,item)=>{ return ( - <div> - + <React.Fragment> <a href={`${item.tarball_url}`} download className="btn-83"> <i className="iconfont icon-xiazai-icon font-16 mr5"></i>TAR </a> <a href={`${item.zipball_url}`} download className="btn-83"> <i className="iconfont icon-xiazai-icon font-16 mr5"></i>ZIP </a> - </div> + </React.Fragment> ) } } @@ -98,7 +119,19 @@ function Tags(props) { return( <div> <SubMenu tab={"tags"} projectsId={projectsId} owner={owner}/> - <Table className="tagTable" dataSource={source} columns={columns} pagination={false}></Table> + <Spin spinning={isSpin}> + <div className="tagSpin"> + { + source && source.length > 0 && + <Table + className="tagTable" + dataSource={source} columns={columns} pagination={false}></Table> + } + { + source && source.length === 0 && <Nonedata _html={'暂无数据~'}/> + } + </div> + </Spin> </div> ) } diff --git a/src/forge/Main/tag/Index.scss b/src/forge/Main/tag/Index.scss index 3bd487db..6adba91e 100644 --- a/src/forge/Main/tag/Index.scss +++ b/src/forge/Main/tag/Index.scss @@ -4,6 +4,7 @@ tr th{ background-color: #fff; padding:5px 0px; + width: 172px; .ant-table-column-title{ font-size: 16px; font-weight: 500; @@ -12,6 +13,9 @@ } } tbody{ + .btn-83{ + margin:0px 8px; + } tr{ &:hover td{ background-color: #fff!important; @@ -20,6 +24,10 @@ padding:0px; height: 69px; line-height: 69px; + color:#333333; + div{ + font-weight: 500; + } } &:last-child{ td{ @@ -28,4 +36,23 @@ } } } +} +.tagSpin{ + min-height: 300px; +} +.tagBranch{ + padding-right: 15px; + text-overflow: ellipsis; + overflow: hidden; + .tagClass{ + color:#333333; + } +} +.tagModel{ + font-weight: 400; + .tagModelImg img{ + width: 25px; + height: 25px; + border-radius: 50%; + } } \ No newline at end of file diff --git a/src/forge/Main/tree/Index.jsx b/src/forge/Main/tree/Index.jsx index 84220a0b..2fc57434 100644 --- a/src/forge/Main/tree/Index.jsx +++ b/src/forge/Main/tree/Index.jsx @@ -91,7 +91,7 @@ function Index(props) { <div className="treeabout"> { (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && - <Link to={`/${owner}/${projectsId}/pulls/new/${i.name}`} className="btn-83">+ 合并请求</Link> + <Link to={`/${owner}/${projectsId}/compare/master...${i.name}`} className="btn-83">+ 合并请求</Link> } <Dropdown overlay={menu(i.zip_url,i.tar_url)} trigger={['click']} placement="bottomRight"> <a className="btn-83 ml15">下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a> diff --git a/src/forge/Main/tree/Index.scss b/src/forge/Main/tree/Index.scss index b077d9de..f65a1c68 100644 --- a/src/forge/Main/tree/Index.scss +++ b/src/forge/Main/tree/Index.scss @@ -22,9 +22,14 @@ border-bottom: none; } .treeinfo{ - max-width: 399px; + width: 399px; flex:1; flex-direction: column; + &>a{ + display: block; + width: 399px; + + } a:hover{ span{ color: #466AFF!important; diff --git a/src/forge/Main/version/version.js b/src/forge/Main/version/version.js index 68cfd774..2896899f 100644 --- a/src/forge/Main/version/version.js +++ b/src/forge/Main/version/version.js @@ -15,10 +15,9 @@ function version(props) { const [ releases , setReleases ] = useState(undefined); const [ isSpin , setIsSpin ] = useState(true); const { projectsId ,owner } = props.match.params; - const { isManager, isDeveloper, location , user } = props; + const { location } = props; const type = props.projectDetail && props.projectDetail.type; const turnFromNew = location && location.query && location.query.turnFromNew; - const current_user_login = user && user.login; useEffect(()=>{ getIssueList(); },[]) @@ -34,7 +33,6 @@ function version(props) { setReleases(result.data.releases); setIsSpin(false); } - }).catch((error) => { console.log(error); }) @@ -130,6 +128,8 @@ function version(props) { addFunc={addFunc} /> ) + } else{ + return (<div></div>) } } diff --git a/src/forge/Merge/CreateMerge.js b/src/forge/Merge/CreateMerge.js new file mode 100644 index 00000000..d7a32009 --- /dev/null +++ b/src/forge/Merge/CreateMerge.js @@ -0,0 +1,448 @@ +import React, { Component } from 'react'; +import { Input, Select, Spin, Alert } from 'antd'; +import axios from 'axios'; +import MergeForm from './merge_form'; +import MergeFooter from './merge_footer'; + +import '../Order/order.css'; +import './merge.css'; + +/** + * 根据url获取目标仓库、目标分支、源仓库、源分支 + * 路由规则:owner/projectId/compare/merge...pullowner:pullBranch + * 可能存在的情况 + * 1、代码库首页跳转,仓库相同,目标分支为默认分支,owner/projectId/compare/pullBranch + * 2、代码库分支列表,仓库相同,目标分支为默认分支,owner/projectId/compare/pullBranch + * 3、合并请求列表页(新建、无数据时的提示),仓库相同,源、目标都为默认分支,owner/projectId/compare + * 4、新建页面,切换分支、切换目标仓库、刷新页面等,存在所有可能情况 + */ +function getBranchParams(pathname) { + const result = { + // 目标仓库所有者 + mergeOwner: undefined, + // 目标分支 + mergeBranch: 'master', + // 源仓库所有者 + pullOwner: undefined, + // 源分支 + pullBranch: 'master', + // 仓库名称 + projectId: undefined, + }; + // 去掉第一个字符/ + const _pathname = pathname.slice(1); + const [ownerProject, branchUrl] = _pathname.split('/compare'); + const [mergeOwner, projectId] = ownerProject.split('/'); + // 同仓库时 + result.mergeOwner = mergeOwner; + result.pullOwner = mergeOwner; + result.projectId = projectId; + if (branchUrl) { + // 如果存在具体的分支 + const _branchUrl = branchUrl.slice(1); + if (_branchUrl.indexOf('...') > -1) { + // 存在源分支与目标分支 + const [mergeBranch, pullObj] = _branchUrl.split('...'); + result.mergeBranch = mergeBranch; + if (pullObj.indexOf(':') > -1) { + // 存在源仓库 + const [pullOwner, pullBranch] = pullObj.split(':'); + result.pullOwner = pullOwner; + result.pullBranch = pullBranch; + } else { + result.pullBranch = pullObj; + } + } else { + result.pullBranch = _branchUrl; + } + } + return result; +} + +const Option = Select.Option; + +class CreateMerge extends Component { + constructor(props) { + super(props); + const { pullBranch, mergeBranch } = getBranchParams( + this.props.location.pathname + ); + this.state = { + data: undefined, + pullBranches: undefined, + mergeBranches: undefined, + mergeProjects: undefined, + merge: mergeBranch || 'master', + pull: pullBranch || 'master', + id: undefined, + // isFork: false, + projects_names: undefined, + isSpin: true, + showMessage: false, + merge_head: false, // 是否向fork后的源项目发起合并请求 + defaultMessage: '必须选择不同的分支', + project_id: undefined, // 当前项目的id,也即开始发送合并请求的源项目id + merge_project_user: undefined, + comparesData: undefined, //提交和文件的内容,保存compare接口返回的数据 + // 比较分支时的加载效果 + isCompareSpin: true, + // 是否是初次加载,用这个字段来控制提示组件和文件组件的显示、隐藏比直接用isCompareSpin交互友好些 + isFirstLoading: true, + }; + } + + componentDidMount = () => { + // 初始化时根据url获取目标仓库、分支,源仓库、分支; + // 再获取对应的仓库列表、分支列表 + // 再调用比较接口 + const branchParams = getBranchParams(this.props.location.pathname); + this.getMergeInfo(branchParams); + }; + + componentDidUpdate = (preProps) => { + // url变化触发时(切换源分支、切换目标仓库、切换目标分支;回退) + const oldPathname = preProps.location.pathname; + const newPathname = this.props.location.pathname; + if (oldPathname !== newPathname) { + const branchParams = getBranchParams(newPathname); + this.getMergeInfo(branchParams); + } + }; + + //获取新建合并请求数据 + getMergeInfo = (branchParams) => { + this.setState({ isSpin: true }); + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + branchParams; + const url = `/${pullOwner}/${projectId}/pulls/new.json`; + axios + .get(url) + .then((result) => { + if (result) { + // 如果url上的分支不存在,取默认值master + const noMergeBranch = + (result.data.branches || []).filter( + (branch) => branch.name === mergeBranch + ).length === 0; + const noPullBranch = + (result.data.branches || []).filter( + (branch) => branch.name === pullBranch + ).length === 0; + this.setState({ + // isFork: result.data.is_fork, + projects_names: result.data.projects_names, + mergeProjects: result.data.merge_projects, + pullBranches: result.data.branches, + mergeBranches: result.data.branches, + project_id: result.data.project_id, + id: result.data.id, + merge: mergeBranch, + pull: pullBranch, + }); + + //判断源分支是否存在 + if(noPullBranch){ + this.setState({ + showMessage: true, + defaultMessage:'源分支不存在', + isCompareSpin: false, + }); + }else{ + if(pullOwner === mergeOwner){ + if (!noMergeBranch) { + this.compareProject(true, branchParams); + } else { + this.setState({ + showMessage: true, + defaultMessage:'目标分支不存在', + isCompareSpin: false, + }); + } + }else{ + this.getBranchList(branchParams); + } + } + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + console.log(error); + }); + }; + + // compare接口,获取分支对比信息 + compareProject = (sameProject, branchParams) => { + // const { project } = this.props; + // const { owner, projectsId } = this.props.match.params; + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + branchParams; + + let url = `/${mergeOwner}/${projectId}/compare`; + if (sameProject) { + url += `/${pullBranch}...${mergeBranch}.json`; + } else { + url += `/${mergeBranch}...${pullOwner}/${projectId}:${pullBranch}.json`; + } + this.setState({ isSpin: false, isCompareSpin: true }); + axios + .get(url) + .then((result) => { + if (result) { + if (result.data.status === 0) { + this.setState({ + showMessage: false, + }); + } else { + this.setState({ + showMessage: true, + defaultMessage: result.data.message, + }); + } + this.setState({ + comparesData: result.data, + }); + } + this.setState({ + isFirstLoading: false, + isSpin: false, + isCompareSpin: false, + }); + }) + .catch((error) => { + this.setState({ isSpin: false, isCompareSpin: false }); + }); + }; + + // 根据所有者、仓库名,获取分支列表,目前仅涉及目标仓库分支查询 + getBranchList = (branchParams) => { + const { mergeOwner, projectId, mergeBranch } = branchParams; + this.setState({ isSpin: true }); + const url = `/${mergeOwner}/${projectId}/pulls/get_branches.json`; + axios + .get(url) + .then((result) => { + if (result) { + const noMergeBranch = + (result.data || []).filter((branch) => branch.name === mergeBranch) + .length === 0; + this.setState({ + mergeBranches: result.data, + showMessage: noMergeBranch, + defaultMessage: '目标分支不存在', + isCompareSpin: false, + }); + !noMergeBranch && this.compareProject(false, branchParams); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + console.log(error); + }); + }; + + // 切换分支事件 + selectBrach = (type, value) => { + const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = + getBranchParams(this.props.location.pathname); + let _url = `/${mergeOwner}/${projectId}/compare/`; + // type为pull时,pullBranch取value,否则取原有值 + // type为pull时,mergeBranch取原有值,否则取value + let _pullBranch = type === 'pull' ? value : pullBranch; + let _mergeBranch = type === 'pull' ? mergeBranch : value; + if (pullOwner === mergeOwner) { + // 如果仓库相同, compare/目标分支...源分支 + _url += `${_mergeBranch}...${_pullBranch}`; + } else { + // 如果仓库不同, compare/目标分支...源分支 + _url += `${_mergeBranch}...${pullOwner}:${_pullBranch}`; + } + this.props.history.push(_url); + }; + + // 切换仓库响应事件,目前仅目标分支可切换仓库 + selectProjectName = (value) => { + const { projects_names, id } = this.state; + const { pullOwner, pullBranch } = getBranchParams( + this.props.location.pathname + ); + let arr = + projects_names && projects_names.filter((item) => item.id === value); + let identifier = arr && arr[0].project_id; + let login = arr && arr[0].project_user_login; + // 目标仓库与源仓库不是一个仓库 + let is_fork = parseInt(value, 10) !== parseInt(id, 10); + this.setState({ + isSpin: true, + // merge_head: is_fork, + data: { + is_original: is_fork, + fork_project_id: is_fork ? id : '', + merge_user_login: is_fork + ? projects_names[0].project_user_login + : undefined, + }, + }); + if (login === pullOwner) { + // 如果切换后, 仓库与源仓库一致了 + this.props.history.push( + `/${login}/${identifier}/compare/master...${pullBranch}` + ); + } else { + this.props.history.push( + `/${login}/${identifier}/compare/master...${pullOwner}:${pullBranch}` + ); + } + // this.newMergelist(login, identifier); + }; + + // 渲染分支列表 + renderBrances = (list) => { + if (list && list.length > 0) { + return list.map((item, key) => { + return ( + <Option key={key + 1} value={item.name}> + {item.name} + </Option> + ); + }); + } + }; + + // 渲染项目列表 + renderProjectNames = (list) => { + if (list && list.length > 0) { + return list.map((item, key) => { + return ( + <Option key={key + 1} value={item.id}> + {item.project_name} + </Option> + ); + }); + } + }; + + // 渲染html内容 + withHtml = (html) => { + return <div dangerouslySetInnerHTML={{ __html: html }}></div>; + }; + + render() { + const { + data, + pullBranches, + mergeBranches, + mergeProjects, + pull, + merge, + isSpin, + isCompareSpin, + isFirstLoading, + showMessage, + defaultMessage, + projects_names, + id, + comparesData, + } = this.state; + + let { project } = this.props; + + return ( + <div> + <Spin spinning={isSpin || isCompareSpin}> + <div className="main"> + <div className="merge-header width100 inline-block"> + <div className="width40 pull-left"> + <div className="color-grey-3 mb10 fwb">源分支:</div> + <Input.Group compact className="display-flex"> + <Select + value={id} + className="hide-1 task-hide flex1" + disabled + > + {this.renderProjectNames(projects_names)} + </Select> + <Select + value={pull} + onSelect={(e) => this.selectBrach('pull', e)} + showSearch + className="merge-flex1 flex1 matchwidth" + dropdownMatchSelectWidth={false} + dropdownClassName="overlihide" + > + {this.renderBrances(pullBranches)} + </Select> + </Input.Group> + </div> + <div className="width10 pull-left text-center mt25"> + <i + className={'iconfont icon-youjiang color-grey-c font-32'} + ></i> + </div> + <div className="width40 pull-left"> + <div> + <div className="color-grey-3 mb10 fwb">目标分支:</div> + <Input.Group compact className="display-flex"> + <Select + value={project && project.id} + className="hide-1 task-hide flex1" + onSelect={(e) => this.selectProjectName(e)} + > + {this.renderProjectNames(mergeProjects)} + </Select> + <Select + value={merge} + onSelect={(e) => this.selectBrach('merge', e)} + showSearch + className="merge-flex1 flex1 matchwidth" + dropdownMatchSelectWidth={false} + dropdownClassName="overlihide" + > + {this.renderBrances(mergeBranches)} + </Select> + </Input.Group> + </div> + </div> + </div> + {/* 非加载状态且有提示 */} + {!isCompareSpin && showMessage && ( + <div className="mb20"> + <Alert + description={this.withHtml(defaultMessage)} + type="error" + /> + </div> + )} + {/* 非加载状态且可以提交 */} + {!isCompareSpin && !showMessage && ( + <MergeForm + {...this.props} + merge_type="new" + data={data} + merge={merge} + pull={pull} + files_count={ + comparesData && + comparesData.diff && + comparesData.diff.files_count + } + commits_count={comparesData && comparesData.commits_count} + ></MergeForm> + )} + </div> + {!isFirstLoading && ( + <MergeFooter + {...this.props} + merge={merge} + pull={pull} + comparesData={comparesData} + ></MergeFooter> + )} + </Spin> + </div> + ); + } +} + +export default CreateMerge; diff --git a/src/forge/Merge/Files.jsx b/src/forge/Merge/Files.jsx index 172038b2..b4cf5b48 100644 --- a/src/forge/Merge/Files.jsx +++ b/src/forge/Merge/Files.jsx @@ -1,7 +1,7 @@ -import React ,{useEffect,useRef,useState } from 'react'; +import React ,{useEffect,useState } from 'react'; import { truncateCommitId } from '../common/util'; import { AlignCenter , FlexAJ } from '../Component/layout'; -import { Button, Tooltip,Progress, Popover, Anchor } from 'antd'; +import { Tooltip,Progress } from 'antd'; import './merge.css'; import './Index.scss'; @@ -16,6 +16,10 @@ function Files({ data,history,owner,projectsId , parentsSha }){ } },[data]); + useEffect(()=>{ + document.addEventListener('click',()=>{setIsOpen(false)}) + }) + function showDown(flag,index,isBin){ if(!isBin){ var lists = files.concat(); @@ -48,7 +52,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){ <span className="cursor-pointer" data-clipboard-text={item.name}>{item.name}</span> </AlignCenter> <div className="see-file"> - <Tooltip placement="top" title={`${item.addition+item.deletion}处更改${item.addition + item.deletion > 0 && ":"}${item.addition>0?item.addition+"处添加":""}${item.addition>0 && item.deletion>0 ?"和":""}${item.deletion>0?item.deletion+"处删除":""}`}> + <Tooltip placement="top" title={`${item.addition+item.deletion}处更改${item.addition + item.deletion > 0 ? ":":""}${item.addition>0?item.addition+"处添加":""}${item.addition>0 && item.deletion>0 ?"和":""}${item.deletion>0?item.deletion+"处删除":""}`}> <Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.addition/(item.addition+item.deletion)*100} /> {item.addition >0 && <span className="color-green ml10">+{item.addition}</span>} {item.deletion >0 && <span className="color-red ml10">-{item.deletion}</span>} @@ -63,7 +67,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){ ) return( - <div> + <div onClick={(e)=>{e.nativeEvent.stopImmediatePropagation()}}> <AlignCenter className="color-grey-9" style={{position:'relative'}}> <div onClick={()=>{setIsOpen(!isOpen)}}> <i className={`iconfont mr5 ${isOpen? "font-18 icon-sanjiaoxing-down":"font-16 icon-triangle"}`}></i> @@ -96,7 +100,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){ </Tooltip> </AlignCenter> <div className="see-file"> - <Tooltip placement="top" title={`${item.addition + item.deletion}处更改${item.addition + item.deletion > 0 && ":"} ${item.addition > 0 ? item.addition + "处添加" : ""}${item.addition > 0 && item.deletion > 0 ? "和" : ""}${item.deletion > 0 ? item.deletion + "处删除" : ""}`}> + <Tooltip placement="top" title={`${item.addition + item.deletion}处更改${item.addition + item.deletion > 0 ? ":":""} ${item.addition > 0 ? item.addition + "处添加" : ""}${item.addition > 0 && item.deletion > 0 ? "和" : ""}${item.deletion > 0 ? item.deletion + "处删除" : ""}`}> <Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.addition/(item.addition+item.deletion)*100} /> <span className="ml10">{item.addition+item.deletion}处</span> </Tooltip> diff --git a/src/forge/Merge/MergeDetail.js b/src/forge/Merge/MergeDetail.js index 76c31ba0..11bd3858 100644 --- a/src/forge/Merge/MergeDetail.js +++ b/src/forge/Merge/MergeDetail.js @@ -228,7 +228,7 @@ class MergeDetail extends Component { <div> { data && data.issue.user_permission ? - <Link to={`/${owner}/${projectsId}/pulls/${mergeid}/updatemerge`} className="color-blue fr">编辑</Link> + <Link to={`/${owner}/${projectsId}/pulls/${mergeid}/edit`} className="color-blue fr">编辑</Link> : '' } </div> diff --git a/src/forge/Merge/MergeItem.js b/src/forge/Merge/MergeItem.js index 884c50fe..61477c14 100644 --- a/src/forge/Merge/MergeItem.js +++ b/src/forge/Merge/MergeItem.js @@ -62,9 +62,9 @@ class MergeItem extends Component { <p className="mb15 df" style={{ alignItems: "center" }}> <i className={`iconfont icon-hebingqingqiu1 font-14 mr3 i_${status}`}></i> <Link - to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/Messagecount`} + to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}`} className="hide-1 font-15 color-grey-3 fwb lineh-30 mr10" - style={{ maxWidth: "300px" }} + style={{ maxWidth: "600px" }} > {item.name} </Link> @@ -110,7 +110,7 @@ class MergeItem extends Component { <Tag className="pr-branch-tag"> <Link 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 hide-1 ver-middle" + className="maxW200px task-hide ver-middle" style={{maxWidth:"200px"}} > {item.is_original ? item.fork_project_user @@ -134,7 +134,7 @@ class MergeItem extends Component { <Tag className="pr-branch-tag"> <Link to={`/${owner}/${projectsId}/tree/${turnbar(item.pull_request_base)}`} - className="maxW200px hide-1 ver-middle" + 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} @@ -175,7 +175,7 @@ class MergeItem extends Component { {item.journals_count ? ( <Link className="mr5 color-grey-8" - to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/Messagecount`} + to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}`} > <i className="iconfont icon-huifu1 font-15 mr5 ver-middle"></i> {item.journals_count} @@ -196,7 +196,7 @@ class MergeItem extends Component { > <div className="grid-item mr15 color-grey-9"> <Link - to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/updatemerge`} + to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/edit`} className="color-grey-9" > <i className="iconfont icon-bianji3 font-14 mr5"></i> diff --git a/src/forge/Merge/MergeLinkFooter.jsx b/src/forge/Merge/MergeLinkFooter.jsx new file mode 100644 index 00000000..2c9aa481 --- /dev/null +++ b/src/forge/Merge/MergeLinkFooter.jsx @@ -0,0 +1,210 @@ +import React, { Component } from 'react'; +import { Tabs, Spin } from 'antd'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; + +import Commits from './Commits'; +import Comments from '../comments/comments'; +import Files from './Files'; + +import '../Order/order.css'; +import './merge.css'; + +const { TabPane } = Tabs; + +class MergeFooter extends Component { + constructor(props) { + super(props); + this.state = { + commitsData: [], + filesData: undefined, + isSpin: false, + activeKey: '1', + commitCount: 0, + filesCount: 0, + // 总评论数量,包含回复 + commentsTotalCount: 0, + }; + } + componentDidMount() { + this.Init(); + // 为父组件绑定当前,以方便调用方法 + this.props.bindFootRef && this.props.bindFootRef(this); + } + + componentDidUpdate(prevProps) { + // 解决切换tab后浏览器回退不刷新的问题、点击tab后url变化但tab未切换的问题 + const newPathname = this.props.location.pathname; + const prevPathname = prevProps.location.pathname; + if (newPathname !== prevPathname) { + this.Init(true); + } + } + + Init = (isTabChange) => { + const { data, location, match } = this.props; + const { pathname } = location; + const { projectsId, owner, mergeId } = match.params; + + let activeKey = '1'; + if (pathname.indexOf('commits') > -1) { + activeKey = '2'; + this.getCommit(owner, projectsId, mergeId); + } else if (pathname.indexOf('files') > -1) { + activeKey = '3'; + this.getFile(owner, projectsId, mergeId); + } + if (isTabChange && activeKey === '1') { + this.refreshComment(); + } + this.setState({ + activeKey: activeKey, + commitCount: data && data.commits_count, + filesCount: data && data.files_count, + }); + }; + + bindCommentRef = (commentRef) => { + this.childComment = commentRef; + } + + refreshComment = () => { + this.childComment && this.childComment.getjournalslist(); + } + + getCommit = (owner, projectsId, mergeId) => { + this.setState({ isSpin: true }); + const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`; + axios + .get(url) + .then((result) => { + if (result) { + this.setState({ + commitsData: result.data.commits, + commitCount: result.data.commits_count, + }); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + }); + }; + + getFile = (owner, projectsId, mergeId) => { + this.setState({ isSpin: true }); + const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`; + axios + .get(url) + .then((result) => { + if (result) { + this.setState({ + filesData: result.data, + filesCount: result.data.files_count, + }); + } + this.setState({ isSpin: false }); + }) + .catch((error) => { + this.setState({ isSpin: false }); + }); + }; + + render() { + const { projectsId, owner, mergeId } = this.props.match.params; + + const { order_id, data = {} } = this.props; + const { + isSpin, + activeKey, + filesCount, + commitCount, + filesData, + commitsData = [], + } = this.state; + + // 评论数量优先取Comment组件中列表接口返回的,其次取合并请求详情接口中的,都没有取默认值0 + const commentsTotalCount = parseInt( + this.state.commentsTotalCount || data.comments_total_count || 0, + 10 + ); + + return ( + <div className="main mergeRequest" style={{ paddingTop: '0px' }}> + <Spin spinning={isSpin}> + <Tabs + activeKey={activeKey} + className="custom-commit-tabs" + animated={false} + > + <TabPane + tab={ + <Link to={`/${owner}/${projectsId}/pulls/${mergeId}`}> + <span className="font-16">评论</span> + {commentsTotalCount > 0 && ( + <span className="tabNum">{commentsTotalCount}</span> + )} + </Link> + } + key="1" + > + <Comments + order_id={order_id} + showNotification={this.props.showNotification} + only_show_content={true} + updateCommentsNum={(commentsCount) => { + this.setState({ commentsTotalCount: commentsCount || 0 }); + }} + {...this.props} + bindCommentRef={this.bindCommentRef} + /> + </TabPane> + {commitCount > 0 && ( + <TabPane + tab={ + <Link to={`/${owner}/${projectsId}/pulls/${mergeId}/commits`}> + <span className="font-16">提交</span> + {commitCount > 0 && ( + <span className="tabNum">{commitCount}</span> + )} + </Link> + } + key="2" + > + {commitsData.length > 0 && ( + <Commits + {...this.props} + commits={commitsData} + projectsId={projectsId} + owner={owner} + ></Commits> + )} + </TabPane> + )} + {filesCount > 0 && ( + <TabPane + tab={ + <Link to={`/${owner}/${projectsId}/pulls/${mergeId}/files`}> + <span className="font-16">文件</span> + {filesCount > 0 && ( + <span className="tabNum">{filesCount}</span> + )} + </Link> + } + key="3" + > + <Files + {...this.props} + data={filesData} + projectsId={projectsId} + owner={owner} + /> + </TabPane> + )} + </Tabs> + </Spin> + </div> + ); + } +} +export default MergeFooter; diff --git a/src/forge/Merge/MessageCount.js b/src/forge/Merge/MessageCount.js index 8f78c093..6d01e8a3 100644 --- a/src/forge/Merge/MessageCount.js +++ b/src/forge/Merge/MessageCount.js @@ -1,7 +1,6 @@ import React, { Component } from "react"; import { Tabs } from 'antd'; import { Link } from "react-router-dom"; -import { AlignCenter } from '../Component/layout'; import axios from "axios"; import { getImageUrl } from "educoder"; import { @@ -11,7 +10,6 @@ import { Dropdown, Icon, Menu, - Select, Tag, Button, Alert, @@ -19,9 +17,8 @@ import { import "./merge.css"; import RenderHtml from "../../components/render-html"; import "../Order/order.css"; -import MergeFooter from "./merge_footer"; +import MergeLinkFooter from "./MergeLinkFooter"; -const Option = Select.Option; const TextArea = Input.TextArea; function turnbar(str){ @@ -61,6 +58,11 @@ class MessageCount extends Component { // this.clickBody(); }; + + bindFootRef = (footRef) => { + this.footRef = footRef; + } + clickBody=()=>{ document.body.addEventListener('click', e => { let name = e.target.className; @@ -150,6 +152,8 @@ class MessageCount extends Component { }); const { getDetail } = this.props; getDetail && getDetail(); + // 调用子组件的方法刷新评论列表 + this.footRef && this.footRef.refreshComment(); } else { this.setState({ SpinMerge: false }); } @@ -283,13 +287,13 @@ class MessageCount extends Component { conflict_files && conflict_files.length>0 && <div> <p className="mt10 font-16 pt10" style={{borderTop:"1px solid #f9d7d5"}}>如下文件有代码冲突:</p> - <p> + <div> { conflict_files.map((i,k)=>{ - return <p>{i}</p> + return <p key={k}>{i}</p> }) } - </p> + </div> </div> } </div> @@ -336,11 +340,11 @@ class MessageCount extends Component { <div> <div className="main"> <div> - <div className="grid-item-top pb20 border-1f"> - <div> + <div className="pb20 border-1f df"> + <div className="flex1"> <div className="ver-middle"> <span className="mr10 ver-middle"> - <span className="font-18 fwb"> + <span className="font-18 fwb" style={{wordBreak:"break-all"}}> {data.issue.subject} </span> </span> @@ -362,7 +366,7 @@ class MessageCount extends Component { <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)}`} - className="ver-middle" + 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} </Link> @@ -377,7 +381,7 @@ class MessageCount extends Component { <Tag className="pr-branch-tag"> <Link to={`/${owner}/${projectsId}/tree/${data.pull_request.base}`} - className="ver-middle" + 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} </Link> @@ -441,14 +445,13 @@ class MessageCount extends Component { </span> </div> </div> - <div className="ml10"> - <div className="mt15 text-right" style={{display:"flex",justifyContent:"flex-end"}}> + <div className="ml10 text-right"> {operate && ( <Button type="green" ghost className="ml20" - onClick={()=>{this.props.history.push(`/${owner}/${projectsId}/pulls/${mergeId}/UpdateMerge`);}} + onClick={()=>{this.props.history.push(`/${owner}/${projectsId}/pulls/${mergeId}/edit`);}} > 编辑 </Button> @@ -465,7 +468,6 @@ class MessageCount extends Component { </Button> )} </div> - </div> </div> { data.issue.description ? @@ -543,7 +545,7 @@ class MessageCount extends Component { onChange={this.changbodypr} /> </div> - <p + <div className="clearfix mt15" style={{ display: this.state.buttonshow }} > @@ -558,19 +560,19 @@ class MessageCount extends Component { 取消 </Button> </Spin> - </p> + </div> </div> </Spin> )} </div> </div> - <MergeFooter - footer_type={true} + <MergeLinkFooter order_id={data && data.issue.id} {...this.props} {...this.state} - ></MergeFooter> + bindFootRef={this.bindFootRef} + ></MergeLinkFooter> </div> ) : ( "" diff --git a/src/forge/Merge/NewMerge.js b/src/forge/Merge/NewMerge.js index 8913b0d1..900dea82 100644 --- a/src/forge/Merge/NewMerge.js +++ b/src/forge/Merge/NewMerge.js @@ -6,6 +6,11 @@ import "./merge.css"; import MergeForm from "./merge_form"; import MergeFooter from "./merge_footer"; const Option = Select.Option; +/** + * 此文件已废弃,新文件为CreateMerge.js + * 2021.10.12 + */ + class NewMerge extends Component { constructor(props) { super(props); @@ -194,7 +199,7 @@ class NewMerge extends Component { // this.ischeckmerge(); let { id ,merge , pull } = this.state; if(type==="pull"){ - this.props.history.push(`/${owner}/${projectsId}/pulls/new/${pull}`) + this.props.history.push(`/${owner}/${projectsId}/compare/${pull}`) this.compareProject(id,value,merge); }else{ this.compareProject(id,pull,value); @@ -216,7 +221,7 @@ class NewMerge extends Component { merge_user_login: is_fork_id ? projects_names[0].project_user_login : undefined } }) - this.props.history.push(`/${login}/${identifier}/pulls/new`); + this.props.history.push(`/${login}/${identifier}/compare`); this.newMergelist(login,identifier); }; diff --git a/src/forge/Merge/UpdateMerge.js b/src/forge/Merge/UpdateMerge.js index 8ed8034a..8918db6e 100644 --- a/src/forge/Merge/UpdateMerge.js +++ b/src/forge/Merge/UpdateMerge.js @@ -64,12 +64,12 @@ class UpdateMerge extends Component { <div className="color-grey-3 mb10 fwb">源分支:</div> <Input.Group compact className="display-flex"> - <Button className="merge-header-button maxW50 hide-1 task-hide"> + <Button className="merge-header-button flex1 maxW50 hide-1 task-hide" disabled> {data.is_original ? `${data.fork_project_user_name}/${data.fork_project_identifier}` : `${data.project_author}/${data.project_name}`} </Button> <Select defaultValue={data.is_original ? `${data.fork_project_user}:${pull}` : `${pull}`} - className="minW50 merge-flex1" + className="minW50 merge-flex1 flex1 matchwidth" disabled ></Select>{" "} </Input.Group>{" "} @@ -83,12 +83,12 @@ class UpdateMerge extends Component { <div> <div className="color-grey-3 mb10 fwb"> 目标分支 : </div>{" "} <Input.Group compact className="display-flex"> - <Button className="merge-header-button maxW50 hide-1 task-hide"> + <Button className="merge-header-button flex1 maxW50 hide-1 task-hide" disabled> {`${data.project_author}/${data.project_name}`} </Button> <Select defaultValue={data.is_original ? `${data.project_login}:${merge}` : `${merge}`} - className="minW50 merge-flex1" + className="minW50 merge-flex1 flex1 matchwidth" disabled ></Select>{" "} </Input.Group>{" "} diff --git a/src/forge/Merge/merge.css b/src/forge/Merge/merge.css index d5ce68d1..d61543b4 100644 --- a/src/forge/Merge/merge.css +++ b/src/forge/Merge/merge.css @@ -40,6 +40,7 @@ form .ant-cascader-picker, form .ant-select { } .merge-header-button{ background:rgba(241,248,255,1); + text-align: left; } .width70{ width:70%; @@ -211,5 +212,14 @@ form .ant-cascader-picker, form .ant-select { } .mergeRequest .folders{ - width: 72rem; - } \ No newline at end of file + /* width: 72rem; */ + width: 100%; + } + + .matchwidth .ant-select-selection__rendered{ + width: 200px; + } + .overlihide li{ + max-width: 450px; + + } \ No newline at end of file diff --git a/src/forge/Merge/merge.js b/src/forge/Merge/merge.js index 399c9bd9..8a0eaede 100644 --- a/src/forge/Merge/merge.js +++ b/src/forge/Merge/merge.js @@ -213,7 +213,7 @@ class merge extends Component { checkOperation() { const { projectsId,owner } = this.props.match.params; - this.props.history.push(`/${owner}/${projectsId}/pulls/new`); + this.props.history.push(`/${owner}/${projectsId}/compare/master...master`); } render() { const { projectsId , owner } = this.props.match.params; diff --git a/src/forge/Merge/merge_footer.js b/src/forge/Merge/merge_footer.js index 5e3a7556..868e736f 100644 --- a/src/forge/Merge/merge_footer.js +++ b/src/forge/Merge/merge_footer.js @@ -1,169 +1,84 @@ -import React, { Component } from "react"; -import { Tabs, Spin } from "antd"; -import "../Order/order.css"; -import "./merge.css"; -import Commits from "./Commits"; -import Comments from "../comments/comments"; -import Files from "./Files"; -import axios from 'axios'; +import React, { Component } from 'react'; +import { Tabs } from 'antd'; +import Commits from './Commits'; +import Files from './Files'; + +import '../Order/order.css'; +import './merge.css'; + const { TabPane } = Tabs; class MergeFooter extends Component { - constructor(props){ + constructor(props) { super(props); - this.state={ - pageData:undefined, - commitsData:undefined, - filesData:undefined, - isSpin:false, - activeKey:"1", - commitCount:0, - filesCount:0 - } - } - componentDidMount=()=>{ - const { footer_type ,data } = this.props; - if(footer_type){ - const { projectsId , owner , mergeId } = this.props.match.params; - this.getCommit(owner,projectsId,mergeId); - this.getFile(owner,projectsId,mergeId); - } - this.setState({ - activeKey:footer_type ? "1" : "2", - commitCount:data && data.commits_count, - filesCount:data && data.files_count - }) - } - componentDidUpdate=(prevProps)=>{ - const { comparesData } = this.props; - const { footer_type } = this.props; - if(footer_type){ - const { data } = this.props; - if(data !== prevProps.data){ - this.setState({ - commitCount:data && data.commits_count, - filesCount:data && data.files_count - }) - } - } - if(comparesData !== prevProps.comparesData){ - this.setState({ - activeKey:footer_type ? "1" : "2" - }) - this.changeTab(footer_type ? "1" : "2"); - } + this.state = { + activeKey: '1', + }; } - changeTab=(index)=>{ + changeTab = (index) => { this.setState({ - isSpin:true - }) - this.setState({ - activeKey:index - }) - const { footer_type , comparesData } = this.props; - const { projectsId , owner , mergeId } = this.props.match.params; - - if(footer_type){ - if(index === "2"){ - this.getCommit(owner,projectsId,mergeId); - }else if(index === "3"){ - this.getFile(owner,projectsId,mergeId); - }else{ - this.setState({ - isSpin:false - }) - } - }else{ - this.setState({ - commitsData:comparesData.commits, - filesData:comparesData.diff, - commitCount:comparesData.commits_count, - filesCount:comparesData.diff && comparesData.diff.files_count, - isSpin:false - }) - } - } - - getCommit =(owner,projectsId,mergeId)=>{ - const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`; - axios.get(url).then(result=>{ - if(result){ - this.setState({ - commitsData:result.data.commits, - isSpin:false, - commitCount:result.data.commits_count - }) - } - }).catch(error=>{}) - } - - getFile =(owner,projectsId,mergeId)=>{ - const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`; - axios.get(url).then(result=>{ - if(result){ - this.setState({ - filesData:result.data, - isSpin:false, - filesCount:result.data.files_count, - }) - } - }).catch(error=>{}) - } + activeKey: index, + }); + }; render() { - const { projectsId , owner } = this.props.match.params; + const { projectsId, owner } = this.props.match.params; + const { comparesData = {} } = this.props; + const { commits, diff, commits_count } = comparesData; + const { activeKey } = this.state; - const { footer_type, order_id, data , comparesData } = this.props; - let { isSpin , activeKey , filesCount, commitCount , filesData , commitsData } = this.state; - - return ( - !footer_type && !comparesData || (comparesData && ((comparesData.commits && comparesData.commits.length===0)||(comparesData && !comparesData.diff)) )?"": - <div className="main mergeRequest" style={{paddingTop:"0px"}}> - <Spin spinning={isSpin}> - <Tabs - activeKey={activeKey} - className="custom-commit-tabs" - animated={false} - onChange={this.changeTab} - > - { - footer_type && - <TabPane - tab={ - <span><span className="font-16">评论</span> - {data && parseInt(data.comments_count) > 0 && <span className="tabNum">{data.comments_count}</span>} - </span> - } key="1"> - <Comments - order_id={order_id} - showNotification={this.props.showNotification} - only_show_content={true} - {...this.props} - /> - </TabPane> - } - { - commitsData && commitsData.length > 0 && - <TabPane tab={<span><span className="font-16">提交</span> - {commitCount > 0 && <span className="tabNum">{commitCount}</span>} - </span>} key="2"> - <Commits {...this.props} commits={commitsData} projectsId={projectsId} owner={owner}></Commits> - </TabPane> - } - { - filesData && filesData.files && filesData.files.length>0 && - <TabPane tab={ - <span><span className="font-16">文件</span> - {filesCount > 0 && <span className="tabNum">{filesCount}</span>} - </span> - } key="3"> - <Files {...this.props} data={filesData} projectsId={projectsId} owner={owner}/> - </TabPane> - } - - </Tabs> - </Spin> + return (commits && commits.length === 0) || !diff ? ( + '' + ) : ( + <div className="main mergeRequest" style={{ paddingTop: '0px' }}> + <Tabs + activeKey={activeKey} + className="custom-commit-tabs" + animated={false} + onChange={this.changeTab} + > + {commits && commits.length > 0 && ( + <TabPane + tab={ + <span> + <span className="font-16">提交</span> + {commits_count > 0 && ( + <span className="tabNum">{commits_count}</span> + )} + </span> + } + key="1" + > + <Commits + {...this.props} + commits={commits} + projectsId={projectsId} + owner={owner} + ></Commits> + </TabPane> + )} + {diff && diff.files && diff.files.length > 0 && ( + <TabPane + tab={ + <span> + <span className="font-16">文件</span> + {diff.files_count > 0 && ( + <span className="tabNum">{diff.files_count}</span> + )} + </span> + } + key="3" + > + <Files + {...this.props} + data={diff} + projectsId={projectsId} + owner={owner} + /> + </TabPane> + )} + </Tabs> </div> ); } diff --git a/src/forge/Merge/merge_form.js b/src/forge/Merge/merge_form.js index 5df9794b..e6378139 100644 --- a/src/forge/Merge/merge_form.js +++ b/src/forge/Merge/merge_form.js @@ -165,7 +165,8 @@ class MergeForm extends Component { this.setState({ isSpin: false, }); - this.props.history.push(`/${owner}/${projectsId}/pulls`); + const { pull_request_id } = result.data; + this.props.history.push(`/${owner}/${projectsId}/pulls/${pull_request_id}`); const { getDetail } = this.props; getDetail && getDetail(); } else { @@ -195,7 +196,7 @@ class MergeForm extends Component { isSpin: false, }); this.props.history.push( - `/${owner}/${projectsId}/pulls/${mergeId}/Messagecount` + `/${owner}/${projectsId}/pulls/${mergeId}` ); } else { this.setState({ @@ -264,7 +265,7 @@ class MergeForm extends Component { }, ], initialValue: title, - })(<Input placeholder="标题" maxLength={50} />)} + })(<Input placeholder="标题" maxLength={200} />)} </Form.Item> <MDEditor placeholder={"请输入合并请求的描述..."} @@ -287,7 +288,7 @@ class MergeForm extends Component { type="default" className="ml30" onClick={()=>{ - this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}/detail`) + this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}`) }} > <span className="plr10">取消</span> diff --git a/src/forge/Merge/no_data.js b/src/forge/Merge/no_data.js index d5584797..d5e4ca89 100644 --- a/src/forge/Merge/no_data.js +++ b/src/forge/Merge/no_data.js @@ -12,7 +12,7 @@ class Nodata extends Component{ <h3>欢迎使用合并请求!</h3> <div className="color-grey-8"> - 合并请求可以帮助您与他人协作编写代码。在使用之前,请先创建一个 <Link className="color-blue" to={`/${owner}/${projectsId}/pulls/new`}>合并请求</Link> + 合并请求可以帮助您与他人协作编写代码。在使用之前,请先创建一个 <Link className="color-blue" to={`/${owner}/${projectsId}/compare/master...master`}>合并请求</Link> </div> </div> </div> diff --git a/src/forge/New/Index.js b/src/forge/New/Index.js index c6eb2c3d..e9aa1f95 100644 --- a/src/forge/New/Index.js +++ b/src/forge/New/Index.js @@ -285,12 +285,16 @@ class Index extends Component { if(value.indexOf("/") > -1){ let arr = value.split("/"); let first = arr[arr.length-1]; - if(first.indexOf(".git") > -1){ + if(first.indexOf(".") > -1){ let second = first.split('.')[0]; if(!second)return; this.props.form.setFieldsValue({ repository_name:second }) + }else{ + this.props.form.setFieldsValue({ + repository_name:first + }) } } } @@ -351,7 +355,7 @@ class Index extends Component { required: true, message: '请填写镜像版本库地址' }], })( - <Input placeholder="请输入需要导入到本项目的仓库地址" onChange={this.ChangeAddr} /> + <Input placeholder="请输入需要导入到本项目的仓库地址" onBlur={this.ChangeAddr} /> )} </Form.Item> <p className="formTip color-orange">示例:https://github.com/facebook/reack.git</p> diff --git a/src/forge/Newfile/UserSubmitComponent.js b/src/forge/Newfile/UserSubmitComponent.js index 84ae9b33..aa4bba6e 100644 --- a/src/forge/Newfile/UserSubmitComponent.js +++ b/src/forge/Newfile/UserSubmitComponent.js @@ -83,8 +83,7 @@ class UserSubmitComponent extends Component { if (result.data && result.data.name) { this.props.showNotification("文件新建成功!"); if(submitType === "1"){ - const { getTopCount , getDetail } = this.props; - getTopCount && getTopCount(values.branchname); + const { getDetail } = this.props; getDetail && getDetail(); } let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`; diff --git a/src/forge/Newfile/m_editor.js b/src/forge/Newfile/m_editor.js index 242f7e0d..13d2ec46 100644 --- a/src/forge/Newfile/m_editor.js +++ b/src/forge/Newfile/m_editor.js @@ -1,5 +1,7 @@ import React, { Component } from "react"; import Editor from "react-monaco-editor"; +// import {UnControlled as CodeMirror} from 'react-codemirror2' + import UserSubmitComponent from "./UserSubmitComponent"; import "./index.css"; @@ -103,6 +105,17 @@ class m_editor extends Component { editorWillMount={this.editorWillMount} editorDidMount={handleEditorMount} /> + {/* <CodeMirror + value={editorValue} + options={{ + theme: 'monokai', + mode: 'JavaScript', + extraKeys: {"Ctrl": "autocomplete"},//ctrl可以弹出提示 + styleActiveLine: true, + lineNumbers: true, + readOnly:true + }} + /> */} </div> {!readOnly && ( <div className="editorBorderSubmitBox" style={{marginTop:"20px",padding:"20px"}}> diff --git a/src/forge/Order/Detail.js b/src/forge/Order/Detail.js index 19ef67c5..4d1b47cf 100644 --- a/src/forge/Order/Detail.js +++ b/src/forge/Order/Detail.js @@ -233,7 +233,7 @@ class Detail extends Component { : "合并请求"} 】 </span> - <span className="font-16 fwb">{data && data.subject}</span> + <span className="font-16 fwb" style={{wordBreak:"break-all"}}>{data && data.subject}</span> </span> {data && data.priority && ( diff --git a/src/forge/Order/order_form.js b/src/forge/Order/order_form.js index 0858ac52..3399a7fe 100644 --- a/src/forge/Order/order_form.js +++ b/src/forge/Order/order_form.js @@ -320,7 +320,7 @@ class order_form extends Component { message: "请填写易修标题", }, ] - })(<Input placeholder="标题" size="large" maxLength={80}/>)} + })(<Input placeholder="标题" size="large" maxLength={200}/>)} </Form.Item> <div className="quillContent"> <MDEditor diff --git a/src/forge/SecuritySetting/Index.jsx b/src/forge/SecuritySetting/Index.jsx index 8419fd31..d9fa1bfa 100644 --- a/src/forge/SecuritySetting/Index.jsx +++ b/src/forge/SecuritySetting/Index.jsx @@ -43,8 +43,9 @@ const PrivateLetter = Loadable({ }); function Index(props){ - const { current_user } = props; + const { current_user,mygetHelmetapi } = props; const { pathname } = props.location; + const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice; return( <div className="newMain clearfix whiteBack"> @@ -59,11 +60,11 @@ function Index(props){ <li>个人信息</li> <li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i><span className="text-shodow-bold">基本资料</span></Link></li> </ul> - <ul className="securityUl ul-border-buttom"> + {notice_url && <ul className="securityUl ul-border-buttom"> <li>消息通知</li> <li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi"></i><span className="text-shodow-bold">我的通知</span></Link></li> - {/* <li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li> */} - </ul> + <li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li> + </ul>} <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><span className="text-shodow-bold">SSH密钥</span></Link></li> @@ -72,18 +73,24 @@ function Index(props){ <LongWidth> <Gap> <Switch> - <Route - path="/settings/notice" - render={(p) => ( - <MyNoticeIndex {...props} {...p}/> - )} - ></Route> <Route path="/settings/notice/config" render={(p) => ( <NoticeManager {...props} {...p}/> )} ></Route> + <Route + path="/settings/notice/privateLetter" + render={(p)=>( + <PrivateLetter{...props} {...p}/> + )} + ></Route> + <Route + path="/settings/notice" + render={(p) => ( + <MyNoticeIndex {...props} {...p}/> + )} + ></Route> <Route path="/settings/SSH/new" render={(p) => ( @@ -102,12 +109,6 @@ function Index(props){ <SSHIndex {...props} {...p}/> )} ></Route> - <Route - path="/settings/notice/privateLetter" - render={(p)=>( - <PrivateLetter{...props} {...p}/> - )} - ></Route> </Switch> </Gap> </LongWidth> diff --git a/src/forge/SecuritySetting/notice/manager/Index.jsx b/src/forge/SecuritySetting/notice/manager/Index.jsx index ebe84bea..5ba82d60 100644 --- a/src/forge/SecuritySetting/notice/manager/Index.jsx +++ b/src/forge/SecuritySetting/notice/manager/Index.jsx @@ -1,9 +1,52 @@ -import { Button, Checkbox } from "antd"; -import React from "react"; - +import { Checkbox } from "antd"; +import React, { useEffect, useState } from "react"; +import axios from 'axios'; import './Index.scss'; function NoticeManager(props){ + const {current_user} = props; + + const [settingTypes, setSettingTypes] = useState(); + const [userNotification, setUserNotification] = useState(); + const [userEmail, setUserEmail] = useState(); + + function onChange(type,e,setting){ + let notification_body = userNotification; + let email_body = userEmail; + if(type){//站内信 + notification_body[setting] = e.target.checked; + }else{//邮件 + email_body[setting] = e.target.checked; + } + axios.post(`/users/${current_user.login}/template_message_settings/update_setting.json`,{ + setting:{ + notification_body:notification_body, + email_body:email_body + } + }).then(response=>{ + if(response && response.status === 0){ + getUserSettings(); + } + }) + } + + function getUserSettings(){ + axios.get(`/users/${current_user.login}/template_message_settings.json`).then((response)=>{ + if(response && response.status === 200 ){ + setUserEmail(response.data.email_body); + setUserNotification(response.data.notification_body); + } + }) + } + + useEffect(()=>{ + axios.get("/template_message_settings.json").then(response => { + if (response && response.status === 200) { + setSettingTypes(response.data.setting_types); + } + }) + getUserSettings(); + },[]) return( <div className="notice01"> @@ -12,87 +55,23 @@ function NoticeManager(props){ </div> <div> <span className="notice-manager-tip">您可以通过通知管理来选择接受通知的方式</span> - <div className="manager-cont-top"> - 我创建或负责的 - </div> - <div className="manager-cont"> - <div className="manager-cont-title">易修状态变更</div> - <Checkbox defaultChecked='true' disabled>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">易修截止日期到达最后一天</div> - <Checkbox defaultChecked='true' disabled>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">合并请求状态变更</div> - <Checkbox defaultChecked='true' disabled>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">易修有新的评论</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">合并请求有新的评论</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - - <div className="manager-cont-top"> - 我管理的仓库 - </div> - <div className="manager-cont"> - <div className="manager-cont-title">被关注</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">被点赞</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">被复刻</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">有新的里程碑</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - - <div className="manager-cont-top"> - 我关注的仓库 - </div> - <div className="manager-cont"> - <div className="manager-cont-title">被删除</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">被转移</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">有新的易修</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">有新的合并请求</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> - <div className="manager-cont"> - <div className="manager-cont-title">有新的版本发布</div> - <Checkbox defaultChecked='true'>站内信</Checkbox> - <Checkbox >邮件</Checkbox> - </div> + {settingTypes && userNotification && userEmail && settingTypes.map((item,key)=>{ + return( + item.type_name && <div key={key}> + <div className="manager-cont-top">{item.type_name}</div> + {item.settings.map((i, k) => { + const setting = item.type.substring(item.type.indexOf("::")+2)+"::"+i.key; + return ( + <div className="manager-cont" key={k}> + <div className="manager-cont-title">{i.name}</div> + <Checkbox disabled = {i.notification_disabled} defaultChecked={userNotification[setting]} onChange={(e)=>{onChange(true,e,setting)}}>站内信</Checkbox> + <Checkbox disabled = {i.email_disabled} defaultChecked={userEmail[setting]} onChange={(e)=>{onChange(false,e,setting)}}>邮件</Checkbox> + </div> + ) + })} + </div> + ) + })} </div> </div> ) diff --git a/src/forge/SecuritySetting/notice/myNotice/Index.jsx b/src/forge/SecuritySetting/notice/myNotice/Index.jsx index 2de2b8c3..22660691 100644 --- a/src/forge/SecuritySetting/notice/myNotice/Index.jsx +++ b/src/forge/SecuritySetting/notice/myNotice/Index.jsx @@ -9,10 +9,9 @@ import './Index.scss'; import '../manager/Index.scss' function MyNotice(props) { - let current_user = props.current_user; - let resetUserInfo = props.resetUserInfo; + let { current_user, resetUserInfo, location, mygetHelmetapi, history}= props; //消息悬停框选择tab - let popover = props.location.query && props.location.query.noticeType; + let popover = location && location.query && location.query.noticeType; let pageSize = 15; const [noticeType, setNoticeType] = useState(popover==="atme"?"2":"0");//消息类别tab栏选择 const [selectedNum, setSelectedNum] = useState(0);//@我批量删除选择消息条数 @@ -27,6 +26,18 @@ function MyNotice(props) { const [currentPage, setCurrentPage] = useState(1);//当前页数 const [onlyUnread, setOnlyUnread] = useState(); + //登录情况下,通过地址访问,直接跳转:/settings/profile + //未登录情况下,通过地址访问,直接跳转:/explore + useEffect(()=>{ + let notice = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice; + let login = current_user && current_user.login; + if(!login){ + history.push(`/explore`); + }else if(!notice){ + history.push(`/settings/profile`); + } + },[mygetHelmetapi]) + useEffect(()=>{ popover==="atme" ? setNoticeType("2"):setNoticeType("0"); },[popover]) @@ -46,7 +57,7 @@ function MyNotice(props) { limit: pageSize, page: currentPage, }; - axios.get(`/users/${current_user.login}/messages.json`, { + current_user && axios.get(`/users/${current_user.login}/messages.json`, { params: params, }).then((response) => { if(response && response.data){ @@ -59,7 +70,7 @@ function MyNotice(props) { } function readNotice(id){ - if(id){ + if(id && current_user){ const params = { type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "", ids:id, diff --git a/src/forge/Settings/Setting.js b/src/forge/Settings/Setting.js index b1af2f99..86383fc7 100644 --- a/src/forge/Settings/Setting.js +++ b/src/forge/Settings/Setting.js @@ -153,18 +153,22 @@ class Setting extends Component { name: values.project_name, description: values.project_description, private: private_check, + identifier:values.project_identifier, ...values, }).then((result) => { if (result) { this.props.showNotification(`仓库信息修改成功!`); - const { getDetail } = this.props; - getDetail && getDetail(); - this.setState({ - loading:false - }) + if(values.project_identifier !== projectsId){ + this.props.history.push(`/${owner}/${values.project_identifier}/settings`); + }else{ + const { getDetail } = this.props; + getDetail && getDetail(); + } } + this.setState({ + loading:false + }) }).catch((error) => { - console.log(error); this.setState({ loading:false }) @@ -263,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> @@ -283,6 +292,20 @@ class Setting extends Component { )} </Form.Item> </div> + <Form.Item + label={<span>项目标识 <span className="color-grey-9">(项目url标识部分,更改项目标识将导致原仓库地址失效)</span></span>} + > + {getFieldDecorator("project_identifier", { + rules: [ + { + required: true, + message: "请输入项目标识", + }, + ], + })( + <Input placeholder="项目标识请使用与项目相关的英文关键字" maxLength="100" /> + )} + </Form.Item> <Form.Item label="项目简介"> {getFieldDecorator("project_description", { rules: [], diff --git a/src/forge/Team/List.jsx b/src/forge/Team/List.jsx index ea23ab07..48a19c22 100644 --- a/src/forge/Team/List.jsx +++ b/src/forge/Team/List.jsx @@ -19,7 +19,6 @@ function List(props){ const [ search , setSearch ] = useState(undefined); const [ page , setPage ] = useState(1); const [ sortBy , setSortBy ] = useState("updated_on"); - const [ sortDirection , setSortDirection ] = useState("asc"); const OIdentifier = props.match.params.OIdentifier; const organizeDetail = props.organizeDetail; @@ -37,7 +36,7 @@ function List(props){ params:{ search,page,limit, sort_by:sortBy, - sort_direction:sortDirection + sort_direction:"desc" } }).then(result=>{ if(result && result.data){ diff --git a/src/forge/Team/ListItem.jsx b/src/forge/Team/ListItem.jsx index a908f80b..ab9d64f2 100644 --- a/src/forge/Team/ListItem.jsx +++ b/src/forge/Team/ListItem.jsx @@ -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}/> diff --git a/src/forge/comments/children_comments.js b/src/forge/comments/children_comments.js index 450a30f1..75330161 100644 --- a/src/forge/comments/children_comments.js +++ b/src/forge/comments/children_comments.js @@ -17,6 +17,7 @@ class children_comments extends Component { page: 1, journal_spin: false, search_count: 0, + isSpin: false, }; } @@ -71,6 +72,9 @@ class children_comments extends Component { .then((result) => { if (result) { this.getChildrenJournals(); + // 删除回复后,如果需手动调用父组件查询评论列表的接口,以保持角标(评论数量)的一致(合并请求页面), + // 否则直接查顶级评论列表,否则只查询当前子评论列表(易修页面) + this.props.refreshCommentList && this.props.refreshCommentList(); } }) .catch((error) => { @@ -80,9 +84,15 @@ class children_comments extends Component { // 翻页 ChangePage = (page) => { - this.state.page = page; - this.state.isSpin = true; - this.getChildrenJournals(); + // this.state.page = page; + // this.state.isSpin = true; + // 使用回调的写法,这样在getChildrenJournals中使用的就是最新的state + this.setState({ + page, + isSpin: true + }, () => { + this.getChildrenJournals(); + }); }; commentCtx = (v) => { @@ -183,7 +193,7 @@ class children_comments extends Component { size="large" loading={isSpin} dataSource={journalsdata.issue_journals} - renderItem={(item) => <List.Item>{this.renderList(item)}</List.Item>} + renderItem={(item) => <List.Item key={item.id}>{this.renderList(item)}</List.Item>} /> {this.Paginations()} </div> diff --git a/src/forge/comments/comments.js b/src/forge/comments/comments.js index c27bb12a..dca7581f 100644 --- a/src/forge/comments/comments.js +++ b/src/forge/comments/comments.js @@ -35,6 +35,8 @@ class comments extends Component { componentDidMount = () => { this.getjournalslist(); + // 给父组件绑定,以使父组件可以使用组件内方法,用于切换tab时重新请求评论列表、合并请求完之后重新请求评论列表 + this.props.bindCommentRef && this.props.bindCommentRef(this); }; //添加评论 @@ -151,6 +153,8 @@ class comments extends Component { isSpin: false, fileList: undefined, }); + const { updateCommentsNum } = this.props; + updateCommentsNum && updateCommentsNum(result.data.journals_total_count); } }) .catch((error) => { @@ -372,7 +376,7 @@ class comments extends Component { const renderList = (item) => { return ( - <div className="width100"> + <div className="width100" key={item.id}> <div className="pb5"> <Link to={`/${item && item.user_login}`} @@ -456,6 +460,7 @@ class comments extends Component { parent_id={item.id} onRef={this.onRef} children_comment_id={new_journal_id} + refreshCommentList={this.getjournalslist} {...this.props} ></ChildrenComments> </div> @@ -500,7 +505,7 @@ class comments extends Component { loading={isSpin} header="" dataSource={journalsdata.issue_journals} - renderItem={(item) => <List.Item>{renderList(item)}</List.Item>} + renderItem={(item) => <List.Item key={item.id}>{renderList(item)}</List.Item>} /> )} {this.Paginations()} @@ -558,7 +563,7 @@ class comments extends Component { header="" dataSource={journalsdata.issue_journals} renderItem={(item) => ( - <List.Item>{renderList(item)}</List.Item> + <List.Item key={item.id}>{renderList(item)}</List.Item> )} /> )} diff --git a/src/forge/css/index.scss b/src/forge/css/index.scss index 5926c734..a19619d7 100644 --- a/src/forge/css/index.scss +++ b/src/forge/css/index.scss @@ -104,6 +104,9 @@ ul,ol,dl{ white-space: normal; &:hover{ text-decoration: underline; + & .markdown-body{ + color: #466AFF; + } } } diff --git a/src/forge/users/Material/Base.jsx b/src/forge/users/Material/Base.jsx index f9707ab0..61a9d6f1 100644 --- a/src/forge/users/Material/Base.jsx +++ b/src/forge/users/Material/Base.jsx @@ -11,7 +11,6 @@ export default Form.create()( const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form; // const { username } = props && props.match && props.match.params; const { resetUserInfo , current_user } = props; - console.log(props); useEffect(()=>{ if(current_user && current_user.login){ diff --git a/src/modules/courses/css/Courses.css b/src/modules/courses/css/Courses.css index cf5f1783..0d550a5c 100644 --- a/src/modules/courses/css/Courses.css +++ b/src/modules/courses/css/Courses.css @@ -298,7 +298,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket { .CodeMirror-scroll { overflow: scroll !important; margin-bottom: -30px; - margin-right: -30px; + margin-right: -30px!important; padding-bottom: 30px; height: 100%; outline: none; diff --git a/src/modules/login/EducoderLogin.js b/src/modules/login/EducoderLogin.js index 9a552673..c687f5f5 100644 --- a/src/modules/login/EducoderLogin.js +++ b/src/modules/login/EducoderLogin.js @@ -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> :"" } diff --git a/src/modules/login/Trialapplication.js b/src/modules/login/Trialapplication.js index 5cb18166..4f23406e 100644 --- a/src/modules/login/Trialapplication.js +++ b/src/modules/login/Trialapplication.js @@ -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; diff --git a/src/modules/login/Trialapplicationysl.js b/src/modules/login/Trialapplicationysl.js index 25522e86..68c4a244 100644 --- a/src/modules/login/Trialapplicationysl.js +++ b/src/modules/login/Trialapplicationysl.js @@ -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; diff --git a/src/modules/tpm/TPMIndexHOC.js b/src/modules/tpm/TPMIndexHOC.js index b9215568..c20e75b1 100644 --- a/src/modules/tpm/TPMIndexHOC.js +++ b/src/modules/tpm/TPMIndexHOC.js @@ -7,6 +7,7 @@ import './TPMIndex.css'; import LoginDialog from '../login/LoginDialog'; import EducoderAccount from '../../forge/Component/EducoderAccount'; import ProfileModal from '../../forge/Component/ProfileModal/Index'; +import SystemNotice from '../../forge/Component/NoticeModal/SystemNotice'; export function TPMIndexHOC(WrappedComponent) { return class II extends React.Component { @@ -27,7 +28,7 @@ export function TPMIndexHOC(WrappedComponent) { giteaVisible:false, email:undefined, completeProfile:false, - showCP:false + showCP:false, } } @@ -214,9 +215,8 @@ export function TPMIndexHOC(WrappedComponent) { }) } - render() { - let { isRender , current_user , giteaVisible , email , completeProfile , showCP } = this.state; + let { isRender , current_user , giteaVisible , email , completeProfile , showCP , mygetHelmetapi } = this.state; const common = { showLoginDialog: this.showLoginDialog, checkIfLogin: this.checkIfLogin, @@ -225,6 +225,11 @@ export function TPMIndexHOC(WrappedComponent) { }; return ( <div className="indexHOC"> + <SystemNotice + system_notification={mygetHelmetapi && mygetHelmetapi.system_notification} + history={this.props.history} + login={current_user && current_user.login} + /> <EducoderAccount visible={giteaVisible} email={email} onOk={this.onOk}/> <ProfileModal visible={!completeProfile && showCP} diff --git a/src/modules/user/FindPasswordComponent.js b/src/modules/user/FindPasswordComponent.js index 85f0285a..c753c66e 100644 --- a/src/modules/user/FindPasswordComponent.js +++ b/src/modules/user/FindPasswordComponent.js @@ -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> { diff --git a/src/modules/user/LoginRegisterComponent.js b/src/modules/user/LoginRegisterComponent.js index db369458..f3258174 100644 --- a/src/modules/user/LoginRegisterComponent.js +++ b/src/modules/user/LoginRegisterComponent.js @@ -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> }