forked from Gitlink/forgeplus-react
255 lines
9.5 KiB
JavaScript
255 lines
9.5 KiB
JavaScript
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.title_sub,
|
||
...wikiParams
|
||
}).then(res=>{
|
||
setLoading(false);
|
||
if (res && res.message === "200") {
|
||
setWikiDetail(res.data);
|
||
const tocContent = getTocContent();
|
||
setWikiContMenu(tocContent);
|
||
} else {
|
||
message.error("加载失败")
|
||
}
|
||
})
|
||
}, [wiki && wiki.title_sub])
|
||
|
||
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 + ""} isLeaf={item.isFile} 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.sectionHelperTitle}</p>
|
||
<Spin spinning={loading}>
|
||
{
|
||
wikiList && (wikiList.length > 0 ? <Box className='zoneHelpBox'>
|
||
{/* wiki目录导航 */}
|
||
<div className='leftMenuBox mr20'>
|
||
<div className='leftMenuHC'>
|
||
<Search
|
||
allowClear
|
||
placeholder="输入关键字搜索文件"
|
||
className="searchHC mb10"
|
||
onSearch={changeSearchValue}
|
||
/>
|
||
<DirectoryTree onSelect={selectTree} expandedKeys={expandedKeys} onExpand={onExpand} autoExpandParent={autoExpandParent} showIcon={false}>
|
||
{renderTreeNodes(initMenuList)}
|
||
</DirectoryTree>
|
||
</div>
|
||
</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.title_sub}/${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;
|