优化wiki目录搜索、支持在目录树进行wiki重命名操作、目录树收起展开、高亮问题
This commit is contained in:
parent
e45efe2467
commit
66e9063952
|
|
@ -14,7 +14,6 @@ import { isArray } from 'lodash';
|
|||
import { Base64 } from 'js-base64';
|
||||
import Sidebar from './components/sidebar';
|
||||
import UploadWiki from './components/uploadWiki';
|
||||
const Search = Input.Search;
|
||||
const InputGroup = Input.Group;
|
||||
const { Option } = Select;
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ export default (props) => {
|
|||
|
||||
const [fileArr, setFileArr] = useState([]);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
const [fileListInit, setFileListInit] = useState([]);
|
||||
const [reload, setReload] = useState();
|
||||
|
||||
const [urlType, setUrlType] = useState('HTTPS');
|
||||
|
|
@ -76,11 +74,7 @@ export default (props) => {
|
|||
const {menuList, sidebar} = info;
|
||||
setSidebar(sidebar)
|
||||
setFileList(menuList);
|
||||
setFileListInit(menuList);
|
||||
let firstWikiNode = undefined;
|
||||
// if(!pathname.endsWith('/wiki')){
|
||||
// firstWikiNode = findNodeByKey(menuList, pathname.split('/').pop())
|
||||
// }
|
||||
const arr = pathname.split('/');
|
||||
if(!pathname.endsWith('/wiki')){
|
||||
firstWikiNode = findNodeByName(menuList, arr[arr.length-2]);
|
||||
|
|
@ -151,15 +145,6 @@ export default (props) => {
|
|||
}
|
||||
}
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if(value){
|
||||
// 过滤不包含wiki的内容, 重新渲染页面
|
||||
let filterList = filterTree(fileListInit, value);
|
||||
setFileList(filterList);
|
||||
}else{
|
||||
setFileList(JSON.parse(JSON.stringify(fileListInit)));
|
||||
}
|
||||
}
|
||||
|
||||
function filterTree(list, value, arr = []){
|
||||
if(!list.length) return []
|
||||
|
|
@ -173,15 +158,6 @@ export default (props) => {
|
|||
}
|
||||
}
|
||||
return arr
|
||||
// return list.filter(item => {
|
||||
// if (item.title.includes(value)) {
|
||||
// return true
|
||||
// }
|
||||
// // if(item.children && item.children.length > 0){
|
||||
// // item.children = findUnHasValue(item.children , value);
|
||||
// return findUnHasValue(item.children , value);
|
||||
// // }
|
||||
// })
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -314,15 +290,9 @@ export default (props) => {
|
|||
<div className="wiki-nav-parent">
|
||||
<div className='wiki-nav-scroll'>
|
||||
<div className="wiki-nav">
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
|
||||
{/* 读sidebar转换成目录树 */}
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys}/>
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} permission={permission} defaultSelectedKeys={defaultSelectedKeys} fileArrInit={fileArrInit}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,9 @@ body {
|
|||
.ant-tree.ant-tree-directory > li span.ant-tree-node-content-wrapper.ant-tree-node-selected, .ant-tree.ant-tree-directory .ant-tree-child-tree > li span.ant-tree-node-content-wrapper.ant-tree-node-selected{
|
||||
color: $primary-color;
|
||||
}
|
||||
.ant-tree.ant-tree-directory > li.ant-tree-treenode-selected > span.ant-tree-switcher, .ant-tree.ant-tree-directory .ant-tree-child-tree > li.ant-tree-treenode-selected > span.ant-tree-switcher{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-nav-title {
|
||||
|
|
@ -411,4 +414,11 @@ body {
|
|||
}
|
||||
.wikiAddMenuError{
|
||||
color: red;
|
||||
}
|
||||
.expendedAllAction{
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
color: $primary-color;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
}
|
||||
|
|
@ -111,7 +111,6 @@ export function markdownToTree(markdownText){
|
|||
let root = {
|
||||
title: "root",
|
||||
children: [],
|
||||
node: undefined,
|
||||
key: key++
|
||||
}
|
||||
lines.map((item, index) =>{
|
||||
|
|
@ -119,18 +118,17 @@ export function markdownToTree(markdownText){
|
|||
const node = {
|
||||
title: item,
|
||||
children: [],
|
||||
node: undefined,
|
||||
key: key++
|
||||
key: undefined
|
||||
}
|
||||
// level:空格的个数(层级)
|
||||
const level = item.search(/\S/);
|
||||
// 下一个节点的空格个数(层级)
|
||||
const nextNodeLevel = lines[index+1] && lines[index+1].search(/\S/);
|
||||
if(!level){
|
||||
node.key = key+"";
|
||||
key++;
|
||||
// push根目录
|
||||
stack = []
|
||||
root.children.push(node);
|
||||
stack = [];
|
||||
stack.push(node);
|
||||
root.children.push(node);
|
||||
}else{
|
||||
// 解析子目录
|
||||
// 当前节点的父节点
|
||||
|
|
@ -139,12 +137,16 @@ export function markdownToTree(markdownText){
|
|||
const rootNode = root.children.filter(i => i.key === stack[0].key)[0];
|
||||
if(level === 1){
|
||||
// 二级目录
|
||||
node.key = `${rootNode.key}-${rootNode.children.length}`;
|
||||
rootNode.children.push(node);
|
||||
}else{
|
||||
// 更深的层级 找key = stack最后一个item的key
|
||||
const parentNodeByRoot = findNodeByKey(rootNode.children, parentNode.key);
|
||||
node.key = `${parentNodeByRoot.key}-${parentNodeByRoot.children.length}`
|
||||
parentNodeByRoot && parentNodeByRoot.children.push(node);
|
||||
}
|
||||
// 下一个节点的空格个数(层级)
|
||||
const nextNodeLevel = lines[index+1] && lines[index+1].search(/\S/);
|
||||
if(nextNodeLevel>level && (title.startsWith('- ') || title.startsWith('* '))){
|
||||
stack.push(node);
|
||||
}else if(nextNodeLevel<level){
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ import '../../components/ModalFun/index.scss';
|
|||
import { deleteWiki, findNodeByChildrenKey, findNodeByKey, treeToMd, updateWiki } from '../../api';
|
||||
import { useEffect } from 'react';
|
||||
import { Base64 } from 'js-base64';
|
||||
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} = props;
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, permission, defaultSelectedKeys, fileArrInit} = props;
|
||||
const {location:{pathname}} = history;
|
||||
const [initMenuList, setInitMenuList] = useState(undefined);
|
||||
const [menuList, setMenuList] = useState(undefined);
|
||||
|
|
@ -19,10 +20,24 @@ export default function Sidebar(props) {
|
|||
const [menuName, setMenuName] = useState(undefined);
|
||||
const [addMenuError, setAddMenuError] = useState(undefined);
|
||||
const [selectedKey, setSelectedKey] = useState(undefined);
|
||||
const [expandedKeys, setExpandedKeys] = useState(undefined);
|
||||
// 是否全部展开
|
||||
const [isExpendedAll, setIsExpendedAll] = useState(false);
|
||||
// 重命名目录名称
|
||||
const [isEditName, setIsEditName] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
setSelectedKey(defaultSelectedKeys);
|
||||
if(defaultSelectedKeys){
|
||||
// 设置选中wiki
|
||||
setSelectedKey(defaultSelectedKeys);
|
||||
// 第一次渲染,默认打开选中的页面所处目录
|
||||
const keys = [];
|
||||
const list = defaultSelectedKeys[0].split('-');
|
||||
list.map((item, index)=>{
|
||||
keys.push(index > 0 ? `${keys[index-1]}-${item}` : item)
|
||||
})
|
||||
setExpandedKeys(keys);
|
||||
}
|
||||
}, [defaultSelectedKeys])
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
@ -43,7 +58,7 @@ export default function Sidebar(props) {
|
|||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { updateName(e, item) }}>
|
||||
<a onClick={(e) => { updateName(e, item, 1) }}>
|
||||
重命名
|
||||
</a>
|
||||
</Menu.Item>
|
||||
|
|
@ -57,6 +72,11 @@ export default function Sidebar(props) {
|
|||
|
||||
const fileMenu = (title, isFile, item) =>{
|
||||
return <Menu>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { updateName(e, item, 2) }}>
|
||||
重命名
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>
|
||||
删除
|
||||
|
|
@ -102,6 +122,7 @@ export default function Sidebar(props) {
|
|||
});
|
||||
};
|
||||
|
||||
// 选中节点
|
||||
function selectTree(keys,event){
|
||||
let {title, key} = event.node.props.dataRef;
|
||||
setSelectedKey([key+""]);
|
||||
|
|
@ -113,6 +134,19 @@ export default function Sidebar(props) {
|
|||
}
|
||||
}
|
||||
|
||||
// 收起展开
|
||||
function onExpand(keys, obj){
|
||||
const oldKeys = new Set(keys);
|
||||
const {expanded} = obj;
|
||||
let {key} = obj.node.props.dataRef;
|
||||
if(expanded){
|
||||
oldKeys.add(key);
|
||||
}else{
|
||||
oldKeys.delete(key);
|
||||
}
|
||||
setExpandedKeys(Array.from(oldKeys))
|
||||
}
|
||||
|
||||
// 删除wiki模态框
|
||||
function deleteFileModal(e, title, isFile, item) {
|
||||
e.stopPropagation();
|
||||
|
|
@ -257,11 +291,11 @@ export default function Sidebar(props) {
|
|||
history.push(`/${owner}/${projectsId}/wiki/add?key=${item.key}`)
|
||||
}
|
||||
|
||||
// 重命名目录文件
|
||||
function updateName(e, item){
|
||||
// 重命名目录文件 type:1 目录 2页面
|
||||
function updateName(e, item, type){
|
||||
e.stopPropagation();
|
||||
setVisible(item);
|
||||
setIsEditName(true);
|
||||
setIsEditName(type);
|
||||
let title = item.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
title = isFile ? title.substring(2,title.length-2) : title.substring(2, title.length);
|
||||
|
|
@ -284,7 +318,7 @@ export default function Sidebar(props) {
|
|||
return;
|
||||
}
|
||||
if(!isEditName){
|
||||
// 校验同级目录中是否有相同名称
|
||||
// 新增目录:校验同级目录中是否有相同名称
|
||||
const {children, node, title, key} = visible;
|
||||
const mList = children.filter(item=>!item.title.trim().startsWith('[[')).map(i=>i.title.trim())
|
||||
if(mList.includes(`- ${menuName}`)){
|
||||
|
|
@ -295,23 +329,52 @@ export default function Sidebar(props) {
|
|||
addChildrenByKey(newList, key, menuName);
|
||||
updateSidebar(treeToMd(newList), 3)
|
||||
}else{
|
||||
// 编辑sidebar
|
||||
// isEditName 1 编辑目录名称 2编辑页面名称
|
||||
const titleToJudge = isEditName === 2 ? `[[${menuName}]]` : `- ${menuName}`;
|
||||
const {children, node, title, key} = visible;
|
||||
const list = findNodeByChildrenKey(menuList, key) || [];
|
||||
if(title.trim() === `- ${menuName}`){
|
||||
setAddMenuError("请修改目录名称");
|
||||
if(title.trim() === titleToJudge){
|
||||
setAddMenuError("请修改名称");
|
||||
return
|
||||
}
|
||||
if(list.map(item=>item.title.trim()).includes(`- ${menuName}`)){
|
||||
if(list.map(item=>item.title.trim()).includes(titleToJudge)){
|
||||
message.error("不能与同级目录名称相同");
|
||||
}else{
|
||||
const newMenuList = JSON.parse(JSON.stringify(menuList))
|
||||
loop(newMenuList, key, (item)=>{
|
||||
const {title} = item;
|
||||
item.title = `${title.substring(0, title.indexOf('- '))}- ${menuName}`
|
||||
})
|
||||
updateSidebar(treeToMd(newMenuList), 4)
|
||||
return
|
||||
}
|
||||
// wiki名称不可重复
|
||||
const sameNameWiki = fileArrInit.filter(item=>item.name === menuName)
|
||||
if(isEditName === 2 && sameNameWiki.length){
|
||||
message.error("wiki名称不可重复");
|
||||
return
|
||||
}
|
||||
// 编辑sidebar
|
||||
const newMenuList = JSON.parse(JSON.stringify(menuList))
|
||||
loop(newMenuList, key, (item)=>{
|
||||
const {key} = item;
|
||||
item.title = `${'\t'.repeat(key.split('-').length-1)}${titleToJudge}`
|
||||
})
|
||||
if(isEditName === 2){
|
||||
// 编辑页面名称-> 修改wiki
|
||||
const oldTitle = title.substring(title.indexOf('[[')+2, title.indexOf(']]'))
|
||||
updateWiki({
|
||||
owner,
|
||||
repo: projectsId,
|
||||
projectId: project.id,
|
||||
// 原名称
|
||||
pageName: oldTitle,
|
||||
// 现名称
|
||||
title: menuName,
|
||||
commit_message: '',
|
||||
}).then(res=>{
|
||||
if(res && res.message === '200'){
|
||||
// 修改wiki名称接口请求成功,更新sidebar
|
||||
updateSidebar(treeToMd(newMenuList), 4);
|
||||
history.push(`/${owner}/${projectsId}/wiki/${encodeURI(menuName)}/${key}`);
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
updateSidebar(treeToMd(newMenuList), 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,16 +503,99 @@ export default function Sidebar(props) {
|
|||
setMenuName(undefined);
|
||||
}
|
||||
|
||||
// 展开收起全部节点
|
||||
function expandAll(){
|
||||
setIsExpendedAll(!isExpendedAll);
|
||||
if(isExpendedAll){
|
||||
setExpandedKeys([]);
|
||||
return;
|
||||
}
|
||||
const allKeys = [];
|
||||
findMenuKey(allKeys, menuList);
|
||||
setExpandedKeys(allKeys);
|
||||
}
|
||||
|
||||
function findMenuKey(keys, list){
|
||||
list.map(item=>{
|
||||
if(item && !item.title.startsWith("[[")){
|
||||
keys.push(item.key);
|
||||
}
|
||||
if(item && item.children.length){
|
||||
findMenuKey(keys, item.children);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function changeSearchValue(value) {
|
||||
if(value){
|
||||
// 找到符合条件的子节点, 重新渲染页面
|
||||
let keysMap = findNodesToKeys(initMenuList, value);
|
||||
const newMenuList = [];
|
||||
let openedKeysMap = [];
|
||||
const oneNodeKeys = new Set();
|
||||
keysMap.map((item, index)=>{
|
||||
const {path, isFile, title} = item;
|
||||
// 把所有的一级目录key筛选出来
|
||||
oneNodeKeys.add(path[0]);
|
||||
openedKeysMap = openedKeysMap.concat(path);
|
||||
if(!index && isFile){
|
||||
setSelectedKey([path[path.length-1]]);
|
||||
const titleSplit = title.substring(2,title.length-2);
|
||||
changeitem({key: path[path.length-1], name: titleSplit})
|
||||
}
|
||||
})
|
||||
Array.from(oneNodeKeys).map(item=>loop(initMenuList, item,i=>{
|
||||
newMenuList.push(i);
|
||||
}))
|
||||
const openedKeys = Array.from(new Set(openedKeysMap));
|
||||
setExpandedKeys(openedKeys);
|
||||
setMenuList(newMenuList);
|
||||
}else{
|
||||
setMenuList(JSON.parse(JSON.stringify(initMenuList)));
|
||||
}
|
||||
}
|
||||
|
||||
function findNodesToKeys(arr, value, path, nodes) {
|
||||
path = path || [];
|
||||
nodes = nodes || [];
|
||||
if (Array.isArray(arr)) {
|
||||
arr.forEach(function (obj, index) {
|
||||
if (obj && typeof obj === 'object') {
|
||||
const {children, key} = obj;
|
||||
const title = obj.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
if (title.match(new RegExp(value, 'i'))) {
|
||||
nodes.push({path: path.concat([key]), isFile, title});
|
||||
} else {
|
||||
findNodesToKeys(children, value, path.concat([key]), nodes);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='wikiSidebar mt5'>
|
||||
{menuList && menuList.length ? <DirectoryTree onSelect={selectTree} draggable={permission} onDrop={onDrop} selectedKeys={selectedKey}>
|
||||
<div className='wikiSidebar'>
|
||||
<Search
|
||||
allowClear
|
||||
placeholder="输入关键字搜索文件"
|
||||
className="wiki-search"
|
||||
onSearch={changeSearchValue}
|
||||
/>
|
||||
|
||||
{/* 目录是否全部展开 */}
|
||||
<div className='expendedAllAction mt5' onClick={expandAll}>{isExpendedAll ? '收起' : '展开'}所有节点</div>
|
||||
|
||||
{/* wiki目录 */}
|
||||
{menuList && menuList.length ? <DirectoryTree onSelect={selectTree} draggable={permission} onDrop={onDrop} selectedKeys={selectedKey} expandedKeys={expandedKeys} onExpand={onExpand}>
|
||||
{renderTreeNodes(menuList)}
|
||||
</DirectoryTree> : ''}
|
||||
|
||||
{/* 新增子目录弹窗 */}
|
||||
<Modal title={isEditName ? "编辑目录" : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} 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>
|
||||
<Modal title={isEditName ? `编辑${isEditName === 1 ? '目录' : '页面'}` : "新增目录"} visible={Boolean(visible)} onCancel={onCancel} onOk={submit} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>{isEditName === 2 ? '页面' : '目录'}名称: </div>
|
||||
<Input placeholder={`请输入${isEditName === 2 ? '页面' : '目录'}名称`} value={menuName} onChange={(e)=>{setMenuName(e.target.value);setAddMenuError(undefined)}} maxLength={50}></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue