Merge remote-tracking branch 'upstream/gitlink_server' into gitlink_ssr_head

This commit is contained in:
黄心宇 2024-04-11 11:07:15 +08:00
commit a8cc8b467f
91 changed files with 2125 additions and 533 deletions

115
package-lock.json generated
View File

@ -224,22 +224,13 @@
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
"@babel/helper-annotate-as-pure": {
"version": "7.22.5",
"resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
"integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
"requires": {
"@babel/types": "^7.22.5"
}
},
"@babel/helper-builder-binary-assignment-operator-visitor": {
"version": "7.22.15",
"resolved": "https://registry.npmmirror.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz",
"integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==",
"requires": {
"@babel/types": "^7.22.15"
},
"dependencies": {
"jsesc": {
"version": "2.5.2",
"resolved": "http://173.15.15.82:8081/repository/npm-all/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
}
}
},
"@babel/helper-compilation-targets": {
@ -446,7 +437,7 @@
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
"resolved": "http://173.15.15.82:8081/repository/npm-all/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
@ -1439,72 +1430,34 @@
"regenerator-runtime": "^0.11.1"
},
"dependencies": {
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"dev": true
"@babel/code-frame": {
"version": "7.21.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
"requires": {
"@babel/highlight": "^7.18.6"
}
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"globals": {
"version": "11.12.0",
"resolved": "http://173.15.15.82:8081/repository/npm-all/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
"ms": {
"version": "2.1.2",
"resolved": "http://173.15.15.82:8081/repository/npm-all/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"@babel/runtime-corejs2": {
"version": "7.23.9",
"resolved": "https://registry.npmmirror.com/@babel/runtime-corejs2/-/runtime-corejs2-7.23.9.tgz",
"integrity": "sha512-lwwDy5QfMkO2rmSz9AvLj6j2kWt5a4ulMi1t21vWQEO0kNCFslHoszat8reU/uigIQSUDF31zraZG/qMkcqAlw==",
"requires": {
"core-js": "^2.6.12",
"regenerator-runtime": "^0.14.0"
},
"dependencies": {
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
}
}
},
"@babel/runtime-corejs3": {
"version": "7.23.9",
"resolved": "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.9.tgz",
"integrity": "sha512-oeOFTrYWdWXCvXGB5orvMTJ6gCZ9I6FBjR+M38iKNXCsPxr4xT0RTdg5uz1H7QP8pp74IzPtwritEr+JscqHXQ==",
"requires": {
"core-js-pure": "^3.30.2",
"regenerator-runtime": "^0.14.0"
}
},
"@babel/template": {
"version": "7.23.9",
"resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.23.9.tgz",
"integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==",
"requires": {
"@babel/code-frame": "^7.23.5",
"@babel/parser": "^7.23.9",
"@babel/types": "^7.23.9"
}
},
"@babel/traverse": {
"version": "7.23.9",
"resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.23.9.tgz",
"integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==",
"requires": {
"@babel/code-frame": "^7.23.5",
"@babel/generator": "^7.23.6",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.9",
"@babel/types": "^7.23.9",
"debug": "^4.3.1",
"globals": "^11.1.0"
}
},
"@babel/types": {
"version": "7.23.9",
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.23.9.tgz",
@ -8791,7 +8744,7 @@
},
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmmirror.com/promise/-/promise-7.3.1.tgz",
"resolved": "http://173.15.15.82:8081/repository/npm-all/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "~2.0.3"
@ -10765,7 +10718,7 @@
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
"resolved": "http://173.15.15.82:8081/repository/npm-all/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",

File diff suppressed because one or more lines are too long

View File

@ -3373,8 +3373,8 @@ a.edu-greyline-btn:hover {
.defalutSubmitbtn {
display: block;
border: 1px solid #4CACFF;
background-color: #4CACFF;
border: 1px solid #466aff;
background-color: #466aff;
color: #fff !important;
width: 120px;
text-align: center;
@ -3382,7 +3382,6 @@ a.edu-greyline-btn:hover {
border-radius: 2px;
width: 130px;
height: 40px;
background: rgba(76, 172, 255, 1);
border-radius: 4px;
font-size: 16px;
font-family: MicrosoftYaHei;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -32,7 +32,8 @@ export function initAxiosInterceptors(props) {
// 判断网络是否连接
initOnlineOfflineListener();
var proxy = "http://172.20.32.202:4000";
var proxy = "https://testforgeplus.trustie.net";
//响应前的设置
axios.interceptors.request.use(

View File

@ -21,7 +21,7 @@ export function getImageUrl(path) {
return `${path}`;
}
export function IsPC(){
export function isPhone(){
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone",
"SymbianOS", "Windows Phone",

View File

@ -7,7 +7,7 @@ export {
getImageUrl as getImageUrl,getImageUrlAbsolute as getImageUrlAbsolute,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, getZoneUrl as getZoneUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl,
turnbar,returnbar,setSeoMeta as setSeoMeta , IsPC as IsPC, addMeta as addMeta
turnbar,returnbar,setSeoMeta as setSeoMeta , isPhone as isPhone, addMeta as addMeta
} from './UrlTool';
export { setmiyah as setmiyah } from './Component';

View File

@ -14,9 +14,10 @@ function Index(props){
const [ content , setContent ] = useState(undefined);
const [ edit , setEdit ] = useState(false);
const [ fileList , setFileList ] = useState(undefined);
const [ editOpration , setEditOpration ] = useState(false);
// const [ editOpration , setEditOpration ] = useState(false);
const { owner , projectsId } = props.match.params;
const { isManager , isDeveloper , current_user, projectDetail } = props;
const editOpration = current_user && current_user.login && isManager
useEffect(()=>{
if(owner && projectsId && projectDetail){
@ -25,11 +26,11 @@ function Index(props){
}
},[owner,projectsId, projectDetail])
useEffect(()=>{
if( (current_user && current_user.login) && (isManager === true || isDeveloper === true)){
setEditOpration(true);
}
},[isManager , isDeveloper])
// useEffect(()=>{
// if( (current_user && current_user.login) && (isManager === true || isDeveloper === true)){
// setEditOpration(true);
// }
// },[isManager , isDeveloper])
function Init(){
const url = `/${owner}/${projectsId}/about.json`;
axios.get(url).then(result=>{

View File

@ -18,11 +18,12 @@ function Index(props){
<img src={logo} alt="" height="48px" />
</div>
<div className={"content"}>
<p>关联账号</p>
<p>关联第三方账号</p>
<div className="panels">
<p className="tips">您已完成头歌/github/gitee/qq/中国研究生赛事平台授权请补充Gitlink登录信息以完成第三方账号绑定</p>
<Menu selectedKeys={[key]} mode={`horizontal`} className="panelsMenu" onClick={chooseMenuFunc}>
<Menu.Item key="0">已有账号进行绑定</Menu.Item>
<Menu.Item key="1">账号注册并绑定</Menu.Item>
<Menu.Item key="0">已有Gitlink账号进行绑定</Menu.Item>
<Menu.Item key="1">Gitlink账号注册并绑定</Menu.Item>
</Menu>
{
key === "0" ?

View File

@ -93,7 +93,7 @@ function Bind(props){
<Input.Password autoComplete="new-password" addonBefore={<img src={lock} alt="" width="13px" />} className="psd" size="large" placeholder="请输入登录密码" />,
)}
</Form.Item>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"40px"}} onClick={bindloginFunc}>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"20px"}} onClick={bindloginFunc}>
绑定并登录
</Button>
</Form>

View File

@ -23,7 +23,17 @@
.panels{
background-color: #fff;
border-radius:6.6px;
padding:32px 42px;
padding:40px 32px;
.tips{
border:1px solid #FF6F00;
color: #FF6F00;
background: rgba(255,111,0,0.1);
padding:5px 15px;
border-radius: 3px;
line-height: 22px;
font-size: 13px;
margin-bottom: 15px!important;
}
.panelsMenu{
border-bottom: none!important;
margin-bottom: 36px!important;
@ -33,7 +43,7 @@
}
}
.bind_form{
padding-bottom: 48px;
padding-bottom: 10px;
.has-error .ant-input:focus,.has-error .ant-input, .has-error .ant-input:hover{
border-color: #ff4d4f!important;
}

View File

@ -310,7 +310,7 @@ function Bind(props){
<Input.Password autoComplete="new-password" addonBefore={<img src={lock} alt="" width="13px" />} className="psd" size="large" placeholder="请确认登录密码" />,
)}
</Form.Item>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"40px"}} onClick={bindRegFunc}>
<Button type="primary" disabled={bindFlag} size="large" style={{width:"100%",marginTop:"20px"}} onClick={bindRegFunc}>
注册并登录
</Button>
</Form>

View File

@ -4,8 +4,9 @@ import './branch.scss';
import SelectOverlay from './SelectOverlay';
import { findDOMNode } from 'react-dom';
import { turnbar } from 'educoder';
import { truncateCommitId } from "../common/util";
export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true })=>{
export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true , commitflag=false })=>{
const [ showValue , setShowValue ] = useState(branch);
const [ visible , setVisible ] = useState(false);
@ -34,10 +35,11 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
setShowValue(branch);
},[branch])
function ChangeB(params) {
function ChangeB(params,type) {
setVisible(false);
let en = turnbar(params);
changeBranch(en);
// let en = turnbar(params);
changeBranch(params);
setShowValue(type ? truncateCommitId(params) : params);
}
const menu = (
@ -46,6 +48,7 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
visible={visible}
changeBranch={ChangeB}
tagflag={tagflag}
commitflag={commitflag}
projectsId={projectsId}
owner={owner}
branchList={branchList}

View File

@ -1,13 +1,15 @@
import React , { useState , useEffect } from 'react';
import { Input , Spin , Menu } from 'antd';
import { getBranch , getTag } from '../GetData/getData';
import { getBranch , getTag , getCommit } from '../GetData/getData';
import { truncateCommitId } from "../common/util";
function SelectOverlay({ changeBranch , tagflag , projectsId , owner , visible }) {
function SelectOverlay({ changeBranch , tagflag , commitflag , projectsId , owner , visible }) {
const [ inputValue , setInputValue] = useState(undefined);
const [ nav , setNav ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
const [ data , setData ] = useState(undefined);
const [ commitData , setCommitData ] = useState(undefined);
const [ datas , setDatas ] = useState(undefined);
const [ keys ,setKeys] = useState("branch");
@ -31,33 +33,68 @@ function SelectOverlay({ changeBranch , tagflag , projectsId , owner , visible }
setDatas(result);
setIsSpin(false);
}
function chooseitem(value){
changeBranch(value);
async function getCommits(id,owner,search){
let result = await getCommit(id,owner,search);
setCommitData(result.data && result.data.commits);
setIsSpin(false);
}
function chooseitem(value,type){
changeBranch(value,type);
}
function changeInputValue(e){
setInputValue(e.target.value);
let filter = e.target.value ? data && data.length>0 && data.filter(item=>item.name.indexOf(e.target.value)>-1) : data;
setDatas(filter);
if(keys ==="commit"){
//
getCommits(projectsId,owner,e.target.value);
}else{
let filter = e.target.value ? data && data.length>0 && data.filter(item=>item.name.indexOf(e.target.value)>-1) : data;
setDatas(filter);
}
}
function changeNav(e){
setKeys(e.key);
setIsSpin(true);
setInputValue(undefined);
if(e.key === "branch"){
getBranchs(projectsId,owner);
setNav(0);
}else{
}else if(e.key === "commit"){
getCommits(projectsId,owner);
setNav(2);
}
else{
getTags(projectsId,owner);
setNav(1);
}
}
const commitEle =(ele)=> (
ele && ele.length>0 ?
<ul className="commitUl">
{
ele.map((i,k)=>{
return(
<li onClick={()=>chooseitem(i.sha,"commit")}>
<span style={{color:"#111"}}>{truncateCommitId(i.sha)}</span>
<div className="commitMsg">
<div className="task-hide">{i.commit_message}</div> @ {i.time_from_now}
</div>
</li>
)
})
}
</ul>
:
<div style={{padding:"10px 20px"}}>暂无提交数据</div>
)
return(
<div className="overlayBranch">
<div className="padding15" style={{paddingBottom:"0px"}}>
<Input
prefix={<i className="iconfont icon-sousuo_icon1 font-14"></i>}
placeholder={`请输入分支${tagflag ? "或标签" :""}名称搜索`}
placeholder={keys === "commit" ?"搜索提交":`请输入分支${tagflag ? "或标签" :""}名称搜索`}
autocomplete="off" className="OptionsInput"
value={inputValue}
onChange={changeInputValue}
@ -66,23 +103,27 @@ function SelectOverlay({ changeBranch , tagflag , projectsId , owner , visible }
<Menu mode="horizontal" className="navUl" selectedKeys={[keys]} onClick={changeNav}>
<Menu.Item key={"branch"}>分支</Menu.Item>
{ tagflag && <Menu.Item key={"tag"}>标签</Menu.Item> }
{commitflag && <Menu.Item key={"commit"}>最近提交</Menu.Item> }
</Menu>
<Spin spinning={isSpin}>
<ul className="OptionsUl" id="ul-btn">
{
datas && datas.length>0 &&
datas.map((item,key)=>{
return(
<li key={key} onClick={()=>chooseitem(item.name)}><a className="task-hide ulALink">{item.name}</a></li>
)
})
}
{
datas && datas.length === 0 &&
<p className="listTips">暂无{inputValue}{nav === 0 ?"分支":"标签"}~</p>
}
</ul>
{
keys === "commit" ? commitEle(commitData)
:
<ul className="OptionsUl" id="ul-btn">
{
datas && datas.length>0 &&
datas.map((item,key)=>{
return(
<li key={key} onClick={()=>chooseitem(item.name)}><a className="task-hide ulALink">{item.name}</a></li>
)
})
}
{
datas && datas.length === 0 &&
<p className="listTips">暂无{inputValue}{nav === 0 ?"分支":"标签"}~</p>
}
</ul>
}
</Spin>
</div>
)

View File

@ -22,7 +22,7 @@
background: #fff;
max-height: 300px;
}
.OptionsUl{
.OptionsUl,.commitUl{
min-height: 50px;
max-height: 220px;
overflow-y: auto;
@ -46,6 +46,26 @@
line-height: 32px;
width: 100%;
}
.commitUl{
padding:0px 20px;
}
.commitUl li{
padding:15px 0px;
border-bottom: 1px dashed rgba(168,168,168,0.31);
&:last-child{
border:none;
}
cursor: pointer;
}
.commitUl .commitMsg{
margin-top: 8px;
display: flex;
div{
word-break: break-all;
flex: 1;
width: 0;
}
}
.branch-tagBox{
border:1px solid #D0D0D0;
border-radius: 3px;

View File

@ -23,7 +23,7 @@ li.ant-menu-item{
border:1px solid #eee;
.img{
margin-right: 20px;
width: 190px;
max-width: 190px;
height: 90px;
border:1px solid rgba(238,238,238,1);
display: flex;

View File

@ -28,7 +28,7 @@ function Releases({ owner, projectsId, releaseVersions, distribution }) {
:
<div className="mt8">
暂无发行版
{distribution && <Link className="color-blue ml20" to={{pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}}>创建新版本</Link>}
{distribution && <Link className="color-blue ml20" to={{pathname:`/${owner}/${projectsId}/releases/new`,state:{stable:true}}}>发布新版本</Link>}
</div>
}

146
src/forge/Dataset/Index.jsx Normal file
View File

@ -0,0 +1,146 @@
import React, { useState , useEffect } from 'react';
import { Button , Table , Tooltip } from 'antd';
import { getImageUrl } from 'educoder';
import paperImg from './image/paperImg.png';
import NewModal from './component/new';
import { Link } from 'react-router-dom';
import "./index.scss";
import axios from 'axios';
function Index(props){
const { project , match , current_user , projectDetail , getProject } =props;
const permission = current_user && current_user.login ? (projectDetail && projectDetail.permission):"";
const { owner ,projectsId } = match && match.params;
const [ dataSource , setDataSource ] = useState([]);
const [ empty , setEmpty ] = useState(project && (!project.has_dataset));
const [ total , setTotal ] = useState(0);
const [ page , setPage ] = useState(1);
const [ visible , setVisible ] = useState(false);
const [ detail , setDetail ] = useState({});
const pageSize = 10;
useEffect(()=>{
if(!empty){
Init();
}
},[empty,page])
function Init(){
const url = `/v1/${owner}/${projectsId}/dataset`;
axios.get(url,{
params:{
page,limit:pageSize
}
}).then((result) => {
if (result) {
setDetail(result.data);
setDataSource(result.data && result.data.attachments);
setTotal(result.data.attachment_total_count);
}
}).catch((error) => { })
}
const columns=[
{
title:"文件名称",
dataIndex:"title",
key:1,
ellipsis:true,
width:"25%"
},
{
title:"描述",
dataIndex:"description",
key:2,
ellipsis:true,
width:"30%",
render:(value)=>{
return <Tooltip title={value}>{value}</Tooltip>
}
},
{
title:"创建者",
dataIndex:"creator",
key:3,
width:"10%",
render:(value,item)=>{
return <Link to={`/${value.login}`}><img src={getImageUrl(`/${value.image_url}`)} alt="" style={{borderRadius:"50%"}} width="32px" height="32px" /></Link>
}
},
{
title:"上传时间",
dataIndex:"created_on",
key:4,
width:"15%"
},
{
title:"大小",
dataIndex:"filesize",
key:5,
},
{
title:"操作",
dataIndex:"url",
key:6,
width:"10%",
render:(value,item)=>{
return <a href={value} className="color-blue">下载</a>
}
},
]
//
function addFunc(){
setEmpty(false);
setVisible(false);
getProject && getProject();
detail && detail.id && Init();
}
return(
<div className={`dataset`}>
<NewModal visible={visible} detail={detail} owner={owner} repo={projectsId} onCancel={()=>setVisible(false)} onOk={addFunc}/>
<div className={`mnistData`}>
{empty ?
<span className="font-16">数据集</span>
:
<React.Fragment>
<div style={{width:0,flex:1}} className="mr20">
<div className="df mb15" style={{alignItems:"center"}}><p className="font-16 weight500 task-hide">{detail.title}</p>{ detail.license_name ? <span className="license ml20">许可证 : {detail.license_name}</span> :""}</div>
<p style={{wordBreak:"break-all"}}>{detail.description}</p>
</div>
{(permission && permission!== "") ? <div className="df">
<Button type="primary" ghost onClick={()=>{setVisible(true)}}><i className="iconfont icon-a-bianji12 color-blue mr5 font-12"></i>编辑</Button>
<Link className="ml20 operateButton" to={`/${owner}/${projectsId}/dataset/upload`}><i className="iconfont icon-a-shangchuan2x color-white mr5 font-13"></i>上传文件</Link>
</div>:""}
</React.Fragment>
}
</div>
{
empty?
<div className={"emtpyData"}>
<img src={require('./image/empty.png')} alt="" width="68px"/>
<span className="font-22 weight400 color-grey-3">暂无数据集</span>
{ (permission && permission!== "") ? <p className="mt25 font-15 color-grey-6 weight400">我们非常欢迎您创建并分享您的数据集以便与其他用户共同促进开源社区的发展</p> :"" }
{ (permission && permission!== "") ? <Button type="primary" className="mt15" onClick={()=>{setVisible(true)}}>创建数据集</Button> :""}
</div>
:
<Table
className={`datasetTable`}
columns={columns}
dataSource={dataSource}
pagination={{total,pageSize,current:page,hideOnSinglePage:true,onChange:(p)=>setPage(p)}}
/>
}
{
detail.paper_content ?
<div className="mt25">
<p className="mb15 df" style={{alignItems:'center'}}><img src={paperImg} width={24} className="mr10" alt="" /><span className="font-16">研究论文</span></p>
<div className={`mnistData`}>{ detail.paper_content }</div>
</div>
:""
}
</div>
)
}
export default Index;

View File

@ -0,0 +1,155 @@
import React, { useEffect, useState } from 'react';
import { Modal ,Form ,Input , AutoComplete , Button , Select } from 'antd';
import '../index.scss';
import axios from 'axios';
const { TextArea } = Input;
const Option = Select.Option;
function New(props){
const { form , visible , onCancel , onOk , owner , repo , detail } = props;
const [ licenseId , setLicenseId ] =useState(undefined);
const [ licensesList , setLicensesList] =useState([]);
const [ filterLicensesList , setFilterLicensesList ] =useState([]);
const { getFieldDecorator , setFieldsValue } = form;
useEffect(()=>{
if(detail && detail.id){
setFieldsValue({...detail,license_id:detail.license_name});
setLicenseId(detail.license_id);
}
},[detail])
useEffect(()=>{
getLicenses();
},[])
function getLicenses(){
const url = `/licenses.json`
axios.get(url).then((result) => {
if (result) {
setLicensesList(result.data.licenses);
setFilterLicensesList(result.data.licenses);
}
}).catch((error) => { })
}
function submit(){
form.validateFields((err, values) => {
if(!err){
const url = `/v1/${owner}/${repo}/dataset.json`;
if(detail && detail.id){
axios.put(url,{
...values,license_id:licenseId
}).then((result) => {
if (result) {
onOk && onOk();
}
}).catch((error) => { })
}else{
axios.post(url,{
...values,license_id:licenseId
}).then((result) => {
if (result) {
onOk && onOk();
}
}).catch((error) => { })
}
}
})
}
function ChangePlatform(value){
let _data = [];
if (licensesList.length>0) {
_data = licensesList.filter(item => item.name.toLowerCase().indexOf(value.toLowerCase()) > -1);
}
setFilterLicensesList(_data);
}
function checkId(rule, value, callback, list, title){
let filter = list.filter(item => item.name === value);
if(!value){
callback();
}
if (filter && filter.length > 0) {
callback();
} else {
callback('请在下拉选项中选择正确的' + title + "!");
}
callback();
}
return(
<Modal
title={`${detail && detail.id?"编辑":"新建"}数据集`}
width="648px"
visible={visible}
footer={null}
className="newBoxForm"
centered={true}
closable={true}
onCancel={onCancel}
>
<Form>
<Form.Item label="数据集名称">
{getFieldDecorator('title', {
rules: [
{
required:true,
message:"请输入数据集名称"
}
],
})(
<Input placeholder="请输入数据集名称" maxLength={100} />,
)}
</Form.Item>
<Form.Item label="开源许可证">
{getFieldDecorator('license_id', {
rules: [{
validator: (rule, value, callback) => checkId(rule, value, callback, licensesList, '开源许可证')
}],
})(
<AutoComplete
placeholder="请选择开源许可证"
onChange={ChangePlatform}
className="plateAutoComplete"
onSelect={(e,e1)=>{e1.props && setLicenseId(e1.props.id)}}
>
{
filterLicensesList && filterLicensesList.map((item) => (
<Option key={item.id} value={item.name} id={item.id}>
{item.name}
</Option>
))
}
</AutoComplete>
)}
</Form.Item>
<Form.Item label="数据集描述">
{getFieldDecorator('description', {
rules: [
{
required:true,
message:"请输入描述内容"
}
]})(
<TextArea maxLength={500} placeholder="请输入描述内容" autosize={{ minRows: 5, maxRows: 5 }} showCount/>,
)}
</Form.Item>
<Form.Item label="研究论文">
{getFieldDecorator('paper_content', {rules:[]})(
<TextArea maxLength={500} placeholder="请输入研究论文" autosize={{ minRows: 5, maxRows: 5 }} showCount/>,
)}
</Form.Item>
<div className="center">
<Button style={{width:"95px"}} onClick={onCancel}>取消</Button>
<Button type="primary" className="ml20" style={{width:"95px"}} onClick={submit}>确定</Button>
</div>
</Form>
</Modal>
)
}
export default Form.create({ name: 'New' })(New);;

View File

@ -0,0 +1,126 @@
import React, { useEffect, useState } from 'react';
import { Upload ,Form ,Input , Button , message } from 'antd';
import '../index.scss';
import { Link } from 'react-router-dom';
import axios from 'axios';
const { Dragger } = Upload;
const { TextArea } = Input;
function UploadModal(props){
const { match , form ,history } =props;
const { owner ,projectsId } = match && match.params;
const [fileList, setFileList] = useState([]);
const [ detail , setDetail ] = useState({});
const [ loading , setLoading ] = useState(false);
const { getFieldDecorator } = form;
useEffect(()=>{
Init();
},[])
function Init(){
const url = `/v1/${owner}/${projectsId}/dataset`;
axios.get(url).then((result) => {
if (result) {
setDetail(result.data);
}
}).catch((error) => { })
}
function submit(){
form.validateFields((err, values) => {
if(!err){
const url = `/attachments.json`;
const { description ,files } = values;
setLoading(true);
const formData = new FormData();
formData.append("container_type","ProjectDataset");
formData.append("description",description);
formData.append("container_id",detail && detail.id);
formData.append("file",files.file);
const config = {
headers: { "Content-Type": "multipart/form-data" }
};
axios.post(url,formData,config).then((result) => {
if (result) {
setLoading(false);
history.push(`/${owner}/${projectsId}/dataset`);
}
}).catch((error) => { })
}
})
}
function isFolder(fileName) {
//
const lastDotIndex = fileName.lastIndexOf('.');
//
const nameAfterLastSlash = fileName.substring(fileName.lastIndexOf('/') + 1);
//
return lastDotIndex !== -1 && nameAfterLastSlash.includes('.');
}
const upload= {
name: 'file',
multiple: false,
maxCount:1,
type:"file",
beforeUpload: file => {
const isDirectory = file.type;
const name = file.name;
if (!isDirectory && !isFolder(name)) {
message.error('不能上传文件夹!');
}else{
setFileList([file]);
}
return false;
},
onRemove: file => {
const index = fileList.indexOf(file);
const newFileList = fileList.slice();
newFileList.splice(index, 1);
setFileList(newFileList);
},
fileList,
};
return(
<div className={`dataset`}>
<p className="font-18 fileTitle">上传数据集文件</p>
<Form className="newBoxForm">
<Form.Item label="文件描述">
{getFieldDecorator('description', {
rules: [
{
required:true,
message:"请输入文件描述"
}
],
})(
<TextArea autosize={{minRows:5,maxRows:5}} placeholder="描述字数不超过255个字符" maxLength={255} />,
)}
</Form.Item>
<Form.Item label="数据上传">
{getFieldDecorator('files', {
rules: [
{
required:true,
message:"请上传文件"
}
],
})(
<Dragger {...upload} className={"drag"}>
<p className="ant-upload-hint font-15">点击添加文件或直接拖拽文件到此处</p>
</Dragger>,
)}
</Form.Item>
<div>
<Link className="btn-83" to={`/${owner}/${projectsId}/dataset`}>取消</Link>
<Button loading={loading} type="primary" className="ml5" style={{width:"95px"}} onClick={submit}>确定</Button>
</div>
</Form>
</div>
)
}
export default Form.create({ name: 'UploadModal' })(UploadModal);;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

View File

@ -0,0 +1,73 @@
.dataset{
margin:0px auto;
width: 1200px;
padding:25px 0px;
.mnistData{
background: #FAFCFF;
border-radius: 3px;
border: 1px solid rgba(42,97,255,0.23);
padding:20px;
display: flex;
align-items: center;
justify-content: space-between;
p{
font-weight: 500;
font-size: 14px;
color: #202D40;
line-height: 22px;
&:first-child{
max-width: 600px;
}
}
.license{
height: 40px;
line-height: 40px;
background: #ECEFFE;
border-radius: 6px 6px 6px 6px;
padding:0px 12px;
font-weight: bold;
font-size: 15px;
color: #202D40;
display: inline-block;
}
}
.datasetTable{
.ant-table-thead > tr > th{
background-color: #fff;
}
}
.emtpyData{
background: #FAFCFF;
border-radius: 4px 4px 0px 0px;
min-height: 356px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 25px;
}
}
.newBoxForm{
.ant-row.ant-form-item{
margin-bottom: 20px!important;
position: relative;
}
.ant-col.ant-form-item-label{
line-height: unset;
font-weight: 400;
font-size: 15px;
color: #4C5B76;
}
.ant-form-explain{
position: absolute;
}
}
.fileTitle{
line-height: 25px;
padding-bottom: 22px;
margin-bottom: 15px!important;
border-bottom:1px solid rgba(167,178,194,0.44);
}
.drag{
height: 110px!important;
}

View File

@ -8,6 +8,10 @@ export const getBranch = async (id,owner)=>{
export const getTag = async (id,owner)=>{
return (await axios.get(`/${owner}/${id}/tags.json`,{params:{only_name:true}})).data.tags;
}
//
export const getCommit = async(id,owner,search)=>{
return await axios.get(`/v1/${owner}/${id}/commits/recent`,{params:{keyword:search}})
}
// hooks-web
export const getHooks = async (id,params)=>{
return (await axios.get(`/projects/${id}/hooks.json`,{params})).data;

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { Button, Popconfirm, Radio, Input, message, Tooltip, Spin, Pagination } from 'antd';
import axios from 'axios';
import { getImageUrl, timeAgo } from 'educoder';
import { getImageUrl, timeAgo, isPhone } from 'educoder';
import RenderHtml from '../../../../components/render-html';
import Attachment from '../../../Upload/attachment';
import EditComment from './editComment';
@ -109,7 +109,7 @@ function IssueCommentList(props){
}
return(
<div className="commentListBox">
(isPhone() && journalsCount === 0) ? '' : <div className="commentListBox info_text mt30">
{/* 评论 */}
<div className={`${!login && journalsCount > 0 && 'pb15'}`}>
<div className='typeActionBox mb20'>
@ -118,7 +118,7 @@ function IssueCommentList(props){
</div>
</div>
{/* 评论快速入口-仅有评论且登录时展示 */}
{login && category !== 'operate' && journalsCount > 0 && <div className={`pb30 ${journalsCount > 0 && 'pt15'}`}><div className='gotoComment'>点击<a className='ml5' href='#addComments'>添加评论</a></div></div>}
{login && category !== 'operate' && journalsCount > 0 && !isPhone() && <div className={`pb30 ${journalsCount > 0 && 'pt15'}`}><div className='gotoComment'>点击<a className='ml5' href='#addComments'>添加评论</a></div></div>}
{/* 评论/操作日志 展示列表 */}
<Spin spinning={spin}>
<div className={`issueCommentsBox ${category === 'comment' && !spin && 'justComment'}`}>
@ -135,7 +135,7 @@ function IssueCommentList(props){
<Link to={`/${item.user.login}`}>{item.user.name}</Link>
<span className='ml15 timeAgo font-14'>{timeAgo(item.created_at)}</span>
</div>
{login && <div>
{login && !isPhone() && <div>
{/* 平台管理员/仓库管理员/发布评论者/issue创建者 */}
{(admin || isManager || login === item.user.login || username === author) && <Popconfirm
placement="bottom"
@ -172,7 +172,7 @@ function IssueCommentList(props){
<span>{i.reply_user && i.reply_user.name}</span>
<span className='ml15 timeAgo font-14'>{timeAgo(i.created_at)}</span>
</div>
{login && <div>
{login && !isPhone() && <div>
{/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */}
{(admin || isManager || login === i.user.login || (i.reply_user && login === i.reply_user.login) || username === author) && <Popconfirm
placement="bottom"
@ -207,7 +207,7 @@ function IssueCommentList(props){
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
</div>}
{/* 添加评论 */}
{category !== 'operate' && <div className="pb30" style={{marginTop: journalsCount ? '-5px' : '30px'}}>
{category !== 'operate' && !isPhone() && <div className="pb30" style={{marginTop: journalsCount ? '-5px' : '30px'}}>
{login ? showEdit === 1 ? <EditComment {...props} cancelMd={cancelMd}/> : <div className="addComments" id='addComments'>
<img src={getImageUrl(image_url)} alt="" />
<div style={{flex:1}}>

View File

@ -1,5 +1,7 @@
import React from 'react';
import './index.scss';
import publicImg from '../../img/gitlink-gz.png';
import qqImg from '../../img/gitlink-qq.png';
function acce({type}){
return(
@ -14,11 +16,11 @@ function acce({type}){
<p style={{fontSize:'1.6em',color:"#141519"}}>欢迎加入GitLink</p>
<ul className="foot_ewm">
<li>
<img src={require('../../img/gitlink-gz.png')} alt="" />
<img src={publicImg} alt="" />
<span>开源发展委员会<br/>公众号</span>
</li>
<li>
<img src={require('../../img/gitlink-qq.png')} alt="" />
<img src={qqImg} alt="" />
<span>GitLink官方<br/>QQ群</span>
</li>
</ul>

View File

@ -50,6 +50,7 @@
width: 7em;
height: 7em;
margin-bottom: 0.8em;
border-radius: 3px;
}
span{
display: block;

View File

@ -0,0 +1,58 @@
.partner_jcc{
.grow_path {
height: 100px;
display: flex;
justify-content: space-around;
align-items: center;
.grow_path_item {
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
align-self: self-start;
cursor: pointer;
transition: 1s;
.grow_path_type {
width: 82px;
height: 58px;
// color: black;
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
}
.dot {
width:28px;
height:28px;
background-color:#ffffff;
border:7px solid;
border-color:#0d8dfd52;
border-radius: 50%;
transition: .5s;
}
}
.line {
height: 0px;
width: 1200px;
border-bottom: solid 1px #ddd;
transform: translate(0, -87px);
}
.zone_part_lists{
margin-top: 40px;
li {
max-height: 414px;
overflow-y: auto;
align-items: flex-start;
}
}
.type_active{
border-color: #0d8ffd !important;
}
::-webkit-scrollbar
{
display: none;
}
}

View File

@ -0,0 +1,81 @@
import React , { useEffect } from 'react';
import { useState } from 'react';
import './index.scss'
function Partner(props) {
const [ topics ,setTopics ] = useState(undefined);
const [ moreTypes ,setMoreTypes ] = useState(false);
const [ currentPartner ,setPartner] = useState(undefined);
const { id, partnerList } = props;
useEffect(()=>{
getUnit(partnerList);
},[id])
function getUnit(array){
const newArray = array
setMoreTypes(array.length > 1)
while(newArray[1] && newArray[1].length + newArray[0].length <= 5) {
newArray[0] = [...newArray[0], ...newArray.splice(1, 1)[0]]
}
setTopics(newArray)
setPartner(newArray[0][0].typeName)
}
return(
<div className='partner_jcc boxmain pt60 pb60'>
{
moreTypes &&
<div className="grow_path">
{
topics && topics.length>0 ?
topics.map((i, k) => {
return (
<div className="grow_path_item" key={ k } onClick={()=> setPartner(i[0].typeName)}>
<div className={`dot ${i[0].typeName === currentPartner ? 'type_active' : ''}`}></div>
<div className="grow_path_type">
{ i[0].typeName }
</div>
</div>
)
})
:
""
}
</div>
}
{
moreTypes && <div className="line"></div>
}
<ul className="zone_part_lists">
{
topics && topics.length>0 ?
topics.map((i, k)=>{
return(
<li key={ k } >
{
i[0].typeName === currentPartner ?
i.map((j,k1)=>{
return(
<div className="zone_part_item" key={ k1 }>
{
j.link ? <a key={ k1 } href={ j.link } target="_blank" ><img src={j.logo} alt=""/></a>
:
<div><img src={j.logo} alt=""/></div>
}
</div>
)
})
:
""
}
</li>
)
})
:
""
}
</ul>
</div>
)
}
export default Partner;

View File

@ -0,0 +1,234 @@
import React,{ useState, useRef, useEffect } from 'react';
import { httpUrl } from '../../fetch';
import img1 from '../../img/img1.png';
import visionFw from '../../img/vision-fw.png';
import visionYj from '../../img/vision-yj.png';
import visionZy from '../../img/vision-zy.png';
import visionJz from '../../img/vision-jz.png';
import guideArrow from '../../img/guideArrow1.png';
import { Link } from 'react-router-dom';
import shijian from '../../img/shijian.png';
import axios from 'axios';
import Projects from '../../Component/projects';
import PartnerJcc from './Component/partnerJcc';
import '../../indexZone1.scss'
import './index.scss'
import { Popover, Tabs } from 'antd';
let tabTimer = undefined;
function HeaderPageJCC(props){
const [ projectList , setProjectList ] = useState(undefined);
const [ personList , setPersonList ] = useState(undefined);
const [ newsList, setNewsList ] = useState(undefined);
const [ partnerList, setParterList ] = useState(undefined);
const { deptId } = props.match.params;
const { data, id, history } = props;
const [ activeKey, setActiveKey ] = useState(undefined);
const vision = [
{imageUrl: visionFw, title: '服务无边界'},
{imageUrl: visionYj, title: '云间有协作'},
{imageUrl: visionZy, title: '资源易共享'},
{imageUrl: visionJz, title: '价值可交换'}
]
useEffect(()=>{
if(id){
getProjectList();
getNewsList();
getPersonList();
getPartnerList();
}
return componentWillUnmount;
},[id])
function componentWillUnmount() {
clearInterval(tabTimer)
}
function getPersonList(){
const url = `${httpUrl}/zone/open/${id}/member/homePageList`;
axios.get(url).then(result=>{
if(result){
setPersonList(result.data.rows);
}
}).catch(error=>{})
}
function getNewsList(){
const url = `${httpUrl}/cms/doc/open/zone/${id}/homePageDocList`;
axios.get(url).then(result=>{
if(result){
setNewsList(result.data.rows);
clearInterval(tabTimer)
tabTimer = setInterval(() => {
handle(result.data.rows && result.data.rows[0].id, result.data.rows)
}, 4000)
}
}).catch(error=>{})
}
function getProjectList(){
const url = `${httpUrl}/zone/open/${id}/project/list`;
axios.get(url,{params:{
isHomepage:1
}}).then(result=>{
if(result){
if (result.data.rows.length < 3) {
setProjectList(result.data.rows)
} else {
setProjectList([...result.data.rows, {}]);
}
}
}).catch(console.error())
}
function getPartnerList(){
const url = `${httpUrl}/zone/open/${id}/partners/list`;
axios.get(url).then(result=>{
if(result){
const array = result.data.rows;
const newArray = [];
for(let i in array) {
let e = array[i]
if (e.zonePartnersList && e.zonePartnersList.length > 0) {
e.zonePartnersList[0].typeName = e.typeName;
}
if ( e.zonePartnersList.length > 0 ) {
newArray.push(e.zonePartnersList);
}
}
setParterList(newArray);
}
}).catch(error=>{})
}
function handle(value, rows){
// id
let idList = rows ? rows.map(obj => obj.id) : []
// tab
let tabs = document.querySelectorAll('.zone_news .ant-tabs-tab')
const tabIndex = idList.indexOf(Number(value))
if(tabIndex !== -1 ){
if(tabIndex !== idList.length -1){
tabs[tabIndex + 1].click()
}else{
tabs[0].click()
}
}
}
function handleTabChange(key) {
setTimeout(()=>{
setActiveKey(key)
},100)
clearInterval(tabTimer)
tabTimer = setInterval(() => {
handle(key, newsList)
}, 4000)
}
return(
<div className="zone_jcc_box">
{
//
data && (data.introductionTitle || data.introductionContent || data.introductionImage) &&
<div className="boxmain zone_infos mb30">
<div className="zone_infos_desc">
{data && <p className="in_title tleft">{data.firstTitle}</p>}
<p className="z_name">{data.introductionTitle}</p>
<p className="z_desc task-hide-2" dangerouslySetInnerHTML={{ __html: data.introductionContent }} ></p>
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="600px" height="361px"/>
}
</div>
}
<div className="zone_vision pt40 pb100">
<p className="in_title mb40">宗旨及愿景</p>
<div className='boxmain'>
{
vision.map((i, k) => {
return (
<div className="vision_item" key={ k }>
<div className="vision_icon">
<img src={i.imageUrl} alt="" />
</div>
<p className="vision_title">{ i.title }</p>
</div>
)
})
}
</div>
</div>
{
//
data && data.cmsShow === 1 && newsList && newsList.length>0 &&
<div className="zone_news">
<div className="boxmain" style={{display:"flex"}}>
<p className="news_title">{ data.homepageCmsTitle }</p>
<Tabs className='news_tab' tabPosition="left" onChange={handleTabChange} >
{
newsList.map((i,k)=>{
return(
<Tabs.TabPane tab={i.name} key={i.id}>
<div className='tab-content'>
<Link to={`/zone/${deptId}/newdetail/${i.id}`}><div className='title'>{i.name}</div>
<p><img src={shijian} alt="" className="mr3 photo" />{i.publishTime}</p>
<p className='new_desc'>{i.summary || '-'}</p>
<img src={newsList[k].headImg || img1} alt="" height="260px" style={{width:"100%",objectFit:"cover"}}/></Link>
</div>
</Tabs.TabPane>
)
})
}
</Tabs>
</div>
</div>
}
{
//
data && data.projectShow === 1 && projectList && projectList.length > 0 &&
<div className="zone_projects pt50 pb30">
<p className="in_title mb40">{ data.homepageProjectTitle }</p>
<Projects projectList={projectList}></Projects>
</div>
}
{
//
data && data.memberShow === 1 && personList && personList.length > 0 &&
<div className="zone_contributor">
<p className="in_title">{ data.homepageMemberTitle }</p>
{
<ul className="boxmain zone_c_lists">
{
personList.map((i,k)=>{
return(
<li key={k}>
<Popover overlayClassName="zone_popover" content={i.introduction || "暂无~"} title={i.name} trigger="hover">
<Link to={`/${i.login}`}><img src={i.imageUrl} alt="" /></Link>
</Popover>
</li>
)
})
}
</ul>
}
</div>
}
{
//
data && data.partnersShow === 1 && partnerList && partnerList.length > 0 &&
<div className="zone_parter">
<p className="in_title">{ data.homepagePartnersTitle }</p>
<PartnerJcc id={ id } partnerList={ partnerList }></PartnerJcc>
</div>
}
</div>
)
}
export default HeaderPageJCC;

View File

@ -0,0 +1,361 @@
@import '../../theme.scss';
.zone_jcc_box{
background-color: #f7f7f7;
padding-top:70px;
.zone_infos{
display: flex;
align-items: center;
margin-top: 54px;
padding:27px 24px;
position: relative;
&>img{
position: absolute;
right: 0;
top: 0;
border-radius: 4px;
object-fit: contain;
}
.zone_infos_desc{
position: relative;
padding:0px 34px 0px 10px;
flex: 1;
.tleft{
text-align: left;
margin-bottom: 25px !important;
}
.z_name{
color:#1f2329;
font-size:26px;
height: 28px;
line-height: 28px;
margin-bottom: 28px!important;
font-size: 20px;
}
.z_desc{
color:#3d485d;
font-size:14px;
line-height:32px;
word-break: break-all;
-webkit-line-clamp: 4;
white-space: pre-wrap;
background: white;
padding: 50px 250px 50px 50px;
margin-right: 350px;
color: inherit;
}
}
}
.zone_vision {
background-color: #ffffff;
.boxmain {
display: flex;
justify-content: space-between;
}
.vision_item {
width: 244px;
display: flex;
align-items: center;
flex-direction: column;
.vision_icon {
height: 162px;
width: 162px;
background-image: url(../../img/visionBack.png);
background-size: 100% 100%;
position: relative;
img {
width: 50%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
.vision_icon:hover {
background-image: url(../../img/visionBackActive.png);
}
.vision_title {
font-weight:700;
color:#1f2329;
font-size:17px;
line-height:27px;
text-align:center;
margin-top: 39px;
margin-top: 0;
font-weight: 600;
}
.vision_content {
margin-top: 21px;
color:#4c5876;
font-size:14px;
line-height:27px;
text-align:center;
}
}
}
.zone_news{
height: 30.2vw;
min-height: 475px;
background-color: #273144;
background-image: url(../../img/bgJccNews.png);
background-repeat: no-repeat;
background-size: cover;
.boxmain {
width: 1200px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.news_title {
height: 44px;
font-weight: 500;
color: #ffffff;
font-size: 32px;
text-align: center;
}
.photo {
margin-bottom: 3px;
margin-right: 5px;
}
.news_tab{
color: #fff;
height: 380px;
.ant-tabs-bar{
background-color: rgba(33, 84, 118, 0.5);
}
.ant-tabs-left-bar {
border-right-color: transparent !important;
}
.ant-tabs-nav-container{
font-size: 18px;
}
.ant-tabs-left-bar .ant-tabs-tab, .ant-tabs-right-bar .ant-tabs-tab {
padding: 34px 24px;
margin: 0;
text-align: left;
width: 260px;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ant-tabs-left-content {
padding-left: 100px;
border-left-color: transparent !important;
}
.ant-tabs-tab-active{
background-color: #04BBF7;
color: white;
}
.ant-tabs-nav .ant-tabs-tab:active {
color: white;
}
.ant-tabs-ink-bar{
display: none !important;
}
.ant-tabs-nav .ant-tabs-tab:hover {
color: white;
}
.tab-content{
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.title{
font-size: 20px;
margin: 10px 0;
}
a:link, a:visited {
color: white;
}
.new_desc{
height: 48px;
font-size: 15px;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
margin-top: 10px;
}
}
}
.zone_projects{
background-image:linear-gradient(180deg,#f1f4fa 0%,#ffffff 100%);
.right_arraw {
width: 48px;
height: 48px;
right: 0;
}
.left_arraw {
width: 48px;
height: 48px;
left: 0;
z-index: 100;
}
.slick-track {
display: flex;
align-items: center;
height: 435px;
.slick-slide {
height: auto;
}
}
.less_three_project {
display: flex;
justify-content: center;
.container {
.zone_p_item {
background: #fefefe;
}
margin: 10px;
}
}
.not_three_project {
.slick-track {
.slick-slide[aria-hidden="true"] {
opacity: 0;
}
}
}
.slick-current {
z-index: 100;
}
.slick-current .zone_p_item {
width:364px;
height:395px;
background:#fefefe;
}
.zone_p_item {
width: 364px;
height: 320px;
border-radius:16px;
padding: 42px 42px 35px 42px;
box-shadow:0px 0px 20px rgba(35, 54, 185, 0.06);
background: url(../../img/projectBg.png) no-repeat;
background-size: 100% 100%;
border-color:#ffffff;
transition: 0.3s;
display: flex;
flex-direction: column;
align-items: center;
.info_img {
width:74px;
height: 74px;
border-radius: 50%;
}
.z_p_title {
font-weight: 700;
color: #1f2329;
font-size: 16px;
text-align: center;
margin-top: 10px;
cursor: pointer;
width: 90%;
}
.z_p_desc{
margin-top: 18px;
color:#4c5876;
font-size:14px;
line-height:22px;
word-break: break-all;
-webkit-line-clamp: 5
}
}
.zone_p_item:hover {
.z_p_title {
color: #07a583;
}
}
}
.zone_contributor{
min-height: 466px;
background-color: #F7F9FC;
padding:44px 0px 30px;
.container {
height: 220px;
}
.zone_c_lists{
display: flex;
align-items: center;
margin-top: 50px;
flex-wrap: wrap;
li{
// background-image: url(./img/contributebg.png);
box-shadow:0px 0px 15px rgba(49,61,113,0.06);
background-size: 100% 100%;
width:80px;
height: 80px;
border:1px solid #fff;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 50%;
margin-bottom: 40px!important;
margin-right: 40px;
& img{
width: 80px;
height: 80px;
border-radius: 50%;
margin-bottom: 16px;
}
}
.slick-track {
display: flex;
}
.c_item {
margin-top: 40px;
margin-left: 10px;
width: 380px;
height: 165px;
background-image: url(../../img/cbg.png);
background-size: 100% 100%;
position: relative;
background-color:#f6f7fa;
border:2px solid;
border-color:#ffffff;
border-radius:4px;
box-shadow:0px 0px 10px rgba(7, 70, 165, 0.11);
padding: 30px 35px;
img {
position: absolute;
width:80px;
height:80px;
border-radius: 50%;
border: solid 2px #ffffff;
right: 27px;
top: -40px;
object-fit: cover;
}
.c_name {
color: #000000;
font-size: 16px;
text-align: center;
max-width: 240px;
}
.c_desc {
color: #4c5876;
font-size: 15px;
line-height: 26px;
}
}
.c_item:hover {
box-shadow:0px 0px 35px rgba(7, 70, 165, 0.11);
}
}
}
}
.zone_popover{
width: 300px;
:global{
.ant-popover-title{
font-weight: 600;
}
.ant-popover-content{
width: 300px;
}
}
}

View File

@ -4,7 +4,7 @@ import { httpUrl } from '../fetch';
import guideArrow from '../img/guideArrow.png';
import { Link } from 'react-router-dom';
import shijian from '../img/shijian.png';
import { getHomePageList , gethomePageDocList , getAllList } from '../api';
import { getHomePageList , gethomePageDocList , getAllList, getPartnerList } from '../api';
import axios from 'axios';
import Partner from '../Component/partner';
@ -21,7 +21,7 @@ function HeaderPage(props){
getProjectList();
getNewsList();
getPersonList();
getPartnerList();
getPartner();
}
},[id])
@ -48,10 +48,9 @@ function HeaderPage(props){
}
}).catch(console.error())
}
function getPartnerList(){
const url = `${httpUrl}/zone/open/${id}/partners/list`;
axios.get(url).then(result=>{
function getPartner(){
getPartnerList(id).then(result => {
if(result){
const array = result.data.rows;
const newArray = [];
@ -66,7 +65,7 @@ function HeaderPage(props){
}
setParterList(newArray);
}
}).catch(error=>{})
})
}
return(
<div className="zone_box">
@ -171,7 +170,7 @@ function HeaderPage(props){
}
</div>
}
{
//

View File

@ -16,12 +16,12 @@ import ra from '../img/ra.png'
import title from '../img/title.png'
import { Link } from 'react-router-dom';
import shijian from '../img/shijian.png';
import axios from 'axios';
import Nodata from '../../Nodata';
import Projects from '../Component/projects';
import Contributor from '../Component/contributor';
import Partner from '../Component/partner';
import '../indexZone1.scss'
import { getProjectsLists, getPartnerList, getHomePageList, gethomePageDocList } from '../api'
function HeaderPage(props){
const [ projectList , setProjectList ] = useState(undefined);
@ -65,13 +65,12 @@ function HeaderPage(props){
getProjectList();
getNewsList();
getPersonList();
getPartnerList();
getPartner();
}
},[id])
function getPersonList(){
const url = `${httpUrl}/zone/open/${id}/member/homePageList`;
axios.get(url).then(result=>{
getHomePageList(id).then(result=>{
if(result){
setPersonList(result.data.rows);
}
@ -79,19 +78,14 @@ function HeaderPage(props){
}
function getNewsList(){
const url = `${httpUrl}/cms/doc/open/zone/${id}/homePageDocList`;
axios.get(url).then(result=>{
gethomePageDocList(id).then(result=>{
if(result){
setNewsList(result.data.rows);
}
}).catch(error=>{})
}
function getProjectList(){
const url = `${httpUrl}/zone/open/${id}/project/list`;
axios.get(url,{params:{
isHomepage:1
}}).then(result=>{
getProjectsLists(id,{ isHomepage:1 }).then(result=>{
if(result){
if (result.data.rows.length < 3) {
setProjectList(result.data.rows)
@ -99,12 +93,11 @@ function HeaderPage(props){
setProjectList([...result.data.rows, {}]);
}
}
}).catch(console.error())
}).catch(error=>{})
}
function getPartnerList(){
const url = `${httpUrl}/zone/open/${id}/partners/list`;
axios.get(url).then(result=>{
function getPartner(){
getPartnerList(id).then(result => {
if(result){
const array = result.data.rows;
const newArray = [];
@ -119,7 +112,7 @@ function HeaderPage(props){
}
setParterList(newArray);
}
}).catch(error=>{})
})
}
return(
<div className="zone1_box">

View File

@ -22,6 +22,7 @@ function NewsCreate(props){
const [dirList, setDirList] = useState(undefined);
const [loading, setLoading] = useState(false);
const [isEdit, setIsEdit] = useState(false);
const { sectionCmsTitle } = props.data;
const layout = {
labelCol: { span: 2 },
wrapperCol: { span: 14 },
@ -123,7 +124,7 @@ function NewsCreate(props){
return(
<div className='boxmain pt30 newsCreateBox pb100'>
<Breadcrumb separator=">">
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{tempConfig[temp].mainTitle}</Breadcrumb.Item>
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{ sectionCmsTitle }</Breadcrumb.Item>
<Breadcrumb.Item>{isEdit ? '编辑' : '新建'}文章</Breadcrumb.Item>
</Breadcrumb>
<div className='formBox'>

View File

@ -4,7 +4,7 @@ import liulan from '../img/liulan.png';
import RenderHtml from '../../../components/render-html';
import { Base64 } from 'js-base64';
import { getNewsDetail } from '../api';
import { IsPC } from 'educoder';
import { isPhone } from 'educoder';
import { tempConfig } from '../tempInfo';
import Acce from '../Component/mobile/accessory';
import { addMeta, setSeoMeta } from 'educoder';
@ -29,7 +29,13 @@ function NewsDetail(props){
useEffect(()=>{
let initialContent = undefined;
if(IsPC() && zonedetail){
//
if(detail){
const { name, cmsDir, summary} = detail;
document.title = `${ name }/${ cmsDir.name }`;
addMeta('Keywords', `${ name },${ cmsDir.name },${ summary }`);
}
if(isPhone() && zonedetail){
setTimeout(()=>{
window.scrollTo(0,1);
if(detail)
@ -39,46 +45,23 @@ function NewsDetail(props){
document.querySelector('meta[name="viewport"]').setAttribute('content','width=device-width, user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0');
}
return()=>{
if(IsPC()){
if(isPhone()){
document.querySelector('meta[name="viewport"]').setAttribute('content', initialContent);
}
if (zonedetail) {
document.title= zonedetail.mainTitle;
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
}
}
},[zonedetail,detail])
useEffect(()=>{
if(id){
window.scrollTo(0,IsPC() ? 0 : 450);
window.scrollTo(0,isPhone() ? 0 : 450);
getDetails();
}
},[id])
useEffect(()=>{
//
if(detail){
setMeta()
}
return componentWillUnmount
}, [detail])
function componentWillUnmount() {
if (zonedetail) {
setMeta(true)
}
}
function setMeta(unmount = false) {
if (unmount) {
document.title= zonedetail.name;
setSeoMeta(`${zonedetail.name}`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
} else {
const { name, cmsDir, summary, id} = detail;
const title = `${ name }/${ cmsDir.name }`;
const keywords = `${ name },${ cmsDir.name },${ zonedetail.name }`;
document.title = title;
setSeoMeta(keywords, title, summary, `/zone/${deptId}/newdetail/${ id }`)
}
}
function getDetails(){
setIsSpin(true)
getNewsDetail(id).then(result=>{
@ -98,24 +81,24 @@ function NewsDetail(props){
return(
<React.Fragment>
{
IsPC() && <Acce type="head"/>
isPhone() && <Acce type="head"/>
}
<div className={IsPC() ? "detail_news_phone" :"detail_news_all"}>
<div className={isPhone() ? "detail_news_phone" :"detail_news_all"}>
<Spin spinning={isSpin} size="large">
{
detail &&
<div className="d_news_box">
{
IsPC() && zonedetail &&
isPhone() && zonedetail &&
<div className="p_title_box">
<p className="p_t_title"><a href={`/zone/${deptId}`}>{zonedetail.name}</a></p>
<p className="p_t_desc" dangerouslySetInnerHTML={{ __html: zonedetail.introductionContent }}></p>
</div>
}
{
!IsPC() &&
!isPhone() &&
<Breadcrumb separator=">">
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{tempConfig[temp].mainTitle}</Breadcrumb.Item>
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{ zonedetail.sectionCmsTitle }</Breadcrumb.Item>
{ cmsDir && <Breadcrumb.Item href={`/zone/${deptId}/news/${cmsDir.id}`}>{cmsDir.name}</Breadcrumb.Item> }
<Breadcrumb.Item>正文</Breadcrumb.Item>
</Breadcrumb>
@ -131,7 +114,7 @@ function NewsDetail(props){
{ detail.auditStatus === '0' && <li><Badge color="#eb1212" text="未通过" className='failed'/></li>}
</ul>
</div>
{ !IsPC() && <Divider dashed={true} /> }
{ !isPhone() && <Divider dashed={true} /> }
{
content ?
<RenderHtml className="informations_detail imageLayerParent" value={content} url={props.history.location} />
@ -141,8 +124,8 @@ function NewsDetail(props){
</div>
{
//
detail.auditStatus === '1' && <div className="info_text mt30">
{/* <CommentList {...props} docId={ id } reload={commentReload} author={ detail.createBy } reloadComment={()=>{setCommentReload(Math.random())}}/> */}
detail.auditStatus === '1' && <div>
<CommentList {...props} docId={ id } reload={commentReload} author={ detail.createBy } reloadComment={()=>{setCommentReload(Math.random())}}/>
</div>
}
@ -151,7 +134,7 @@ function NewsDetail(props){
</Spin>
</div>
{
IsPC() && <Acce type="foot" />
isPhone() && <Acce type="foot" />
}
</React.Fragment>
)

View File

@ -22,6 +22,7 @@ function SelfList(props){
const pathname = props.location.pathname;
const { deptId} = props.match.params;
const { id , temp , data } = props;
const { sectionCmsTitle } = data;
const limit = 15;
useEffect(()=>{
@ -142,7 +143,7 @@ function SelfList(props){
</div>
</Modal>
<Breadcrumb separator=">" style={{paddingTop:"20px"}}>
<Breadcrumb.Item><Link className="primaryColor" to={pathname.replace("/self","")}>{source ? "资源发布": temp === tempEnum.zone1 ? "新闻资讯":"领域资讯"}</Link></Breadcrumb.Item>
<Breadcrumb.Item><Link className="primaryColor" to={pathname.replace("/self","")}>{ sectionCmsTitle }</Link></Breadcrumb.Item>
<Breadcrumb.Item>我的{source ? "资源":"文章"}</Breadcrumb.Item>
</Breadcrumb>
<ul className="selfMenu">

View File

@ -33,8 +33,8 @@ function SourceDetail(props){
function componentWillUnmount() {
if (zonedetail) {
document.title= zonedetail.name;
setSeoMeta(`${zonedetail.name}`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
document.title= zonedetail.mainTitle;
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
}
}

View File

@ -46,6 +46,13 @@ export function getAllList(id,params){
})
}
export function getPartnerList(id){
return fetch({
url:`/zone/open/${id}/partners/list`,
method: 'get',
})
}
/**领域资讯 */
export function getNewsAllList(id,params){
return fetch({

View File

@ -5,11 +5,22 @@ function beforeFetch(actionUrl){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const service = axios.create({
const config = {
baseURL: actionUrl,
timeout: 1800000, // 请求超时时间
});
}
const service = axios.create(config);
service.interceptors.request.use(request => {
let deptId = sessionStorage.getItem('deptId')
if (deptId) {
// 用于权限区分
request.headers['Dept-Id'] = deptId
}
return request
})
service.interceptors.response.use(
response => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -11,9 +11,9 @@ import Loading from "../../Loading";
import { getMainInfos , getCheckZoneRole, getCurrentRole } from './api';
import PublicBanner from "./Component/publicBanner";
import './index.scss';
import { IsPC } from 'educoder';
// import "slick-carousel/slick/slick.css";
// import "slick-carousel/slick/slick-theme.css";
import { isPhone } from 'educoder';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { tempEnum } from "./tempInfo";
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
import { setSeoMeta } from 'educoder';
@ -89,6 +89,11 @@ const Help = Loadable({
loading: Loading,
});
const HomePageByJCC = Loadable({
loader : () => import("./Pages/JCC/homePage"),
loading : Loading,
});
function Index(props){
const [ data , setData ] = useState( props.zoneDetail || undefined);
const [ adminUrl , setAdminUrl ] = useState(undefined);
@ -97,7 +102,7 @@ function Index(props){
const [ temp , setTemp ] = useState( props.zoneDetail ? props.zoneDetail.template : tempEnum.zone);
const [ role , setRole ] = useState(undefined);
const { pathname } = props.history.location;
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && IsPC();
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && isPhone();
const {current_user} = props;
if (__SERVER__ && data) {
@ -127,17 +132,19 @@ function Index(props){
return
}
let data = result.data.data;
if (data.template) {
if (data && data.template) {
setTemp( data.template )
}
setData(data);
document.title= data && data.name;
document.title= data && data.mainTitle;
setId(data.id);
if(data.id){
getAdminUrl(data.id);
getRole(data.id)
}
setSeoMeta(`${data.name}`, data.name, data.subTitle, `/zone/${deptId}`)
// deptIdheader
sessionStorage.setItem('deptId', data.deptId)
setSeoMeta(`${data.name},`, data.name, data.subTitle, `/zone/${deptId}`)
}
}).catch(console.error())
}
@ -257,8 +264,10 @@ function Index(props){
<Route
path="/zone/:deptId"
render={(p) => (
deptId==="CCF-ODC"?<HeaderPageCCF {...props} {...p} data={data} id={id} temp={ temp } />:
temp === tempEnum.zone ? <HeaderPage {...props} {...p} data={data} id={id} temp={ temp }/> : <HeaderPageZone1 {...props} {...p} data={data} id={id} temp={ temp }/>
deptId==="CCF-ODC" ? <HeaderPageCCF {...props} {...p} data={data} id={id} temp={ temp } />:
deptId === "JCC" ? <HomePageByJCC {...props} {...p} data={data} id={id}/>:
temp === tempEnum.zone ? <HeaderPage {...props} {...p} data={data} id={id} temp={ temp }/>:
<HeaderPageZone1 {...props} {...p} data={data} id={id} temp={ temp }/>
)}
></Route>
</Switch>

View File

@ -16,8 +16,8 @@ function NewPanel(props,ref){
const { form: { getFieldDecorator, validateFields , setFieldsValue } } = props;
useState(()=>{
title && setTimeout(()=>{
setFieldsValue({subject:title});
setTimeout(()=>{
title && setFieldsValue({subject:title});
desc && onContentChange(desc);
},100)
files && setAttachments(files);

View File

@ -77,6 +77,10 @@ function List(props){
}else{
Init();
}
// issue
if (props.location.state && props.location.state.issue) {
chooseTagFunc(props.location.state.issue)
}
},[])
useEffect(()=>{

View File

@ -189,6 +189,12 @@ function Sign(props){
c && setDefaultColor(`#${c}`);
setEdit(true);
}
// issue
function toIssues(issue) {
props.history.push({ pathname: `/${owner}/${projectsId}/issues`, state: { issue: issue } });
}
return(
<div>
<DelBox
@ -236,11 +242,11 @@ function Sign(props){
<p>
<span className="square mr10" style={{backgroundColor:`${i.color}`}}></span>
<i className="iconfont icon-biaoji mr3 font-12 " style={{color:`${i.color}`}}></i>
<span className="task-hide" title={i.name} style={{maxWidth:210}}>{i.name}</span>
<span className="task-hide color-blue pointer" title={i.name} style={{maxWidth:210}} onClick={() => toIssues(i) }>{i.name}</span>
</p>
<p style={{flex:2}} className="task-hide">{i.description}</p>
<p>
<span>{i.issues_count || 0} 疑修</span>
<span><span className="task-hide color-blue pointer" onClick={() => toIssues(i) } >{i.issues_count || 0} </span> 疑修</span>
{/* <span className="line">{i.pull_requests_count || 0} 合并请求</span> */}
</p>
<p>

View File

@ -69,7 +69,7 @@ function CoderDepot(props){
let pathname = props.history.location.pathname;
let search = props.history.location.search
//distribution
const distribution = projectDetail && projectDetail.type ===0 && ( projectDetail.permission && projectDetail.permission !== "Reporter");
const distribution = projectDetail && ( projectDetail.permission && projectDetail.permission !== "Reporter");
useEffect(()=>{
if(bannerList && bannerList.length>0){
@ -113,9 +113,9 @@ function CoderDepot(props){
const { author, name, description, default_branch} = projectDetail;
if(branchName && branchName !== default_branch){
// /
document.title = `${author && author.name}/${name}-${branchName}-for gitlink;for git`;
document.title = `${author.name}/${name}-${branchName} | GitLink`;
}else{
document.title = `${author && author.name}/${name}${description?': '+description:''}-for gitlink;for git` ;
document.title = `${author.name}/${name}${description?': '+description:''} | GitLink` ;
}
}
}, [treeValuePath, projectDetail, branchName])
@ -517,9 +517,9 @@ function CoderDepot(props){
}
{
checkIfLogin && checkIfLogin()?
<a className='newBtn' onClick={()=>eventTrack(`/${owner}/${projectsId}/webIDE/tree/${branchName||defaultBranch}`)}>Web IDE <div className='newBtnImg'></div></a>
<a className='newBtn' onClick={()=>eventTrack(`/${owner}/${projectsId}/webIDE/tree/${branchName||defaultBranch}`)}>Web IDE</a>
:
<a className='newBtn' onClick={()=>{showLoginDialog(`/${owner}/${projectsId}`)}}>Web IDE <div className='newBtnImg'></div></a>
<a className='newBtn' onClick={()=>{showLoginDialog(`/${owner}/${projectsId}`)}}>Web IDE</a>
}
<Dropdown overlay={downloadMenu} placement="bottomRight" trigger={['click']}>

View File

@ -104,7 +104,14 @@ const TrendsIndex = Loadable({
loader: () => import('../Activity/Activity'),
loading: Loading,
})
const DataSetIndex = Loadable({
loader: () => import('../Dataset/Index'),
loading: Loading,
})
const DataSetUpload = Loadable({
loader: () => import('../Dataset/component/uploadModal'),
loading: Loading,
})
const DevAbout = Loadable({
loader: () => import('../About/Index'),
loading: Loading,
@ -158,6 +165,8 @@ function checkPathname(projectsId, owner, pathname) {
name = "activity"
} else if (url.indexOf("/settings") > -1) {
name = "settings"
} else if (url.indexOf("/dataset") > -1) {
name = "dataset"
} else if (url.indexOf(`/devops`) > -1) {
name = "devops"
} else if (url.indexOf(`/source`) > -1) {
@ -596,6 +605,7 @@ class Detail extends Component {
const common = {
getDetail: this.getDetail,
getBanner: this.getBanner,
getProject:this.getProject,
changeOpenDevops: this.changeOpenDevops,
defaultBranch
}
@ -742,6 +752,18 @@ class Detail extends Component {
() => (<DevAbout {...this.props} {...this.state} {...common} />)
}
></Route>
{/* 数据集 */}
<Route path="/:owner/:projectsId/dataset/upload"
render={
(props) => (<DataSetUpload {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/dataset"
render={
(props) => (<DataSetIndex {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* wiki新增文件 */}
<Route path="/:owner/:projectsId/wiki/add"
render={

View File

@ -551,4 +551,10 @@
.replaceStyle{
height: 140px!important;
}
.hovergrey:hover{
border-color: #afb7c2;
color:#4c5b76;
background-color: rgba(175, 183, 194, 0.16);
}

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Tooltip } from 'antd';
import { Button, Tooltip } from 'antd';
import { getImageUrl } from 'educoder';
import { AlignCenter } from '../Component/layout';
import { Link } from 'react-router-dom';
@ -78,6 +78,10 @@ class IndexItem extends Component {
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
</span>
{/* <span>
<Button type="primary" size="small">置顶</Button>
<Button size="small" style={{height:"26px",lineHeight:"24px"}} className="hovergrey">取消置顶</Button>
</span> */}
</div>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

View File

@ -228,7 +228,7 @@
li{
text-align: center;
padding:0px;
margin-right: 35px;
margin-right: 30px;
display: flex;
& > a{
position: relative;

View File

@ -3,6 +3,7 @@ import { Skeleton , Tooltip} from 'antd';
import { Link } from 'react-router-dom';
import { numFormat } from 'educoder';
import QuitBox from './quit';
import datasetImg from '../img/dataset.jpg';
import axios from 'axios';
function DetailBanner({ history,list , owner , projectsId ,showNotification , url , pathname , state , urlFlag , projectDetail , platform ,open_devops ,current_user, showNpsModal }){
@ -98,7 +99,17 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
<li className={pathname==="devops" ? "active" : ""}>
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
<Link className='newTab' to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>引擎(Engine)<div className='newBtnImg'></div>
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>引擎(Engine)
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
</Link>
</li>
:""
}
{
item.menu_name === "dataset" ?
<li className={pathname==="dataset" ? "active" : ""}>
<Link className='newTab' to={{ pathname: `/${owner}/${projectsId}/dataset`, state:{...state,open_devops} }}>
<img src={datasetImg} height={15} alt="" className="mr5 mt5"/>数据集
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
</Link>
</li>
@ -138,7 +149,7 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
<li className={pathname === "server" ? "active" : ""}>
<Link className='newTab' to={{ pathname: `/${owner}/${projectsId}/service`, state }}>
<i className={"iconfont icon-fuwuicon color-grey-3 mr5 font-15"} style={{marginTop:"1px"}}></i>
<span>服务</span><div className='newBtnImg'></div>
<span>服务</span>
</Link>
</li>
}
@ -168,6 +179,7 @@ function DetailBanner({ history,list , owner , projectsId ,showNotification , ur
)
})
}
</ul>
:
<Skeleton paragraph={false} active={true}/>

View File

@ -41,7 +41,7 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_u
if(value.length > 5){
callback("最多创建5个标签");
}else if(boolLength.length){
callback("每个标签最多支持20个字");
callback("每个标签最多支持20个");
}else if(bool.length){
callback("标签名只允许包含中文、字母、数字或者中划线(-), 不能以中划线开头");
}

View File

@ -3,7 +3,7 @@ import CopyTool from '../../Component/CopyTool';
import { truncateCommitId } from '../../common/util';
import { Link } from 'react-router-dom';
import { getImageUrl , turnbar } from 'educoder';
import { Button, Dropdown , Input, Menu , message, Modal, Select, Spin, Table, Tooltip } from 'antd';
import { Button, Dropdown , Input, Menu , message, Modal, Select, Spin, Table, Tooltip , Radio , Popover } from 'antd';
import Modals from '../../Component/PublicModal/Index';
import './Index.scss';
@ -29,26 +29,13 @@ function Index(props) {
const [total, setTotal] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [ chooseStatus , setChooseStatus ] = useState("all");
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail , defaultBranch, getDetail } = props;
useEffect(()=>{
// getList();
const url = `/v1/${owner}/${projectsId}/branches.json`;
Axios.get(url,{
params:{
keyword: name,
page: current,
limit: pageSize
}
}).then(result=>{
if(result){
const {branches, total_count} = result.data;
setTotal(total_count)
setList(branches);
}
setIsSpin(false);
}).catch(error=>{setIsSpin(false);})
},[name, reload, pageSize, current])
getList();
},[name, reload, pageSize, current,chooseStatus])
useEffect(()=>{
if(projectDetail && document.title.indexOf('所有分支-') === -1){
@ -84,7 +71,7 @@ function Index(props) {
dataIndex: "commit",
key: 3,
ellipsis: true,
width: "240px",
width: "260px",
render: (txt, item) => {
return (
<div className='treeinfo'>
@ -96,7 +83,7 @@ function Index(props) {
</Link>
:
<React.Fragment>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ txt.committer.image_url}`)} alt="" className='branchLastUpdateUserImage'/>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ txt && txt.committer && txt.committer.image_url}`)} alt="" className='branchLastUpdateUserImage'/>
<span style={{fontWeight:"500"}}>{txt && txt.committer && txt.committer.name}</span>
</React.Fragment>
}
@ -110,7 +97,7 @@ function Index(props) {
dataIndex: "commit_id",
key: 4,
ellipsis: true,
width: "250px",
width: "225px",
render: (txt, item) => {
return <div className="treecopy">
<div className='treeCopyBox'>
@ -123,53 +110,83 @@ function Index(props) {
</div>
</div>
}
},
{
title: "分支类型",
dataIndex: "default_branch",
key: 5,
width: "150px",
render: (txt, item) => {
return isManager ? txt === item.name ? <a className='btn-83' style={{width: '93px'}}>默认分支</a> : <Select value={1} onChange={(value)=>{updateDefaultBranch(value, item)}}>
<Select.Option value={0}>默认分支</Select.Option>
<Select.Option value={1}>普通分支</Select.Option>
</Select> : txt === item.name ? '默认分支' : '普通分支'
}
},
{
title: "操作",
dataIndex: "name",
key: 6,
align: "center",
width: "320px",
className: "branchActionColumn",
render: (txt, item) => {
return (
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Tooltip title={`${txt}为源分支创建一个合并请求`}><Link to={
// forkpr
projectDetail && projectDetail.fork_info?
`/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}/compare/${turnbar(txt || defaultBranch)}...${owner + ':' + turnbar(txt)}`
:`/${owner}/${projectsId}/compare/${turnbar(defaultBranch)}...${turnbar(txt)}`
} className="btn-83">+ 合并请求</Link></Tooltip>
}
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight">
<a className="btn-83" style={{width: '76px'}}>下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a>
</Dropdown>
{isManager && (txt === item.default_branch ? <Tooltip title={`该分支为默认分支,不支持删除操作`}><Button style={{width: '76px'}} disabled>删除</Button></Tooltip> : <Button className='f6_bor_but' style={{width: '76px'}} onClick={()=>{setVisible(item)}}>删除</Button>)}
</div>
)
}
}
];
const mergeCol = {
title: "分支类型",
dataIndex: "default_branch",
key: 5,
width:"150px",
render: (txt, item) => {
return isManager ? txt === item.name ? <a className='btn-83' style={{width: '93px'}}>默认分支</a> : <Select value={1} onChange={(value)=>{updateDefaultBranch(value, item)}}>
<Select.Option value={0}>默认分支</Select.Option>
<Select.Option value={1}>普通分支</Select.Option>
</Select> : txt === item.name ? '默认分支' : '普通分支'
}
}
const operCol ={
title: "操作",
dataIndex: "name",
key: 6,
align: "center",
width: "300px",
className: "branchActionColumn",
render: (txt, item,key) => {
return (
item.is_deleted ?
isManager ? <div style={{textAlign:"right"}}>
<Popover content={<span>将基于 Commits<span className="color-blue ml3 mr3">{truncateCommitId(item.commit_id)}</span>恢复分支</span>}>
<Button onClick={()=>restoreBranch(item.branch_id,item.name)}>恢复分支</Button>
</Popover>
</div>:""
:
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Tooltip title={`${txt}为源分支创建一个合并请求`}><Link to={
// forkpr
projectDetail && projectDetail.fork_info?
`/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}/compare/${turnbar(txt || defaultBranch)}...${owner + ':' + turnbar(txt)}`
:`/${owner}/${projectsId}/compare/${turnbar(defaultBranch)}...${turnbar(txt)}`
} className="btn-83">+ 合并请求</Link></Tooltip>
}
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight">
<a className="btn-83" style={{width: '76px'}}>下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a>
</Dropdown>
{isManager && (txt === item.default_branch ? <Tooltip title={`该分支为默认分支,不支持删除操作`}><Button style={{width: '76px'}} disabled>删除</Button></Tooltip> : <Button className='f6_bor_but' style={{width: '76px'}} onClick={()=>{setVisible(item)}}>删除</Button>)}
</div>
)
}
}
function restoreBranch(id,name){
const url = `/v1/${owner}/${projectsId}/branches/restore.json`;
Axios.post(url,{
branch_id:id,branch_name:name
}).then(result=>{
message.success('恢复分支成功');
let copyList = list;
copyList = copyList.filter(i=>i.branch_id !== id);
setList([...copyList]);
})
}
function getList() {
const url = `/${owner}/${projectsId}/branches_slice.json`;
Axios.get(url).then(result=>{
const url = `/v1/${owner}/${projectsId}/branches.json`;
Axios.get(url,{
params:{
keyword: name,
page: current,
limit: pageSize,
state:chooseStatus==="deleted" ?chooseStatus:undefined
}
}).then(result=>{
if(result){
setList(result.data);
const {branches, total_count} = result.data;
setTotal(total_count)
setList(branches);
}
setIsSpin(false);
}).catch(error=>{setIsSpin(false);})
@ -202,9 +219,10 @@ function Index(props) {
}).then(res=>{
if(res && res.data){
//
setIsSpin(true);
getDetail();
setReload(Math.random());
setAddBranch(false);
setTimeout(()=>{getList()},1500);
message.success('新建分支成功');
}
})
@ -275,7 +293,14 @@ function Index(props) {
<Spin spinning={isSpin}>
<div style={{paddingTop:"10px",minHeight:"400px",paddingBottom:"30px"}}>
<div className='branchListActionBox'>
<Input.Search placeholder='请输入分支名称' style={{width: '354px'}} className='searchBranch' onSearch={(value)=>{setCurrent(1);setName(value)}} allowClear/>
<div>
<Input.Search placeholder='请输入分支名称' style={{width: '354px'}} className='searchBranch' onSearch={(value)=>{setCurrent(1);setName(value)}} allowClear/>
<Radio.Group value={chooseStatus} buttonStyle="solid" onChange={(v)=>{setChooseStatus(v.target.value)}} style={{marginLeft:"25px"}}>
<Radio.Button value="all">代码库分支</Radio.Button>
<Radio.Button value="deleted">已删除分支</Radio.Button>
</Radio.Group>
</div>
<div>
{isManager && <Link to={`/${owner}/${projectsId}/settings/branches`} className="btn-83" style={{width: '102px'}}><i className="iconfont icon-xuanzhongjibenshezhiicon font-14 mr5"></i>分支设置</Link>}
{(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && <Button type='primary' onClick={addBranchBtn}>+ 新建分支</Button>}
@ -285,8 +310,14 @@ function Index(props) {
rowKey={'name'}
className='branchListTable'
dataSource={list}
columns={columns}
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
columns={chooseStatus==="deleted"?[...columns,operCol]:[...columns,mergeCol,operCol]}
pagination={{current: current,
pageSize: pageSize,
total: total,
showSizeChanger: true,
onShowSizeChange:onShowSizeChange,
showQuickJumper: true, onChange: changePage,hideOnSinglePage:true
}}
>
</Table>
{/* {
@ -369,7 +400,7 @@ function Index(props) {
>
<div className="contents mb30" style={{justifyContent: 'flex-start'}}>
<i className='iconfont icon-shanchu_tc_icon1 font-36 mr18' style={{color: '#ca0002'}}></i>
<p style={{color: '#333'}}>确定删除{visible.name}分支?</p>
<p style={{color: '#333'}}>删除分支本操作将会关闭已开启的合并请求(PR)</p>
</div>
</Modals>
<Modal

View File

@ -136,12 +136,12 @@
margin-right: 5px;
}
}
.treeabout{
flex:1;
text-align: right;
}
}
}
.treeabout{
display: flex;
justify-content: space-between;
}
.treecopy{
flex:1;
display: flex;

View File

@ -30,6 +30,7 @@ export default Form.create()(
const [tagList, setTagList] = useState(undefined);
const [desc, setDesc] = useState(null);
const [branch, setBranch ] = useState(null);
const [saveBranch, setSaveBranch ] = useState(null);
const [fileList, setFileList] = useState(undefined);
const [attachment, setAttachment] = useState(undefined);
const [options , setOptions] = useState(undefined);
@ -104,7 +105,7 @@ export default Form.create()(
...value,
body: desc,
attachment_ids: fileList,
target_commitish:branch
target_commitish:saveBranch
})
.then(result => {
if (result) {
@ -118,7 +119,7 @@ export default Form.create()(
...value,
body: desc,
attachment_ids: fileList,
target_commitish:branch
target_commitish:saveBranch
})
.then(result => {
if (result) {
@ -138,7 +139,8 @@ export default Form.create()(
}
function changeBranch(params) {
setBranch(params);
// setBranch(params);
setSaveBranch(params)
}
return (
@ -177,6 +179,7 @@ export default Form.create()(
owner={owner}
history={history}
tagflag={false}
commitflag={true}
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
<p className="font-12 color-grey-6 weight400">选择一个已经存在的标签或者在发布时新建一个标签</p>
@ -214,7 +217,7 @@ export default Form.create()(
icon={
<i className="iconfont icon-shangchuanicon dragIcon" />
}
size={100}
size={300}
showNotification={showNotification}
/>
{versionId && attachment && attachment.length > 0 ? (
@ -264,7 +267,7 @@ export default Form.create()(
<div className="infosTip">
<p className="font-16 mb14 weight">附件大小说明</p>
<p>
单个附件不能超过 100M每个仓库总附件不可超过
单个附件不能超过 300M每个仓库总附件不可超过
1G附件总容量统计包括仓库附件和发行版附件
</p>
</div>

View File

@ -82,8 +82,8 @@ function version(props) {
<div className="versionName">
<Link onClick={()=>{window.scrollTo(0,0)}} 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 &&
{data && data.user_admin_permission && <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 &&
<Popconfirm
placement="bottom"
title={'您确定要删除当前发行版吗?'}
@ -131,7 +131,7 @@ function version(props) {
return (
<React.Fragment>
{
data && data.user_permission && type !== 2 &&
data && data.user_permission &&
<div className="addReleaseBtn">
<Button type={"primary"} onClick={addFunc} className="btnblue" style={{height:"36px"}}>发布新版本</Button>
</div>
@ -144,7 +144,7 @@ function version(props) {
} else if (releases && releases.length === 0) {
return (
<Empty
operation={data && data.user_permission && type !== 2}
operation={data && data.user_permission}
addFunc={addFunc}
/>
)

View File

@ -67,7 +67,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){
)
function subStrContent(content){
return content ? content.slice(1) :"";
return content ? " " + content.slice(1) :"";
}
return(
@ -131,10 +131,11 @@ function Files({ data,history,owner,projectsId , parentsSha }){
<span>{item.leftIdx && item.leftIdx !=="0" ? item.leftIdx :"" }</span>
<span>{item.rightIdx && item.rightIdx !=="0" ? item.rightIdx :"" }</span>
</span>
<p style={{display:"flex"}}>
<div style={{display:"flex"}}>
<span className="linetype">{item.type===2 ? "+" : item.type===3 ? "-" :""}</span>
{ (item.type===3 || item.type===2) ? subStrContent(item.content) : item.content}
</p>
<div dangerouslySetInnerHTML={{__html:(item.type===3 || item.type===2) ? subStrContent(item.content) : item.content}}></div>
{/* <div><span style={{whiteSpace:"pre-wrap"}}>{(item.type===3 || item.type===2) ? subStrContent(item.content) : item.content}</span></div> */}
</div>
</div>
)
})

View File

@ -182,10 +182,11 @@ form .ant-cascader-picker, form .ant-select {
color: #333;
flex-wrap: wrap;
}
.linesContent > p{
.linesContent > div{
flex:1;
word-break: break-all;
/* white-space: pre; */
white-space: pre-wrap;
font-family: auto;
}
.linesContent .lines{
display: flex;

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, Fragment } from "react";
import { Button, message, Form, Input, Select } from "antd";
import { Button, message, Form, Input, Select, Modal, Checkbox } from "antd";
import "./index.scss";
import "../../users/Material/Index.scss";
import axios from "axios";
@ -17,6 +17,9 @@ function CreateSite(props) {
const [language, setLanguage] = useState(undefined);
const [themes, setThemes] = useState([]);
const [theme, setTheme] = useState(undefined);
const [modalVisibal, setmodalVisibal] = useState(false);
const [accept, setAccept] = useState(false);
const [formValues, setFormValues] = useState({});
useEffect(() => {
document.title = "创建站点";
@ -59,22 +62,34 @@ function CreateSite(props) {
e.preventDefault();
validateFieldsAndScroll((err, values) => {
if (!err) {
axios.post(`/projects/page_migrate.json`,{
...values,
clone_addr: theme && theme.clone_url,
theme: theme && theme.name,
user_id: current_user.user_id,
identifier: values.identifier.split("http://")[1]
}).then(res=>{
if(res && res.status === 200){
message.success("新建成功");
history.push(`/settings/mysite`);
}
})
setmodalVisibal(true)
setFormValues(values)
}
});
}
function confirmSubmit() {
axios.post(`/projects/page_migrate.json`,{
...formValues,
site_name: formValues.identifier,
clone_addr: theme && theme.clone_url,
theme: theme && theme.name,
user_id: current_user.user_id,
identifier: formValues.identifier.split("http://")[1]
}).then(res=>{
onCancel()
if(res && res.status === 200){
message.success("新建成功");
history.push(`/settings/mysite`);
}
})
}
function onCancel() {
setmodalVisibal(false);
setAccept(false);
setFormValues({});
}
return (
<Fragment>
<div className="mySites_head mb30"><Link to={`/settings/mysite`} className="font-16">我的站点 / </Link><span>新建站点</span></div>
@ -89,22 +104,18 @@ function CreateSite(props) {
onSubmit={submit}
>
<div className="formTitle font-16 mb20 ml20">站点配置</div>
<Form.Item name="站点名称" label="站点名称">
{/* <Form.Item name="" label="">
{getFieldDecorator("site_name", {
rules: [{ required: true, message: "请输入站点名称" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(<Input placeholder="请输入站点名称" />)}
</Form.Item>
</Form.Item> */}
<Form.Item
name="站点标识"
label="站点标识"
className="Create-Form-biaoshi"
>
{getFieldDecorator("identifier", {
rules: [{ required: true, message: "请输入站点标识" },
// {pattern: /^[a-zA-Z0-9]{2,100}$/, message: '2-100'}
],
})(
{getFieldDecorator("identifier")(
<Input
onChange={(e) => {
setTag(e.target.value);
@ -154,7 +165,7 @@ function CreateSite(props) {
{/* <span className="mb20 font-18">/</span> 0.87 */}
<Form.Item name="项目标识" label="项目标识" style={{flex: '0.83'}} labelCol={{span: 4}}>
{getFieldDecorator("repository_name", {
rules: [{ required: true, message: "请输入项目标识" },
rules: [
{pattern: /^[a-zA-Z0-9][a-zA-Z0-9_.-]{2,100}[a-zA-Z0-9]$/, message: "长度2-100只能包含数字、字母、下划线、中划线、英文句号必须以数字和字母开头不能以下划线/中划线/英文句号开头和结尾"}],
})(
<Input placeholder="请输入项目标识" disabled/>
@ -166,8 +177,7 @@ function CreateSite(props) {
label="项目名称"
>
{getFieldDecorator("name", {
rules: [{ required: true, message: "请输入项目名称" },
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
rules: [{type: 'string', max: 50, min: 1, message: "长度1-50"}],
})(
<Input placeholder="请输入项目名称" disabled/>
)}
@ -192,6 +202,40 @@ function CreateSite(props) {
</Button>
</Form.Item>
</Form>
<Modal
visible={modalVisibal}
onCancel={onCancel}
footer={null}
closable={false}
bodyStyle={{ padding: 0 }}
width={'650px'}
>
<div className="m-header">
个人建站声明
<i className="iconfont icon-shanchuicon2 mr5 font-14" onClick={onCancel}></i>
</div>
<div className="m-content">
<p>个人建站服务仅供JekyllHugoHexo静态网站效果演示用途请勿用于违规内容包括但不仅限于</p>
<ul>
<ol>发布诱导分享/诱导关注/诱导下载/诱导跳转内容</ol>
<ol>发布欺诈/谣言/骚扰信息/广告信息/垃圾信息/特殊识别码口令类信息</ol>
<ol>发布低俗内容/宗教性捐献及相关信息</ol>
<ol>发布侵害他人权利/违法经营及可疑服务类内容</ol>
<ol>发布其它违反国家法律法规的内容</ol>
</ul>
<p>平台保留使用规则最终解释权如发现上述违规行为平台将视违规情况严重程度予以建站服务封禁以至账号永久封禁惩罚</p>
<Checkbox checked={accept} onChange={(e) => { setAccept(e.target.checked) }}>我已阅读并接受 个人建站声明条款</Checkbox>
<div className="divider"></div>
</div>
<div className="m-footer">
<Button onClick={onCancel}>
取消
</Button>
<Button type="primary" className="mr20" disabled={ !accept } onClick={confirmSubmit}>
确认
</Button>
</div>
</Modal>
</Fragment>
);
}

View File

@ -29,4 +29,71 @@
color: $primary-color;
}
}
}
.m-header {
background: url(../img/header.png) no-repeat;
background-size: 101% 100%;
height: 56px;
text-align: center;
line-height: 56px;
font-size: 18px;
font-weight: 500;
color: #FFFFFF;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
position: relative;
i {
position: absolute;
right: 16px;
top: 0;
cursor: pointer;
}
}
.m-content {
padding: 19px 26px;
font-size: 15px;
color: #4C5B76;
line-height: 30px;
ul {
padding: 10px 0;
}
ol {
display: flex;
align-items: center;
&:before {
content: '';
display: inline-block;
width: 5px;
height: 5px;
background: #202D40;
opacity: 1;
border-radius: 100%;
margin-right: 8px;
}
}
.divider {
width: 100%;
height: 0px;
opacity: 1;
border: 1px solid rgba(153,153,153,0.1);
margin-top: 21px;
}
}
.m-footer {
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0 27px 0;
button {
width: 120px;
height: 36px;
border-radius: 4px 4px 4px 4px;
font-size: 15px;
font-weight: 400;
color: #FFFFFF;
&:nth-of-type(1) {
color: #666666;
margin-right: 78px;
}
}
}

View File

@ -16,6 +16,7 @@ const menu = [
{name:"疑修 (Issue)",index:"issues"},
{name:"合并请求 (PR)",index:"pulls"},
{name:"引擎 (Engine)",index:"devops"},
{name:"数据集",index:"dataset"},
// {name:"资源库",index:"resources"},
{name:"里程碑",index:"versions"},
{name:"维基 (Wiki)",index:"wiki"},
@ -163,13 +164,16 @@ class Setting extends Component {
update=(values)=>{
const { projectsId , owner } = this.props.match.params;
const { private_check , project_units } = this.state;
const {project_language_id, project_category_id, pr_view_admin} = values;
const url = `/${owner}/${projectsId}.json`;
axios.put(url, {
axios.patch(url, {
name: values.project_name,
description: values.project_description,
private: private_check,
identifier:values.project_identifier,
...values,
project_language_id,
project_category_id,
pr_view_admin
}).then((result) => {
if (result) {
this.props.showNotification(`仓库信息修改成功!`);
@ -394,7 +398,7 @@ class Setting extends Component {
{getFieldDecorator("project_units", {
rules: [],
})(
<Checkbox.Group>
<Checkbox.Group className="unitStyle">
{
menu.map((item,key)=>{
return(

View File

@ -348,4 +348,11 @@
border-top: 1px solid rgba(90, 117, 193, 0.23)
}
}
}
.unitStyle{
.ant-checkbox-wrapper + .ant-checkbox-wrapper{
margin-left: 0px;
margin-right: 8px;
margin-bottom: 10px;
}
}

View File

@ -561,4 +561,19 @@
align-items: center;
justify-content: center;
}
}
.plusCard{
display: flex;
flex-direction: column;
justify-content: center;
width: 104px;
height: 104px;
margin-bottom: 8px;
text-align: center;
vertical-align: top;
background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 4px;
cursor: pointer;
}

View File

@ -1,22 +1,26 @@
import React,{ forwardRef , useCallback , useEffect, useState } from 'react';
import './Index.scss';
import { Form , Input , Radio , Checkbox , Button } from "antd";
import UploadImage from './Component/UploadImage';
import { Form , Input , Icon , Checkbox , Button } from "antd";
// import UploadImage from './Component/UploadImage';
import AvatarModal from '../users/Avatar/Index';
import axios from 'axios';
const port = window.location.port;
const hostname = window.location.hostname;
export default Form.create()(
forwardRef(({ form , showNotification , history, showNpsModal })=>{
const [ image , setImage ] = useState(undefined);
const [ imageFlag , setImageFlag] = useState(false);
const [ imageVisible , setIamgeVisible ] = useState(false);
const [ descNum ,setDescNum ] = useState(0);
const { getFieldDecorator, validateFields , setFieldsValue } = form;
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
}
// const radioStyle = {
// display: 'block',
// height: '30px',
// lineHeight: '30px',
// }
const helper = useCallback(
(label, name, rules, widget, isRequired = true) => (
<React.Fragment>
@ -68,14 +72,27 @@ export default Form.create()(
if(!value){
callback();
}
if(value && !value.match(/^[a-zA-Z0-9][a-zA-Z0-9_-]{3,19}$/)){
callback("只能使用以字母、数字开头包含字母、数字、下划线、横杠等长度4到20个字符");
let reg = /^[a-zA-Z0-9]+([-_.][a-zA-Z0-9]+)*$/
if(value && !value.match(reg)){
callback("只能以数字或字母开头仅支持横杠、下划线、点三种符号不允许符号连续排列长度4-50个字符");
}
if(value && (value.length<4 || value.length>50)){
callback("只能以数字或字母开头仅支持横杠、下划线、点三种符号不允许符号连续排列长度4-50个字符");
}
callback();
}
function saveImgFunc(imgUrl){
setImage(imgUrl);
setIamgeVisible(false);
}
return(
<div className="main" style={{padding:"0px",border:"none"}}>
{
imageVisible &&
<AvatarModal avatarImg={image} onCancel={()=>{setIamgeVisible(false)}} saveFunc={saveImgFunc}/>
}
<div className="teamBox">
<p className="teamBox-title">新建组织</p>
<Form className="teamBox-form">
@ -90,7 +107,7 @@ export default Form.create()(
],
<Input
addonBefore={port ? `${hostname}:${port}/`:`https://${hostname}/`}
placeholder="组织账号" maxLength={100}
placeholder="组织账号" maxLength={50}
/>
)}
{helper(
@ -131,11 +148,23 @@ export default Form.create()(
<Radio style={radioStyle} value="privacy" key={3}>私有<span className="color-grey-8">仅对组织成员可见</span></Radio>
</Radio.Group>
)} */}
<p className="font-16 lables must">选择头像</p>
<UploadImage getImage={getImage}/>
{imageFlag && <p className="color-red" style={{marginTop:"-10px"}}>请上传头像</p> }
<div className="mb25">
<p className="font-16 lables must mb10">选择头像</p>
{/* <UploadImage getImage={getImage}/> */}
<div className="plusCard" onClick={()=>{setIamgeVisible(true)}}>
{
image ?
<img src={image} alt="avatar" style={{ width: '100%' }} />
:
<React.Fragment>
<Icon type={'plus'} />
<div className="ant-upload-text">点击上传</div>
</React.Fragment>
}
</div>
{/* <Button onClick={()=>{setIamgeVisible(true)}} type={"primary"}>点击选择</Button> */}
{imageFlag && <p className="color-red">请上传头像</p> }
</div>
{helper(
'权限',
"repo_admin_change_team_access",

View File

@ -1,11 +1,12 @@
import React, { forwardRef , useCallback , useEffect, useState } from 'react';
import { Form , Input , Radio ,Checkbox , Divider , Button , InputNumber } from 'antd';
import { Form , Input , Icon ,Checkbox , Divider , Button , InputNumber } from 'antd';
import { WhiteBack , FlexAJ } from '../../Component/layout';
import Title from '../../Component/Title';
import styled from 'styled-components';
import UploadImage from '../Component/UploadImage';
// import UploadImage from '../Component/UploadImage';
import axios from 'axios';
import Modals from '../Component/Modals';
import AvatarModal from '../../users/Avatar/Index';
import { getImageUrl } from 'educoder';
const TextArea = Input.TextArea;
@ -27,6 +28,7 @@ export default Form.create()(
const [ visible , setVisible ] = useState(false);
const [ privacyVisible , setPrivacyVisible ] = useState(false);
const [ descNum , setDescNum ] = useState(0);
const [ imageVisible , setIamgeVisible ] = useState(false);
const { getFieldDecorator , validateFields , setFieldsValue , getFieldsValue} = form;
useEffect(()=>{
@ -75,7 +77,7 @@ export default Form.create()(
function getImage(image){
setImageFlag(true);
setImage(image);
console.log(image);
setIamgeVisible(false);
}
//
@ -123,6 +125,10 @@ export default Form.create()(
}
return(
<div style={{border:"1px solid #eee"}}>
{
imageVisible &&
<AvatarModal avatarImg={image&&image.startsWith('images/avatars/Organization')?getImageUrl(`/${image}`):image} onCancel={()=>{setIamgeVisible(false)}} saveFunc={getImage}/>
}
<WhiteBack>
<Title>基本设置</Title>
<Modals visible={privacyVisible} okText={"确认"} cancelText={"取消"} onCancel={concelChangePrivacy} onOk={()=>setPrivacyVisible(false)}>
@ -135,9 +141,9 @@ export default Form.create()(
"name",
[
{ required: true, message: "请输入组织账号" },
{
validator:checkname
}
// {
// validator:checkname
// }
],
<Input placeholder="请输入组织账号" disabled/>,true
)}
@ -164,7 +170,7 @@ export default Form.create()(
"官方网站:",
"website",
[],
<Input placeholder="请输入官方网站" />
<Input placeholder="请输入官方网站" maxLength={100}/>
)}
{helper(
'所在地区:',
@ -196,8 +202,19 @@ export default Form.create()(
<InputNumber value="-1" max={1000} style={{width:"350px"}}/>,false,false,
"当输入栏为空时,默认数量无限制"
)}
<p>选择头像:</p>
<UploadImage url={image&&image.startsWith('images/avatars/Organization')?getImageUrl(`/${image}`):image} getImage={getImage}/>
<p className="font-15 mb10">选择头像:</p>
{/* <UploadImage url={image&&image.startsWith('images/avatars/Organization')?getImageUrl(`/${image}`):image} getImage={getImage}/> */}
<div className="plusCard mb20" onClick={()=>{setIamgeVisible(true)}}>
{
image ?
<img src={image&&image.startsWith('images/avatars/Organization')?getImageUrl(`/${image}`):image} alt="avatar" style={{ width: '100%' }} />
:
<React.Fragment>
<Icon type={'plus'} />
<div className="ant-upload-text">点击上传</div>
</React.Fragment>
}
</div>
<Button type={"primary"} onClick={updateDetail}>更新组织设置</Button>
</Form>
</Div>

View File

@ -55,28 +55,28 @@ function Detail(props){
useEffect(()=>{
if(OIdentifier){
getDetail(OIdentifier);
getDetail();
}
},[OIdentifier]);
function getDetail(id) {
const url = `/organizations/${id}.json`;
function getDetail() {
const url = `/organizations/${OIdentifier}.json`;
axios.get(url).then(result=>{
if(result && result.data){
setDetail(result.data);
let keyWords=`${result.data.nickname},${id}`;
let title= result.data.nickname+'('+ id +') ' +result.data.description;
setSeoMeta(keyWords,title,title,`/${id}`,id);
let keyWords=`${result.data.nickname},${OIdentifier},`;
let title= result.data.nickname+'('+ OIdentifier +') ' +result.data.description;
setSeoMeta(keyWords,title,title,`/${OIdentifier}`,OIdentifier);
}
}).catch(error=>{})
}
function updateDetail(name,desc){
let d = detail;
d.name = name;
d.description = desc;
setDetail(d);
}
// function updateDetail(name,desc){
// let d = detail;
// d.name = name;
// d.description = desc;
// setDetail(d);
// }
return(
<div className="teamDetail">
{
@ -140,7 +140,7 @@ function Detail(props){
<Route
path="/:OIdentifier/setting"
render={(p) => {
return <Setting {...props} {...p} organizeDetail={detail} updateFunc={updateDetail}/>
return <Setting {...props} {...p} organizeDetail={detail} updateFunc={getDetail}/>
}}
></Route>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -18,7 +18,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
let wikiName = ''
let key = '';
if (!pathname.endsWith('/wiki/add')) {
wikiName = decodeURI(pathname.split('/')[4]);
wikiName = pathname.split('/')[4];
key = pathname.split('/')[5];
}else{
key = search.split('=').pop();
@ -116,12 +116,6 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
};
validateFields((err, values) => {
if (!err) {
let regEn = /[\\/:*?"<>|[\]-]/g;
if (regEn.test(values.title)) {
message.error('文件名不能有特殊字符: \\ / : * ? \" < > | [ \] -');
setOperateFlag(false);
return;
}
const {md_content, title} = values;
if (wikiName && search!== "?copy") {
updateWiki({
@ -130,7 +124,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
projectId: project.id,
pageName: wikiName,
title,
commit_message: '',
message: '',
content_base64: Base64.encode(md_content)
}).then(res => dealRes(res, title));
} else {
@ -223,7 +217,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
projectId: project.id,
pageName: '_Sidebar',
title: '_Sidebar',
commit_message: '',
message: '',
content_base64: Base64.encode(content)
}).then(res => {
if (res && res.message === "200") {
@ -333,7 +327,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
"title",
[
{ required: true, message: "请输入标题" },
// { pattern: /[^`\[\]\/:*?''<>|%-+_]/g, message: '' }
{ pattern: /^(?!-).*$/, message: '不能以-开头' }
],
<Input
placeholder={"请输入标题"}
@ -360,7 +354,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
<Checkbox checked={modal} onChange={changeModal}>添加模版</Checkbox>
<Checkbox checked={modal} onChange={changeModal} className='mt10'>添加模版</Checkbox>
<Radio.Group onChange={changeModalType} value={modalType}>
<Radio value='周报'>周报</Radio>

View File

@ -5,7 +5,7 @@ import CopyTool from '../Component/CopyTool';
import Welcome from './Welcome';
import RenderHtml from "../../components/render-html";
import { wikiPages, getWiki, parseSidebar, updateWiki, treeToMd } from './api';
import { wikiPages, getWiki, parseSidebar, updateWiki, treeToMd, deleteSameKey } from './api';
import { httpUrl } from './fetch';
import './Index.scss';
import './components/ModalFun/index.scss';
@ -38,18 +38,9 @@ export default (props) => {
const [addMenuError, setAddMenuError] = useState(undefined);
const [sidebar, setSidebar] = useState(undefined);
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(undefined);
const [masterProtected, setMasterProtected] = useState(false);
useEffect(()=>{
window.scrollTo(0,0);
// master
if(projectsId && owner){
axios.get(`/v1/${owner}/${projectsId}/branches.json`, {keyword: 'master'}).then(res=>{
if(res && res.data && res.data.total_count === 1){
setMasterProtected(res.data.branches[0].protected);
}
})
}
}, [])
useEffect(()=>{
@ -84,9 +75,9 @@ export default (props) => {
setSidebar(sidebar)
setFileList(menuList);
let firstWikiNode = undefined;
const arr = pathname.split('/');
if(!pathname.endsWith('/wiki')){
firstWikiNode = findNodeByName(menuList, arr[arr.length-2]);
const {wikiName} = match.params;
firstWikiNode = findNodeByName(menuList, wikiName);
} else{
// [[]]
firstWikiNode = findFirstWiki(menuList);
@ -95,14 +86,14 @@ export default (props) => {
!firstWikiNode && (firstWikiNode = findFirstWiki(menuList));
//
setDefaultSelectedKeys([firstWikiNode.key+""]);
const title = firstWikiNode.title.trim().substring(2,firstWikiNode.title.trim().length-2);
const title = firstWikiNode.titleStr;
const firstWiki = res.data.filter(item => { return title === item.name })[0];
setCheckItem({
...firstWiki,
key: firstWikiNode.key
})
if(window.location.pathname.indexOf('/wiki') === -1) return
history.push(`/${owner}/${projectsId}/wiki/${title}/${firstWikiNode.key}`);
history.push(encodeURI(`/${owner}/${projectsId}/wiki/${firstWiki.sub_url}/${firstWikiNode.key}`));
} else {
setFileArr([]);
setFileArrInit([]);
@ -110,12 +101,12 @@ export default (props) => {
});
}, [project, reload])
// wiki
// wiki,sub_url:
useEffect(() => {
project && checkItem.name && getWiki({
project && checkItem.sub_url && getWiki({
owner: owner,
repo: projectsId,
pageName: checkItem.name,
pageName: checkItem.sub_url,
projectId: project.id
}).then(res => {
if (res && res.message === "200") {
@ -190,11 +181,6 @@ export default (props) => {
setAddMenuError("不能仅输入空格");
return;
}
const regEn = /[\\/:*?"<>|[\]-]/g;
if (regEn.test(menuName)) {
setAddMenuError("不能有特殊字符: \\ / : * ? \" < > | [ \] -");
return;
}
//
const oneMenuList = fileList.filter(item=>!item.title.trim().startsWith('[[')).map(i=>i.title.trim())
if(oneMenuList.includes(`- ${menuName}`)){
@ -215,7 +201,7 @@ export default (props) => {
projectId: project.id,
pageName: '_Sidebar',
title: '_Sidebar',
commit_message: '',
message: '',
content_base64: Base64.encode(treeToMd(sidebarList))
}).then(res => {
if (res && res.message === "200") {
@ -233,11 +219,11 @@ export default (props) => {
}
function goEdit(params) {
history.push(`/${owner}/${projectsId}/wiki/${encodeURI(checkItem.name)}/${checkItem.key}/edit${params}`);
history.push(encodeURI(`/${owner}/${projectsId}/wiki/${checkItem.sub_url}/${checkItem.key}/edit${params}`));
}
function preview() {
window.open(`/${owner}/${projectsId}/wiki/preview/${encodeURI(project.name)}/${project.id}`);
window.open(encodeURI(`/${owner}/${projectsId}/wiki/preview/${project.name}/${project.id}`));
}
// MarkdownHtmlPdf
@ -262,11 +248,18 @@ export default (props) => {
function changeitem(item){
const {name, key} = item;
const wiki = fileArrInit.filter(item => { return item.name == name })[0];
if(!wiki){
// wikisidebarfindSameKeyByDel
const menuListByDel = deleteSameKey(fileList, key);
updateWikiFun(menuListByDel);
message.success("不存在的wiki")
return
}
setCheckItem({
...wiki,
key
})
history.push(`/${owner}/${projectsId}/wiki/${name}/${key}`);
history.push(encodeURI(`/${owner}/${projectsId}/wiki/${name}/${key}`));
}
return (
@ -302,7 +295,7 @@ export default (props) => {
<div className="wiki-nav">
{/* 读sidebar转换成目录树 */}
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys} fileArrInit={fileArrInit} masterProtected={masterProtected}/>
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys} fileArrInit={fileArrInit}/>
</div>
</div>
@ -332,7 +325,7 @@ export default (props) => {
{permission && <Button type="primary" onClick={()=>{goEdit("")}}>编辑</Button>}
</div>
{itemDetail&&itemDetail.md_content&&<RenderHtml className="wiki-content-detail editor-content-panel imageLayerParent" value={ itemDetail && Base64.decode(itemDetail.md_content) } url={history.location}/>}
{itemDetail && itemDetail.md_content && <RenderHtml className="wiki-content-detail editor-content-panel imageLayerParent" value={Base64.decode(itemDetail.md_content) } url={history.location}/>}
</div>
</div>
</div >

View File

@ -102,13 +102,13 @@ body {
font-family: "PingFangSC-Regular";
}
.has-error .ant-form-explain,
.has-error .ant-form-split {
position: absolute;
}
.wiki-md .ant-form-explain{
bottom: -6px;
}
// .has-error .ant-form-explain,
// .has-error .ant-form-split {
// position: absolute;
// }
// .wiki-md .ant-form-explain{
// bottom: -6px;
// }
.wiki-nav {
max-height: 60vh;
.wiki-search {

View File

@ -60,10 +60,10 @@ export default (props) => {
useEffect(() => {
projectsId && checkItem.name && getWiki({
projectsId && checkItem.sub_url && getWiki({
owner: owner,
repo: projectsId,
pageName: checkItem.name,
pageName: checkItem.sub_url,
projectId: projectId,
}).then(res => {
if (res && res.message === "200") {
@ -111,12 +111,13 @@ export default (props) => {
}
function selectTree(keys,event){
let {title, key} = event.node.props.dataRef;
title = title.trim()
const isFile = title.startsWith('[[') && title.endsWith(']]');
let {isFile, key, titleStr} = event.node.props.dataRef;
if(isFile){
title = title.substring(2,title.length-2);
setCheckItem({name: title, key});
const wiki = fileArr.filter(item => { return item.name == titleStr })[0];
setCheckItem({
...wiki,
key
})
}
}

View File

@ -72,7 +72,7 @@ export async function parseSidebar(wikiList, info){
})
}
ReInfo={
menuList: markdownToTree(list.length ? `${sidebarCont}${wikiSidebar}` : sidebarCont),
menuList: markdownToTree(list.length ? `${sidebarCont}${wikiSidebar}` : sidebarCont, wikiList),
sidebar: sidebarCont
}
}
@ -96,14 +96,14 @@ export async function parseSidebar(wikiList, info){
content_base64: Base64.encode(wikiSidebarStr)
})
ReInfo={
menuList: markdownToTree(wikiSidebarStr),
menuList: markdownToTree(wikiSidebarStr, wikiList),
sidebar: wikiSidebarStr
}
}
return ReInfo;
}
export function markdownToTree(markdownText){
export function markdownToTree(markdownText, wikiList){
const lines = markdownText.split('\n');
let key = -1;
// stack: [{一级目录}{二级目录}]
@ -113,16 +113,23 @@ export function markdownToTree(markdownText){
children: [],
key: key++
}
const wikiNameAndSubUrl = {}
wikiList && wikiList.map(item=>{
return wikiNameAndSubUrl[item.name] = item.sub_url
})
lines.map((item, index) =>{
const title = item.trim();
const isFile = title.startsWith('[[') && title.endsWith(']]');
const titleStr = isFile ? title.substring(2, title.length - 2) : title.substring(2, title.length)
const node = {
// 带空格+[[]]或者- 标识
title: item,
children: [],
key: undefined,
// 纯内容
titleStr: isFile ? title.substring(2, title.length - 2) : title.substring(2, title.length),
titleStr,
// 编码后的title,获取wiki内容用title_sub
title_sub: wikiNameAndSubUrl[titleStr],
// 是否是wiki文件
isFile: isFile
}
@ -253,4 +260,17 @@ export function findFirstWiki(list){
}
}
}
}
// 递归过滤相同key值的节点
export function deleteSameKey(list, key){
return list.filter(item => {
if (item.key === key) {
return false
}
if(item.children && item.children.length > 0){
item.children = deleteSameKey(item.children , key)
}
return true
})
}

View File

@ -12,7 +12,7 @@ const {DirectoryTree, TreeNode} = Tree;
// wiki
export default function Sidebar(props) {
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit, masterProtected} = props;
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit} = props;
const {location:{pathname}} = history;
const [initMenuList, setInitMenuList] = useState(undefined);
const [menuList, setMenuList] = useState(undefined);
@ -66,10 +66,7 @@ export default function Sidebar(props) {
</a>
</Menu.Item>
<Menu.Item>
{
masterProtected && item.children.length ? <a onClick={() => { message.error("由于master分支设置了分支保护不允许删除wiki") }}>删除</a> :
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
}
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
</Menu.Item>
</Menu>
};
@ -82,10 +79,7 @@ export default function Sidebar(props) {
</a>
</Menu.Item>
<Menu.Item>
{
masterProtected ? <a onClick={() => { message.error("由于master分支设置了分支保护不允许删除wiki") }}>删除</a> :
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
}
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
</Menu.Item>
</Menu>
};
@ -163,7 +157,7 @@ export default function Sidebar(props) {
const {node, children, key} = item;
if(isFile){
//
deleteWiki({owner: owner, repo: projectsId, projectId: project.id, pageName: title}).then(res => {
deleteWiki({owner: owner, repo: projectsId, projectId: project.id, pageName: item.title_sub}).then(res => {
if(res && res.message === "204"){
// key
const menuListByDel = findSameKeyByDel(menuList, key);
@ -267,7 +261,7 @@ export default function Sidebar(props) {
projectId: project.id,
pageName: '_Sidebar',
title: '_Sidebar',
commit_message: '',
message: '',
content_base64: Base64.encode(content)
}).then(res => {
if (res && res.message === "200") {
@ -303,20 +297,18 @@ export default function Sidebar(props) {
e.stopPropagation();
setVisible(item);
setIsEditName(type);
let title = item.title.trim();
const isFile = title.startsWith('[[') && title.endsWith(']]');
title = isFile ? title.substring(2,title.length-2) : title.substring(2, title.length);
const {titleStr, title_sub} = item;
type === 2 && getWiki({
owner: owner,
repo: projectsId,
pageName: title,
pageName: title_sub,
projectId: project.id
}).then(res => {
if (res && res.message === "200") {
setWikiContent(res.data.md_content);
}
})
setMenuName(title);
setMenuName(titleStr);
}
//
@ -329,9 +321,8 @@ export default function Sidebar(props) {
setAddMenuError("不能仅输入空格");
return;
}
const regEn = /[\\/:*?"<>|[\]-]/g;
if (regEn.test(menuName)) {
setAddMenuError('不能有特殊字符: \\ / : * ? \" < > | [ \] -');
if (isEditName === 2 && /^-/.test(menuName)) {
setAddMenuError('不能以-开头');
return;
}
if(!isEditName){
@ -356,14 +347,15 @@ export default function Sidebar(props) {
}else{
// isEditName 1 2
const titleToJudge = isEditName === 2 ? `[[${menuName}]]` : `- ${menuName}`;
const {title, key} = visible;
if(title.trim() === titleToJudge){
const {key, titleStr, title_sub} = visible;
//
if(titleStr === menuName){
setAddMenuError("请修改名称");
return
}
//
const list = findBrotherNodesByKey(menuList, key) || [];
if(list.map(item=>item.title.trim()).includes(titleToJudge)){
if(list.map(item=>item.titleStr).includes(menuName)){
setAddMenuError("不能与同级目录名称相同");
return
}
@ -381,17 +373,16 @@ export default function Sidebar(props) {
})
if(isEditName === 2){
// -> wiki
const oldTitle = title.substring(title.indexOf('[[')+2, title.indexOf(']]'))
updateWiki({
owner,
repo: projectsId,
projectId: project.id,
//
pageName: oldTitle,
pageName: title_sub,
//
title: menuName,
content_base64: wikiContent,
commit_message: '',
message: '',
}).then(res=>{
if(res && res.message === '200'){
// wikisidebar

View File

@ -59,10 +59,8 @@ export default function UploadWiki(props) {
message.error('只能上传md、txt文件');
return false;
}
let regEn = /[\\/:*?"<>|[\]-]/g;
if (regEn.test(file.name)) {
message.error('文件名不能有特殊字符: \\ / : * ? \" < > | [ \] -');
if (/^-/.test(file.name)) {
message.error('文件名不能以-开头');
return false;
}
@ -86,7 +84,7 @@ export default function UploadWiki(props) {
projectId: project.id,
pageName: '_Sidebar',
title: '_Sidebar',
commit_message: '',
message: '',
content_base64: Base64.encode(treeToMd(sidebarList))
}).then(res => {
if (res && res.message === "200") {

View File

@ -5,9 +5,7 @@ import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
import axios from 'axios';
function Index({onCancel,avatarImg,login}){
console.log('avatarImg,login', avatarImg,login);
function Index({onCancel,avatarImg,login,saveFunc}){
const [ avatarPhoto , setAvatarPhoto ] = useState(avatarImg);
useEffect(()=>{
@ -23,15 +21,19 @@ function Index({onCancel,avatarImg,login}){
if (!imgUrl) {
message.info('请先上传图片');
}
const url = `/users/${login}/update_image.json`;
axios.put(url,{
image:imgUrl
}).then(result=>{
if(result){
message.success("头像修改成功!");
onCancel(true);
}
}).catch(error=>{})
if(login){
const url = `/users/${login}/update_image.json`;
axios.put(url,{
image:imgUrl
}).then(result=>{
if(result){
message.success("头像修改成功!");
onCancel(true);
}
}).catch(error=>{})
}else{
saveFunc && saveFunc(imgUrl);
}
}
function onChange(e){
@ -73,30 +75,35 @@ function Index({onCancel,avatarImg,login}){
footer={null}
centered
maskClosable={false}
title="修改头像"
title={`${login ? "修改": "上传组织" }头像`}
onCancel={()=>onCancel(false)}
className="avatarBox"
>
<div className="avatarDiv">
<div>
<Cropper
style={{ height: 320, width: 320 }}
src={avatarPhoto}
guides={false}
preview="#updateAvatarImg"
ref={cropper}
aspectRatio={1}
/>
{/* <span className={"tips"}>仅支持JPG、GIF、PNG且文件小于2M</span> */}
</div>
{
avatarPhoto ?
<div>
<Cropper
style={{ height: 320, width: 320 }}
src={avatarPhoto}
guides={false}
preview="#updateAvatarImg"
ref={cropper}
aspectRatio={1}
/>
{/* <span className={"tips"}>仅支持JPG、GIF、PNG且文件小于2M</span> */}
</div>
:
<div className="font-18 tipsPosi">选择本地照片上传编辑{!login && "组织"}头像</div>
}
<div className="previewBox">
<div className={"previewImg"} id="updateAvatarImg"></div>
<div className={`previewImg ${login ? "ring":""}`} id="updateAvatarImg"></div>
<div className="uploadBtn">
<label className={"uploadButton"} id="uploadBtn" htmlFor="inputImage">
<input type="file" className="sr-only" id="inputImage" name="file" accept="image/*" style={{ display: "none" }} onChange={onChange}></input>
点击上传
</label>
<a onClick={saveAvatar}>保存头像</a>
{(login || (avatarPhoto && !login)) && <a onClick={saveAvatar}>{login ? "保存头像":"确定"}</a> }
</div>
</div>
</div>

View File

@ -12,12 +12,22 @@
}
.avatarDiv{
display: flex;
position: relative;
min-height: 320px;
.tipsPosi{
position: absolute;
width: 100%;
text-align: center;
top:40%;
height: 30px;
margin-top: -15px;
}
.previewBox{
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
margin-left: 30px;
margin-left: auto;
.uploadBtn{
margin-bottom: 30px;
display: flex;
@ -57,10 +67,12 @@
.previewImg{
overflow: hidden;
background-color: #fff;
border-radius: 50%;
text-align: center;
width: 100px!important;
height: 100px!important;
&.ring{
border-radius: 50%;
}
}
}
}

View File

@ -10,16 +10,18 @@ function Calendar({ userLogin , time , chooseTime }) {
useEffect(()=>{
if(time){
let e,b = "";
if(parseInt(time,0) === parseInt(moment().get('year'),0)){
let y = moment().get('year');
let m = moment().get('month');
let d = moment().get('date');
e = `${y}-${m+1}-${d}`;
b = `${y-1}-${m+1}-${d}`;
}else{
e = `${time}-12-31`;
b = `${time}-01-01`;
}
// if(parseInt(time,0) === parseInt(moment().get('year'),0)){
// let y = moment().get('year');
// let m = moment().get('month');
// let d = moment().get('date');
// e = `${y}-${m+1}-${d}`;
// b = `${y-1}-${m+1}-${d}`;
// }else{
// e = `${time}-12-31`;
// b = `${time}-01-01`;
// }
e = `${time}-12-31`;
b = `${time}-01-01`;
setEndT(e);
setBaginT(b);
}
@ -33,7 +35,9 @@ function Calendar({ userLogin , time , chooseTime }) {
function getData() {
const url = `/users/${userLogin}/headmaps.json`;
Axios.get(url).then(result=>{
Axios.get(url,{
params:{year:time}
}).then(result=>{
if(result && result.data){
let m = [];
let flag = true;
@ -90,6 +94,26 @@ function Calendar({ userLogin , time , chooseTime }) {
}
function Init(data,max,flag) {
let piecesList = [];
let number = 1;
if(max>500){
number = 6;
}else if(max<=500 && max>200){
number = 5;
}else if(max<=200 && max>100){
number = 4;
}else if(max<=100 && max>=30){
number = 3;
}else if(max<30 && max>=10){
number = 2;
}else{
number = 1;
}
for(var i=number;i>0;i--){
let gt = {gt: parseInt(max/number*(i-1)+1,0), lte: parseInt(max/number*i,0)};
piecesList.push(gt);
}
var huan_val = document.getElementById("Calendar");
var myEcharts = echarts.init(huan_val);
let option = {
@ -111,7 +135,17 @@ function Calendar({ userLogin , time , chooseTime }) {
inRange:{
color:flag ? ['#fafafa', '#216e39'] :['#fff','#fff']
},
show:flag
show:flag,
pieces: [
// {gt: parseInt(max/5*4,0), lte: max},
// {gt: parseInt(max/5*3,0), lte: parseInt(max/5*4,0)},
// {gt: parseInt(max/5*2,0), lte: parseInt(max/5*3,0)},
// {gt: parseInt(max/5*1,0), lte: parseInt(max/5*2,0)},
// {gt: 1, lte: parseInt(max/5*1,0)},
...piecesList,
{max: 1,label:"0"}
],
itemGap:25
},
calendar: {
top: 50,

View File

@ -81,8 +81,8 @@ li {
.markdown-body {
text-align: justify;
word-break: break-all;
text-align: left;
word-break: unset;
}
/* !!覆盖之前的样式*/

View File

@ -2139,7 +2139,8 @@ input.ant-input-number-input:focus {
}
/* md代码类样式渲染 */
.new_li .markdown-body ul>li ,.markdown-body ul>li {
/* issues/3984: ,.markdown-body ul>li */
.new_li .markdown-body ul>li {
list-style-type: disc !important;
margin-bottom: 0 !important;
white-space: pre-wrap;

View File

@ -19,13 +19,13 @@ function LoginRegisterPage(props){
<img src={banner} className="banner" alt="" />
</div>
<div className="login_register_right">
{props.location.pathname === "/login" ?
<Login {...props}/>
{props.location.pathname === "/resetPassword" ?
<ResetPassword mygetHelmetapi={mygetHelmetapi}/>
:
props.location.pathname === "/register" ?
<Register mygetHelmetapi={mygetHelmetapi}/>
:
<ResetPassword mygetHelmetapi={mygetHelmetapi}/>
<Login {...props}/>
}
<img src={img1} className="img1" alt="" />
<img src={img2} className="img2" alt="" />

View File

@ -11,7 +11,7 @@ import SupplyPhoneModal from '../../forge/Component/ProfileModal/SupplyPhone';
import SystemNotice from '../../forge/Component/NoticeModal/SystemNotice';
import cookie from 'react-cookies';
import NpsModal from '../modals/npsModal';
import { IsPC } from 'educoder';
import { isPhone } from 'educoder';
export function TPMIndexHOC(WrappedComponent) {
return class II extends React.Component {
@ -293,7 +293,7 @@ export function TPMIndexHOC(WrappedComponent) {
showNpsModal: this.showNpsModal
};
let path =this.props.location.pathname;
let pathCheck = path.indexOf("/zone")>-1 && path.indexOf("/newdetail")>-1 && IsPC();
let pathCheck = path.indexOf("/zone")>-1 && path.indexOf("/newdetail")>-1 && isPhone();
return (
<div className="indexHOC">
<SystemNotice