添加wiki目录排序和目录重命名功能
This commit is contained in:
parent
95db1723f0
commit
fc955d3234
|
|
@ -69,14 +69,6 @@ class Detail extends Component {
|
|||
data: result.data,
|
||||
isSpins: false
|
||||
});
|
||||
const {description} = result.data;
|
||||
const md_content = description;
|
||||
// 所有的一级目录(包含其子目录)
|
||||
const menuTree1 = md_content.split('\n-');
|
||||
console.log('所有的一级目录(包含其子目录)', menuTree1);
|
||||
console.log('\n分割', menuTree1[0].split('\n')[6].split('\t'));
|
||||
const menuTree2 = menuTree1[0].split('\n\t').filter(i=>{return i.indexOf('\t') === -1});
|
||||
console.log('二级目录', menuTree2);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ export default (props) => {
|
|||
const [menuName, setMenuName] = useState(undefined);
|
||||
const [addMenuError, setAddMenuError] = useState(undefined);
|
||||
const [sidebar, setSidebar] = useState(undefined);
|
||||
// 改变了目录顺序
|
||||
const [reloadBySort, setReloadBySort] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(projectDetail){
|
||||
|
|
@ -72,9 +74,13 @@ export default (props) => {
|
|||
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 = findNodeByKey(menuList, pathname.split('/').pop())
|
||||
}else{
|
||||
firstWikiNode = findNodeByName(menuList, arr[arr.length-2]);
|
||||
} else{
|
||||
// 找到目录树中第一个[[页面]]
|
||||
firstWikiNode = findFirstWiki(menuList);
|
||||
}
|
||||
|
|
@ -84,6 +90,7 @@ export default (props) => {
|
|||
...firstWiki,
|
||||
key: firstWikiNode.key
|
||||
})
|
||||
history.push(`/${owner}/${projectsId}/wiki/${title}/${firstWikiNode.key}`);
|
||||
} else {
|
||||
setFileArr([]);
|
||||
setFileArrInit([]);
|
||||
|
|
@ -107,6 +114,40 @@ export default (props) => {
|
|||
});
|
||||
}, [project, checkItem]);
|
||||
|
||||
// 监听reloadBySort的变化,修改路由
|
||||
// useEffect(()=>{
|
||||
// console.log('reloadBySort', reloadBySort, fileList);
|
||||
// if(reloadBySort){
|
||||
// const node = findNodeByName(fileList, reloadBySort);
|
||||
// const firstWiki = fileArrInit.filter(item => { return node.title.indexOf(item.name) !== -1})[0];
|
||||
// setCheckItem({
|
||||
// ...checkItem,
|
||||
// key: node.key
|
||||
// })
|
||||
// console.log('node', node, {
|
||||
// ...checkItem,
|
||||
// key: node.key
|
||||
// });
|
||||
// history.push(`/${owner}/${projectsId}/wiki/${reloadBySort}/${node.key}`);
|
||||
// setReloadBySort(undefined);
|
||||
// }
|
||||
|
||||
// }, [reloadBySort, fileList])
|
||||
|
||||
function findNodeByName(list, name){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.title.indexOf(name) !== -1) {
|
||||
return element;
|
||||
}else if (element.children.length) {
|
||||
let result = findNodeByName(element.children, name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化页面:递归menulist找到第一个页面
|
||||
function findFirstWiki(list){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
|
|
@ -255,10 +296,10 @@ export default (props) => {
|
|||
const uploadProps = {
|
||||
name: 'multipartFile',
|
||||
withCredentials: true,
|
||||
action: `${httpUrl}/api/wikiExport/uploadWiki/${owner}/${projectsId}/${project && project.id}?token=${sessionStorage.taskToken}`, //?token=${sessionStorage.taskToken}
|
||||
action: `${httpUrl}/api/wikiExport/uploadWiki/${owner}/${projectsId}/${project && project.id}`, //?token=${sessionStorage.taskToken}
|
||||
showUploadList: false,
|
||||
headers: {
|
||||
Authorization: cookie.load(TokenKey) || sessionStorage.taskToken,
|
||||
Authorization: cookie.load(TokenKey),
|
||||
},
|
||||
beforeUpload: beforeUpload,
|
||||
onChange(info) {
|
||||
|
|
@ -291,7 +332,7 @@ export default (props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
< Spin spinning={!fileArrInit} className="opacitySpin">
|
||||
<Spin spinning={!fileArrInit} className="opacitySpin">
|
||||
{fileArrInit && fileArrInit.length ?
|
||||
<div className="wiki-main">
|
||||
<div className="wiki-head">
|
||||
|
|
@ -333,7 +374,7 @@ export default (props) => {
|
|||
/>
|
||||
|
||||
{/* 读sidebar转换成目录树 */}
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history}/>
|
||||
<Sidebar fileList = {fileList} changeitem={changeitem} projectsId={projectsId} owner={owner} project={project} setReload={setReload} sidebar={sidebar} history={history} setReloadBySort={setReloadBySort}/>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
@ -368,7 +409,7 @@ export default (props) => {
|
|||
:
|
||||
<Welcome {...props} reloadList={setReload} />
|
||||
}
|
||||
<Modal title="新增目录" visible={visible} onCancel={()=>{setVisible(false);setAddMenuError(undefined)}} onOk={addMenu} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<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={30}></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
|
|
|
|||
|
|
@ -137,20 +137,6 @@ export function markdownToTree(markdownText){
|
|||
}
|
||||
}
|
||||
})
|
||||
// 找到所有的一级目录以及其子目录,使用\n- 会找不到一级[[页面]]
|
||||
let oneMenuAndChildren = [];
|
||||
markdownText.split('\n- ').map(item=>{
|
||||
if(item.includes('\n[[')){
|
||||
const list = item.split('\n[[').map((item, index)=>index ? `[[${item}` : item);
|
||||
oneMenuAndChildren = oneMenuAndChildren.concat(list);
|
||||
}else{
|
||||
item && oneMenuAndChildren.push(item);
|
||||
}
|
||||
})
|
||||
// 给每一个节点赋值一级目录以及其子目录的md内容,后续对子节点进行操作时,对md内容整个替换
|
||||
oneMenuAndChildren.map((item, index)=>{
|
||||
assignmentNode(root.children[index], item);
|
||||
})
|
||||
return root.children;
|
||||
}
|
||||
|
||||
|
|
@ -169,13 +155,20 @@ export function findNodeByKey(list, key){
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// 给每个子节点赋值 整个层级的md内容
|
||||
function assignmentNode(root, value){
|
||||
const val = value.startsWith('[[') || value.startsWith('- ') ? value : `- ${value}`
|
||||
if(root){
|
||||
root.node = val;
|
||||
root.children.length && root.children.map(item=>{assignmentNode(item, val)})
|
||||
// 找到children中有相同key的数组
|
||||
export function findNodeByChildrenKey(list, key){
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (element.key == key) {
|
||||
return list;
|
||||
}else if (element.children.length) {
|
||||
let result = findNodeByChildrenKey(element.children, key);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// 目录结构转md内容
|
||||
|
|
|
|||
|
|
@ -3,23 +3,28 @@ import { Tree, Dropdown, Menu, message, Modal, Input } from 'antd';
|
|||
import DelModal from '../../components/ModalFun';
|
||||
import './index.scss';
|
||||
import '../../components/ModalFun/index.scss';
|
||||
import { deleteWiki, findNodeByKey, treeToMd, updateWiki } from '../../api';
|
||||
import { deleteWiki, findNodeByChildrenKey, findNodeByKey, treeToMd, updateWiki } from '../../api';
|
||||
import { useEffect } from 'react';
|
||||
import { Base64 } from 'js-base64';
|
||||
const {DirectoryTree, TreeNode} = Tree;
|
||||
|
||||
// wiki目录树
|
||||
export default function Sidebar(props) {
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history} = props;
|
||||
const {fileList, changeitem, owner, projectsId, project, setReload, sidebar, history, setReloadBySort} = props;
|
||||
const {location:{pathname}} = history;
|
||||
const [initMenuList, setInitMenuList] = useState(undefined);
|
||||
const [menuList, setMenuList] = useState(undefined);
|
||||
const [actionVisible, setActionVisible] = useState(undefined);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [menuName, setMenuName] = useState(undefined);
|
||||
const [addMenuError, setAddMenuError] = useState(undefined);
|
||||
// 重命名目录名称
|
||||
const [isEditName, setIsEditName] = useState(undefined);
|
||||
let key = 1;
|
||||
|
||||
useEffect(()=>{
|
||||
setMenuList(fileList);
|
||||
setInitMenuList(fileList);
|
||||
}, [fileList])
|
||||
|
||||
const menu = (title, isFile, item, level) =>{
|
||||
|
|
@ -34,6 +39,11 @@ export default function Sidebar(props) {
|
|||
添加子目录
|
||||
</a>
|
||||
</Menu.Item>}
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { updateName(e, item) }}>
|
||||
重命名
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={(e) => { deleteFileModal(e, title, isFile, item) }}>
|
||||
删除
|
||||
|
|
@ -77,6 +87,18 @@ export default function Sidebar(props) {
|
|||
});
|
||||
}
|
||||
|
||||
// 遍历list,找到相同的key callback
|
||||
const loop = (data, key, callback) => {
|
||||
data.forEach((item, index, arr) => {
|
||||
if (item.key === key) {
|
||||
return callback(item, index, arr);
|
||||
}
|
||||
if (item.children) {
|
||||
return loop(item.children, key, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function selectTree(keys,event){
|
||||
let {title, key} = event.node.props.dataRef;
|
||||
title = title.trim()
|
||||
|
|
@ -183,7 +205,7 @@ export default function Sidebar(props) {
|
|||
})
|
||||
}
|
||||
|
||||
// 修改此仓库的sidebar文件 type 1删除 3添加子目录
|
||||
// 修改此仓库的sidebar文件 type 1删除 2拖拽 3添加子目录 4目录重命名
|
||||
function updateSidebar(content, type, addWikiName){
|
||||
updateWiki({
|
||||
owner,
|
||||
|
|
@ -198,10 +220,19 @@ export default function Sidebar(props) {
|
|||
setReload(Math.random());
|
||||
if(type === 1){
|
||||
message.success('删除成功');
|
||||
}else{
|
||||
}else if(type === 3){
|
||||
setMenuName(undefined);
|
||||
setVisible(false);
|
||||
message.success('新增成功');
|
||||
}else if(type === 4){
|
||||
setIsEditName(false);
|
||||
setMenuName(undefined);
|
||||
setVisible(false);
|
||||
message.success('重命名成功');
|
||||
}else{
|
||||
// const arr = pathname.split('/');
|
||||
// setReloadBySort(arr[arr.length-2]);
|
||||
console.log('修改文件目录顺序成功');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -213,8 +244,19 @@ export default function Sidebar(props) {
|
|||
history.push(`/${owner}/${projectsId}/wiki/add?key=${item.key}`)
|
||||
}
|
||||
|
||||
// 新增子目录
|
||||
async function addMenu(){
|
||||
// 重命名目录文件
|
||||
function updateName(e, item){
|
||||
e.stopPropagation();
|
||||
setVisible(item);
|
||||
setIsEditName(true);
|
||||
let title = item.title.trim();
|
||||
const isFile = title.startsWith('[[') && title.endsWith(']]');
|
||||
title = isFile ? title.substring(2,title.length-2) : title.substring(2, title.length);
|
||||
setMenuName(title);
|
||||
}
|
||||
|
||||
// 新增子目录 或者 重命名目录
|
||||
function submit(){
|
||||
if(!menuName){
|
||||
setAddMenuError('请输入目录名称');
|
||||
return;
|
||||
|
|
@ -224,21 +266,36 @@ export default function Sidebar(props) {
|
|||
setAddMenuError('不能有特殊字符');
|
||||
return;
|
||||
}
|
||||
// 校验同级目录中是否有相同名称
|
||||
const {children, node, title, key} = visible;
|
||||
const mList = children.filter(item=>!item.title.trim().startsWith('[[')).map(i=>i.title.trim())
|
||||
if(mList.includes(`- ${menuName}`)){
|
||||
setAddMenuError('不能与已有文件标题相同');
|
||||
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}`)){
|
||||
setAddMenuError('不能与已有目录名称相同');
|
||||
return;
|
||||
}
|
||||
const newList = menuList;
|
||||
addChildrenByKey(newList, key, menuName);
|
||||
updateSidebar(treeToMd(newList), 3)
|
||||
}else{
|
||||
// 编辑sidebar
|
||||
const {children, node, title, key} = visible;
|
||||
const list = findNodeByChildrenKey(menuList, key) || [];
|
||||
if(title.trim() === `- ${menuName}`){
|
||||
setAddMenuError("请修改目录名称");
|
||||
return
|
||||
}
|
||||
if(list.map(item=>item.title.trim()).includes(`- ${menuName}`)){
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
const newList = menuList;
|
||||
addChildrenByKey(newList, key, menuName);
|
||||
updateSidebar(treeToMd(newList), 3)
|
||||
// const isFindEnd = await addMenuFindKey(newList, key, menuName, title);
|
||||
// if(isFindEnd){
|
||||
// updateSidebar(treeToMd(newList), 3)
|
||||
// }
|
||||
}
|
||||
|
||||
// 递归menuList 创建子目录
|
||||
|
|
@ -261,32 +318,120 @@ export default function Sidebar(props) {
|
|||
}
|
||||
}
|
||||
|
||||
// 递归menuList,nodeTitle 增加子节点的节点名称
|
||||
// async function addMenuFindKey(list, key, name, nodeTitle){
|
||||
// let bool = false;
|
||||
// await list.map(item=>{
|
||||
// if(item.key === key){
|
||||
// item.children.push({
|
||||
// title: `\t${nodeTitle.substring(0, nodeTitle.indexOf('- '))}- ${name}`,
|
||||
// children: []
|
||||
// })
|
||||
// bool = true;
|
||||
// }else{
|
||||
// addMenuFindKey(item.children, key, name, nodeTitle)
|
||||
// }
|
||||
// })
|
||||
// return true;
|
||||
// }
|
||||
|
||||
function onDrop(info){
|
||||
// dragNode: 拖拽的节点 node: 放入的节点
|
||||
const dropKey = info.node.props.dataRef.key;
|
||||
const dragKey = info.dragNode.props.dataRef.key;
|
||||
const dropPos = info.node.props.pos.split('-');
|
||||
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
|
||||
// dropPosition 0元素之上 1元素之下 -1元素之上
|
||||
|
||||
function upTitle(list, title){
|
||||
const kongGe = title.substring(0, title.indexOf('- ') === -1 ? title.indexOf('[[') : title.indexOf('- '));
|
||||
list.map(item=>{
|
||||
item.title = `\t${kongGe}${item.title.trim()}`;
|
||||
if(item.children){
|
||||
return upTitle(item.children, item.title);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const data = JSON.parse(JSON.stringify(menuList));
|
||||
|
||||
// Find dragObject
|
||||
let dragObj;
|
||||
loop(data, dragKey, (item, index, arr) => {
|
||||
arr.splice(index, 1);
|
||||
dragObj = item;
|
||||
});
|
||||
// 如果拖拽的是目录,那么需要遍历所有的子节点,改变\t个数
|
||||
if(dragObj.title.indexOf('- ') !== -1){
|
||||
// 放入的节点标题
|
||||
const title = info.node.props.dataRef.title;
|
||||
const addBool = !info.dropToGap || ((info.node.props.children || []).length > 0 && dropPosition === 1)
|
||||
upTitle(dragObj.children, addBool ? `\t${title}` : title);
|
||||
}
|
||||
|
||||
// 是否要保存sidebar文件
|
||||
let bool = false;
|
||||
|
||||
// dropToGap: true拖拽在节点中间 false拖拽到节点之上
|
||||
if (!info.dropToGap) {
|
||||
// 追加到尾部
|
||||
loop(data, dropKey, (item, index, arr) => {
|
||||
const {title} = item;
|
||||
if(title.indexOf('- ') === -1){
|
||||
return;
|
||||
}
|
||||
const childrenName = item.children.map(item=>item.title.trim());
|
||||
if(childrenName.includes(dragObj.title.trim())){
|
||||
message.error('不能与同级目录名称相同');
|
||||
return;
|
||||
}
|
||||
dragObj.title = `\t${title.substring(0, title.indexOf('- '))}${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 {
|
||||
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())){
|
||||
message.error('不能与同级目录名称相同');
|
||||
return;
|
||||
}
|
||||
dragObj.title = `${ar[i].title.substring(0, ar[i].title.indexOf('- '))}${dragObj.title.trim()}`;
|
||||
if (dropPosition === -1) {
|
||||
ar.splice(i, 0, dragObj);
|
||||
} else {
|
||||
ar.splice(i + 1, 0, dragObj);
|
||||
}
|
||||
bool = true;
|
||||
}
|
||||
bool && updateSidebar(treeToMd(data), 2);
|
||||
setMenuList(bool ? data : initMenuList);
|
||||
}
|
||||
|
||||
// modal cancel
|
||||
function onCancel(){
|
||||
setVisible(false);
|
||||
setAddMenuError(undefined);
|
||||
setIsEditName(false);
|
||||
setMenuName(undefined);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='wikiSidebar mt5'>
|
||||
<DirectoryTree onSelect={selectTree}>
|
||||
<DirectoryTree onSelect={selectTree} draggable onDrop={onDrop}>
|
||||
{menuList && renderTreeNodes(menuList)}
|
||||
</DirectoryTree>
|
||||
|
||||
{/* 新增子目录弹窗 */}
|
||||
<Modal title="新增目录" visible={Boolean(visible)} onCancel={()=>{setVisible(false);setAddMenuError(undefined)}} onOk={addMenu} className="wikiAddMenu myself-modal" width={450} centered>
|
||||
<div className='mb10'>请输入目录名称:</div>
|
||||
<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={30}></Input>
|
||||
<div className='wikiAddMenuError mt5'>{addMenuError}</div>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
|
||||
import javaFetch from '../javaFetch';
|
||||
|
||||
|
||||
let settings = JSON.parse(localStorage.chromesetting);
|
||||
let actionUrl = settings && settings.common.wiki;
|
||||
|
||||
const service = javaFetch(actionUrl);
|
||||
export const httpUrl = actionUrl;
|
||||
export default service;
|
||||
|
||||
|
||||
|
||||
export const TokenKey = 'autologin_trustie';
|
||||
Loading…
Reference in New Issue