添加wiki目录排序和目录重命名功能

This commit is contained in:
谢思 2023-03-28 18:46:00 +08:00
parent 95db1723f0
commit fc955d3234
5 changed files with 246 additions and 79 deletions

View File

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

View File

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

View File

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

View File

@ -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) {
});
}
// listkey 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) {
}
}
// menuListnodeTitle
// 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>

View File

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