Merge pull request '2023-7-21周上线代码合并' (#567) from gitlink_server into pre_gitlink_server
|
|
@ -29,6 +29,7 @@
|
|||
"fs-extra": "3.0.1",
|
||||
"immutability-helper": "^2.6.6",
|
||||
"install": "^0.12.2",
|
||||
"intersection-observer": "^0.12.2",
|
||||
"jest": "20.0.4",
|
||||
"js-base64": "^2.5.2",
|
||||
"js2wordcloud": "^1.1.12",
|
||||
|
|
@ -92,12 +93,14 @@
|
|||
"redux-thunk": "2.3.0",
|
||||
"rsuite": "^4.3.4",
|
||||
"scroll-into-view": "^1.14.2",
|
||||
"scrollama": "^3.2.0",
|
||||
"sha1": "^1.1.1",
|
||||
"showdown": "^1.9.1",
|
||||
"showdown-katex": "^0.8.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"store": "^2.0.12",
|
||||
"styled-components": "^4.4.1",
|
||||
"weixin-js-sdk": "^1.6.0",
|
||||
"whatwg-fetch": "2.0.3",
|
||||
"wrap-md-editor": "^0.2.20",
|
||||
"xss": "^1.0.14",
|
||||
|
|
|
|||
|
|
@ -7854,12 +7854,6 @@ a.singlepublishtwo {
|
|||
|
||||
|
||||
|
||||
|
||||
.markdown-body {
|
||||
text-align: justify;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.RightPaneDrawer .ant-drawer-content {
|
||||
background: #070f1a;
|
||||
overflow: hidden !important;
|
||||
|
|
|
|||
|
|
@ -4200,10 +4200,6 @@ html>body #ajax-indicator {
|
|||
color: #898989 !important;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
text-align: justify;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.RightPaneDrawer .ant-drawer-content {
|
||||
background: #070f1a;
|
||||
|
|
|
|||
|
|
@ -6635,13 +6635,6 @@ ul.count_ul li:not(:last-child):after {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.markdown-body {
|
||||
text-align: justify;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.RightPaneDrawer .ant-drawer-content {
|
||||
background: #070f1a;
|
||||
overflow: hidden !important;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
|
||||
<script src="%PUBLIC_URL%js/alex/moment.js"></script>
|
||||
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.19/bundle/alex.all.global.min.js"></script>
|
||||
<!-- <meta name="viewport" content="width=device-width, user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"> -->
|
||||
<meta name="viewport" content="">
|
||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
|
|
@ -73,11 +73,7 @@
|
|||
window.location.hash = $(this).attr("name");
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// window.addEventListener("popstate", function(e) {
|
||||
// console.log("popstate",e);
|
||||
// history.go(-2);
|
||||
// }, false);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,21 @@ export function getImageUrl(path) {
|
|||
return `${path}`;
|
||||
}
|
||||
|
||||
export function IsPC(){
|
||||
var userAgentInfo = navigator.userAgent;
|
||||
var Agents = ["Android", "iPhone",
|
||||
"SymbianOS", "Windows Phone",
|
||||
"iPad", "iPod"];
|
||||
var flag = false;
|
||||
for (var v = 0; v < Agents.length; v++) {
|
||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
export function getImageUrlAbsolute(path) {
|
||||
// https://www.educoder.net
|
||||
// https://testbdweb.trustie.net
|
||||
|
|
|
|||
|
|
@ -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
|
||||
turnbar,returnbar,setSeoMeta as setSeoMeta , IsPC as IsPC
|
||||
} from './UrlTool';
|
||||
|
||||
export { setmiyah as setmiyah } from './Component';
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ export function configShareForCourses () {
|
|||
|
||||
// detail
|
||||
export function configShareForCustom (title, desc, imgUrl, path) {
|
||||
console.log('--------------------', title,desc)
|
||||
requestForSignatrue(() => {
|
||||
console.log('configShareForCustom', host)
|
||||
const _url = window.location.href.split('#')[0];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import './index.scss';
|
||||
|
||||
function acce({type}){
|
||||
return(
|
||||
type === "head" ?
|
||||
<div className="head_zone">
|
||||
<a href="/"><img src={require('../../img/logo.png')} alt=""/></a>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<div style={{height: "26em"}}></div>
|
||||
<div className="foot_zone">
|
||||
<p style={{fontSize:'1.6em',color:"#141519"}}>欢迎加入GitLink!</p>
|
||||
<ul className="foot_ewm">
|
||||
<li>
|
||||
<img src={require('../../img/gitlink-gz.png')} alt="" />
|
||||
<span>开源发展委员会<br/>公众号</span>
|
||||
</li>
|
||||
<li>
|
||||
<img src={require('../../img/gitlink-qq.png')} alt="" />
|
||||
<span>GitLink官方<br/>QQ群</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="foot_copyright">
|
||||
<span>©Copyright 2023 CCF 开源发展委员会</span>
|
||||
<span>Powered by Trustie& IntelliDE </span>
|
||||
<span>京ICP备13000930号</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default acce;
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
@media screen and (max-width: 750px){
|
||||
#root{
|
||||
width: 100%;
|
||||
}
|
||||
.newContainers{
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
.head_zone{
|
||||
height: 4.4em;
|
||||
background-color:#183553;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
img{
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
.foot_zone{
|
||||
background-image: url(../../img/f_banner.png);
|
||||
background-size: 100% 100%;
|
||||
height: 26em;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
left: 0px;
|
||||
padding:1.6em 1.2em 0em 1.2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
.foot_ewm{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
padding:1.2em;
|
||||
li{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
img{
|
||||
width: 7em;
|
||||
height: 7em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
span{
|
||||
display: block;
|
||||
color:#444549;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.foot_copyright{
|
||||
padding:1em 0;
|
||||
border-top: 1px solid rgba(129, 131, 138, 0.15);
|
||||
color:#63687a;
|
||||
font-size: 1em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ function PublicBanner(props){
|
|||
const project = /[A-Za-z0-9]{0,}\/projects/g;
|
||||
const source = /[A-Za-z0-9]{0,}\/source/g;
|
||||
const vip = /[A-Za-z0-9]{0,}\/VIP/g;
|
||||
const help = /[A-Za-z0-9]{0,}\/help/g;
|
||||
if(pathname === `/zone/${deptId}`){
|
||||
setKey("1");
|
||||
}
|
||||
|
|
@ -34,6 +35,9 @@ function PublicBanner(props){
|
|||
else if(vip.test(pathname)){
|
||||
setKey("6");
|
||||
}
|
||||
else if(help.test(pathname)){
|
||||
setKey("8");
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
|
|
@ -55,6 +59,7 @@ function PublicBanner(props){
|
|||
<Menu.Item key={"4"}><Link to={`/zone/${deptId}/news`}><img src={require(`../img/${temp}Menu4.png`)} alt="" width="32px"/>{ tempConfig[temp].mainTitle }</Link></Menu.Item>
|
||||
{/* <Menu.Item key={"5"}><img src={Menu5} alt="" width="27px"/>Sig小组</Menu.Item> */}
|
||||
<Menu.Item key={"6"}><Link to={`/zone/${deptId}/VIP`}><img src={require(`../img/${temp}Menu6.png`)} alt="" width="36px"/>{ tempConfig[temp].member }</Link></Menu.Item>
|
||||
{data && data.helperShow === 1 && <Menu.Item key={"8"}><Link to={`/zone/${deptId}/help`}><img src={require(`../img/${temp}Menu8.png`)} alt="" width="36px"/>{data.helperTitle}</Link></Menu.Item>}
|
||||
{ adminUrl && <Menu.Item key={"7"}><a onClick={()=>{window.location.href=adminUrl}}><img src={require(`../img/${temp}Menu7.png`)} alt="" width="34px"/>后台管理</a></Menu.Item> }
|
||||
</Menu>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,252 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, LongWidth, FlexAJ } from '../../Component/layout';
|
||||
import { Affix, Button, Input, message, Spin, Tree } from 'antd';
|
||||
import Nodata from '../../Nodata';
|
||||
import nodata from '../img/nodata.png';
|
||||
import { tempEnum } from '../tempInfo';
|
||||
import { findFirstWiki, generateList, getParentKey, getWiki, parseSidebar, wikiPages } from '../../Wiki/api';
|
||||
import RenderHtml from '../../../components/render-html';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { getTocContent } from '../../../common/marked';
|
||||
import { splitAndCombine } from '../../Wiki/utils';
|
||||
|
||||
const { Search } = Input;
|
||||
const { DirectoryTree, TreeNode } = Tree;
|
||||
|
||||
function ProjectSource(props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [wikiList, setWikiList] = useState(undefined);
|
||||
const [flattenedMenuList, setFlattenedMenuList] = useState([]);
|
||||
const [initMenuList, setInitMenuList] = useState([]);
|
||||
const [expandedKeys, setExpandedKeys] = useState(undefined);
|
||||
const [wiki, setWiki] = useState(undefined);
|
||||
const [wikiDetail, setWikiDetail] = useState(undefined);
|
||||
const [autoExpandParent, setAutoExpandParent] = useState(false);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [wikiContMenu, setWikiContMenu] = useState(undefined);
|
||||
const { temp, history, data, adminUrl } = props;
|
||||
let scroller = undefined;
|
||||
|
||||
const { owner, identifier, projectId } = (data && data.gitlinkProject) || {};
|
||||
const wikiParams = {
|
||||
owner,
|
||||
repo: identifier,
|
||||
projectId
|
||||
};
|
||||
const reg = /[.,/#!$%^&*;:{}=\-_`~():,。¥;「」|?》《~·【】‘、!]/g;
|
||||
|
||||
useEffect(() => {
|
||||
if(!owner) return
|
||||
// 获取wiki 列表
|
||||
wikiPages(wikiParams).then(async res => {
|
||||
if (res && res.message === "200") {
|
||||
setWikiList(res.data);
|
||||
// 解析sidebar获取目录list
|
||||
const info = await parseSidebar(res.data, wikiParams);
|
||||
if (!info) return
|
||||
const { menuList } = info;
|
||||
// 默认打开第一个wiki
|
||||
const firstWiki = findFirstWiki(menuList);
|
||||
if(firstWiki){
|
||||
setExpandedKeys(splitAndCombine(firstWiki.key+""));
|
||||
setWiki(firstWiki);
|
||||
}
|
||||
setFlattenedMenuList(generateList(menuList));
|
||||
setInitMenuList(menuList);
|
||||
} else {
|
||||
setWikiList([]);
|
||||
}
|
||||
})
|
||||
}, [owner])
|
||||
|
||||
useEffect(() =>{
|
||||
if(!wiki){
|
||||
return
|
||||
}
|
||||
// 获取wiki内容
|
||||
setLoading(true);
|
||||
getWiki({
|
||||
pageName: wiki.titleStr,
|
||||
...wikiParams
|
||||
}).then(res=>{
|
||||
setLoading(false);
|
||||
if (res && res.message === "200") {
|
||||
setWikiDetail(res.data);
|
||||
const tocContent = getTocContent();
|
||||
setWikiContMenu(tocContent);
|
||||
} else {
|
||||
message.error("加载失败")
|
||||
}
|
||||
})
|
||||
}, [wiki && wiki.titleStr])
|
||||
|
||||
useEffect(() => {
|
||||
if (scroller) {
|
||||
scroller.destroy();
|
||||
}
|
||||
if (!document.querySelector('.wikiContentHC .markdown_anchors')) {
|
||||
return;
|
||||
}
|
||||
require('intersection-observer');
|
||||
// 默认第一个高亮
|
||||
setTimeout(() => {
|
||||
const linkNode = document.querySelectorAll(`.wikiContMenuBox li a`)[0];
|
||||
linkNode && (linkNode.className = 'current');
|
||||
}, 100);
|
||||
const scrollama = require('scrollama');
|
||||
scroller = scrollama();
|
||||
scroller
|
||||
.setup({
|
||||
step: '.wikiContentHC .markdown_anchors',
|
||||
offset: 0.2,
|
||||
})
|
||||
.onStepEnter(({ element }) => {
|
||||
[].forEach.call(document.querySelectorAll('.wikiContMenuBox li a'), node => {
|
||||
node.className = '';
|
||||
});
|
||||
const currentNode = document.querySelectorAll(`.wikiContMenuBox li a[href="#${element.textContent.toLocaleLowerCase()}"]`)[0];
|
||||
if (currentNode) {
|
||||
currentNode.className = 'current';
|
||||
}
|
||||
})
|
||||
}, [wikiDetail])
|
||||
|
||||
useEffect(() =>{
|
||||
// 点击事件
|
||||
const handleClick = (event) => {
|
||||
let target = event.target;
|
||||
if (target.tagName.toUpperCase() === 'A') {
|
||||
let ancher = target.getAttribute('href');
|
||||
if (ancher && ancher.startsWith('#')) {
|
||||
event.preventDefault()
|
||||
let viewEl = document.getElementById(ancher.replace(reg,""))
|
||||
if (viewEl) {
|
||||
viewEl.scrollIntoView(true);
|
||||
// 滚动结束后向下偏移170像素
|
||||
window.scrollBy(0,-170);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 滚动
|
||||
const scroll = () =>{
|
||||
var element = document.getElementById('wikiContMenuBox');
|
||||
var scrollTop = document.documentElement.scrollTop||document.body.scrollTop;
|
||||
var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
|
||||
var scrollHeight = document.documentElement.scrollHeight||document.body.scrollHeight;
|
||||
if(scrollHeight-scrollTop <= windowHeight*1.2){
|
||||
const maxElementHeight = (scrollHeight-scrollTop)/2-200;
|
||||
element && (element.style.maxHeight = maxElementHeight + 'px');
|
||||
}else if(scrollHeight-scrollTop <= windowHeight*1.5){
|
||||
const maxElementHeight = (scrollHeight-scrollTop)/2-100;
|
||||
element && (element.style.maxHeight = maxElementHeight + 'px');
|
||||
}else{
|
||||
element && (element.style.maxHeight = '80vh');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("click", handleClick);
|
||||
document.addEventListener("scroll", scroll);
|
||||
return () => {
|
||||
document.removeEventListener("click", handleClick);
|
||||
document.removeEventListener("scroll", scroll);
|
||||
};
|
||||
}, [])
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if (value) {
|
||||
const va = value.trim();
|
||||
// 找到命中搜索的item的父元素
|
||||
const expandedKeys = flattenedMenuList.map(item => {
|
||||
if (item.title.match(new RegExp(va, 'i'))) {
|
||||
return getParentKey(item.key, initMenuList);
|
||||
}
|
||||
return null;
|
||||
}).filter((item, i, self) => item && self.indexOf(item) === i);
|
||||
setSearchValue(va);
|
||||
setExpandedKeys(splitAndCombine(expandedKeys[0]));
|
||||
} else {
|
||||
setSearchValue("");
|
||||
}
|
||||
}
|
||||
|
||||
// 收起展开
|
||||
function onExpand(expandedKeys) {
|
||||
setAutoExpandParent(false);
|
||||
setExpandedKeys(expandedKeys);
|
||||
}
|
||||
|
||||
// 选中节点
|
||||
function selectTree(keys,event){
|
||||
let {isFile} = event.node.props.dataRef;
|
||||
if(isFile){
|
||||
setWiki(event.node.props.dataRef);
|
||||
}
|
||||
}
|
||||
|
||||
function renderTreeNodes(data) {
|
||||
return data && data.length > 0 && data.map((item) => {
|
||||
const matchResult = item.titleStr.match(new RegExp(searchValue, 'i'));
|
||||
const title = matchResult ? (
|
||||
<span>
|
||||
{item.titleStr.substr(0, matchResult.index)}
|
||||
<span style={{ color: '#f50' }}>{item.titleStr.substring(matchResult.index, matchResult.index+searchValue.length)}</span>
|
||||
{item.titleStr.substr(matchResult.index+searchValue.length)}
|
||||
</span>
|
||||
) : (
|
||||
<span>{item.titleStr}</span>
|
||||
);
|
||||
return (
|
||||
<TreeNode title={title} key={item.key + ""} dataRef={item} className='menuHcNode font-15'>
|
||||
{renderTreeNodes(item.children)}
|
||||
</TreeNode>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const noDataText = adminUrl ? <a href={`${data && data.gitlinkProject.wiki_url}`} target="_blank" style={{color: '#466aff'}}>前往配置</a> : "正在建设中,敬请期待"
|
||||
return (
|
||||
<div className="in_pro">
|
||||
<div className="boxmain" style={{ paddingTop: "56px" }}>
|
||||
<p className="in_title mb30">{data && data.helperTitle}</p>
|
||||
<Spin spinning={loading}>
|
||||
{
|
||||
wikiList && (wikiList.length > 0 ? <Box className='zoneHelpBox'>
|
||||
{/* wiki目录导航 */}
|
||||
<div className='leftMenuHC mr20'>
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="searchHC mb10"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
<DirectoryTree onSelect={selectTree} expandedKeys={expandedKeys} onExpand={onExpand} autoExpandParent={autoExpandParent} showIcon={false}>
|
||||
{renderTreeNodes(initMenuList)}
|
||||
</DirectoryTree>
|
||||
</div>
|
||||
{/* 内容区 */}
|
||||
{wikiDetail && <LongWidth className='wikiContentBox'>
|
||||
<Box>
|
||||
<LongWidth>
|
||||
<FlexAJ style={{alignItems: 'stretch'}}>
|
||||
<div>
|
||||
<div className='wikiTitleHC font-20 mb5'>{wikiDetail.name}</div>
|
||||
{wikiDetail.commit && <div className='wikiUpdateTimeHC font-15'>更新时间:{wikiDetail.commit.author.date || '刚刚'}</div>}
|
||||
</div>
|
||||
{adminUrl && <Button onClick={()=>{wiki && window.open(`${data && data.gitlinkProject.wiki_url}/${wiki.titleStr}/${wiki.key}`)}} className='zone_apply_button' style={{width: '90px', height: '34px'}}>配置内容</Button>}
|
||||
</FlexAJ>
|
||||
{wikiDetail.md_content && <RenderHtml className="wikiContentHC editor-content-panel mt20 imageLayerParent" value={ Base64.decode(wikiDetail.md_content) } url={history.location}/>}
|
||||
</LongWidth>
|
||||
{wikiContMenu && wikiContMenu !== "<ul></ul>" && <Affix offsetTop={80}>
|
||||
<div id="wikiContMenuBox" className='wikiContMenuBox font-15' dangerouslySetInnerHTML={{ __html: wikiContMenu }}></div>
|
||||
</Affix>}
|
||||
</Box>
|
||||
</LongWidth>}
|
||||
</Box> : (temp === tempEnum.zone1 ? <Nodata _html={noDataText} img={nodata} /> : <Nodata _html={noDataText} />))}
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ProjectSource;
|
||||
|
|
@ -3,9 +3,12 @@ import { Breadcrumb , Divider, Spin } from 'antd';
|
|||
import liulan from '../img/liulan.png';
|
||||
import RenderHtml from '../../../components/render-html';
|
||||
import { Base64 } from 'js-base64';
|
||||
import axios from 'axios';
|
||||
import { getNewsDetail } from '../api';
|
||||
import { IsPC } from 'educoder';
|
||||
import { tempConfig } from '../tempInfo';
|
||||
import Acce from '../Component/mobile/accessory';
|
||||
|
||||
|
||||
|
||||
function NewsDetail(props){
|
||||
const { deptId , id } = props.match.params;
|
||||
|
|
@ -13,11 +16,23 @@ function NewsDetail(props){
|
|||
const [ cmsDir , setCmsDir ] = useState(undefined);
|
||||
const [ content , setContent]=useState(undefined);
|
||||
const [ isSpin , setIsSpin]=useState(false);
|
||||
const temp = props.temp
|
||||
const temp = props.temp;
|
||||
const zonedetail = props.data;
|
||||
|
||||
useEffect(()=>{
|
||||
if(IsPC() && zonedetail){
|
||||
setTimeout(()=>{
|
||||
window.scrollTo(0,1);
|
||||
if(detail)
|
||||
document.title = detail.name;
|
||||
},100);
|
||||
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');
|
||||
}
|
||||
},[zonedetail,detail])
|
||||
|
||||
useEffect(()=>{
|
||||
if(id){
|
||||
window.scrollTo(0,450);
|
||||
window.scrollTo(0,IsPC() ? 0 : 450);
|
||||
getDetails();
|
||||
}
|
||||
},[id])
|
||||
|
|
@ -33,41 +48,60 @@ function NewsDetail(props){
|
|||
setIsSpin(false)
|
||||
}
|
||||
}).catch(error=>{
|
||||
props.history.push(`/zone/${deptId}/news`);
|
||||
setIsSpin(false)
|
||||
})
|
||||
}
|
||||
return(
|
||||
<div className="detail_news_all">
|
||||
<Spin spinning={isSpin} size="large">
|
||||
{
|
||||
detail &&
|
||||
<div style={{width:'1200px',margin:"0px auto",padding:"30px 0px"}}>
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{tempConfig[temp].mainTitle}</Breadcrumb.Item>
|
||||
{ cmsDir && <Breadcrumb.Item href={`/zone/${deptId}/news/${cmsDir.id}`}>{cmsDir.name}</Breadcrumb.Item> }
|
||||
<Breadcrumb.Item>正文</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className="info_text">
|
||||
<div className="info_text_main">
|
||||
<p className="i_name">{detail.name}</p>
|
||||
<ul className="i_ul_value">
|
||||
{ cmsDir && <li><a href={ detail.publishUserUrl } ><img src={ detail.publishUserImage } alt="" className="headimg" /> { detail.publishUserNickname }</a></li> }
|
||||
{ cmsDir && <li>{ detail.isFirstPublish ? '创建于' : '更新于' }{detail.publishTime}</li> }
|
||||
{ detail.visits && <li><img src={liulan} alt="" className="mr5" />{detail.visits}</li> }
|
||||
</ul>
|
||||
</div>
|
||||
<Divider dashed={true} />
|
||||
<React.Fragment>
|
||||
{
|
||||
IsPC() && <Acce type="head"/>
|
||||
}
|
||||
<div className={IsPC() ? "detail_news_phone" :"detail_news_all"}>
|
||||
<Spin spinning={isSpin} size="large">
|
||||
{
|
||||
detail &&
|
||||
<div className="d_news_box">
|
||||
{
|
||||
content ?
|
||||
<RenderHtml className="informations_detail imageLayerParent" value={content} url={props.history.location} />
|
||||
:
|
||||
<span>暂无详情~</span>
|
||||
IsPC() && 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() &&
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{tempConfig[temp].mainTitle}</Breadcrumb.Item>
|
||||
{ cmsDir && <Breadcrumb.Item href={`/zone/${deptId}/news/${cmsDir.id}`}>{cmsDir.name}</Breadcrumb.Item> }
|
||||
<Breadcrumb.Item>正文</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
}
|
||||
<div className="info_text">
|
||||
<div className="info_text_main">
|
||||
<p className="i_name">{detail.name}</p>
|
||||
<ul className="i_ul_value">
|
||||
{ cmsDir && <li><a href={ detail.publishUserUrl } ><img src={ detail.publishUserImage } alt="" className="headimg" /> { detail.publishUserNickname }</a></li> }
|
||||
{ cmsDir && <li>{ detail.isFirstPublish ? '创建于' : '更新于' }{detail.publishTime}</li> }
|
||||
{ detail.visits && <li><img src={liulan} alt="" className="mr5" />{detail.visits}</li> }
|
||||
</ul>
|
||||
</div>
|
||||
{ !IsPC() && <Divider dashed={true} /> }
|
||||
{
|
||||
content ?
|
||||
<RenderHtml className="informations_detail imageLayerParent" value={content} url={props.history.location} />
|
||||
:
|
||||
<span>暂无详情~</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Spin>
|
||||
</div>
|
||||
}
|
||||
</Spin>
|
||||
</div>
|
||||
{
|
||||
IsPC() && <Acce type="foot" />
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
export default NewsDetail;
|
||||
|
|
@ -86,40 +86,45 @@ function Source(props){
|
|||
</div>
|
||||
</div>
|
||||
<div className="sources">
|
||||
<div className="source_left_ul">
|
||||
{
|
||||
zoneList && zoneList.length>0&&
|
||||
<ul className="source_l_u">
|
||||
<p>资源领域</p>
|
||||
<div>
|
||||
<li onClick={()=>{setChooseZoneId(undefined);window.scrollTo(0,450)}} className={!chooseZoneId ?"active":""}>全部</li>
|
||||
{
|
||||
zoneList.map((i,k)=>{
|
||||
return(
|
||||
<li key={k} onClick={()=>{setChooseZoneId(i.id);window.scrollTo(0,450)}} className={chooseZoneId && chooseZoneId === i.id ? "active":"" }>{i.name}</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
}
|
||||
{
|
||||
typeList && typeList.length>0 &&
|
||||
<ul className="source_l_u">
|
||||
<p className="borderRight">资源类别</p>
|
||||
<div>
|
||||
<li onClick={()=>{setChooseTypeId(undefined);window.scrollTo(0,450)}} className={!chooseTypeId ?"active":""}>全部</li>
|
||||
{
|
||||
typeList.map((i,k)=>{
|
||||
return(
|
||||
<li key={k} onClick={()=>{setChooseTypeId(i.id);window.scrollTo(0,450)}} className={chooseTypeId && chooseTypeId === i.id ? "active":"" }>{i.name}</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
{
|
||||
(zoneList && zoneList.length>0 ) || (typeList && typeList.length >0 ) ?
|
||||
<div className="source_left_ul">
|
||||
{
|
||||
zoneList && zoneList.length>0 &&
|
||||
<ul className="source_l_u">
|
||||
<p>资源领域</p>
|
||||
<div>
|
||||
<li onClick={()=>{setChooseZoneId(undefined);window.scrollTo(0,450)}} className={!chooseZoneId ?"active":""}>全部</li>
|
||||
{
|
||||
zoneList.map((i,k)=>{
|
||||
return(
|
||||
<li key={k} onClick={()=>{setChooseZoneId(i.id);window.scrollTo(0,450)}} className={chooseZoneId && chooseZoneId === i.id ? "active":"" }>{i.name}</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
}
|
||||
{
|
||||
typeList && typeList.length>0 &&
|
||||
<ul className="source_l_u">
|
||||
<p className="borderRight">资源类别</p>
|
||||
<div>
|
||||
<li onClick={()=>{setChooseTypeId(undefined);window.scrollTo(0,450)}} className={!chooseTypeId ?"active":""}>全部</li>
|
||||
{
|
||||
typeList.map((i,k)=>{
|
||||
return(
|
||||
<li key={k} onClick={()=>{setChooseTypeId(i.id);window.scrollTo(0,450)}} className={chooseTypeId && chooseTypeId === i.id ? "active":"" }>{i.name}</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
:""
|
||||
}
|
||||
|
||||
<Spin spinning={isSpin}>
|
||||
<div className="s_m_list">
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ function ZoneVIP(props){
|
|||
<div className={`boxmain ${temp}_VIP_box`}>
|
||||
<p className="in_title">{ tempConfig[temp].member }</p>
|
||||
<p className="vip_sub_title">{ tempConfig[temp].vipDesc }</p>
|
||||
{ memberStatus === memberStatusEnum.notMember && <Button className="apply_button" onClick={ apply }>申请加入</Button> }
|
||||
{ memberStatus === memberStatusEnum.notMember && <Button className="zone_apply_button" onClick={ apply }>申请加入</Button> }
|
||||
{ memberStatus === memberStatusEnum.applying && <div className="mt20 tips" ><Icon type="check-circle" theme="filled" />您的申请已提交,请耐心等待管理员审核!</div> }
|
||||
<MemberApply visible={applyVisible} onOk={ onOk } onCancel={() => setApplyVisible(!applyVisible)} current_user={props.current_user}/>
|
||||
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
|
@ -11,9 +11,11 @@ import Loading from "../../Loading";
|
|||
import { getMainInfos , getCheckZoneRole } 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 { tempEnum } from "./tempInfo";
|
||||
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
|
||||
|
||||
const SourceDetail = Loadable({
|
||||
loader: () => import("./Pages/sourceDetail"),
|
||||
|
|
@ -55,12 +57,19 @@ const Main = Loadable({
|
|||
loading: Loading,
|
||||
});
|
||||
|
||||
const Help = Loadable({
|
||||
loader: () => import("./Pages/help"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Index(props){
|
||||
const [ data , setData ] = useState(undefined);
|
||||
const [ adminUrl , setAdminUrl ] = useState(undefined);
|
||||
const { deptId } = props.match.params;
|
||||
const [ id , setId ] = useState(undefined);
|
||||
const [ temp , setTemp ] = useState(tempEnum.zone);
|
||||
const { pathname } = props.history.location;
|
||||
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && IsPC();
|
||||
|
||||
useEffect(()=>{
|
||||
if(deptId){
|
||||
|
|
@ -109,7 +118,7 @@ function Index(props){
|
|||
}
|
||||
return(
|
||||
<div className="information_main">
|
||||
{ id ? <PublicBanner {...props} data={data} temp={ temp } adminUrl={adminUrl}/> : <div style={{ width: '100%', height: '450px' }}></div>}
|
||||
{ !sourcedetail && (id ? <PublicBanner {...props} data={data} temp={ temp } adminUrl={adminUrl}/> : <div style={{ width: '100%', height: '450px' }}></div>)}
|
||||
<Switch>
|
||||
<Route
|
||||
path="/zone/:deptId/news/:cateId"
|
||||
|
|
@ -120,7 +129,7 @@ function Index(props){
|
|||
<Route
|
||||
path="/zone/:deptId/newdetail/:id"
|
||||
render={(p) => (
|
||||
<NewsDetail {...props} {...p} id={id} temp={ temp }/>
|
||||
<NewsDetail {...props} {...p} id={id} temp={ temp } data={data}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
|
|
@ -153,6 +162,12 @@ function Index(props){
|
|||
<Main {...props} {...p} id={id} temp={ temp }/>
|
||||
)}
|
||||
></Route>
|
||||
{data && data.helperShow === 1 && <Route
|
||||
path="/zone/:deptId/help"
|
||||
render={(p) => (
|
||||
<Help {...props} {...p} id={id} temp={ temp } data={data} adminUrl={adminUrl}/>
|
||||
)}
|
||||
></Route>}
|
||||
<Route
|
||||
path="/zone/:deptId"
|
||||
render={(p) => (
|
||||
|
|
@ -163,5 +178,8 @@ function Index(props){
|
|||
</div>
|
||||
)
|
||||
}
|
||||
export default withRouter((CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index))))
|
||||
export default withRouter((CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(ImageLayerOfCommentHOC({
|
||||
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
|
||||
parentSelector: ".newContainer",
|
||||
})(Index)))))
|
||||
);
|
||||
|
|
@ -1,5 +1,20 @@
|
|||
@import './theme.scss';
|
||||
|
||||
// 按钮
|
||||
.zone_apply_button {
|
||||
flex-grow: 0;
|
||||
margin-top: 20px;
|
||||
width: 111px;
|
||||
height: 40px;
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
font-size: 15px;
|
||||
&:hover, &:active, &:focus {
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
.information_main{
|
||||
background-color:#f3f5f8;
|
||||
min-height: 100vh;
|
||||
|
|
@ -72,7 +87,7 @@
|
|||
line-height: 88px;
|
||||
border-bottom: none!important;
|
||||
padding:0px ;
|
||||
margin-right: 100px!important;
|
||||
margin-right: 80px!important;
|
||||
img{
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
@ -418,12 +433,75 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.detail_news_phone{
|
||||
font-size: 62.5%;
|
||||
padding:1.5em;
|
||||
background-color: #E8EAF1;
|
||||
.markdown-body p{
|
||||
font-size: 1em!important;
|
||||
}
|
||||
.p_title_box{
|
||||
padding:2em 1.5em;
|
||||
min-height: 15em;
|
||||
background-image: url('./img/p_banner.png');
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: 2em;
|
||||
.p_t_title{
|
||||
font-weight:600;
|
||||
color:#141519;
|
||||
font-size:2em;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
margin-bottom: 1.5em!important;
|
||||
text-align: center;
|
||||
}
|
||||
.p_t_desc{
|
||||
color:#75767a;
|
||||
font-size:1.6em;
|
||||
letter-spacing:2px;
|
||||
line-height: 2em;
|
||||
}
|
||||
}
|
||||
.info_text{
|
||||
padding:1.5em;
|
||||
background-image:linear-gradient(180deg,#f8f9fd 0%,#f8f9fd 100%);
|
||||
border:1px solid #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.info_text_main{
|
||||
margin-bottom: 2em;
|
||||
.i_name{
|
||||
font-size: 2.4em;
|
||||
font-weight: 400;
|
||||
}
|
||||
.i_ul_value{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 1em;
|
||||
font-size: 1.5em;
|
||||
li{
|
||||
margin-right: 1.5em;
|
||||
}
|
||||
.headimg{
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.detail_news_all{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 600px;
|
||||
justify-content: unset;
|
||||
.d_news_box{
|
||||
width:1200px;
|
||||
margin:0px auto;
|
||||
padding:30px 0px;
|
||||
}
|
||||
.ant-spin-spinning {
|
||||
margin: auto;
|
||||
}
|
||||
|
|
@ -1010,19 +1088,6 @@
|
|||
}
|
||||
}
|
||||
.zone_VIP_box, .zone1_VIP_box {
|
||||
.apply_button {
|
||||
flex-grow: 0;
|
||||
margin-top: 20px;
|
||||
width: 111px;
|
||||
height: 40px;
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
font-size: 15px;
|
||||
&:hover, &:active, &:focus {
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
.tips {
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
|
|
@ -1347,6 +1412,87 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
// 帮助中心
|
||||
.zoneHelpBox{
|
||||
.leftMenuHC{
|
||||
width: 290px;
|
||||
min-height: 500px;
|
||||
overflow-x: auto;
|
||||
&::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
background: #ffffff;
|
||||
padding: 25px 15px;
|
||||
.ant-tree.ant-tree-directory > .menuHcNode span.ant-tree-node-content-wrapper.ant-tree-node-selected, .ant-tree.ant-tree-directory .ant-tree-child-tree > .menuHcNode span.ant-tree-node-content-wrapper.ant-tree-node-selected{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.ant-tree.ant-tree-directory > .menuHcNode.ant-tree-treenode-selected > span.ant-tree-switcher, .ant-tree.ant-tree-directory .ant-tree-child-tree > .menuHcNode.ant-tree-treenode-selected > span.ant-tree-switcher{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.ant-tree.ant-tree-directory > .menuHcNode span.ant-tree-node-content-wrapper:hover::before, .ant-tree.ant-tree-directory .ant-tree-child-tree > .menuHcNode span.ant-tree-node-content-wrapper:hover::before{
|
||||
background: var(--more-light-color);
|
||||
}
|
||||
.menuHcNode{
|
||||
.ant-tree-node-selected::before{
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
.searchHC.ant-input-affix-wrapper{
|
||||
&:hover .ant-input, .ant-input:focus{
|
||||
border-color: var(--primary-color) !important;
|
||||
}
|
||||
}
|
||||
.searchHC .ant-input-search-icon:hover{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
.wikiTitleHC{
|
||||
font-weight: 700;
|
||||
}
|
||||
.wikiUpdateTimeHC{
|
||||
color:#6c7283;
|
||||
}
|
||||
.wikiContentBox{
|
||||
min-height: 500px;
|
||||
background-color: #fff;
|
||||
padding: 20px 40px;
|
||||
}
|
||||
.wikiContMenuBox{
|
||||
border-left: 3px solid #e6e8ef;
|
||||
padding: 5px 0 5px 15px;
|
||||
min-width: 100px;
|
||||
max-width: 220px;
|
||||
margin-left: 60px;
|
||||
background-color: #fff;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
a{
|
||||
color:#6c7283;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
width: 190px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
&.current{
|
||||
color: var(--primary-color);
|
||||
position: relative;
|
||||
&::before{
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -18px;
|
||||
width:3px;
|
||||
height:30px;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
&:hover{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.primaryColor{
|
||||
color: var(--primary-color)!important;
|
||||
}
|
||||
|
|
@ -171,8 +171,8 @@
|
|||
}
|
||||
.quillFlagBox{
|
||||
color: red;
|
||||
margin-top: -10px;
|
||||
margin-bottom: 5px !important;
|
||||
height: 28px;
|
||||
}
|
||||
.addComments{
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@
|
|||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ class MessageCount extends Component {
|
|||
</Menu>
|
||||
);
|
||||
const permission = projectDetail && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner" || projectDetail.permission === "Manager");
|
||||
const permissionByCodeReview = projectDetail && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner" || projectDetail.permission === "Manager" || projectDetail.permission === "Developer")
|
||||
const userLogin = current_user && current_user.login;
|
||||
const operate = userLogin && projectDetail && pr_status === 0 && permission;
|
||||
const open_blockchain = project && project.open_blockchain;
|
||||
|
|
@ -507,7 +508,7 @@ class MessageCount extends Component {
|
|||
</Button>
|
||||
)}
|
||||
|
||||
{userLogin && projectDetail && permission && (
|
||||
{userLogin && projectDetail && permissionByCodeReview && (
|
||||
<Button
|
||||
type="blue"
|
||||
ghost
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ function MilepostDetail(props){
|
|||
<CheckProfile {...props} sureFunc={()=>{history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} checklogin className="operateButton ml20">+ 创建疑修</CheckProfile>
|
||||
</div>
|
||||
</FlexAJ>}
|
||||
{/* 里程碑描述 */}
|
||||
<div className="color-grey-89 mt10">{milepost && milepost.description}</div>
|
||||
<div className="lists mt25">
|
||||
<div className="listheader">
|
||||
<div style={{display:"flex"}}>
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
placeholder={"请输入标题"}
|
||||
className="contact-input"
|
||||
maxLength={50}
|
||||
autoFocus
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -343,6 +344,7 @@ export default Form.create()(({ form, history, showNotification, projectDetail,
|
|||
initValue={content}
|
||||
onChange={onContentChange}
|
||||
className="mt20 wikiMEDEditor"
|
||||
isFocus={false}
|
||||
></MDEditor>
|
||||
{getFieldDecorator('md_content', {
|
||||
rules: [{ required: true, message: "请输入wiki内容" }],
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
||||
import { Button, Dropdown, Icon, Input, Menu, Tooltip, Select, Upload, message, Spin, Modal } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Dropdown, Icon, Input, Menu, Select, message, Spin, Modal } from 'antd';
|
||||
import { getImageUrl, timeAgo } from 'educoder';
|
||||
import cookie from 'react-cookies';
|
||||
import CopyTool from '../Component/CopyTool';
|
||||
import Welcome from './Welcome';
|
||||
import RenderHtml from "../../components/render-html";
|
||||
|
||||
import { wikiPages, getWiki, parseSidebar, updateWiki, treeToMd } from './api';
|
||||
import { httpUrl, TokenKey } from './fetch';
|
||||
import { httpUrl } from './fetch';
|
||||
import './Index.scss';
|
||||
import './components/ModalFun/index.scss';
|
||||
import { isArray } from 'lodash';
|
||||
import { Base64 } from 'js-base64';
|
||||
import Sidebar from './components/sidebar';
|
||||
import UploadWiki from './components/uploadWiki';
|
||||
import axios from 'axios';
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
|
|
@ -38,9 +38,18 @@ 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(()=>{
|
||||
|
|
@ -292,7 +301,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}/>
|
||||
<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}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -321,7 +330,7 @@ export default (props) => {
|
|||
{permission && <Button type="primary" onClick={goEdit}>编辑</Button>}
|
||||
</div>
|
||||
|
||||
{itemDetail&&itemDetail.md_content&&<RenderHtml className="wiki-content-detail editor-content-panel" value={ itemDetail && Base64.decode(itemDetail.md_content) } url={history.location}/>}
|
||||
{itemDetail&&itemDetail.md_content&&<RenderHtml className="wiki-content-detail editor-content-panel imageLayerParent" value={ itemDetail && Base64.decode(itemDetail.md_content) } url={history.location}/>}
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
|
|
@ -330,7 +339,7 @@ export default (props) => {
|
|||
}
|
||||
<Modal title="新增目录" visible={visible} onCancel={()=>{setVisible(false);setAddMenuError(undefined);setMenuName(undefined);}} onOk={addMenu} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>目录名称: </div>
|
||||
<Input placeholder='请输入目录名称' value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<Input placeholder='请输入目录名称' value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50} autoFocus></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
</Spin>
|
||||
|
|
|
|||
|
|
@ -115,10 +115,16 @@ export function markdownToTree(markdownText){
|
|||
}
|
||||
lines.map((item, index) =>{
|
||||
const title = item.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
const node = {
|
||||
// 带空格+[[]]或者- 标识
|
||||
title: item,
|
||||
children: [],
|
||||
key: undefined
|
||||
key: undefined,
|
||||
// 纯内容
|
||||
titleStr: isFile ? title.substring(2, title.length - 2) : title.substring(2, title.length),
|
||||
// 是否是wiki文件
|
||||
isFile: isFile
|
||||
}
|
||||
// level:空格的个数(层级)
|
||||
const level = item.search(/\S/);
|
||||
|
|
@ -200,4 +206,51 @@ function readList(list, readChildren){
|
|||
list.push(item.title)
|
||||
readList(list, item.children)
|
||||
})
|
||||
}
|
||||
|
||||
export function generateList(list) {
|
||||
const dataList = [];
|
||||
generate(list);
|
||||
|
||||
function generate(data){
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const node = data[i];
|
||||
const { key, titleStr } = node;
|
||||
dataList.push({ key, title: titleStr });
|
||||
if (node.children) {
|
||||
generate(node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataList;
|
||||
};
|
||||
|
||||
export const getParentKey = (key, tree) => {
|
||||
let parentKey;
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
const node = tree[i];
|
||||
if (node.children) {
|
||||
if (node.children.some(item => item.key === key)) {
|
||||
parentKey = node.key;
|
||||
} else if (getParentKey(key, node.children)) {
|
||||
parentKey = getParentKey(key, node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return parentKey;
|
||||
};
|
||||
|
||||
export function findFirstWiki(list){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.isFile) {
|
||||
return element;
|
||||
}else if (element.children.length) {
|
||||
let result = findFirstWiki(element.children);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,13 @@ import '../../components/ModalFun/index.scss';
|
|||
import { deleteWiki, findNodeByChildrenKey, findNodeByKey, getWiki, treeToMd, updateWiki } from '../../api';
|
||||
import { useEffect } from 'react';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { splicingTabs } from '../../utils';
|
||||
const Search = Input.Search;
|
||||
const {DirectoryTree, TreeNode} = Tree;
|
||||
|
||||
// wiki目录树
|
||||
export default function Sidebar(props) {
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit} = props;
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit, masterProtected} = props;
|
||||
const {location:{pathname}} = history;
|
||||
const [initMenuList, setInitMenuList] = useState(undefined);
|
||||
const [menuList, setMenuList] = useState(undefined);
|
||||
|
|
@ -65,9 +66,10 @@ export default function Sidebar(props) {
|
|||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>
|
||||
删除
|
||||
</a>
|
||||
{
|
||||
masterProtected && item.children.length ? <a onClick={() => { message.error("由于master分支设置了分支保护,不允许删除wiki") }}>删除</a> :
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
|
||||
}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
};
|
||||
|
|
@ -80,9 +82,10 @@ export default function Sidebar(props) {
|
|||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>
|
||||
删除
|
||||
</a>
|
||||
{
|
||||
masterProtected ? <a onClick={() => { message.error("由于master分支设置了分支保护,不允许删除wiki") }}>删除</a> :
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>删除</a>
|
||||
}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
};
|
||||
|
|
@ -281,8 +284,10 @@ export default function Sidebar(props) {
|
|||
setVisible(false);
|
||||
message.success('重命名成功');
|
||||
}else{
|
||||
console.log('修改文件目录顺序成功');
|
||||
// message.success('修改文件目录顺序成功');
|
||||
}
|
||||
}else{
|
||||
message.error(res.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -447,10 +452,10 @@ export default function Sidebar(props) {
|
|||
dragObj = item;
|
||||
});
|
||||
// 如果拖拽的是目录,那么需要遍历所有的子节点,改变\t个数
|
||||
if(dragObj.title.indexOf('- ') !== -1){
|
||||
if(!dragObj.isFile){
|
||||
// 放入的节点标题
|
||||
const title = info.node.props.dataRef.title;
|
||||
const addBool = !info.dropToGap || ((info.node.props.children || []).length > 0 && dropPosition === 1)
|
||||
const addBool = !info.dropToGap
|
||||
upTitle(dragObj.children, addBool ? `\t${title}` : title);
|
||||
}
|
||||
|
||||
|
|
@ -459,52 +464,56 @@ export default function Sidebar(props) {
|
|||
|
||||
// dropToGap: true拖拽在节点中间 false拖拽到节点之上
|
||||
if (!info.dropToGap) {
|
||||
// 追加到尾部
|
||||
// 拖拽到节点之上->追加到尾部
|
||||
loop(data, dropKey, (item, index, arr) => {
|
||||
const {title} = item;
|
||||
const {title, key} = item;
|
||||
if(title.indexOf('- ') === -1){
|
||||
message.error('不能拖拽到页面上');
|
||||
return;
|
||||
}
|
||||
const childrenName = item.children.map(item=>item.title.trim());
|
||||
if(childrenName.includes(dragObj.title.trim())){
|
||||
const childrenName = item.children.map(item=>item.titleStr);
|
||||
if(childrenName.includes(dragObj.titleStr)){
|
||||
message.error('不能与同级目录名称相同');
|
||||
return;
|
||||
}
|
||||
dragObj.title = `\t${title.substring(0, title.indexOf('- '))}${dragObj.title.trim()}`;
|
||||
dragObj.title = `${splicingTabs(key.split('-').length)}${dragObj.title.trim()}`;
|
||||
item.children = item.children || [];
|
||||
item.children.push(dragObj);
|
||||
bool = true;
|
||||
});
|
||||
} else if (
|
||||
(info.node.props.children || []).length > 0 && // Has children
|
||||
dropPosition === 1 // On the bottom gap
|
||||
) {
|
||||
// 拖拽到目录节点下
|
||||
loop(data, dropKey, item => {
|
||||
const childrenName = item.children.map(item=>item.title.trim())
|
||||
if(childrenName.includes(dragObj.title.trim())){
|
||||
message.error('不能与同级目录名称相同');
|
||||
return;
|
||||
}
|
||||
const {title} = item;
|
||||
dragObj.title = `\t${title.substring(0, title.indexOf('- '))}${dragObj.title.trim()}`;
|
||||
item.children = item.children || [];
|
||||
item.children.unshift(dragObj);
|
||||
});
|
||||
bool = true;
|
||||
} else {
|
||||
}
|
||||
// else if (
|
||||
// (info.node.props.children || []).length > 0 && // Has children
|
||||
// dropPosition === 1 // On the bottom gap
|
||||
// ) {
|
||||
// // 拖拽到目录节点下
|
||||
// loop(data, dropKey, item => {
|
||||
// const childrenName = item.children.map(item=>item.title.trim())
|
||||
// if(childrenName.includes(dragObj.title.trim())){
|
||||
// message.error('不能与同级目录名称相同');
|
||||
// return;
|
||||
// }
|
||||
// console.log('item', item, 'dragObj', dragObj);
|
||||
// const {title} = item;
|
||||
// dragObj.title = `\t${title.substring(0, title.indexOf('- '))}${dragObj.title.trim()}`;
|
||||
// item.children = item.children || [];
|
||||
// item.children.unshift(dragObj);
|
||||
// });
|
||||
// bool = true;
|
||||
// }
|
||||
else {
|
||||
let ar;
|
||||
let i;
|
||||
loop(data, dropKey, (item, index, arr) => {
|
||||
ar = arr;
|
||||
i = index;
|
||||
});
|
||||
const childrenName = ar.map(item=>item.title.trim())
|
||||
if(childrenName.includes(dragObj.title.trim())){
|
||||
const childrenName = ar.map(item=>item.titleStr)
|
||||
if(childrenName.includes(dragObj.titleStr)){
|
||||
message.error('不能与同级目录名称相同');
|
||||
return;
|
||||
}
|
||||
dragObj.title = `${ar[i].title.substring(0, ar[i].title.indexOf('- '))}${dragObj.title.trim()}`;
|
||||
dragObj.title = `${splicingTabs(ar[i].key.split('-').length-1)}${dragObj.title.trim()}`;
|
||||
if (dropPosition === -1) {
|
||||
ar.splice(i, 0, dragObj);
|
||||
} else {
|
||||
|
|
@ -614,9 +623,9 @@ export default function Sidebar(props) {
|
|||
</DirectoryTree> : ''}
|
||||
|
||||
{/* 新增子目录弹窗 */}
|
||||
<Modal title={isEditName ? `编辑${isEditName === 1 ? '目录' : '页面'}` : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<Modal title={isEditName ? `编辑${isEditName === 1 ? '目录' : '页面'}` : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} className="wikiAddMenu myself-modal" width={450} centered destroyOnClose>
|
||||
<div className='mb10'>{isEditName === 2 ? '页面' : '目录'}名称: </div>
|
||||
<Input placeholder={`请输入${isEditName === 2 ? '页面' : '目录'}名称`} value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<Input placeholder={`请输入${isEditName === 2 ? '页面' : '目录'}名称`} value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50} autoFocus></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// 拼接\t
|
||||
export function splicingTabs(count) {
|
||||
var tabs = "";
|
||||
for (var i = 0; i < count; i++) {
|
||||
tabs += "\t";
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
|
||||
// 输入:'2-2-2-1' 返回:['2', '2-2', '2-2-2', '2-2-2-1']
|
||||
export function splitAndCombine(str) {
|
||||
if(!str) return []
|
||||
var parts = str.split('-');
|
||||
var result = parts.reduce(function(acc, curr) {
|
||||
if (acc.length === 0) {
|
||||
acc.push(curr);
|
||||
} else {
|
||||
var lastItem = acc[acc.length - 1];
|
||||
acc.push(lastItem + '-' + curr);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -228,6 +228,12 @@ form{
|
|||
.normal{
|
||||
width: 100%;
|
||||
}
|
||||
#root{
|
||||
width: 1200px;
|
||||
}
|
||||
.newContainers{
|
||||
min-width: 1200px;
|
||||
}
|
||||
}
|
||||
.ver-middle{
|
||||
vertical-align: middle;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@
|
|||
list-style-type: none;
|
||||
}
|
||||
|
||||
.markdown-body video{
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
li {
|
||||
|
|
|
|||
|
|
@ -26,10 +26,6 @@ function Register(props){
|
|||
const [setting, setSetting] = useState(undefined);
|
||||
const seconds = useRef();
|
||||
let interval = undefined;
|
||||
//页面加载完自动聚焦到第一个输入框
|
||||
// const inputEl = useRef(null);
|
||||
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
//获取第三方登录平台信息
|
||||
|
|
@ -220,11 +216,6 @@ function Register(props){
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// inputEl.current.focus();
|
||||
// clear;
|
||||
}, [])
|
||||
|
||||
function changeRegisterType(type){
|
||||
setGetCaptchaBut(false);
|
||||
setRegisterType(type);
|
||||
|
|
@ -277,8 +268,7 @@ function Register(props){
|
|||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateFirst: true,
|
||||
// ref={inputEl}
|
||||
})(<Input placeholder="请输入4-15位用户名,以字母开头,只能使用字母和数字" autoComplete="off"/>)}
|
||||
})(<Input autoFocus placeholder="请输入4-15位用户名,以字母开头,只能使用字母和数字" autoComplete="off"/>)}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
{getFieldDecorator('email',{
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ function ResetPassword(props) {
|
|||
const [emailGo, setEmailGo] = useState(true);
|
||||
const seconds = useRef();
|
||||
let interval = undefined;
|
||||
//页面加载完自动聚焦到第一个输入框
|
||||
const inputEl = useRef(null);
|
||||
|
||||
// 配置网页标题
|
||||
useEffect(()=>{
|
||||
|
|
@ -187,10 +185,6 @@ function ResetPassword(props) {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
inputEl.current.focus();
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="right_cont ResetPassword_content">
|
||||
|
|
@ -213,7 +207,7 @@ function ResetPassword(props) {
|
|||
],
|
||||
validateTrigger: "onBlur",
|
||||
validateFirst: true,
|
||||
})(<Input ref={inputEl} className="account" placeholder="请输入已注册的手机号/邮箱" autoComplete="off"/>)}
|
||||
})(<Input autoFocus className="account" placeholder="请输入已注册的手机号/邮箱"/>)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
|
|
|
|||
|
|
@ -16,20 +16,25 @@ export function ImageLayerOfCommentHOC(options = {}) {
|
|||
onDelegateClick = (event) => {
|
||||
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
|
||||
// 判断imageSrc是否是图片
|
||||
const fileName = event.target.innerHTML.trim()
|
||||
if (isImageExtension(imageSrc.trim()) || isImageExtension(fileName) || event.target.tagName == 'IMG') {
|
||||
// 非回复里的头像图片; 非emoticons
|
||||
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1) {
|
||||
this.setState({
|
||||
showImage: true,
|
||||
imageSrc,
|
||||
})
|
||||
const fileName = event.target.innerHTML.trim();
|
||||
const parentNodeUrl = event.target.parentNode && event.target.parentNode.getAttribute('href');
|
||||
if(parentNodeUrl){
|
||||
window.location.href = parentNodeUrl;
|
||||
}else{
|
||||
if (isImageExtension(imageSrc.trim()) || isImageExtension(fileName) || event.target.tagName == 'IMG') {
|
||||
// 非回复里的头像图片; 非emoticons
|
||||
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1) {
|
||||
this.setState({
|
||||
showImage: true,
|
||||
imageSrc,
|
||||
})
|
||||
}
|
||||
event.stopPropagation()
|
||||
event.preventDefault && event.preventDefault()
|
||||
event.originalEvent.preventDefault()
|
||||
return false;
|
||||
}
|
||||
event.stopPropagation()
|
||||
event.preventDefault && event.preventDefault()
|
||||
event.originalEvent.preventDefault()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +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';
|
||||
|
||||
export function TPMIndexHOC(WrappedComponent) {
|
||||
return class II extends React.Component {
|
||||
|
|
@ -285,6 +286,8 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
showCompeleteDialog:this.showCompeleteDialog,
|
||||
showNpsModal: this.showNpsModal
|
||||
};
|
||||
let path =this.props.location.pathname;
|
||||
let pathCheck = path.indexOf("/zone")>-1 && path.indexOf("/newdetail")>-1 && IsPC();
|
||||
return (
|
||||
<div className="indexHOC">
|
||||
<SystemNotice
|
||||
|
|
@ -307,7 +310,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
{...this.state}
|
||||
/> : ""}
|
||||
{npsModalVisible && <NpsModal closeNpsModal={()=>{this.closeNpsModal()}} npsActionType={npsActionType} npsActionId={npsActionId}/>}
|
||||
<Header {...this.state} {...this.props} {...common} publicNav={publicNav}></Header>
|
||||
{ !pathCheck && <Header {...this.state} {...this.props} {...common} publicNav={publicNav}></Header> }
|
||||
<Spin spinning={this.state.globalLoading} delay={0} className="globalSpin"
|
||||
size="large" tip={this._gLoadingTip || "加载中..."}
|
||||
>
|
||||
|
|
@ -321,7 +324,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
}
|
||||
</div>
|
||||
</Spin>
|
||||
<NewFooter {...this.state} {...this.props} />
|
||||
{!pathCheck && <NewFooter {...this.state} {...this.props} /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||