1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-01 15:09:14 +00:00

[wiki] change drag page into another page (#6422)

* 01 change drag page into another page

* 02 delete useless codes

* update move page

* fix invalid position

* delete useless codes

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
This commit is contained in:
Michael An
2024-07-27 07:52:12 +08:00
committed by GitHub
parent 2056b73c91
commit ed1424d4f9
8 changed files with 168 additions and 100 deletions

View File

@@ -12,7 +12,7 @@
}
.wiki-nav-body {
padding-bottom: 0.5rem;
padding-bottom: 1rem;
overflow: auto;
user-select: none;
max-height: 100%;
@@ -234,21 +234,29 @@
margin: 0.2rem 0;
}
.wiki-nav .wiki-page-item.page-can-drop::after,
.wiki-nav .wiki-page-item.page-can-drop-bottom::after,
.wiki-nav .wiki-page-item.page-can-drop-top::after {
content: '';
width: 100%;
height: 1px;
height: 5px;
position: absolute;
left: 0;
top: 39px;
background-color: #666 !important;
background-color: rgb(200, 220, 240) !important;
}
.wiki-nav .wiki-page-item.page-can-drop-bottom::after {
top: 32px;
}
.wiki-nav .wiki-page-item.page-can-drop-top::after {
top: 0;
}
.wiki-nav .wiki-page-item.dragged-page-over {
border-radius: 3px;
background-color: rgb(200, 220, 240) !important;
}
.dtable-dropdown-menu .dropdown-item .sf3-font {
font-size: 14px;
margin-right: 10px;

View File

@@ -89,8 +89,7 @@ class SidePanel extends Component {
movePage = ({ moved_page_id, target_page_id, move_position }) => {
let config = deepCopy(this.props.config);
let { navigation } = config;
PageUtils.movePage(navigation, moved_page_id, target_page_id, move_position);
config.navigation = navigation;
config.navigation = PageUtils.movePage(navigation, moved_page_id, target_page_id, move_position);
JSON.stringify(config);
this.props.updateWikiConfig(config);
};

View File

@@ -63,21 +63,29 @@ export default class PageUtils {
return pages.findIndex(page => page.id === pageId);
};
static insertPage(navigation, page_id, target_page_id, target_id, move_position) {
if (!target_id) {
let insertIndex = target_page_id ? navigation.findIndex(item => item.id === target_page_id) : -1;
if (insertIndex < 0) {
this.addPage(navigation, page_id, target_id);
return true;
static generatePaths = (tree) => {
tree._path = '';
function runNode(node) {
const newPath = node._path ? (node._path + '-' + node.id) : (node.id || '');
if (node.children) {
node.children.forEach(child => {
if (child) {
child._path = newPath;
runNode(child);
}
if (move_position === 'move_below') {
insertIndex++;
}
navigation.splice(insertIndex, 0, new NewPage(page_id));
return;
});
}
}
runNode(tree);
};
/**
* move page to another page
* @param {object} navigation
* @param {string} moved_page_id
* @param {string} target_page_id
* @param {string} move_position, one of'move_into', 'move_below', 'move_into'
*/
static movePage(navigation, moved_page_id, target_page_id, move_position) {
let movedPage = null;
function _cutPageRecursion(item, page_id) {
@@ -101,42 +109,47 @@ export default class PageUtils {
});
}
}
function _insertPageRecursion(item, page_id, target_page_id, target_id, move_position) {
if (item.id === target_id) {
if (item.children) {
let insertIndex = target_page_id ? item.children.findIndex(item => item.id === target_page_id) : -1;
if (move_position === 'move_below') {
insertIndex++;
}
item.children.splice(insertIndex, 0, movedPage);
} else {
item.children = [];
item.children.push(movedPage);
}
return;
}
item.children && item.children.forEach(item => {
_insertPageRecursion(item, page_id, target_page_id, target_id, move_position);
});
}
function _insertPage(navigation, page_id, target_page_id, target_id, move_position) {
if (!target_id) {
let insertIndex = target_page_id ? navigation.findIndex(item => item.id === target_page_id) : -1;
if (insertIndex < 0) {
navigation.splice(0, 0, movedPage);
return;
}
if (move_position === 'move_below') {
insertIndex++;
}
navigation.splice(insertIndex, 0, movedPage);
return;
}
navigation.forEach(item => {
_insertPageRecursion(item, page_id, target_page_id, target_id, move_position);
});
}
_cutPage(navigation, moved_page_id);
_insertPage(navigation, moved_page_id, target_page_id, target_page_id, move_position);
if (!movedPage) return;
function _insertPage(tree, target_page_id, move_position) {
if (!tree) return;
if (!Array.isArray(tree.children)) {
tree.children = [];
}
const target_page = tree.children.find(item => item.id === target_page_id);
const target_index = tree.children.findIndex(item => item.id === target_page_id);
if (target_page) {
switch (move_position) {
case 'move_into': {
if (!Array.isArray(target_page.children)) {
target_page.children = [];
}
target_page.children.push(movedPage);
break;
}
case 'move_above': {
tree.children.splice(target_index, 0, movedPage);
break;
}
case 'move_below': {
tree.children.splice(target_index + 1, 0, movedPage);
break;
}
default:
break;
}
} else {
tree.children.forEach(child => {
_insertPage(child, target_page_id, move_position);
});
}
}
let tree = {};
tree.children = navigation;
_insertPage(tree, target_page_id, move_position);
this.generatePaths(tree);
return tree.children;
}
}

View File

@@ -31,31 +31,37 @@ const dragSource = {
const dropTarget = {
drop(props, monitor) {
const dragSource = monitor.getItem();
const className = props.getClassName();
let move_position;
if (className.includes('page-can-drop-bottom')) {
move_position = 'move_below';
} else if (className.includes('page-can-drop-top')) {
move_position = 'move_above';
} else if (className.includes('dragged-page-over')) {
move_position = 'move_into';
}
if (!move_position) return;
if (dragSource.mode === 'wiki-page') {
const { pageIndex: targetIndex, page: targetPage } = props;
const draggedPageId = dragSource.data.id;
const targetPageId = targetPage.id;
if (draggedPageId !== targetPageId) {
const sourceIndex = dragSource.idx;
const move_position = sourceIndex > targetIndex ? 'move_above' : 'move_below';
wikiAPI.moveWiki2Page(wikiId, draggedPageId, targetPageId, move_position).then(res => {
props.onMovePage({
moved_page_id: draggedPageId,
target_page_id: targetPageId,
move_position,
});
}).catch((error) => {
if (error.response && error.response.status === 400 && error.response.data.error_msg === 'Internal Server Error') {
const targetPage = props.page;
const draggedPage = dragSource.data;
const moved_page_id = draggedPage.id;
const target_page_id = targetPage.id;
if (moved_page_id === target_page_id) {
return;
}
if (targetPage._path && targetPage._path.includes(moved_page_id)) {
toaster.danger(gettext('Cannot move parent page to child page'));
} else {
return;
}
wikiAPI.moveWiki2Page(wikiId, moved_page_id, target_page_id, move_position).then(res => {
props.onMovePage({ moved_page_id, target_page_id, move_position });
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
}
});
}
return;
}
}
};
const dragCollect = (connect, monitor) => ({

View File

@@ -143,6 +143,9 @@ class PageItem extends Component {
addPageInside={this.props.addPageInside}
getFoldState={this.props.getFoldState}
toggleExpand={this.props.toggleExpand}
setClassName={this.props.setClassName}
getClassName={this.props.getClassName}
layerDragProps={this.props.layerDragProps}
/>
);
};
@@ -157,17 +160,32 @@ class PageItem extends Component {
this.props.addPageInside(Object.assign({ parentPageId: this.props.page.id }, newPage));
};
render() {
const {
connectDragSource, connectDragPreview, connectDropTarget, isOver, canDrop, isDragging,
page, pagesLength, isEditMode, isOnlyOnePage, pathStr,
} = this.props;
const { isShowNameEditor, pageName, isSelected } = this.state;
getPageClassName = () => {
const { isOver, canDrop, isEditMode, layerDragProps } = this.props;
const isOverPage = isOver && canDrop;
if (!isOverPage || ! layerDragProps || !layerDragProps.clientOffset) {
return classnames('wiki-page-item', { 'selected-page': this.state.isSelected }, { 'readonly': !isEditMode });
}
let y = layerDragProps.clientOffset.y;
let top = this.pageItemRef.getBoundingClientRect().y;
const className = classnames(
'wiki-page-item',
{ 'dragged-page-over': (top + 10 < y && y < top + 30) },
{ 'page-can-drop-top': (top + 10 > y) },
{ 'page-can-drop-bottom': (top + 30 < y) },
{ 'selected-page': this.state.isSelected },
{ 'readonly': !isEditMode },
);
this.props.setClassName(className);
return className;
};
render() {
const { connectDragSource, connectDragPreview, connectDropTarget,
page, pagesLength, isEditMode, isOnlyOnePage, pathStr } = this.props;
const { isShowNameEditor, pageName, isSelected } = this.state;
if (isSelected) this.setDocUuid(page.docUuid);
let pageCanDropTop = isOverPage && isDragging;
let pageCanDrop = isOverPage && !isDragging;
let navItemId = `page-editor-${page.id}`;
let fn = isEditMode ? connectDragSource : (argu) => { argu; };
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
@@ -180,12 +198,7 @@ class PageItem extends Component {
fn(connectDropTarget(
connectDragPreview(
<div
className={classnames('wiki-page-item',
{ 'selected-page': isSelected },
{ 'page-can-drop-top': pageCanDropTop },
{ 'page-can-drop': pageCanDrop },
{ 'readonly': !isEditMode },
)}
className={this.getPageClassName()}
ref={ref => this.pageItemRef = ref}
onMouseEnter={this.onMouseEnter}
onMouseMove={this.onMouseMove}
@@ -296,6 +309,8 @@ PageItem.propTypes = {
getFoldState: PropTypes.func,
toggleExpand: PropTypes.func,
updateWikiConfig: PropTypes.func,
getClassName: PropTypes.func,
setClassName: PropTypes.func,
};
export default PageItem;

View File

@@ -25,6 +25,7 @@ class WikiNav extends Component {
constructor(props) {
super(props);
this.folderClassNameCache = '';
this.idFoldedStatusMap = this.getFoldedFromLocal();
}
@@ -52,6 +53,14 @@ class WikiNav extends Component {
this.idFoldedStatusMap = idFoldedStatusMap;
};
setClassName = (name) => {
this.folderClassNameCache = name;
};
getClassName = () => {
return this.folderClassNameCache;
};
renderPage = (page, index, pagesLength, isOnlyOnePage, id_page_map, layerDragProps) => {
const { isEditMode, pages } = this.props;
const id = page.id;
@@ -78,6 +87,8 @@ class WikiNav extends Component {
toggleExpand={this.toggleExpand}
id_page_map={id_page_map}
layerDragProps={layerDragProps}
setClassName={this.setClassName}
getClassName={this.getClassName}
/>
);
};

View File

@@ -571,6 +571,13 @@ class Wiki2PagesView(APIView):
id_set = get_all_wiki_ids(navigation)
target_page_id = request.data.get('target_id', '')
moved_page_id = request.data.get('moved_id', '')
move_position = request.data.get('move_position', '')
# check arguments
valid_move_positions = ['move_below', 'move_above', 'move_into']
if move_position not in valid_move_positions:
error_msg = 'Invalid move_position value: ' + move_position
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if (target_page_id not in id_set) or (moved_page_id not in id_set):
error_msg = 'Page not found'
logger.error(error_msg)
@@ -583,7 +590,7 @@ class Wiki2PagesView(APIView):
logger.error(error_msg)
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
move_nav(navigation, target_page_id, moved_nav)
move_nav(navigation, target_page_id, moved_nav, move_position)
wiki_config['navigation'] = navigation
wiki_config = json.dumps(wiki_config)

View File

@@ -244,14 +244,23 @@ def pop_nav(navigation, page_id):
return None
def move_nav(navigation, target_id, moved_nav):
for nav in navigation:
def move_nav(navigation, target_id, moved_nav, move_position):
def move_item(nav_list, nav_index, moved_nav, move_position):
if move_position == 'move_below':
nav_list.insert(nav_index + 1, moved_nav)
elif move_position == 'move_above':
nav_list.insert(nav_index, moved_nav)
for nav_index, nav in enumerate(navigation):
if nav['id'] == target_id:
if move_position == 'move_below' or move_position == 'move_above':
move_item(navigation, nav_index, moved_nav, move_position)
if move_position == 'move_into':
if 'children' in nav:
nav['children'].insert(0, moved_nav)
nav['children'].append(moved_nav)
else:
nav['children'] = [moved_nav]
return
if 'children' in nav:
move_nav(nav['children'], target_id, moved_nav)
move_nav(nav['children'], target_id, moved_nav, move_position)