Merge pull request 'FIX 代码库二级页面升级改版' (#121) from feature_repo_second_page into pre_develop_dev

This commit is contained in:
jasder 2021-10-09 14:37:14 +08:00
commit 3be536d37a
65 changed files with 2173 additions and 1069 deletions

2
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

19
package-lock.json generated
View File

@ -3425,9 +3425,9 @@
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
},
"clipboard": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
@ -7286,7 +7286,8 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -7703,7 +7704,8 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -7759,6 +7761,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -7802,12 +7805,14 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"optional": true
}
}
},

View File

@ -22,7 +22,7 @@
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"classnames": "^2.2.5",
"clipboard": "^2.0.6",
"clipboard": "^2.0.8",
"code-prettify": "^0.1.0",
"codemirror": "^5.53.0",
"connected-react-router": "4.4.1",
@ -125,7 +125,7 @@
"scripts": {
"start": "node --max_old_space_size=15360 scripts/start.js",
"build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
"test-build": "NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
"test-build": "cross-env NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
"pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js",
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
"ana": "webpack-bundle-analyzer ./stats.json",

View File

@ -1520,7 +1520,15 @@ a.edu-txt-w80,
.font-16 {
font-size: 16px !important;
}
.weight400{
font-weight: 400;
}
.weight500{
font-weight: 500;
}
.weight{
font-weight: bold;
}
.font-17 {
font-size: 17px !important;
}
@ -1540,6 +1548,9 @@ a.edu-txt-w80,
.font-25 {
font-size: 25px !important;
}
.font-26 {
font-size: 26px !important;
}
.font-24 {
font-size: 24px !important;
@ -1561,6 +1572,9 @@ a.edu-txt-w80,
font-size: 36px !important;
}
.font-40 {
font-size: 40px !important;
}
.font-50 {
font-size: 50px !important;
}
@ -1746,6 +1760,14 @@ a.decoration {
margin-bottom: 10px;
}
.mb12 {
margin-bottom: 12px;
}
.mb13 {
margin-bottom: 13px;
}
.mb14 {
margin-bottom: 14px;
}
@ -1927,7 +1949,9 @@ a.decoration {
}
.mr20 {
margin-right: 20px;
margin-right: 10px;
margin-left: 10px;
float: left;
}
.mr25 {
@ -1935,7 +1959,7 @@ a.decoration {
}
.mr30 {
margin-right: 30px;
margin-right: 10px;
}
.mr35 {
@ -2460,7 +2484,7 @@ a:hover{
.color-grey-B3 {
color: #B3B3B3 !important;
}
`
.color-grey-B4 {
color: #B4B4B4 !important;
}

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1631773579834') format('woff2'),
url('iconfont.woff?t=1631773579834') format('woff'),
url('iconfont.ttf?t=1631773579834') format('truetype');
src: url('iconfont.woff2?t=1632964996877') format('woff2'),
url('iconfont.woff?t=1632964996877') format('woff'),
url('iconfont.ttf?t=1632964996877') format('truetype');
}
.iconfont {
@ -13,6 +13,30 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-wenjian7:before {
content: "\e8e0";
}
.icon-xiangyoujiantou:before {
content: "\e8de";
}
.icon-xiangzuojiantou:before {
content: "\e8df";
}
.icon-a-liulanicon2x:before {
content: "\e8dd";
}
.icon-wenjianicon:before {
content: "\e8dc";
}
.icon-a-yuanquan2x:before {
content: "\e8db";
}
.icon-xiangmubiaoqian:before {
content: "\e8da";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,48 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "24656750",
"name": "文件",
"font_class": "wenjian7",
"unicode": "e8e0",
"unicode_decimal": 59616
},
{
"icon_id": "630094",
"name": "向右箭头",
"font_class": "xiangyoujiantou",
"unicode": "e8de",
"unicode_decimal": 59614
},
{
"icon_id": "630095",
"name": "向左箭头",
"font_class": "xiangzuojiantou",
"unicode": "e8df",
"unicode_decimal": 59615
},
{
"icon_id": "24600282",
"name": "浏览icon@2x",
"font_class": "a-liulanicon2x",
"unicode": "e8dd",
"unicode_decimal": 59613
},
{
"icon_id": "24567893",
"name": "文件icon",
"font_class": "wenjianicon",
"unicode": "e8dc",
"unicode_decimal": 59612
},
{
"icon_id": "24527422",
"name": "圆圈@2x",
"font_class": "a-yuanquan2x",
"unicode": "e8db",
"unicode_decimal": 59611
},
{
"icon_id": "24378423",
"name": "项目标签",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -69,7 +69,7 @@ export function appendFileSizeToUploadFile(item) {
}
export function appendFileSizeToUploadFileAll(fileList) {
return fileList.map(item => {
if (item.name.indexOf(uploadNameSizeSeperator) == -1) {
if (item.name.indexOf(uploadNameSizeSeperator) === -1) {
return Object.assign({}, item, { name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}` })
}
return item

View File

@ -1,32 +1,60 @@
import React , { useState , useEffect } from 'react';
import { Popover , Dropdown , Input , Spin } from 'antd';
import React , { useState , useEffect , useRef } from 'react';
import { Dropdown} from 'antd';
import './branch.scss';
import { getBranch , getTag } from '../GetData/getData';
import SelectOverlay from './SelectOverlay';
import { findDOMNode } from 'react-dom';
export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true })=>{
const [ showValue , setShowValue ] = useState(branch);
const [ visible , setVisible ] = useState(false);
const refFa = useRef(null);
const refBox = useRef(null);
useEffect(() => {
document.addEventListener('click', clickMe , false);
}, [])
const clickMe = ({ target }) => {
//
const faComponent = findDOMNode(refFa.current);
const boxComponent = findDOMNode(refBox.current);
if (faComponent && boxComponent) {
const isChild = faComponent.contains(target);
const isBox = boxComponent.contains(target);
if(!isChild && !isBox){
setVisible(false);
}
}
}
useEffect(()=>{
setShowValue(branch);
},[branch])
function ChangeB(params) {
setVisible(false);
changeBranch(params);
}
const menu = (
<SelectOverlay
changeBranch={changeBranch}
tagflag={tagflag}
projectsId={projectsId}
owner={owner}
branchList={branchList}
<div ref={refFa}>
<SelectOverlay
changeBranch={ChangeB}
tagflag={tagflag}
projectsId={projectsId}
owner={owner}
branchList={branchList}
/>
</div>
);
return(
<Dropdown placement='bottomLeft' overlay={menu} overlayClassName="branch-tagBox-list" trigger={['click']} >
<div className="branch-tagBox">
<Dropdown placement='bottomLeft' visible={visible} overlay={menu} overlayClassName="branch-tagBox-list" trigger={['click']} >
<div className="branch-tagBox" ref={refBox} onClick={()=>setVisible(visible ? false : true)}>
{/* {nav === 0 ?"分支":"标签"} */}
<span className="color-grey-9 mr3 ml8"><i className="iconfont icon-fenzhi2 font-18"></i></span>
<span className="ant-dropdown-link task-hide" style={{fontWeight:"500"}}>
<span className="ant-dropdown-link task-hide" style={{fontWeight:"500",minWidth:"45px",maxWidth:"270px"}}>
{showValue}
</span>
<i className="showtag iconfont icon-sanjiaoxing-down font-15 color-grey-9 mr5 ml5 mt1" />

View File

@ -55,7 +55,7 @@ function SelectOverlay({ changeBranch , tagflag , branchList , projectsId , owne
<div className="padding15" style={{paddingBottom:"0px"}}>
<Input
prefix={<i className="iconfont icon-sousuo_icon1 font-14"></i>}
placeholder="请输入分支或标签名称搜索"
placeholder={`请输入分支${tagflag ? "或标签" :""}名称搜索`}
autocomplete="off" className="OptionsInput"
value={inputValue}
onChange={changeInputValue}

View File

@ -87,6 +87,13 @@
line-height: 30px;
padding:0px 5px;
margin-left: 20px!important;
&.ant-menu-item-selected{
border-color:#466aff!important;
color:#466aff!important;
}
&.ant-menu-item-active{
border-color:transparent ;
}
}
}
}
@ -109,7 +116,7 @@
color: #333;
}
&.ant-menu-item-selected{
border-color:#1890ff!important;
border-color:#466aff!important;
}
&.ant-menu-item-active{
border-color:transparent ;

View File

@ -2,8 +2,7 @@ import React from 'react';
import { AlignTop } from '../Component/layout';
import { Link } from 'react-router-dom';
function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}){
function Releases({ owner, projectsId, releaseVersions, distribution }) {
return(
<div>
<Link to={`/${owner}/${projectsId}/releases`} className="font-16 color-ooo hoverA">
@ -16,8 +15,9 @@ function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}
return(
key === 0 &&<AlignTop className="mt10">
<div>
<p className="font-16 color-grey-6">
<Link to={`/${owner}/${projectsId}/releases`}>{item.name}</Link>
<p className="font-16 color-grey-6" style={{display:'flex',alignItems:'center'}}>
{/* 如果是点击最新则发行版列表页只展示最新的一个 */}
<Link to={{pathname:`/${owner}/${projectsId}/releases`,query:{turnFromNew:true}}} style={{maxWidth:'200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis'}}>{item.name}</Link>
<span className="font-12 laterest ml5">最新</span>
</p>
<p className="color-grey-3 font-12">{item.created_at}</p>
@ -27,7 +27,8 @@ function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}
})
:
<div className="mt8">
您暂未发布任何版本{baseOperate && projectType !==2 && <Link className="color-blue ml20" to={`/${owner}/${projectsId}/releases/new`}>创建新版本</Link>}
您暂未发布任何版本
{distribution && <Link className="color-blue ml20" to={{pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}}>创建新版本</Link>}
</div>
}

View File

@ -5,6 +5,7 @@ import { Link } from 'react-router-dom';
export default ({ url , name , column , id , login })=>{
const Img = styled.span`
display:flex;
font-weight: bold;
${column && "flex-direction: column;text-align:center;"}
align-items: center;
& img{

View File

@ -51,7 +51,7 @@ function PipelineName({visible,onCancel,onOk,value ,branchList}){
})
}
</Select>
<Select mode="multiple" allowClear value={eventValue} dropdownClassName="chooseCon" style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{console.log(e);setEventValue(e)}}>
<Select mode="multiple" allowClear value={eventValue} dropdownClassName="chooseCon" style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{setEventValue(e)}}>
{
EVENT.map((item,key)=>{
return(

View File

@ -24,7 +24,6 @@ function onLayout(term, el) {
entry.target.offsetHeight,
term,
);
console.log('cols, rows', cols, rows);
term.resize(cols, rows);
mediator.publish('ssh-xterm-resize', {
columns: cols,
@ -139,12 +138,10 @@ export default ({ sshConfigData, sid }) => {
}, 1000);
}
isFirstConnected.current = true;
console.log('event:', event);
const data = Base64.decode(event.data.toString());
let w = term._core._renderService.dimensions.actualCellWidth || 9.5;
console.log('data:', data, w, term);
term.write(data);
};

View File

@ -19,7 +19,7 @@ function Footer(){
return(
<div>
<div style={{height:"497px"}}></div>
<div style={{height:"543px"}}></div>
<div className="newFooter edu-txt-center">
{value && showhtml(value)}
{/* <div className="footerInfos">

View File

@ -120,7 +120,6 @@ class NewHeader extends Component {
})
};
HideAddcoursestypess = (i) => {
console.log("调用了");
this.setState({
Addcoursestypes: false,
mydisplay: true,
@ -269,7 +268,7 @@ class NewHeader extends Component {
)
})
}
<li><Link to={`/settings/profile`}>设置</Link></li>
{/* <li><Link to={`/settings/profile`}>设置</Link></li> */}
<Menu.Item><a onClick={() => this.educoderloginysl()}>退出</a></Menu.Item>
</Menu>
)

View File

@ -67,6 +67,7 @@ function CoderDepot(props){
const [ editReadme , setEditReadme ] = useState(false);
const [ pullsFlag , setPullsFlag ] = useState(true);
const [ issuesFlag , setIssuesFlag ] = useState(true);
const [ releaseVersions , setReleaseVersions] = useState(undefined);
const owner = props.match.params.owner;
const projectsId = props.match.params.projectsId;
@ -74,7 +75,8 @@ function CoderDepot(props){
branchName = returnbar(branchName);
const details = props.projectDetail;
let pathname = props.history.location.pathname;
//distribution
const distribution = details && details.type != 2 && (details.permission === "Admin" || details.permission === "Owner" || details.permission === "Manager");
const { bannerList } = props;
useEffect(()=>{
@ -119,14 +121,47 @@ function CoderDepot(props){
setTreeValue(url);
getFileInfo(url,branchName);
setType("file");
// getReadmeInfo(url,branchName);
// setReadme(undefined);
}else{
setTreeValue(undefined);
getDirInfo(branchName || defaultBranch);
setType("dir");
// getReadmeInfo('', branchName || defaultBranch);
}
}
},[projectsId,owner,pathname,defaultBranch])
useEffect(()=>{
axios.get(`/${owner}/${projectsId}/releases.json`).then((result)=>{
if(result && result.data){
const release = {
"list":result.data.releases,
"total_count":result.data.releases.length
}
setReleaseVersions(release);
}
})
},[])
// readme
function getReadmeInfo(path, ref) {
axios.get(`/${owner}/${projectsId}/readme.json`, {
params:{
owner: owner,
repo: projectsId,
filepath:path,
ref:ref || branchName
}
}).then((result) => {
if (result) {
setReadme(result.data);
} else {
setReadme(undefined);
}
})
}
//
function getDirInfo(branch){
setIsSpin(true);
@ -146,9 +181,10 @@ function CoderDepot(props){
setLastCommitAuthor(c && c.committer);
setMainFlag(true);
setReadOnly(true);
setReadme(result.data.readme);
// setReadme(result.data.readme);
setEditReadme(false);
setHide(true);
getReadmeInfo('', branchName || defaultBranch);
}
setTimeout(function(){setIsSpin(false);},500);
}).catch(error=>{setIsSpin(false);})
@ -181,15 +217,18 @@ function CoderDepot(props){
setDirInfo(undefined);
setFileInfo(en);
setType(en.type);
setReadme(undefined);
}else{
setFileInfo(undefined);
setDirInfo(en);
setType("dir");
getReadmeInfo(path, branchName || defaultBranch);
}
let c = result.data.last_commit
setLastCommit(c && c.commit);
setLastCommitAuthor(c && c.committer);
setMainFlag(false);
setReadOnly(true);
setReadOnly(!editReadme);
setHide(true);
}
@ -301,6 +340,7 @@ function CoderDepot(props){
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
const { current_user } = props;
const baseOper = projectDetail && projectDetail.permission;
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter";
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
@ -348,34 +388,47 @@ function CoderDepot(props){
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
:
<span>分支<span className="color-grey-6">{branchName || defaultBranch}</span></span>
<span>分支<span className="color-grey-6">{branchName || defaultBranch}</span></span>
}
</div>
<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>
</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>
</Link>
</AlignCenter>
{
treeValuePath && treeValuePath.length > 0 ?
<Path
identifier={projectDetail && projectDetail.identifier}
treeValuePath={treeValuePath}
returnUlr={returnUlr}
returnMain={returnMain}
getPathUrl={getPathUrl}
/>
:
<div>
<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>
</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>
</Link>
</AlignCenter>
</div>
}
</AlignCenter>
<AlignCenter className="depotBtn">
{
baseOperate && ((projectDetail.type !== 2 && pullsFlag) || issuesFlag )&&
((baseOperate && projectDetail.type !== 2 && pullsFlag) || (baseOper && issuesFlag)) &&
<div className="addOptionBtn">
{
projectDetail.type !== 2 && pullsFlag &&
<CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</CheckProfile>
baseOperate && projectDetail.type !== 2 && pullsFlag &&
<CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/pulls/new/${branchName || defaultBranch}`)} >+ 合并请求</CheckProfile>
}
{
issuesFlag &&
baseOper && issuesFlag &&
<CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/issues/new`)} >+ 易修</CheckProfile>
}
</div>
@ -403,8 +456,8 @@ function CoderDepot(props){
lastCommit &&
<div className="listtablehead">
<User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/>
<div onClick={()=>props.history.push(`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`)} className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt">{lastCommit.message}</pre>
<div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt"><Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(lastCommit.sha)}`}>{lastCommit.message}</Link></pre>
</div>
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
@ -418,16 +471,6 @@ function CoderDepot(props){
</div>
}
<ul className="listtablebody">
{
treeValuePath && treeValuePath.length > 0 &&
<Path
identifier={projectDetail && projectDetail.identifier}
treeValuePath={treeValuePath}
returnUlr={returnUlr}
returnMain={returnMain}
getPathUrl={getPathUrl}
/>
}
{
dirInfo && dirInfo.length > 0 &&
dirInfo.map((item,key)=>{
@ -461,11 +504,11 @@ function CoderDepot(props){
(dirInfo && dirInfo.length === 0) && !fileInfo ? <Nodata _html="暂未发现文件"/> :""
}
{/* readme文件显示(显示文件详情时不显示readme文件) */}
{ dirInfo && (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
{ (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
</div>
</LongWidth>
{
!fileInfo &&
(!(treeValuePath && treeValuePath.length > 0) && !fileInfo) &&
<ShortWidth>
<Gap style={{paddingLeft:"30px"}}>
<div className="panelmenu">
@ -516,16 +559,15 @@ function CoderDepot(props){
}
{/* 发布 */}
{
projectDetail && projectDetail.release_versions &&
releaseVersions &&
<React.Fragment>
<Divider />
<Releases
owner={owner}
projectsId={projectsId}
releaseVersions={projectDetail.release_versions}
releaseVersions={releaseVersions}
history={props.history}
baseOperate={baseOperate}
projectType={projectDetail.type}
distribution={distribution}
/>
</React.Fragment>
}
@ -535,7 +577,7 @@ function CoderDepot(props){
<Contributors contributors={projectDetail.contributors} owner={owner} projectsId={projectsId} />
}
{/* 语言 */}
{ projectDetail && projectDetail.languages &&
{ projectDetail && projectDetail.languages &&
<React.Fragment>
<Divider />
<LanguagePower languages={projectDetail.languages}/>

View File

@ -1,3 +1,4 @@
import { result } from 'lodash';
import React from 'react';

View File

@ -1,92 +0,0 @@
import React , { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import { Dropdown , Menu , Icon , Tooltip , Spin } from 'antd';
import { truncateCommitId } from '../common/util';
import { getBranch } from '../GetData/getData';
import Nodata from '../Nodata';
import './list.scss';
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
export default ((props)=>{
const [ data , setData ] =useState(undefined);
const [ isSpin , setIsSpin ] =useState(true);
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail } = props;
useEffect(()=>{
getBranchs(projectsId, owner);
},[projectsId])
async function getBranchs(id,owner){
let result = await getBranch(id,owner);
setData(result);
setIsSpin(false);
}
const list =()=>{
if(data && data.length>0){
return(
<React.Fragment>
<ul className="branchUl">
{
data.map((item,key)=>{
return(
<li key={key}>
<div>
<Link to={`/${owner}/${projectsId}/tree/${turnbar(item.name)}`} className="color-blue font-15" style={{"maxWidth":"100px"}}>{item.name}</Link>
<p className="f-wrap-alignCenter mt15">
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.last_commit.sha}`)}`} className="mr5 commitKey" style={{marginLeft:0}}>{item.last_commit && truncateCommitId(item.last_commit.sha)}</Link>
<span className="color-grey-3 hide-1 messages leftPoint">{item.last_commit && item.last_commit.message}</span>
<span className="color-grey-8 ml30">最后更新于{item.last_commit && item.last_commit.time_from_now}</span>
</p>
</div>
<span>
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={`/${owner}/${projectsId}/pulls/new/${item.name}`} className="mr20 color-blue mr30">创建合并请求</Link>
}
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight" className="color-green-file">
<a className="ant-dropdown-link">
<Tooltip title={`下载分支${item.name}`}><Icon type="cloud-download" className="font-18"/></Tooltip>
</a>
</Dropdown>
</span>
</li>
)
})
}
</ul>
</React.Fragment>
)
}else if(data && data.length === 0){
return ( <Nodata _html="暂无数据"/>)
}
}
const menu =(zip_url,tar_url)=> (
<Menu>
<Menu.Item key={'0'}><a href={zip_url}>ZIP</a></Menu.Item>
<Menu.Item key={'1'}><a href={tar_url}>TAR.GZ</a></Menu.Item>
</Menu>
)
return(
<React.Fragment>
<div className="main">
<Spin spinning={isSpin}>
<div className="branchTable">
<p className="branchTitle bor-bottom-greyE">分支列表</p>
<div style={{minHeight:"400px"}}>{list()}</div>
</div>
</Spin>
</div>
</React.Fragment>
)
})

View File

@ -1,13 +1,18 @@
import React , { Component } from 'react';
import { Spin , Pagination } from 'antd';
import { Spin , Pagination, Timeline } from 'antd';
import { getImageUrl } from 'educoder';
import { truncateCommitId } from '../common/util';
import { truncateCommitId ,timeFormat } from '../common/util';
import { AlignTop } from '../Component/layout';
import SelectBranch from '../Branch/Select';
import Nodata from '../Nodata';
import User from '../Component/User'
import Tree from './img/tree.png';
import axios from 'axios';
import {Link} from "react-router-dom";
import CopyTool from '../Component/CopyTool';
import './tree/Index.scss'
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
@ -15,14 +20,16 @@ function returnbar(str){
}
return str;
}
//代码库--提交页面
class CoderRootCommit extends Component{
constructor(props){
super(props)
super(props);
this.state={
commitDatas:undefined,
dataCount:undefined,
limit:20,
page:1,
limit:10,
page: 1,
isSpining:false,
branchList:undefined
}
@ -50,20 +57,34 @@ class CoderRootCommit extends Component{
this.Init();
}
}
UrlParamHash(url){
const params = {};
let h;
let hash = url.slice(url.indexOf('?')+1).split('&');
for(let i = 0; i<hash.length;i++){
h = hash[i].split('=');
params[h[0]] = h[1];
}
return params;
}
Init =()=>{
const { branchName } = this.props.match.params;
const { page , limit } = this.state;
const { limit } = this.state;
const {search} = this.props.location;
const realPage = (search && this.UrlParamHash(search).page) ? parseInt(this.UrlParamHash(search).page) : 1;
this.setState({
isSpining:true
isSpining:true,
page:realPage
})
this.getCommitList( branchName , page , limit );
this.getCommitList( branchName , realPage , limit );
}
getCommitList=(branch , page , limit)=>{
this.setState({
isSpining:true
})
console.log(returnbar(branch));
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/commits.json`;
axios.get(url,{
@ -86,7 +107,8 @@ class CoderRootCommit extends Component{
image_url:item.author && item.author.image_url,
sha:item.sha,
time_from_now:item.time_from_now,
message:item.message
message:item.message,
timestamp:item.timestamp
})
})
this.setState({
@ -105,10 +127,9 @@ class CoderRootCommit extends Component{
}
ChangePage=(page)=>{
const { branchName } = this.props.match.params;
const { limit } = this.state;
this.getCommitList(branchName , page , limit);
this.props.history.push({pathname: this.props.history.location.pathname,search: `page=${page}`})
}
render(){
const { commitDatas , dataCount , limit , page , isSpining , branchList } = this.state;
const { projectDetail, commit_class , defaultBranch } = this.props;
@ -129,46 +150,50 @@ class CoderRootCommit extends Component{
></SelectBranch>
</div>
<Spin spinning={isSpining}>
<div className="commonBox">
<div className="commonBox-title">
<div className="f-wrap-between" style={{alignItems:"center"}}>
<span className="font-16">{dataCount}次提交代码({branch})</span>
</div>
</div>
<div className="commitList">
{
commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{
return(
<div key={k}>
<AlignTop>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="commitKey" style={{marginLeft:0,marginTop:"3px"}}>{truncateCommitId(`${item.sha}`)}</Link>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="commitDesc">{item.message}</Link>
</AlignTop>
<p className="f-wrap-alignCenter mt15">
{
item.id ?
<Link to={`/${item.login}`} className="show-user-link">
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
</Link>:
<span className="show-user-link">
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
</span>
}
</p>
<Timeline className="commitList">
{
commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{
return(
<Timeline.Item key={k} dot={page ===1 && k===0 ?<span className="new-conmmit">最新</span>:<i className="iconfont icon-a-yuanquan2x"></i>}>
<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>
</AlignTop>
<p className="f-wrap-alignCenter mt15 pb5">
<User
id={item.id}
url={(item.image_url && getImageUrl(`/${item.image_url}`)) || "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
name={item.name}
login={item.login}
/>
{item.timestamp && <label className="font-14 color-grey-3 ml3">提交于 {timeFormat(item.timestamp)}</label>}
</p>
</div>
<div>
<div className="treecopy">
<div>
<span className="treecopy-cont shadow">
<img src={Tree} alt="sha" width={"16px"}/>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`}>{truncateCommitId(`${item.sha}`)}</Link>
<input type="text" id={`value${k}`} value={`${truncateCommitId(`${item.sha}`)}`}/>
</span>
<CopyTool beforeText="复制commit id" afterText="复制成功" inputId={`value${k}`}/>
</div>
<button className="btn-83" onClick={()=>{window.location.href=`/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}`}}>浏览文件</button>
</div>
</div>
</div>
)
})
}
{commitDatas && commitDatas.length === 0 && <Nodata _html="暂无数据"/>}
</div>
</div>
</Timeline.Item>
)
})
}
{commitDatas && commitDatas.length === 0 && <Nodata _html="暂无数据"/>}
</Timeline>
{
dataCount > limit ?
<div className="edu-txt-center pt30 mb30">
<Pagination simple defaultCurrent={page} total={dataCount} pageSize={limit} onChange={this.ChangePage}></Pagination>
<Pagination simple current={page} total={dataCount} pageSize={limit} onChange={this.ChangePage}></Pagination>
</div>
:""
}

View File

@ -1,10 +1,12 @@
import React, { Component } from "react";
import { Popconfirm , Select } from "antd";
import { Popconfirm , Select , Dropdown , Spin , Anchor } from "antd";
import "./list.scss";
import axios from "axios";
import Meditor from "../Newfile/m_editor";
import RenderHtml from "../../components/render-html";
import ReadmeCatelogue from "./sub/ReadmeCatelogue";
const $ = window.$;
function bytesToSize(bytes) {
if (bytes === 0) return "0 B";
let k = 1024,
@ -19,7 +21,8 @@ class CoderRootFileDetail extends Component {
value: undefined,
language: undefined,
languages: undefined,
description: props.detail.content
description: props.detail.content,
menuList:undefined
};
}
@ -169,6 +172,31 @@ class CoderRootFileDetail extends Component {
});
};
renderMenulist=()=>{
const { description } = this.state;
if(description){
const items = $.map($("#files-md").find("h1,h2,h3,h4,h5,h6"), function (el, _) {
const anchor = el.id;
const level = el.tagName.replace("H", "");
const href = `#${anchor}`;
return { href:`${href}`,text:el.textContent , level:level }
});
return items;
}
return [];
}
menu=()=>{
const menuList = this.renderMenulist();
if(menuList && menuList.length > 0){
return(
<ReadmeCatelogue menuList={menuList} hash={this.props.history.location.hash}/>
)
}else{
return <Spin />
}
}
render() {
const {
readOnly,
@ -186,79 +214,88 @@ class CoderRootFileDetail extends Component {
const Option = Select.Option;
return (
<React.Fragment>
<div className="grid-item branchTitle">
<div className="grid-item">
<span className="ml20 color-grey-6 font-16">
<Anchor className="griditemAnchor" offsetTop={70} targetOffset={160}>
<div className="griditemCate">
{
md && readOnly &&
<Dropdown overlay={this.menu()} trigger={['hover']} overlayClassName="menuslist">
<span className="catelogue mr20">
<i className="iconfont icon-muluicon font-12 mr5"></i>
<span>目录</span>
</span>
</Dropdown>
}
<span className="color-grey-6 font-16">
{bytesToSize(detail && detail.size)}
</span>
</div>
<p className="text-right">
{flag && platform && (
<div>
{readOnly ? (
<span>
{
!detail.direct_download?
<span>
<a onClick={() => this.DownLoadFile(detail.download_url)} className="ml20">
<i className="iconfont icon-xiazai1 font-15 color-grey-6"></i>
</a>
{
type !==2 &&
<a onClick={() => this.EditFile(false)} className="ml20">
<i className="iconfont icon-bianji1 font-15 color-grey-6"></i>
{flag && platform && (
<div>
{readOnly ? (
<span>
{
!detail.direct_download?
<span>
<a onClick={() => this.DownLoadFile(detail.download_url)} className="ml20">
<i className="iconfont icon-xiazai1 font-15 color-grey-6"></i>
</a>
}
</span>:""
}
</span>
) : (
<React.Fragment>
<Select
showSearch={true}
placeholder={"请选择文本语言"}
style={{ width: 200 }}
value={language}
onChange={this.select_language}
>
<Option value={undefined}>请选择文本语言</Option>
{languages &&
languages.map((item, key) => {
return (
<Option value={item} key={key}>
{item}
</Option>
);
})}
</Select>
<button
type="button"
className="ant-btn ant-btn-sm ml20"
onClick={() => this.EditFile(true)}
>
<span> </span>
</button>
</React.Fragment>
)}
{
type !==2 &&
<Popconfirm
title="确认删除这个文件?"
className="ml20"
okText="确定"
cancelText="取消"
onConfirm={this.deleteFile}
>
<a>
<i className="iconfont icon-shanchu font-15 color-grey-6"></i>
</a>
</Popconfirm>
}
</div>
)}
</p>
</div>
{
type !==2 &&
<a onClick={() => this.EditFile(false)} className="ml20">
<i className="iconfont icon-bianji1 font-15 color-grey-6"></i>
</a>
}
</span>:""
}
</span>
) : (
<React.Fragment>
<Select
showSearch={true}
placeholder={"请选择文本语言"}
style={{ width: 200 }}
value={language}
onChange={this.select_language}
>
<Option value={undefined}>请选择文本语言</Option>
{languages &&
languages.map((item, key) => {
return (
<Option value={item} key={key}>
{item}
</Option>
);
})}
</Select>
<button
type="button"
className="ant-btn ant-btn-sm ml20"
onClick={() => this.EditFile(true)}
>
<span> </span>
</button>
</React.Fragment>
)}
{
type !==2 &&
<Popconfirm
title="确认删除这个文件?"
className="ml20"
okText="确定"
cancelText="取消"
onConfirm={this.deleteFile}
>
<a>
<i className="iconfont icon-shanchu font-15 color-grey-6"></i>
</a>
</Popconfirm>
}
</div>
)}
</p>
</Anchor>
<div>
{detail.image_type ? (
<div className="edu-txt-center pt20 pb20">
@ -272,7 +309,7 @@ class CoderRootFileDetail extends Component {
</div>
) : (
md && readOnly ?
<div className="files-md">
<div className="files-md" id="files-md">
<RenderHtml className="file-md imageLayerParent" value={description} url={this.props.history.location}/>
</div>
:

View File

@ -1,9 +1,10 @@
import React , { Component } from 'react';
import { Route , Switch } from 'react-router-dom';
import Top from './DetailTop';
// import Top from './DetailTop';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import axios from 'axios';
import './Index.scss';
const FileNew = Loadable({
loader: () => import('../Newfile/Index'),
@ -18,25 +19,25 @@ const CoderRootCommit = Loadable({
loading: Loading,
})
const CoderRootBranch = Loadable({
loader: () => import('./CoderRootBranch'),
loader: () => import('./tree/Index'),
loading: Loading,
})
const CoderRootTag = Loadable({
loader: () => import('./CoderRootTag'),
loader: () => import('./tag/Index'),
loading: Loading,
})
const CoderRootVersion = Loadable({
loader: () => import('../Version/version'),
loading: Loading,
})
const CoderRootVersionNew = Loadable({
loader: () => import('../Version/New'),
loading: Loading,
})
const CoderRootVersionUpdate = Loadable({
loader: () => import('../Version/New'),
loader: () => import('./version/Index'),
loading: Loading,
})
// const CoderRootVersionNew = Loadable({
// loader: () => import('./version/New'),
// loading: Loading,
// })
// const CoderRootVersionUpdate = Loadable({
// loader: () => import('./version/New'),
// loading: Loading,
// })
const Diff = Loadable({
loader: () => import('./Diff'),
loading: Loading,
@ -83,8 +84,8 @@ class CoderRootIndex extends Component{
}
render(){
return(
<div>
<Top {...this.props} {...this.state}/>
<div className="coderSubPage">
{/* <Top {...this.props} {...this.state}/> */}
<Switch {...this.props}>
{/* 新建文件 */}
<Route path="/:owner/:projectsId/:branch/newfile/:path"
@ -117,7 +118,7 @@ class CoderRootIndex extends Component{
() => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />)
}
></Route>
<Route path="/:owner/:projectsId/releases/:versionId/update"
{/* <Route path="/:owner/:projectsId/releases/:versionId/update"
render={
(props) => (<CoderRootVersionUpdate {...this.props} {...this.state} {...props} />)
}
@ -126,7 +127,7 @@ class CoderRootIndex extends Component{
render={
() => (<CoderRootVersionNew {...this.props} {...this.state} />)
}
></Route>
></Route> */}
<Route path="/:owner/:projectsId/releases"
render={
() => (<CoderRootVersion {...this.props} {...this.state} />)

View File

@ -1,35 +1,55 @@
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import { Button ,Spin } from "antd";
import { truncateCommitId } from '../common/util';
import { timeFormat, truncateCommitId } from '../common/util';
import { getImageUrl } from 'educoder';
import Files from '../Merge/Files';
import Tree from "./img/tree.png";
import User from "../Component/User";
import Keys from "../Component/Keys";
import axios from "axios";
import { Link } from "react-router-dom";
const Infos = styled.div`
border: 1px solid #dddddd;
border: 1px solid #FAFCFF;
margin-bottom:15px;
& .commitinfos {
background-color: #f1f8ff;
border-bottom: 1px solid #ddd;
padding: 20px;
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;
}
& .task-hide{
width: 65rem;
overflow:hidden;
white-space:normal;
word-break:break-all;
font-weight: bold;
color: #333333;
font-size: 16px;
}
}
& > .f-wrap-between {
padding: 10px 20px;
padding: 14px 20px 14px 16px;
border-radius: 3px 3px 0px 0px;
border: 1px solid #D0D0D0;
.df{
align-items: center;
& .underline:hover{
text-decoration: underline;
}
}
}
`;
export default ({ match , history }) => {
//
export default (props) => {
const {match , history } = props;
const [data, setData] = useState({undefined});
const [commit, setCommit] = useState(undefined);
const [parents, setParents] = useState(undefined);
const [committer, setCommitter] = useState(undefined);
const [isSpin, setIsSpin] = useState(true);
const { sha , projectsId, owner } = match.params;
useEffect(() => {
if (projectsId && owner && sha) {
@ -43,6 +63,7 @@ export default ({ match , history }) => {
setParents(result.data.parents);
setCommitter(result.data.committer || (result.data.commit && result.data.commit.committer));
setIsSpin(false);
}
})
.catch(error => {
@ -56,29 +77,42 @@ export default ({ match , history }) => {
<Infos>
<div className="commitinfos">
<div className="f-wrap-between">
{commit && commit.message &&
<pre className="task-hide" style={{marginBottom:"0px",height:"28px",whiteSpace:"pre-wrap"}}>{commit.message}</pre>
}
<Button type="primary" onClick={()=>{history.push(`/${owner}/${projectsId}/tree/${truncateCommitId(sha)}`)}} className="ml30">浏览代码</Button>
<div>
{commit && commit.message &&
<pre className="task-hide">{commit.message}</pre>
}
<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>
</div>
<div className="f-wrap-between" style={{ alignItems: "center" }}>
<ul className="df">
<User
url={(committer && getImageUrl(`/${committer.image_url}`))|| "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
id = {committer && committer.id}
url={(committer && getImageUrl(`/${committer.image_url}`))}
name={committer && committer.name}
login={committer && committer.login}
/>
{committer && committer.time_from_now && <li className="ml20 mt2">{committer.time_from_now}</li>}
{commit && commit.timestamp && <li className="ml4">提交于{timeFormat(commit.timestamp)}</li>}
</ul>
<li className="df">
{
parents && parents.length > 0 && parents.map((item,key)=>{
return(
<Keys title="父节点" value={truncateCommitId(item.sha)} key={key} className="mr20"></Keys>
<div className="ml40 f-wrap-alignCenter">
<label className="mr8">父节点</label>
<img src={Tree} alt="sha" width={"16px"} className="mr4"/>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}/${data.branch}`} className="underline">{truncateCommitId(item.sha)}</Link>
</div>
)
})
}
<Keys title="当前节点" value={truncateCommitId(sha)}></Keys>
<div className="ml40 f-wrap-alignCenter">
<label className="mr8">当前节点</label>
<img src={Tree} alt="sha" width={"16px"} className="mr4"/>
<span>{truncateCommitId(sha)}</span>
</div>
</li>
</div>
</Infos>
@ -87,6 +121,7 @@ export default ({ match , history }) => {
data={data}
owner={owner}
projectsId={projectsId}
parentsSha={parents && parents.length > 0 && parents[0].sha}
/>
</Spin>
</div>

View File

@ -189,13 +189,13 @@
flex-wrap: wrap;
padding-bottom: 2px;
a{
margin: 0px 17px 0px 0px;
margin: 0px 17px 10px 0px;
img{
border-radius: 50%;
width: 40px;
height: 40px;
}
&:nth-child(6){
&:nth-child(5){
margin-right: 0px;
}
}
@ -253,7 +253,7 @@
background-color: #FAFCFF;
.ellipsistxt{
margin-top: 6px;
cursor: pointer;
// cursor: pointer;
#ptxt{
margin-bottom: 0px;
word-break: break-all;
@ -402,6 +402,16 @@
cursor: pointer;
background: #FAFBFC;
border-radius: 4px;
.ant-dropdown-menu-item{
border-radius: 8px;
text-align: left!important;
a{
width: 350px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
border: 1px solid #D0D0D0;
font-size: 15px;
font-weight: normal;
@ -447,4 +457,28 @@
.ant-anchor-ink::before{
background-color: #fff;
}
}
.coderSubPage{
width: 1200px;
margin:0px auto;
}
.griditemAnchor{
margin-left: 0px;
padding: 0px;
border-bottom: 1px solid #ddd;
.ant-anchor{
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 20px;
}
.griditemCate{
color: #333;
font-size: 16px;
display: flex;
align-items: center;
.catelogue{
margin-left: 0px;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
src/forge/Main/img/tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

View File

@ -575,6 +575,9 @@
border:1px solid #ddd;
margin-top: 18px;
border-radius: 4px;
.ant-anchor-wrapper{
overflow: unset!important;
}
}
.commonBox .commonBox-title{
padding:0px 20px;
@ -719,12 +722,76 @@ a.color-grey-ccc:hover{
padding:0px 30px;
min-height: 400px;
}
.commitList > div{
border-bottom: 1px solid #EEEEEE;
padding:16px 0px;
}
.commitList > div:last-child{
border-bottom: none;
.main{
margin: 30px auto;
.ant-timeline{
margin-top: 28px;
.commitList-item{
position: relative;
padding: 20px 20px;
background: #FAFCFF;
border: 1px solid rgba(42, 97, 255, 0.23);
border-radius: 4px;
margin-left: 16px;
align-items: center;
&:after,&:before{
content: "";
position: absolute;
left: -10px;
top: 10px;
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-right: 10px solid rgba(42, 97, 255, 0.23);
}
&:after{
left: -8px;
border-right: 10px solid #FAFCFF;
&:hover{
border-right: 10px solid #EEF6FF;
}
}
&:hover{
background: #EEF6FF;
border: 1px solid rgba(42, 97, 255, 0.58);
&:after{
border-right: 10px solid #EEF6FF;
}
&:before{
border-right: 10px solid rgba(42, 97, 255, 0.58);
}
}
.treecopy-cont{
padding: 4px 15px;
}
.btn-83{
margin-left: 20px;
}
}
.ant-timeline-item{
padding: 8px 0 20px;
}
.ant-timeline-item-tail{
height: calc(100% - 20px);
border-left: 2px solid #EEEEEE;
top: 12px;
&:after{
content: ' ';
height: 0;
position: absolute;
width: 0;
border: 7px solid transparent;
border-top-color: #EEEEEE;
top: 100%;
left: 50%;
margin-left: -8px;
}
}
.ant-timeline-item-head-custom{
top:20px;
padding: 0 1px;
}
}
}

View File

@ -86,7 +86,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
</li>
:""
}
{
{/* {
item.menu_name === "resources" &&
<li className={pathname==="source" ? "active" : ""}>
<Link to={{ pathname: `/${owner}/${projectsId}/source`, state }}>
@ -95,7 +95,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
{projectDetail && projectDetail.source_count ? <span className="num">{projectDetail.source_count}</span> :""}
</Link>
</li>
}
} */}
{
item.menu_name === "versions" &&
<li className={pathname==="milestones" ? "active" : ""}>

View File

@ -1,7 +1,7 @@
import React , { useState , useEffect } from 'react';
import React , { useState } from 'react';
import { Anchor , Input } from 'antd';
import './sub.scss';
import { Base64 } from 'js-base64';
import { useEffect } from 'react';
const { Link } = Anchor;
@ -18,7 +18,7 @@ function ReadmeCatelogue({ menuList , hash }) {
function changeValue(e) {
setValue(e.target.value);
if(e.target.value){
let m = menuList.filter(i=>i.text.indexOf(e.target.value)>-1);
let m = menuList.filter(i=>i.text.toLowerCase().indexOf(e.target.value.toLowerCase())>-1);
setMenu(m);
}else{
setMenu(menuList);

View File

@ -0,0 +1,13 @@
import React from 'react';
import { Link } from 'react-router-dom';
import'./sub.scss'
function SubMenu({tab,owner,projectsId}) {
return(
<ul className="subMenu">
<Link to={`/${owner}/${projectsId}/tags`} className={tab==="tags"?"active":""}>标签</Link>
<Link to={`/${owner}/${projectsId}/releases`} className={tab==="releases"?"active":""}>发行版</Link>
</ul>
)
}
export default SubMenu;

View File

@ -64,4 +64,28 @@
background-color: #fff;
}
}
}
.subMenu{
display: flex;
padding-top: 30px;
a{
width: 83px;
font-weight: 500;
line-height: 30px;
height: 32px;
color: #333333!important;
text-align: center;
border: 1px solid #D0D0D0;
border-radius: 0px 4px 4px 0px;
background: rgba(250, 251, 252, 0);
&:first-child{
border-right: none;
border-radius: 4px 0px 0px 4px;
}
&.active{
background-color: #466AFF;
color: #fff!important;
border-color: #466AFF;
}
}
}

View File

@ -0,0 +1,105 @@
import React,{ useEffect , useState } from 'react';
import SubMenu from '../sub/SubMenu';
import { Table , Tooltip } from 'antd';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { truncateCommitId } from '../../common/util';
import './Index.scss';
import Tree from '../img/tree.png'
import moment from 'moment';
function Tags(props) {
const [ source , setSource ] = useState([]);
const { projectsId , owner } = props.match.params;
useEffect(() => {
if (projectsId) {
const url = `/${owner}/${projectsId}/tags.json`;
axios.get(url).then((result) => {
if (result) {
setSource(result.data);
}
}).catch(error => {})
}
}, [owner, projectsId]);
const columns=[
{
title:"标签名",
dataIndex:"name",
key:1,
ellipsis:true,
render:(txt,item)=>{
return <Link className="hover" to={`/${owner}/${projectsId}/tree/${item.name}`} >{item.name}</Link>
}
},
{
title:"创建时间",
dataIndex:"time_ago",
key:2,
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>创建于{txt}</span>
</span>
)
}
},
{
title:"提交ID",
dataIndex:"id",
key:3,
ellipsis:true,
render:(txt,item)=>{
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.id}`)}`}>{truncateCommitId(item.id)}</Link>
</Tooltip>
)
}
},
{
title:"描述信息",
dataIndex:"message",
key:4,
ellipsis:true,
render:(txt,item)=>{
return item.message || "--"
}
},
{
title:"下载",
dataIndex:"stage_type",
key:5,
ellipsis:true,
align:"center",
width:"181px",
render:(txt,item)=>{
return (
<div>
<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>
)
}
}
]
return(
<div>
<SubMenu tab={"tags"} projectsId={projectsId} owner={owner}/>
<Table className="tagTable" dataSource={source} columns={columns} pagination={false}></Table>
</div>
)
}
export default Tags;

View File

@ -0,0 +1,31 @@
.tagTable{
margin-top: 30px;
thead{
tr th{
background-color: #fff;
padding:5px 0px;
.ant-table-column-title{
font-size: 16px;
font-weight: 500;
color: #333333;
}
}
}
tbody{
tr{
&:hover td{
background-color: #fff!important;
}
td{
padding:0px;
height: 69px;
line-height: 69px;
}
&:last-child{
td{
border-bottom: none!important;
}
}
}
}
}

View File

@ -0,0 +1,114 @@
import React , { useEffect , useState } from 'react';
import CopyTool from '../../Component/CopyTool';
import { truncateCommitId } from '../../common/util';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
import { Dropdown , Menu , Spin } from 'antd';
import './Index.scss';
import Tree from '../img/tree.png';
import Axios from 'axios';
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function Index(props) {
const [ list , setList ] = useState([]);
const [ isSpin , setIsSpin ] = useState(true);
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail } = props;
useEffect(()=>{
getList();
},[])
const menu =(zip_url,tar_url)=> (
<Menu>
<Menu.Item key={'0'}><a href={zip_url}>ZIP</a></Menu.Item>
<Menu.Item key={'1'}><a href={tar_url}>TAR.GZ</a></Menu.Item>
</Menu>
)
function getList() {
const url = `/${owner}/${projectsId}/branches_slice.json`;
Axios.get(url).then(result=>{
if(result){
setList(result.data);
}
setIsSpin(false);
}).catch(error=>{setIsSpin(false);})
}
return(
<Spin spinning={isSpin}>
<div style={{paddingTop:"10px",minHeight:"400px"}}>
{
list && list.length>0 && list.map((item,key)=>{
return(
<React.Fragment>
<p className="branchSort">{item.branch_type === "default" ? "默认分支" : item.branch_type==="protected"?"保护分支":"其它分支"}</p>
{
item.list && item.list.length>0 &&
<ul className="treeUl">
{
item.list.map((i,k)=>{
let last_commit = i.last_commit;
return(
<li>
<div className="treeinfo">
<Link to={`/${owner}/${projectsId}/tree/${turnbar(i.name)}`} className="task-hide">{i.name}</Link>
<div>
{
last_commit && last_commit.committer && last_commit.committer.id?
<Link to={`/${ last_commit.committer.login}`}>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ last_commit.committer.image_url}`)} alt=""/>
<span className="mr3 color-grey-3" style={{fontWeight:"500"}}>{last_commit && last_commit.committer && last_commit.committer.name}</span>
</Link>
:
<React.Fragment>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ last_commit.committer.image_url}`)} alt=""/>
<span className="mr3 color-grey-3" style={{fontWeight:"500"}}>{last_commit && last_commit.committer && last_commit.committer.name}</span>
</React.Fragment>
}
<span className="color-grey-3">更新于{last_commit && last_commit.time_from_now}</span>
</div>
</div>
<div className="treecopy">
<div>
<span>
<img src={Tree} alt="sha" width={"16px"}/>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(last_commit && last_commit.sha)}`}>{truncateCommitId(last_commit && last_commit.sha)}</Link>
<input type="text" id={`value${key}${k}`} value={`${truncateCommitId(last_commit && last_commit.sha)}`}/>
</span>
<CopyTool beforeText="复制commit id" afterText="复制成功" inputId={`value${key}${k}`}/>
</div>
</div>
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={`/${owner}/${projectsId}/pulls/new/${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>
</Dropdown>
</div>
</li>
)
})
}
</ul>
}
</React.Fragment>
)
})
}
</div>
</Spin>
)
}
export default Index;

View File

@ -0,0 +1,96 @@
.branchSort{
font-weight: 500;
color: #333333;
font-size: 15px;
height: 20px;
line-height: 20px;
padding-left: 10px;
margin-top: 20px;
margin-bottom: 6px!important;
}
.treeUl{
background: #FAFCFF;
border-radius: 4px;
border: 1px solid rgba(42, 97, 255, 0.23);
li{
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 20px;
border-bottom: 1px solid rgba(42, 97, 255, 0.23);
&:last-child{
border-bottom: none;
}
.treeinfo{
max-width: 399px;
flex:1;
flex-direction: column;
a:hover{
span{
color: #466AFF!important;
}
}
img{
height: 20px;
width: 20px;
margin-right: 5px;
}
}
.treeabout{
flex:1;
text-align: right;
}
}
}
.treecopy{
flex:1;
display: flex;
justify-content: center;
&>div{
height: 32px;
background: #FAFBFC;
border-radius: 4px;
border: 1px solid #D0D0D0;
position: relative;
z-index: 1;
display: flex;
align-items: center;
&>span{
padding:0px 15px;
border-right: 1px solid rgba(153, 153, 153, 0.4);
height: 100%;
img{
margin-right: 4px;
}
a{
color: #466AFF;
&:hover{
text-decoration: underline;
}
}
}
&>i{
margin:0px 12px;
color: #333!important;
}
input{
position: absolute;
z-index: 0;
opacity: 0;
top: 32px;
}
}
}
.new-conmmit{
width: 30px;
height: 18px;
line-height: 18px;
display: block;
background: #FF6832;
color: white;
font-size: 12px;
border-radius: 4px;
}
.icon-a-yuanquan2x{
color: #466AFF;
}

View File

@ -0,0 +1,22 @@
import { Button } from 'antd';
import React from 'react';
import './version.scss';
function Empty({operation,addFunc}) {
return(
<div className="emptyPanel color-grey-3">
<i className="iconfont icon-banbenicon font-50 color-grey-3" style={{height:"50px",lineHeight:"50px",marginBottom:"13px"}}></i>
<span className="weight font-26 mb15">这里暂未发布过任何版本</span>
<span className="weight400" style={{textAlign:"center",lineHeight:"20px"}}>发行版功能基于仓库中的历史标记<br/>建议使用类似 V1.0 的版本标记作为发布点</span>
<div className="operation">
{
operation ?
<Button type={"primary"} onClick={addFunc} className="btnblue" style={{width:"118px",height:"36px"}}>发布新版本</Button>
:
<span className="color-grey-3 weight font-16">该项目暂时没有发布版本</span>
}
</div>
</div>
)
}
export default Empty;

View File

@ -0,0 +1,41 @@
import React from 'react';
import { Switch , Route } from 'react-router';
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import SubMenu from '../sub/SubMenu';
import "./version.scss";
const CoderRootVersion = Loadable({
loader: () => import('./version'),
loading: Loading,
})
const CoderRootVersionNew = Loadable({
loader: () => import('./New'),
loading: Loading,
})
function Index(props) {
const { projectsId , owner } = props.match.params;
return(
<div>
<SubMenu tab={"releases"} projectsId={projectsId} owner={owner}/>
<Switch>
<Route path="/:owner/:projectsId/releases/:versionId/update"
render={
(p) => (<CoderRootVersionNew {...props} {...p} />)
}
></Route>
<Route path="/:owner/:projectsId/releases/new"
render={
(p) => (<CoderRootVersionNew {...props} {...p} />)
}
></Route>
<Route path="/:owner/:projectsId/releases"
render={
(p) => (<CoderRootVersion {...props} {...p} />)
}
></Route>
</Switch>
</div>
)
}
export default Index;

View File

@ -0,0 +1,266 @@
import React, { useState, useEffect, forwardRef } from "react";
import styled from "styled-components";
import { AutoComplete , Input, Checkbox, Button, Form } from "antd";
import SelectBranch from '../../Branch/Select';
import Editor from "../../../modules/tpm/challengesnew/tpm-md-editor";
import Upload from "../../Upload/Index";
import Attachments from "../../Upload/attachment";
import axios from "axios";
import "./version.scss";
import { trim } from "lodash";
const { Option } = AutoComplete;
const Span = styled.span`
margin: 0px 15px;
color: #bbb;
line-height: 35px;
font-size:16px;
font-weight:400;
color:#666;
`;
export default Form.create()(
forwardRef(
(
{ form, projectDetail , match, showNotification, history },
ref
) => {
const { getFieldDecorator, validateFields, setFieldsValue } = form;
const [tagList, setTagList] = useState(undefined);
const [desc, setDesc] = useState(null);
const [branch, setBranch ] = useState(null);
const [fileList, setFileList] = useState(undefined);
const [attachment, setAttachment] = useState(undefined);
const [options , setOptions] = useState(undefined);
const stable = history && history.location && history.location.state.stable;
const { projectsId, versionId , owner } = match.params;
useEffect(()=>{
if(projectDetail && projectDetail.default_branch){
setBranch(projectDetail.default_branch);
}
},[projectDetail])
useEffect(() => {
if (versionId) {
const url = `/${owner}/${projectsId}/releases/${versionId}/edit.json`;
axios.get(url).then(result => {
if (result) {
setFieldsValue(result.data);
setDesc(result.data.body);
setAttachment(result.data.attachments);
}
});
}
}, [versionId]);
useEffect(() => {
if (projectsId) {
const url = `/${owner}/${projectsId}/tags.json`;
axios
.get(url,{params:{
limit:1000
}})
.then(result => {
if (result) {
setTagList(result.data);
setOptions(renderTagList(result.data));
}
})
.catch(error => {
console.log(error);
});
}
}, [projectsId]);
function renderTagList(list) {
if (list) {
let array = list.map((item, key) => {
return (
<Option key={key} value={item.name}>
{item.name}
</Option>
);
});
return array || undefined;
}
}
function submit() {
validateFields((err, value) => {
if(err)return;
if (versionId) {
let url = `/${owner}/${projectsId}/releases/${versionId}.json`;
axios
.put(url, {
...value,
body: desc,
attachment_ids: fileList,
target_commitish:branch
})
.then(result => {
if (result) {
showNotification("版本修改成功!");
history.push(`/${owner}/${projectsId}/releases`);
}
});
} else {
let url = `/${owner}/${projectsId}/releases.json`;
axios.post(url, {
...value,
body: desc,
attachment_ids: fileList
})
.then(result => {
if (result) {
showNotification("版本发布成功!");
history.push(`/${owner}/${projectsId}/releases`);
}
});
}
});
}
//
function changeAuto(value){
let l = tagList.filter(item=>item.name.indexOf(value) > -1);
setOptions(renderTagList(l));
}
function changeBranch(params) {
setBranch(params);
}
return (
<div className="df pt15">
<Form className="versionForm">
<div className="itemInline">
<Form.Item>
{getFieldDecorator("tag_name",
{ rules:[
{ required: true, message: "请输入获取或选择一个标签" },
{ validator: (rule,val,callback) =>{
if(val.length>30 || val.indexOf(' ')>0 || val.match(/^\s+$/) || trim(val).length!=val.length){
callback('无效的标签名称请参考右侧建议命名标签并确认长度在1~30个字符之间');
}else{
callback();
}
}}],
validateFirst: true
})(
<AutoComplete
placeholder="标记一个版本"
onChange={changeAuto}
style={{ width: "200px" }}
>
{options}
</AutoComplete>
)}
</Form.Item>
<Span>@</Span>
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
branch={branch}
changeBranch={changeBranch}
owner={owner}
history={history}
tagflag={false}
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
<p className="font-12 color-grey-6 weight400">选择一个已经存在的标签或者在发布时新建一个标签</p>
</div>
<Form.Item className="pt20">
{getFieldDecorator("name",
{ rules:[
{ required: true, message: "请输入发行版的标题" },
{ validator: (rule,val,callback) =>{
if(val.length>50){
callback('标题长度在1~50个字符之间');
}else{
callback();
}
}}],
validateFirst: true
})(
<Input placeholder="发行版的标题" />
)}
</Form.Item>
<Editor
placeholder={"描述此发行版"}
height={200}
mdID={`version-comments-description`}
initValue={desc}
onChange={setDesc}
noStorage={true}
/>
<div className="mt5 dragBox">
<Upload
className="versionStyle"
isComplete={true}
load={setFileList}
icon={
<i className="iconfont icon-shangchuanicon dragIcon" />
}
size={100}
showNotification={showNotification}
/>
{versionId && attachment && attachment.length > 0 ? (
<Attachments
attachments={attachment}
showNotification={showNotification}
canDelete={true}
/>
) : (
""
)}
</div>
<Form.Item className="prerelease">
{getFieldDecorator("prerelease",
{ rules:[],
validateFirst: true
})(
<Checkbox defaultChecked={!stable}>这是一个预览版本</Checkbox>
)}
</Form.Item>
<p className="pt20" style={{borderTop:"1px solid #eee"}}>
<Button onClick={submit} type="primary" className="mr30 btnblue">
{versionId ? "保存" : "创建"}发行版
</Button>
<Button
onClick={() =>history.push(`/${owner}/${projectsId}/releases`)} className="btngrey"
>取消</Button>
</p>
</Form>
<div className="versionTips">
<div className="infosTip">
<p className="font-16 mb14 weight">标签命名建议</p>
<p className="mb15">
通常的做法是在版本名称前加上字母 v 前缀 v1.0 或者 v2.3.4
</p>
<p>
如果标签不适合在生产环境下使用请在版本名称后添加预发行版本例如v0.2-alpha
或者 v5.9-beta.3
</p>
</div>
<div className="infosTip">
<p className="font-16 mb14 weight">语义化版本</p>
<p>
如果你是第一次发布版本我们强烈建议你阅读<a href='https://semver.org/lang/zh-CN' target='_blank' className="color-blue">语义化版本</a>
</p>
</div>
<div className="infosTip">
<p className="font-16 mb14 weight">附件大小说明</p>
<p>
单个附件不能超过 100MGVP 项目200M每个仓库总附件不可超过
1G推荐项目不可超过 5GGVP 项目不可超过
20G附件总容量统计包括仓库附件和发行版附件
</p>
</div>
</div>
</div>
);
}
)
);

View File

@ -0,0 +1,150 @@
import React, { useEffect , useState } from "react";
import { Link } from 'react-router-dom';
import { Spin , Button } from 'antd';
import { getImageUrl } from 'educoder';
import {truncateCommitId} from '../../common/util';
import Empty from './Empty';
import './version.scss';
import axios from 'axios';
import Tree from '../img/tree-black.png';
import RenderHtml from '../../../components/render-html';
import User from "../../Component/User";
function version(props) {
const [ data , setData ] = useState(undefined);
const [ releases , setReleases ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(true);
const { projectsId ,owner } = props.match.params;
const { isManager, isDeveloper, location , user } = 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();
},[])
// 获取列表数据
function getIssueList(){
const url = `/${owner}/${projectsId}/releases.json`;
axios.get(url).then((result) => {
if (result) {
setData(result.data);
const { releases = [] } = result.data;
//默认第一个展开body参数)
releases.length && (releases[0].bodyshow = true);
setReleases(result.data.releases);
setIsSpin(false);
}
}).catch((error) => {
console.log(error);
})
}
// 显示版本描述
function showBody(key,flag){
var lists = releases.concat();
lists[key].bodyshow = !flag ? true : false;
lists.splice();
setReleases(lists);
}
//删除
function deleteRelease(releaseId) {
if(releaseId){
axios.delete(`/${owner}/${projectsId}/releases/${releaseId}.json`).then((result)=>{
if(result){
getIssueList();
}
})
}
}
function release(item,key){
return (
<div className="versionInfo" key={key}>
<span className="versionInfo_left">
<span className={`${item.draft === "稳定" ?"versionTag green":"versionTag orange"}`}>{item.draft}</span>
<span className="color-grey-3 mt15 font-12">
<i className="iconfont icon-biaoqianicon mr3 font-14"></i>
<Link className="hover" to={`/${owner}/${projectsId}/tree/${item.tag_name}`} >{item.tag_name}</Link>
</span>
<span className="color-grey-3 font-12">
<img src={Tree} width="16px" color="#333333" className="mr3"/>
<Link className="hover" to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`}>{truncateCommitId(item.sha)}</Link>
</span>
</span>
<div className="versionInfo_right">
<div className="versionName">
<Link to={`/${owner}/${projectsId}/tree/${item.tag_name}`} className="task-hide color-blue hover font-18">{item.name}</Link>
<span>
{data && data.user_admin_permission && type !== 2 && <Link to={{pathname:`/${owner}/${projectsId}/releases/${item.version_id}/update`,state:{"stable":item.draft==="稳定"}}} className="ml15"><i className="iconfont icon-a-bianji1 font-16 color-grey-6"></i></Link>}
{data && data.user_admin_permission && type !== 2 && <i className ="iconfont icon-shanchuicon1 font-16 ml15" onClick={()=>{deleteRelease(item.version_id)}}></i>}
</span>
</div>
<span className="color-grey-3 mb15 version-user">
<i className={`${item.bodyshow ? "iconfont icon-sanjiaoxing-down color-grey-8 mr3 font-14":"iconfont icon-triangle color-grey-8 mr3 font-14"}`} onClick={()=>showBody(key,item.bodyshow)}></i>
<User
id={item.id}
url={(item.image_url && getImageUrl(`/${item.image_url}`)) || "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
name={item.user_name}
login={item.user_login}
/>
<span className="ml5">发布于{item.created_at}</span>
</span>
{
item.bodyshow &&
<div className="body-show">
<RenderHtml className="break_word_comments imageLayerParent" value={item.body || ''} url={props.history.location}/>
</div>
}
<RenderHtml />
<p className="versionFile">
{/* 发行版附件下载 */}
{item.attachments && item.attachments.map((item)=>{
return(<a href={item.url}><i className="iconfont icon-wenjian7 font-14 mr10 color-grey-3"></i> {item.title}</a>)
})}
{/* 发行版下载包 */}
<a href={item.tarball_url}><i className="iconfont icon-wenjian7 font-14 mr10 color-grey-3"></i> {item.tag_name}.TAR.gz</a>
<a href={item.zipball_url}><i className="iconfont icon-wenjian7 font-14 mr10 color-grey-3"></i> {item.tag_name}.ZIP</a>
</p>
</div>
</div>
)
}
function renderList(releases){
if (releases && releases.length > 0) {
return (
<React.Fragment>
{
data && data.user_admin_permission && type !== 2 &&
<div className="addReleaseBtn">
<Button type={"primary"} onClick={addFunc} className="btnblue" style={{height:"36px"}}>发布新版本</Button>
</div>
}
<div>
{!turnFromNew ? releases.map((item, key) => release(item,key)) : release(releases[0],0)}
</div>
</React.Fragment>
)
} else if (releases && releases.length === 0) {
return (
<Empty
operation={data && data.user_admin_permission && type !== 2}
addFunc={addFunc}
/>
)
}
}
function addFunc(){
props.history.push({pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}});
}
return (
<div className="releaseIndex">
<div className="releasesVersion">
<Spin spinning={isSpin}>
{renderList(releases)}
</Spin>
</div>
</div>
)
}
export default version;

View File

@ -0,0 +1,357 @@
.topWrapper {
padding: 20px 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
border-bottom: 1px solid #EEEEEE;
align-items: center;
}
.topWrapper_btn_new {
background: #fff;
color: #5091FF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
border:1px solid #5091FF;
}
.versionInfo{
display: flex;
width: 100%;
}
.versionInfo_left{
display: flex;
width: 182px;
flex-direction: column;
align-items: flex-end;
padding-right: 15px;
&>.color-grey-3{
max-width: 10rem;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.versionInfo_right{
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
border-left: 1px solid #eee;
position: relative;
padding: 0px 30px 60px 24px;
&::before{
position: absolute;
left: -4px;
top:0px;
content: '';
width: 8px;
height: 8px;
background-color: #466AFF;
border-radius: 50%;
}
.sendAuthorImg{
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 5px;
}
.body-show{
padding: 5px 10px 10px 10px;
}
& .version-user>a>span{
display: inline-block;
& img{
width: 20px;
height: 20px;
}
}
}
.versionTag{
display: inline;
padding:0px 9px;
color: #fff;
position: relative;
margin-top: -8px;
height: 22px;
line-height: 20px;
border-radius: 4px;
&::before{
position: absolute;
content: "";
width: 0;
height: 0px;
border-left: 4px solid #cccccc;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-right: 4px solid transparent;
z-index: 9;
top: 6px;
right: -9px;
}
&:after{
width: 0px;
height: 0px;
top:6px;
right: -7px;
position: absolute;
border-left:4px solid #fff;
border-top:4px solid transparent;
border-bottom:4px solid transparent;
border-right:4px solid transparent;
content:'';
z-index:10;
}
}
.versionFile{
margin-top: 5px;
padding-top: 20px;
border-top: 1px solid #eee;
// width: 100%;
a{
display: block;
color: #333;
font-weight: 400;
height: 20px;
margin-bottom: 10px;
}
}
.versionTag.yellow{
border: 1px solid #FBBC06;
color: #FBBC06;
&::before{
border-left-color: #FBBC06;
}
}
.versionTag.green{
border: 1px solid #2DB44D;
color: #2DB44D;
&::before{
border-left-color: #2DB44D;
}
}
.versionTag.orange{
border: 1px solid #FF6E23;
color: #FF6E23;
&::before{
border-left-color: #FF6E23;
}
}
.addReleaseBtn{
text-align: right;
margin-bottom: 30px;
}
.versionName{
font-size: 16px;
color: #333;
margin-bottom: 15px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 18px;
line-height: 18px;
margin-top: -5px;
}
.versionmilepostleft{
padding: 15px;
margin-right: 50px;
width: 80%;
}
.topWrapper_btn_close {
background: #504b4b;
color: #FFFFFF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.topWrapper_btn_delete {
background: #da1010;
color: #FFFFFF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.versionrighe{
flex: 2;
}
.versionleft{
flex: 1;
text-align: right;
display: flex;
justify-content: right;
}
/* .version_line{
display: flex;
height: 30px;
margin: auto;
border-left:1px solid #eee;
} */
.version_line_one{
display: flex;
height: 45px;
margin: auto;
border-left:1px solid #eee;
}
.version_line_tpw{
display: flex;
height: 80px;
margin: auto;
border-left:1px solid #eee;
}
.versiondiv{
display: flex;
}
.verwinth{
width: 80%;
}
/*开启中 关闭中*/
.opendversionetail{
display: inline-block;
background: #21ba45;
color: #ffffff!important;
padding:0px 5px;
text-align: center;
height: 25px;
/*width: 110px;*/
border-radius: 4px;
line-height: 25px;
}
.closedversionetail{
display: inline-block;
background: #e60b0b;
color: #ffffff!important;
padding:0px 5px;
text-align: center;
height: 25px;
/*width: 110px;*/
border-radius: 4px;
line-height: 25px;
}
.versionrectangle {
width: 8px;
height: 8px;
border-radius: 100%;
margin-top: 15px;
margin-left: -4px;
margin-bottom: 10px;
background: rgb(83, 81, 81);
}
.ver-middle{
vertical-align: middle;
}
/* new */
.versionForm{
flex:1;
padding-right: 40px;
box-sizing: border-box;
.ant-select-auto-complete.ant-select .ant-input:hover,.ant-input:hover {
border-color: rgba(153, 153, 153, 0.8);
}
}
.versionTips{
width:268px;
box-sizing: border-box;
}
.infosTip{
border-bottom: 1px solid #EEEEEE;
color: #333;
padding-bottom: 26px;
margin-bottom: 26px;
font-weight: 400;
text-align: justify;
&:last-child{
border-bottom: none;
}
}
.dragBox{
background: rgba(153, 153, 153, 0.04);
border-radius: 4px;
border: 1px dashed #d9d9d9;
padding:20px;
.versionStyle{
border: none!important;
padding-bottom:20px;
.dragIcon{
font-size: 40px!important;
color: #666!important;
line-height: 40px;
height: 40px;
margin-bottom: 14px;
display: block;
}
}
.ant-upload-list-item:hover .ant-upload-list-item-info {
background-color: rgba(239, 244, 255, 1);
}
.ant-upload-list-item-info{
padding:0px 20px 0px 8px;
&>span{
display: flex;
align-items: center;
}
}
}
.set-ant-row .ant-row{
display: flex;
height: 20px;
align-items: center;
}
.itemInline{
display: flex;
align-items: flex-start;
position: relative;
&>p{
position: absolute;
bottom: -5px;
}
}
.itemInline .ant-row{
margin-bottom: 0px;
}
.prerelease{
padding-top: 20px;
.ant-form-item-control{
height: 20px;
line-height: 20px;
}
}
.releaseIndex{
margin: 30px auto;
width: 1200px;
}
.emptyPanel{
width: 100%;
background: #FAFCFF;
border-radius: 4px;
border: 1px solid rgba(42, 97, 255, 0.23);
min-height: 418px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.operation{
width: 400px;
border-top: 1px solid #eee;
padding-top: 34px;
text-align: center;
margin-top: 30px;
}
}
.ant-form-item-control{
line-height: initial;
}

View File

@ -1,17 +1,20 @@
import React ,{useEffect,useState } from 'react';
import React ,{useEffect,useRef,useState } from 'react';
import { truncateCommitId } from '../common/util';
import { AlignCenter , FlexAJ } from '../Component/layout';
import { Button } from 'antd';
import { Button, Tooltip,Progress, Popover, Anchor } from 'antd';
import './merge.css';
import './Index.scss';
function Files({data,history,owner,projectsId}){
function Files({ data,history,owner,projectsId , parentsSha }){
const [ files , setFiles ] = useState(data && data.files);
const [ copyfileTipTitle, setCopyfileTipTitle] = useState("复制文件路径");
const [ isOpen, setIsOpen] = useState(false);
useEffect(()=>{
if(data){
setFiles(data.files);
}
},[data])
},[data]);
function showDown(flag,index,isBin){
if(!isBin){
@ -22,35 +25,83 @@ function Files({data,history,owner,projectsId}){
}
}
function copyFileName(fileName){
var copyCont = document.createElement('input');
copyCont.defaultValue = fileName;
document.body.appendChild(copyCont);
copyCont.select(); //
document.execCommand("Copy"); //
copyCont.className = 'copyCont';
copyCont.style.display='none';
setCopyfileTipTitle("复制成功");
}
const folderOpen = (
<div className="folders">
<div className="folderList">
{files && files.map((item, key) => {
return (
<a href={`#value${key}`}>
<FlexAJ className="filesInfo" key={key} onClick={() => {item.flag && showDown(item.flag, key, item.isBin);setIsOpen(false);}}>
<AlignCenter>
<i className="iconfont icon-wenjianicon mr4"></i>
<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+"处删除":""}`}>
<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>}
</Tooltip>
</div>
</FlexAJ>
</a>
)
})}
</div>
</div>
)
return(
<div>
<AlignCenter className="color-grey-9 pb10" style={{borderBottom:"1px solid #eee"}}>
<i className="iconfont icon-sanjiaoxing-down mr5"></i>
<span>
共有<span className="color-grey-3"> {data && data.files_count} 个文件被更改</span>包括
<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>
<span className="color-grey-6 update-file-count">
共有<span className="color-grey-3"> {data && data.files_count} 个文件 </span>被更改包括
{ data && data.total_addition ? <span className="color-green"> {data && data.total_addition} 次插入</span>:"" }
{ data && data.total_addition && data.total_deletion ? " 和 ":""}
{ data && data.total_deletion ? <span className="color-red"> {data && data.total_deletion} 次删除</span>:""}
</span>
</span>
</div>
{isOpen && folderOpen}
</AlignCenter>
{
files && files.length>0 &&
<div>
<div className="fileList">
{
files.map((item,key)=>{
return(
<div className="files" key={key}>
<FlexAJ className="filesInfo" style={{cursor:item.isBin ? "default":"pointer"}} onClick={()=>showDown(item.flag,key,item.isBin)}>
<a id= {`value${key}`} className="anchorPoint"></a>
<FlexAJ className="filesInfo">
<AlignCenter>
{!item.isBin ? <i className={!item.flag?"iconfont icon-xiajiantou font-16 mr15 color-grey-9":"iconfont icon-youjiantou font-16 mr15 color-grey-9"}></i>:""}
<i className="iconfont icon-wenjia font-16 mr8 color-grey-9"></i>
<span>{item.name}</span>
{!item.isBin ? <i className={!item.flag?"iconfont icon-sanjiaoxing-down color-grey-9 mt4":"iconfont icon-triangle font-15 color-grey-9"} onClick={()=>showDown(item.flag,key,item.isBin)}></i>:""}
<span className="cursor-pointer" data-clipboard-text={item.name} onClick={()=>showDown(item.flag,key,item.isBin)}>{item.name}</span>
<Tooltip
title={copyfileTipTitle}
onVisibleChange={()=>setCopyfileTipTitle("复制文件路径")}
>
<i className="iconfont icon-fuzhiicon ml6" onClick={()=>copyFileName(item.name)}></i>
</Tooltip>
</AlignCenter>
<span>
<Button className="mr20" onClick={()=>{history.push(`/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}/${item.name}`)}}>查看文件</Button>
<span className="color-green">+{item.addition}</span>
<span className="color-red ml20">-{item.deletion}</span>
</span>
<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 + "处删除" : ""}`}>
<Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.addition/(item.addition+item.deletion)*100} />
<span className="ml10">{item.addition+item.deletion}</span>
</Tooltip>
<span className="see-file-btn" onClick={()=>{history.push(`/${owner}/${projectsId}${item.isDeleted ? `/commits/${truncateCommitId(parentsSha)}`:`/tree/${truncateCommitId(item.sha)}/${item.name}`}`)}}>查看文件</span>
</div>
</FlexAJ>
{
item.sections && item.sections.length >= 1 && !item.flag &&

View File

@ -18,4 +18,86 @@
.pr_tags_closed{
border:1px solid #FA6400;
color: #FA6400;
}
.update-file-count{
cursor: pointer;
& .color-grey-3{
font-weight: bold;
}
}
.fileList{
.sc-bxivhb{
width: 55rem;
overflow: hidden;
white-space: normal;
word-break: break-all;
}
.see-file{
width: 14rem;
.ml10{
display: inline-block;
width: 4.5rem;
cursor: default;
}
span{
width: 7%;
}
}
.anchorPoint{
position: relative;
top: -5rem;
display: block;
height: 0;
}
}
.filesInfo{
background: #FAFCFF;
border-color:rgba(42, 97, 255, 0.23);
.ant-progress-line {
width: 5rem;
}
.ant-progress-inner{
background-color: #D14A4A;
}
}
.folders,.ant-anchor{
margin-left: 0;
padding-left: 0;
}
.folders{
position: absolute;
z-index: 2;
left: 0px;
top: 37px;
color: #333333;
width: 75rem;
box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5);
border-radius: 4px;
border: 1px solid rgba(153, 153, 153, 0.32);
.ant-anchor-link-active > .ant-anchor-link-title {
color: #466AFF;
}
.ant-anchor-link {
padding: 0px;
}
.folderList{
max-height: 275px;
overflow:auto;
.files{
border: 0px;
}
.filesInfo {
padding: 10px 18px 10px 15px;
height: 55px;
background: #FFF;
border-bottom: 1px solid #EEEEEE;
&:hover{
background: #F3F4F6;
}
.color-green,.color-red{
width: 3%;
text-align: right;
}
}
}
}

View File

@ -152,13 +152,23 @@ form .ant-cascader-picker, form .ant-select {
margin-top: 15px;
border-radius: 2px;
}
.see-file-btn{
color: #466AFF;
cursor: pointer;
}
.filesInfo{
padding:10px 15px;
background-color: #fafafa;
}
.filesInfo .cursor-pointer{
cursor: pointer;
}
.filesContent{
border-top: 1px solid #ddd;
}
.icon-fuzhiicon:hover{
color: #466AFF;
}
.linesContent{
display: flex;
min-height: 30px;

View File

@ -65,16 +65,18 @@ class Index extends Component {
<FileLanguage language={language} select_language={this.select_language}></FileLanguage>
</div>
</div>
<Meditor
{...this.props}
{...this.state}
filepath={`${file_path}`}
language = {language}
content={undefined}
readOnly={false}
editorType="new"
descName={filename && `Add ${filename}`}
></Meditor>
<div className="editorBorder">
<Meditor
{...this.props}
{...this.state}
filepath={`${file_path}`}
language = {language}
content={undefined}
readOnly={false}
editorType="new"
descName={filename && `Add ${filename}`}
></Meditor>
</div>
</div>
</div>
</React.Fragment>

View File

@ -8,7 +8,13 @@
border: 1px solid #d9d9d9!important;
border-right: none!important;
}
.editorBorder .editorBorderBox{
border:1px solid #eee;
border-radius: 2px;
}
.editorBorder .editorBorderSubmitBox{
padding:20px 0px!important;
}
.userScrew{
margin:20px 0px;
border:1px solid #f4f4f4;
@ -39,7 +45,7 @@
z-index: 1;
}
.ant-input-group .ant-input:focus{
border-right: 1px solid #d9d9d9!important;
border-right: 1px solid rgba(70, 106, 255, 1)!important;
}
.ant-btn-primary.grey{
border:1px solid #BBBBBB;

View File

@ -10,6 +10,7 @@ class m_editor extends Component {
super(props);
this.state = {
editorValue: this.props.content,
prevHeight:0
};
}
componentDidUpdate=(prevProps)=>{
@ -41,42 +42,81 @@ class m_editor extends Component {
folding: true,
foldingStrategy: "indentation", // 代码可分小段折叠
automaticLayout: true, // 自适应布局
// overviewRulerBorder: false, // 不要滚动条的边框
// scrollBeyondLastLine: false, // 取消代码后面一大段空白
overviewRulerBorder: false, // 不要滚动条的边框
scrollBeyondLastLine: false, // 取消代码后面一大段空白
minimap: {
// 不要小地图
enabled: false,
},
};
const handleEditorMount = (editor, monaco) => {
editor.onDidChangeModelDecorations(() => {
requestAnimationFrame(updateEditorHeight); // folding
});
const updateEditorHeight = () => {
const editorElement = editor.getDomNode();
if (!editorElement) {
return;
}
const padding = 40;
const lineHeight = editor.getOption(
monaco.editor.EditorOption.lineHeight
);
const lineCount = editor.getModel().getLineCount() || 1;
let height =
editor.getTopForLineNumber(lineCount + 1) +
lineHeight +
padding ;
if(height<400){height = 400;}
if (this.state.prevHeight !== height) {
this.setState({
prevHeight:height
})
// setPrevHeight(height);
editorElement.style.height = `${height}px`;
editor.layout();
}
};
updateEditorHeight(); // typing
};
return (
<React.Fragment>
<div>
<div className="branchTable" style={{border:"1px solid #eee"}}>
<Editor
height="400px"
language={language ? language : "plaintext"}
theme={"vs-grey"}
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
editorWillMount={this.editorWillMount}
/>
</div>
{!readOnly && (
<div style={{marginTop:"20px",padding:"20px"}}>
<UserSubmitComponent
{...this.props}
{...this.state}
filepath={`${this.props.filepath}`}
content={editorValue}
editor_type={editorType}
currentBranch={currentBranch}
descName={descName}
></UserSubmitComponent>
</div>
)}
</div>
<div className="editorBorderBox">
<Editor
language={language ? language : "plaintext"}
theme={"vs-grey"}
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
editorWillMount={this.editorWillMount}
editorDidMount={handleEditorMount}
/>
</div>
{!readOnly && (
<div className="editorBorderSubmitBox" style={{marginTop:"20px",padding:"20px"}}>
<UserSubmitComponent
{...this.props}
{...this.state}
filepath={`${this.props.filepath}`}
content={editorValue}
editor_type={editorType}
currentBranch={currentBranch}
descName={descName}
></UserSubmitComponent>
</div>
)}
</React.Fragment>
);
}

View File

@ -16,7 +16,7 @@ const menu = [
{name:"合并请求",index:"pulls"},
{name:"Wiki",index:"wiki"},
{name:"工作流(beta版)",index:"devops"},
{name:"资源库",index:"resources"},
// {name:"资源库",index:"resources"},
{name:"里程碑",index:"versions"},
{name:"动态",index:"activity"},
]

View File

@ -87,7 +87,7 @@ export default Form.create()(
}
],
<Input
addonBefore={`https://`+ port ? `${hostname}:${port}/organize`:`${hostname}/organize`}
addonBefore={port ? `${hostname}:${port}/`:`https://${hostname}/`}
placeholder="组织账号" maxLength={100}
/>
)}

View File

@ -122,7 +122,7 @@ export default Form.create()(
validator:checkname
}
],
<Input placeholder="请输入组织账号" maxLength={100} disabled/>,true
<Input placeholder="请输入组织账号" disabled/>,true
)}
{helper(
"组织名称:",

View File

@ -1,7 +1,6 @@
import React, { Component } from "react";
import { Upload, Icon , Button } from 'antd';
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
import { AlignCenter } from '../Component/layout';
import axios from 'axios';
const { Dragger } = Upload;
@ -64,7 +63,6 @@ class Index extends Component {
changeIsComplete && changeIsComplete(true);
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList) });
this.fileIdList(fileList);
}
@ -111,7 +109,7 @@ class Index extends Component {
:
<Dragger {...upload} className={className}>
{icon || <Icon type="inbox" />}
<p className="ant-upload-text font-14">拖动文件或<span className="color-blue">点击此处上传</span></p>
<p className="ant-upload-text font-16 color-grey-3">拖动文件或点击此处上传</p>
</Dragger>
)
}

View File

@ -1,21 +0,0 @@
1.请求URL https://code.ihub.org.cn/api/v1/mirrors/create.json
2.请求方式: POST
3.参数:
{
"image_url": "xxx.git", #必填,且后缀必为.git,
"language": "Ruby", #必填,如数据库不存在,则会创建新的记录
}
4. 返回值: {
"status": 1,
"message": "同步成功项目ID===1806"
}
5. 返回值说明: 仅有当有返回值且返回值的status 的值为1 才是创建成功,其余均为创建失败

View File

@ -1,278 +0,0 @@
import React, { useState, useEffect, useCallback, forwardRef } from "react";
import styled from "styled-components";
import { AutoComplete, Select, Input, Checkbox, Button, Form } from "antd";
import Editor from "../../modules/tpm/challengesnew/tpm-md-editor";
import Upload from "../Upload/Index";
import Attachments from "../Upload/attachment";
import axios from "axios";
import "./version.css";
import UploadImg from "../Images/upload.png";
import { getBranch } from '../GetData/getData';
const { Option } = AutoComplete;
export default Form.create()(
forwardRef(
(
{ form, projectDetail , match, showNotification, history },
ref
) => {
const { getFieldDecorator, validateFields, setFieldsValue } = form;
const [tagList, setTagList] = useState(undefined);
const [branchList, setBranchList] = useState(undefined);
const [desc, setDesc] = useState(null);
const [fileList, setFileList] = useState(undefined);
const [attachment, setAttachment] = useState(undefined);
const [options , setOptions] = useState(undefined);
const repo_id = projectDetail && projectDetail.repo_id;
const { projectsId, versionId , owner } = match.params;
useEffect(()=>{
getBranchs(projectsId,owner);
},[projectsId])
async function getBranchs(id,owner){
let result = await getBranch(id,owner);
setBranchList(result);
}
const Span = styled.span`
margin: 0px 15px;
color: #bbb;
line-height: 35px;
`;
useEffect(() => {
if (versionId) {
const url = `/${owner}/${projectsId}/releases/${versionId}/edit.json`;
axios.get(url).then(result => {
if (result) {
setFieldsValue(result.data);
setDesc(result.data.body);
setAttachment(result.data.attachments);
}
});
}
}, [versionId]);
useEffect(() => {
if (projectsId) {
const url = `/${owner}/${projectsId}/tags.json`;
axios
.get(url,{params:{
limit:1000
}})
.then(result => {
if (result) {
setTagList(result.data);
setOptions(renderTagList(result.data));
}
})
.catch(error => {
console.log(error);
});
}
}, [projectsId]);
function renderTagList(list) {
if (list) {
let array = list.map((item, key) => {
return (
<Option key={key} value={item.name}>
{item.name}
</Option>
);
});
return array || undefined;
}
}
function submit() {
validateFields((err, value) => {
if(err)return;
if (versionId) {
let url = `/${owner}/${projectsId}/releases/${versionId}.json`;
axios
.put(url, {
...value,
body: desc,
attachment_ids: fileList
})
.then(result => {
if (result) {
showNotification("版本修改成功!");
history.push(`/${owner}/${projectsId}/releases`);
}
});
} else {
let url = `/${owner}/${projectsId}/releases.json`;
axios.post(url, {
...value,
body: desc,
attachment_ids: fileList
})
.then(result => {
if (result) {
showNotification("版本发布成功!");
history.push(`/${owner}/${projectsId}/releases`);
}
});
}
});
}
const helper = useCallback(
(label, name, rules, widget, isRequired = true) => (
<React.Fragment>
<span required={isRequired}>{label}</span>
<Form.Item>
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
</Form.Item>
</React.Fragment>
),
[]
);
//
function changeAuto(value){
let l = tagList.filter(item=>item.name.indexOf(value) > -1);
setOptions(renderTagList(l));
}
return (
<div className="main df">
<Form className="versionForm">
<div>
<p className="font-16 color-grey-3 mb15">{versionId?"编辑":"创建"}发行版</p>
<div>
<div className="itemInline">
{helper(
"",
"tag_name",
[{ required: true, message: "请输入获取或选择一个标签" }],
<AutoComplete
placeholder="标记一个版本"
onChange={changeAuto}
style={{ width: "200px" }}
>
{options}
</AutoComplete>
)}
<Span>@</Span>
{helper(
"",
"target_commitish",
[{ required: true, message: "请选择一个分支" }],
<Select
placeholder="请选择一个分支"
style={{ width: "200px" }}
showArrow={false}
>
{renderTagList(branchList)}
</Select>
)}
</div>
<p className="font-13 color-grey-8">
选择一个已经存在的标签或者在发布时新建一个标签
</p>
</div>
<div className="pt20">
{helper(
"",
"name",
[{ required: true, message: "请输入发行版的标题" }],
<Input placeholder="发行版的标题" />
)}
</div>
<div>
<Editor
placeholder={"描述此发行版"}
height={200}
mdID={`version-comments-description`}
initValue={desc}
onChange={setDesc}
/>
</div>
<div className="set-ant-row">
{helper(
"",
"prerelease",
[],
<Checkbox>这是一个预览版本</Checkbox>
)}
</div>
<div>
<Upload
className="versionStyle"
isComplete={true}
load={setFileList}
icon={
<img
src={UploadImg}
width="58"
alt=""
style={{ marginBottom: 15 }}
/>
}
size={100}
showNotification={showNotification}
/>
{versionId && attachment && attachment.length > 0 ? (
<Attachments
attachments={attachment}
showNotification={showNotification}
canDelete={true}
/>
) : (
""
)}
</div>
<p className="pt20">
<Button onClick={submit} type="primary" className="mr30">
{versionId ? "保存" : "创建"}发行版
</Button>
<Button
onClick={() =>
history.push(`/${owner}/${projectsId}/releases`)
}
style={{
backgroundColor: "rgba(187,187,187,1)",
color: "#fff"
}}
>
取消
</Button>
</p>
</div>
</Form>
<div className="versionTips">
<div className="infosTip">
<p className="font-16 mb15">标签命名建议</p>
<p className="mb15">
通常的做法是在版本名称前加上字母 v 前缀 v1.0 或者 v2.3.4
</p>
<p>
如果标签不适合在生产环境下使用请在版本名称后添加预发行版本例如v0.2-alpha
或者 v5.9-beta.3
</p>
</div>
<div className="infosTip">
<p className="font-16 mb15">语义化版本</p>
<p className="mb15">
如果你是第一次发布版本我们强烈建议你阅读语义化版本
</p>
</div>
<div className="infosTip">
<p className="font-16 mb15">附件大小说明</p>
<p className="mb15">
单个附件不能超过 100MGVP 项目200M每个仓库总附件不可超过
1G推荐项目不可超过 5GGVP 项目不可超过
20G附件总容量统计包括仓库附件和发行版附件
</p>
</div>
</div>
</div>
);
}
)
);

View File

@ -1,205 +0,0 @@
.topWrapper {
padding: 20px 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
border-bottom: 1px solid #EEEEEE;
align-items: center;
}
.topWrapper_btn_new {
background: #fff;
color: #5091FF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
border:1px solid #5091FF;
}
.versionInfo{
display: flex;
width: 100%;
}
.versionInfo_left{
display: flex;
width: 30%;
padding-top: 20px;
flex-direction: column;
align-items: flex-end;
padding-right: 15px;
}
.versionInfo_right{
flex: 1;
padding: 20px 0px 20px 15px;
display: flex;
flex-direction: column;
align-items: flex-start;
border-left: 1px solid #eee;
}
.versionTag{
display: inline;
border-radius: 2px;
padding:2px 12px;
font-size: 12px;
color: #fff;
}
.versionTag.yellow{
background-color: #FBBC06;
}
.versionTag.green{
background-color: #20BA45;
}
.versionTag.orange{
background-color: #F2711D;
}
.versionName{
font-size: 16px;
color: #333;
margin-bottom: 10px;
display: flex;
align-items: flex-end;
position: relative;
}
.versionName::before{
position: absolute;
left: -19px;
top:8px;
content: '';
width: 8px;
height: 8px;
background-color: #5091FF;
border-radius: 50%;
}
.versionmilepostleft{
padding: 15px;
margin-right: 50px;
width: 80%;
}
.topWrapper_btn_close {
background: #504b4b;
color: #FFFFFF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.topWrapper_btn_delete {
background: #da1010;
color: #FFFFFF!important;
padding:0px 12px;
text-align: center;
height: 32px;
line-height: 32px;
border-radius: 4px;
}
.versionrighe{
flex: 2;
}
.versionleft{
flex: 1;
text-align: right;
display: flex;
justify-content: right;
}
/* .version_line{
display: flex;
height: 30px;
margin: auto;
border-left:1px solid #eee;
} */
.version_line_one{
display: flex;
height: 45px;
margin: auto;
border-left:1px solid #eee;
}
.version_line_tpw{
display: flex;
height: 80px;
margin: auto;
border-left:1px solid #eee;
}
.versiondiv{
display: flex;
}
.verwinth{
width: 80%;
}
/*开启中 关闭中*/
.opendversionetail{
display: inline-block;
background: #21ba45;
color: #ffffff!important;
padding:0px 5px;
text-align: center;
height: 25px;
/*width: 110px;*/
border-radius: 4px;
line-height: 25px;
}
.closedversionetail{
display: inline-block;
background: #e60b0b;
color: #ffffff!important;
padding:0px 5px;
text-align: center;
height: 25px;
/*width: 110px;*/
border-radius: 4px;
line-height: 25px;
}
.versionrectangle {
width: 8px;
height: 8px;
border-radius: 100%;
margin-top: 15px;
margin-left: -4px;
margin-bottom: 10px;
background: rgb(83, 81, 81);
}
.ver-middle{
vertical-align: middle;
}
/* new */
.versionForm{
flex:1;
padding-right: 30px;
box-sizing: border-box;
}
.versionTips{
width:30%;
padding-left: 15px;
box-sizing: border-box;
}
.infosTip{
padding:20px;
background-color: #F1F8FF;
margin-bottom: 22px;
color: #333;
}
.versionStyle{
height: 200px!important;
border: 1px dashed rgba(80,145,255,1)!important;
}
.set-ant-row .ant-row{
display: flex;
height: 20px;
align-items: center;
}
.itemInline{
display: flex;
align-item: center;
margin-bottom: 5px;
}
.itemInline .ant-row{
margin-bottom: 0px;
}

View File

@ -1,128 +0,0 @@
import React, { Component } from "react";
import { Link } from 'react-router-dom';
import { Spin } from 'antd';
import NoneData from '../Nodata';
import './version.css';
import axios from 'axios';
import RenderHtml from '../../components/render-html';
/**
* issue_chosen:下拉的筛选列表,
* data:列表接口返回的所有数据,
* issues:列表数组,
* isSpin:加载中,
*/
class version extends Component {
constructor(props) {
super(props);
this.state = {
issue_chosen: undefined,
data: undefined,
releases:undefined,
issues: undefined,
isSpin: true,
search: undefined,
search_count: undefined,
}
}
componentDidMount = () => {
this.getIssueList();
}
// 获取列表数据
getIssueList = () => {
const { projectsId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/releases.json`;
axios.get(url).then((result) => {
if (result) {
this.setState({
data: result.data,
releases:result.data.releases,
issues: result.data.issues,
isSpin: false
})
}
}).catch((error) => {
console.log(error);
})
}
// 显示版本描述
showBody=(key,flag)=>{
let { releases } = this.state;
releases[key].bodyshow = !flag;
this.setState({
releases
})
}
renderList = (releases) => {
const { projectsId , owner } = this.props.match.params;
const { isManager , isDeveloper } = this.props;
const type = this.props.projectDetail && this.props.projectDetail.type;
if (releases && releases.length > 0) {
return (
releases.map((item, key) => {
return (
<div className="versionInfo" key={key}>
<span className="versionInfo_left">
<span className={`${item.draft === "稳定" ?"versionTag green":"versionTag yellow"}`}>{item.draft}</span>
<span className="mt10">{item.created_at}</span>
<span className="color-grey-8">
<i className="iconfont icon-biaoqian3 mr3 font-14"></i>
{item.tag_name}
</span>
</span>
<div className="versionInfo_right">
<span className="versionName">
<span className="task-hide">{item.name}</span>
{
(isManager || isDeveloper) && type !==2 &&
<Link to={`/${owner}/${projectsId}/releases/${item.version_id}/update`} className="color-blue ml3 font-12">编辑</Link>
}
</span>
<span className="color-grey-3">
<i className={`${item.bodyshow ? "iconfont icon-sanjiaoxing-down color-grey-8 mr3 font-14":"iconfont icon-triangle color-grey-8 mr3 font-14"}`} onClick={()=>this.showBody(key,item.bodyshow)}></i>
{item.user_name}:<span className="color-grey-8">发布了这个版本并在发布后提交给{item.target_commitish}</span>
</span>
{
item.bodyshow && <RenderHtml className="break_word_comments imageLayerParent" value={item.body} url={this.props.history.location}/>
}
<RenderHtml />
<p className="mt10 pl3">
<a href={item.tarball_url} style={{color:"#4CC1DA"}} className="mr30"><i className="iconfont icon-TAR font-18 mr5"></i>TAR</a>
<a href={item.zipball_url} style={{color:"#28BD6C"}}><i className="iconfont icon-ZIP font-18 mr5"></i>ZIP</a>
</p>
</div>
</div>
)
})
)
} else if (releases && releases.length === 0) {
return ( <NoneData _html="暂时还没有相关数据!" /> )
}
}
render() {
const { projectsId ,owner } = this.props.match.params;
const { data , releases , isSpin } = this.state;
const type = this.props.projectDetail && this.props.projectDetail.type;
return (
<div className="main" style={{padding:"0px"}}>
<div className="topWrapper" style={{padding:"15px 20px"}}>
<span className="font-18 color-grey-3">版本发布</span>
{
data && data.user_permission && type !== 2 ?
<Link to={`/${owner}/${projectsId}/releases/new`} className="topWrapper_btn_new">+ 发布新版</Link>
: ''
}
</div>
<div className="releasesVersion">
<Spin spinning={isSpin}><div>{this.renderList(releases)}</div></Spin>
</div>
</div>
)
}
}
export default version;

View File

@ -6,3 +6,11 @@ export function truncateCommitId(str) {
return str
}
}
// 秒数转2021-9-29 01:01
export function timeFormat(SecondsStr){
const time = new Date(SecondsStr*1000);
const hour = time.getHours()<10?"0".concat(time.getHours()):time.getHours();
const minutes = time.getMinutes()<10?"0".concat(time.getMinutes()):time.getMinutes();
return time.getFullYear()+"-"+(time.getMonth()+1)+"-"+time.getDate()+" "+hour+":"+minutes;
}

View File

@ -88,14 +88,23 @@ ul,ol,dl{
display: flex;
flex-wrap: wrap;
align-items: center;
&>span, & a:link, a:visited{
color: #333;
}
&>span{
cursor: default;
}
}
.commitDesc{
flex:1;
margin-left:20px;
font-size:16px;
color:#333;
line-height:26px;
line-height:20px;
word-break: break-all;
width: 49rem;
overflow: hidden;
white-space: normal;
&:hover{
text-decoration: underline;
}
}
.normalBox{
@ -143,7 +152,7 @@ form.ant-form{
}
form{
.ant-row.ant-form-item{
margin-bottom: 15px;
margin-bottom: 20px;
}
}
@media screen and (max-width: 1000px){
@ -285,4 +294,50 @@ form{
border-bottom: none;
}
}
}
.btn-83{
width: 83px;
height: 32px;
line-height: 30px;
text-align: center;
background: #FAFBFC;
border: 1px solid #D0D0D0;
display: inline-block;
border-radius: 5px;
font-weight: 500;
color: #333333!important;
margin-right: 15px;
&:hover{
background: #F3F4F6;
color: #333333!important;
}
&:active{
background: #D0D0D0;
color: #333333!important;
}
}
.shadow:hover{
background: #eeeff1;
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
}
a.hover:hover{
text-decoration: underline;
}
button.ant-btn-primary.btnblue{
background-color:rgba(70, 106, 255, 1);
border-color:rgba(70, 106, 255, 1);
&:hover{
background-color:rgba(70, 106, 255, 0.85);
border-color:rgba(70, 106, 255, 0.85);
}
}
button.btngrey{
background-color:#FFFFFF;
border-color:#D0D0D0;
color: #666666;
&:hover,&:focus{
border-color:rgba(153, 153, 153, 0.5);
color: #666666;
}
}

View File

@ -30,11 +30,11 @@
.ant-upload-list-item-info .anticon-loading,
.ant-upload-list-item-info .anticon-paper-clip {
color: #29bd8b !important;
color: rgba(102, 102, 102, 1) !important;
}
.anticon anticon-paper-clip {
color: #29bd8b !important;
color: rgba(102, 102, 102, 1) !important;
}
.MuiModal-root-15 {

View File

@ -53,8 +53,6 @@ class CompetitionContentspdfpeopledata extends Component {
let url = `/users/accounts/${id}.json`;
axios.get(url).then((result) => {
if (result.data) {
console.log("GetuseridApi");
console.log(result.data);
this.setState({
userdata:result.data
})

View File

@ -938,7 +938,7 @@ body #root {
}
.ant-input:focus {
border: 1px solid #d9d9d9 !important;
border: 1px solid rgba(70, 106, 255, 1) !important;
}
/* 公用的文字按钮:蓝、白、灰 */
@ -1423,7 +1423,7 @@ samp {
}
.newcourses .ant-select-selection--single:hover {
border: 1px solid #d9d9d9 !important;
border: 1px solid rgba(70, 106, 255, 1) !important;
}
.pd20 {
@ -1528,9 +1528,6 @@ samp {
display: none;
}
.exerciselist .ant-input {
border: 1px solid #d9d9d9 !important;
}
.exercisetime .ant-form-explain {
margin-left: 107px;
@ -1689,18 +1686,12 @@ samp {
height: 40px;
}
.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled) {
border: 1px solid #d9d9d9 !important;
}
.ant-input-affix-wrapper .ant-input-prefix,
.ant-input-affix-wrapper .ant-input-suffix {
background: #fafafa !important;
}
.ant-input:hover {
border: 1px solid #d9d9d9 !important;
}
.ant-input:focus {
box-shadow: none !important;

View File

@ -11,9 +11,6 @@
.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled){
border:1px solid #d9d9d9!important;
}
.ant-input:hover{
border:1px solid #d9d9d9!important;
}
.ant-input:focus{
box-shadow:none!important;
background-color: #fff!important;

View File

@ -163,7 +163,6 @@ body>.-task-title {
outline: 0;
-webkit-box-shadow: 0 0 0 2px transparent;
box-shadow: 0 0 0 2px transparent;
border: 1px solid #d9d9d9;
}
.HeaderSearch .ant-input-search .ant-input::-webkit-input-placeholder {