Merge pull request '撤回wiki名称支持特殊字符修改' (#616) from Eeeros/forgeplus-react:gitlink_server into pre_gitlink_server

1
This commit is contained in:
Eeeros 2024-01-27 15:27:55 +08:00
commit b938349026
33 changed files with 473 additions and 195 deletions

View File

@ -19,7 +19,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

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

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

@ -0,0 +1,9 @@
import React, { } from 'react';
import './index.scss';
function HomePage(props) {
return (
<div>云际计算社区首页自定义</div>
)
}
export default HomePage;

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])
@ -49,9 +49,8 @@ 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">

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

@ -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';
@ -19,11 +19,17 @@ function NewsDetail(props){
const [ isSpin , setIsSpin]=useState(false);
const [ commentReload, setCommentReload] = useState(undefined);
const temp = props.temp;
const { zonedetail, sectionCmsTitle, mainTitle } = props.data;
const zonedetail = props.data;
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)
@ -33,37 +39,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()=>{
document.title = mainTitle;
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){
const { name, cmsDir, summary} = detail;
document.title = `${ name }/${ cmsDir.name }`;
addMeta('Keywords', `${ name },${ cmsDir.name },${ summary }`);
}
return componentWillUnmount
}, [detail])
function componentWillUnmount() {
if (zonedetail) {
document.title= zonedetail.name;
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
}
}
function getDetails(){
setIsSpin(true)
getNewsDetail(id).then(result=>{
@ -83,24 +75,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`}>{ sectionCmsTitle }</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>
@ -116,7 +108,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} />
@ -126,7 +118,7 @@ function NewsDetail(props){
</div>
{
//
detail.auditStatus === '1' && <div className="info_text mt30">
detail.auditStatus === '1' && <div>
<CommentList {...props} docId={ id } reload={commentReload} author={ detail.createBy } reloadComment={()=>{setCommentReload(Math.random())}}/>
</div>
}
@ -136,7 +128,7 @@ function NewsDetail(props){
</Spin>
</div>
{
IsPC() && <Acce type="foot" />
isPhone() && <Acce type="foot" />
}
</React.Fragment>
)

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

View File

@ -11,7 +11,7 @@ import Loading from "../../Loading";
import { getMainInfos , getCheckZoneRole, getCurrentRole } from './api';
import PublicBanner from "./Component/publicBanner";
import './index.scss';
import { IsPC } from 'educoder';
import { isPhone } from 'educoder';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { tempEnum } from "./tempInfo";
@ -85,6 +85,11 @@ const Help = Loadable({
loading: Loading,
});
const HomePageByJCC = Loadable({
loader : () => import("./Pages/JCC/homePage"),
loading : Loading,
});
function Index(props){
const [ data , setData ] = useState(undefined);
const [ adminUrl , setAdminUrl ] = useState(undefined);
@ -93,7 +98,7 @@ function Index(props){
const [ temp , setTemp ] = useState(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;
useEffect(()=>{
@ -119,7 +124,7 @@ function Index(props){
return
}
let data = result.data.data;
if (data.template) {
if (data && data.template) {
setTemp( data.template )
}
setData(data);
@ -129,6 +134,8 @@ function Index(props){
getAdminUrl(data.id);
getRole(data.id)
}
// deptIdheader
sessionStorage.setItem('deptId', data.deptId)
setSeoMeta(`${data.name},`, data.name, data.subTitle, `/zone/${deptId}`)
}
}).catch(console.error())
@ -249,8 +256,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

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

@ -511,9 +511,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

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

View File

@ -98,7 +98,7 @@ 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>
@ -138,7 +138,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>
}

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 } 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(chooseStatus);
},[name, reload, pageSize, current,chooseStatus])
useEffect(()=>{
if(projectDetail && document.title.indexOf('所有分支-') === -1){
@ -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>
}
@ -141,10 +128,12 @@ function Index(props) {
dataIndex: "name",
key: 6,
align: "center",
width: "320px",
width: "300px",
className: "branchActionColumn",
render: (txt, item) => {
render: (txt, item,key) => {
console.log("-----:",item.is_deleted);
return (
item.is_deleted ? <div style={{textAlign:"right"}}><Button onClick={()=>restoreBranch(item.branch_id,item.name)}>恢复分支</Button></div>:
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
@ -165,11 +154,32 @@ function Index(props) {
}
];
function getList() {
const url = `/${owner}/${projectsId}/branches_slice.json`;
Axios.get(url).then(result=>{
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(state) {
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);})
@ -275,7 +285,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>}

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;

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

@ -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个字符12");
}
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)}>
@ -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

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

@ -90,6 +90,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 +131,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

@ -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 {
@ -287,7 +287,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