mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-14 14:21:23 +00:00
refactor(tag): parent tag files count includes children (#7420)
This commit is contained in:
@@ -52,10 +52,7 @@ class ActionsCell extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getRecordNo = () => {
|
getRecordNo = () => {
|
||||||
if (this.props.showRecordAsTree) {
|
return (this.props.showRecordAsTree ? this.props.treeNodeIndex : this.props.index) + 1;
|
||||||
return this.props.treeNodeDisplayIndex;
|
|
||||||
}
|
|
||||||
return this.props.index + 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@@ -24,11 +24,12 @@ const Cell = React.memo(({
|
|||||||
frozen,
|
frozen,
|
||||||
height,
|
height,
|
||||||
showRecordAsTree,
|
showRecordAsTree,
|
||||||
nodeDepth,
|
treeNodeIndex,
|
||||||
|
treeNodeDepth,
|
||||||
hasChildNodes,
|
hasChildNodes,
|
||||||
isFoldedNode,
|
isFoldedTreeNode,
|
||||||
checkCanModifyRecord,
|
checkCanModifyRecord,
|
||||||
toggleExpandNode,
|
toggleExpandTreeNode,
|
||||||
}) => {
|
}) => {
|
||||||
const cellEditable = useMemo(() => {
|
const cellEditable = useMemo(() => {
|
||||||
return checkIsColumnEditable(column) && checkCanModifyRecord && checkCanModifyRecord(record);
|
return checkIsColumnEditable(column) && checkCanModifyRecord && checkCanModifyRecord(record);
|
||||||
@@ -168,19 +169,19 @@ const Cell = React.memo(({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderCellContent = useCallback(() => {
|
const renderCellContent = useCallback(() => {
|
||||||
const columnFormatter = isValidElement(column.formatter) && cloneElement(column.formatter, { isCellSelected, value: cellValue, column, record, onChange: modifyRecord });
|
const columnFormatter = isValidElement(column.formatter) && cloneElement(column.formatter, { isCellSelected, value: cellValue, column, record, treeNodeIndex, onChange: modifyRecord });
|
||||||
if (showRecordAsTree && isNameColumn) {
|
if (showRecordAsTree && isNameColumn) {
|
||||||
return (
|
return (
|
||||||
<div className="sf-table-cell-tree-node">
|
<div className="sf-table-cell-tree-node">
|
||||||
{hasChildNodes && <span className="sf-table-record-tree-expand-icon" style={{ left: nodeDepth * NODE_ICON_LEFT_INDENT }} onClick={toggleExpandNode}><i className={classnames('sf3-font sf3-font-down', { 'rotate-270': isFoldedNode })}></i></span>}
|
{hasChildNodes && <span className="sf-table-record-tree-expand-icon" style={{ left: treeNodeDepth * NODE_ICON_LEFT_INDENT }} onClick={toggleExpandTreeNode}><i className={classnames('sf3-font sf3-font-down', { 'rotate-270': isFoldedTreeNode })}></i></span>}
|
||||||
<div className="sf-table-cell-tree-node-content" style={{ paddingLeft: NODE_CONTENT_LEFT_INDENT + nodeDepth * NODE_ICON_LEFT_INDENT }}>
|
<div className="sf-table-cell-tree-node-content" style={{ paddingLeft: NODE_CONTENT_LEFT_INDENT + treeNodeDepth * NODE_ICON_LEFT_INDENT }}>
|
||||||
{columnFormatter}
|
{columnFormatter}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return columnFormatter;
|
return columnFormatter;
|
||||||
}, [isNameColumn, column, isCellSelected, cellValue, record, showRecordAsTree, nodeDepth, hasChildNodes, isFoldedNode, modifyRecord, toggleExpandNode]);
|
}, [isNameColumn, column, isCellSelected, cellValue, record, showRecordAsTree, treeNodeIndex, treeNodeDepth, hasChildNodes, isFoldedTreeNode, modifyRecord, toggleExpandTreeNode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={`${record._id}-${column.key}`} {...containerProps}>
|
<div key={`${record._id}-${column.key}`} {...containerProps}>
|
||||||
@@ -208,14 +209,15 @@ Cell.propTypes = {
|
|||||||
column: PropTypes.object.isRequired,
|
column: PropTypes.object.isRequired,
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
needBindEvents: PropTypes.bool,
|
needBindEvents: PropTypes.bool,
|
||||||
modifyRecord: PropTypes.func,
|
|
||||||
highlightClassName: PropTypes.string,
|
highlightClassName: PropTypes.string,
|
||||||
bgColor: PropTypes.string,
|
bgColor: PropTypes.string,
|
||||||
showRecordAsTree: PropTypes.bool,
|
showRecordAsTree: PropTypes.bool,
|
||||||
nodeDepth: PropTypes.number,
|
treeNodeIndex: PropTypes.number,
|
||||||
|
treeNodeDepth: PropTypes.number,
|
||||||
hasChildNodes: PropTypes.bool,
|
hasChildNodes: PropTypes.bool,
|
||||||
isFoldedNode: PropTypes.bool,
|
isFoldedTreeNode: PropTypes.bool,
|
||||||
toggleExpandNode: PropTypes.func,
|
modifyRecord: PropTypes.func,
|
||||||
|
toggleExpandTreeNode: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Cell;
|
export default Cell;
|
||||||
|
@@ -36,11 +36,11 @@ class Record extends React.Component {
|
|||||||
nextProps.searchResult !== this.props.searchResult ||
|
nextProps.searchResult !== this.props.searchResult ||
|
||||||
nextProps.columnColor !== this.props.columnColor ||
|
nextProps.columnColor !== this.props.columnColor ||
|
||||||
nextProps.showRecordAsTree !== this.props.showRecordAsTree ||
|
nextProps.showRecordAsTree !== this.props.showRecordAsTree ||
|
||||||
nextProps.nodeKey !== this.props.nodeKey ||
|
nextProps.treeNodeIndex !== this.props.treeNodeIndex ||
|
||||||
nextProps.nodeDepth !== this.props.nodeDepth ||
|
nextProps.treeNodeKey !== this.props.treeNodeKey ||
|
||||||
|
nextProps.treeNodeDepth !== this.props.treeNodeDepth ||
|
||||||
nextProps.hasChildNodes !== this.props.hasChildNodes ||
|
nextProps.hasChildNodes !== this.props.hasChildNodes ||
|
||||||
nextProps.treeNodeDisplayIndex !== this.props.treeNodeDisplayIndex ||
|
nextProps.isFoldedTreeNode !== this.props.isFoldedTreeNode
|
||||||
nextProps.isFoldedNode !== this.props.isFoldedNode
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,10 +116,11 @@ class Record extends React.Component {
|
|||||||
highlightClassName={highlightClassName}
|
highlightClassName={highlightClassName}
|
||||||
bgColor={bgColor}
|
bgColor={bgColor}
|
||||||
showRecordAsTree={this.props.showRecordAsTree}
|
showRecordAsTree={this.props.showRecordAsTree}
|
||||||
nodeDepth={this.props.nodeDepth}
|
treeNodeIndex={this.props.treeNodeIndex}
|
||||||
|
treeNodeDepth={this.props.treeNodeDepth}
|
||||||
hasChildNodes={this.props.hasChildNodes}
|
hasChildNodes={this.props.hasChildNodes}
|
||||||
isFoldedNode={this.props.isFoldedNode}
|
isFoldedTreeNode={this.props.isFoldedTreeNode}
|
||||||
toggleExpandNode={this.props.toggleExpandNode}
|
toggleExpandTreeNode={this.props.toggleExpandTreeNode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -183,10 +184,11 @@ class Record extends React.Component {
|
|||||||
highlightClassName={highlightClassName}
|
highlightClassName={highlightClassName}
|
||||||
bgColor={bgColor}
|
bgColor={bgColor}
|
||||||
showRecordAsTree={this.props.showRecordAsTree}
|
showRecordAsTree={this.props.showRecordAsTree}
|
||||||
nodeDepth={this.props.nodeDepth}
|
treeNodeIndex={this.props.treeNodeIndex}
|
||||||
|
treeNodeDepth={this.props.treeNodeDepth}
|
||||||
hasChildNodes={this.props.hasChildNodes}
|
hasChildNodes={this.props.hasChildNodes}
|
||||||
isFoldedNode={this.props.isFoldedNode}
|
isFoldedTreeNode={this.props.isFoldedTreeNode}
|
||||||
toggleExpandNode={this.props.toggleExpandNode}
|
toggleExpandTreeNode={this.props.toggleExpandTreeNode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -273,7 +275,7 @@ class Record extends React.Component {
|
|||||||
recordId={record._id}
|
recordId={record._id}
|
||||||
index={index}
|
index={index}
|
||||||
showRecordAsTree={this.props.showRecordAsTree}
|
showRecordAsTree={this.props.showRecordAsTree}
|
||||||
treeNodeDisplayIndex={this.props.treeNodeDisplayIndex}
|
treeNodeIndex={this.props.treeNodeIndex}
|
||||||
onSelectRecord={this.onSelectRecord}
|
onSelectRecord={this.onSelectRecord}
|
||||||
isLastFrozenCell={!lastFrozenColumnKey}
|
isLastFrozenCell={!lastFrozenColumnKey}
|
||||||
height={cellHeight}
|
height={cellHeight}
|
||||||
@@ -316,12 +318,12 @@ Record.propTypes = {
|
|||||||
searchResult: PropTypes.object,
|
searchResult: PropTypes.object,
|
||||||
columnColor: PropTypes.object,
|
columnColor: PropTypes.object,
|
||||||
showRecordAsTree: PropTypes.bool,
|
showRecordAsTree: PropTypes.bool,
|
||||||
nodeKey: PropTypes.string,
|
treeNodeIndex: PropTypes.number,
|
||||||
nodeDepth: PropTypes.number,
|
treeNodeKey: PropTypes.string,
|
||||||
|
treeNodeDepth: PropTypes.number,
|
||||||
hasChildNodes: PropTypes.bool,
|
hasChildNodes: PropTypes.bool,
|
||||||
treeNodeDisplayIndex: PropTypes.number,
|
isFoldedTreeNode: PropTypes.bool,
|
||||||
isFoldedNode: PropTypes.bool,
|
toggleExpandTreeNode: PropTypes.func,
|
||||||
toggleExpandNode: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Record;
|
export default Record;
|
||||||
|
@@ -99,7 +99,7 @@ class TreeBody extends Component {
|
|||||||
if (row && checkIsTreeNodeShown(nodeKey, keyNodeFoldedMap)) {
|
if (row && checkIsTreeNodeShown(nodeKey, keyNodeFoldedMap)) {
|
||||||
shownNodes.push({
|
shownNodes.push({
|
||||||
...node,
|
...node,
|
||||||
node_display_index: index + 1,
|
node_index: index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -535,7 +535,7 @@ class TreeBody extends Component {
|
|||||||
const rowHeight = this.getRowHeight();
|
const rowHeight = this.getRowHeight();
|
||||||
const cellMetaData = this.getCellMetaData();
|
const cellMetaData = this.getCellMetaData();
|
||||||
let shownNodes = visibleNodes.map((node, index) => {
|
let shownNodes = visibleNodes.map((node, index) => {
|
||||||
const { _id: recordId, node_key, node_depth, node_display_index } = node;
|
const { _id: recordId, node_key, node_depth, node_index } = node;
|
||||||
const hasChildNodes = checkTreeNodeHasChildNodes(node);
|
const hasChildNodes = checkTreeNodeHasChildNodes(node);
|
||||||
const record = this.props.recordGetterById(recordId);
|
const record = this.props.recordGetterById(recordId);
|
||||||
const isSelected = TreeMetrics.checkIsTreeNodeSelected(node_key, treeMetrics);
|
const isSelected = TreeMetrics.checkIsTreeNodeSelected(node_key, treeMetrics);
|
||||||
@@ -553,7 +553,6 @@ class TreeBody extends Component {
|
|||||||
}}
|
}}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
index={recordIndex}
|
index={recordIndex}
|
||||||
treeNodeDisplayIndex={node_display_index}
|
|
||||||
isLastRecord={isLastRecord}
|
isLastRecord={isLastRecord}
|
||||||
showSequenceColumn={this.props.showSequenceColumn}
|
showSequenceColumn={this.props.showSequenceColumn}
|
||||||
record={record}
|
record={record}
|
||||||
@@ -567,17 +566,18 @@ class TreeBody extends Component {
|
|||||||
cellMetaData={cellMetaData}
|
cellMetaData={cellMetaData}
|
||||||
columnColor={columnColor}
|
columnColor={columnColor}
|
||||||
searchResult={this.props.searchResult}
|
searchResult={this.props.searchResult}
|
||||||
nodeKey={node_key}
|
treeNodeIndex={node_index}
|
||||||
nodeDepth={node_depth}
|
treeNodeKey={node_key}
|
||||||
|
treeNodeDepth={node_depth}
|
||||||
hasChildNodes={hasChildNodes}
|
hasChildNodes={hasChildNodes}
|
||||||
isFoldedNode={isFoldedNode}
|
isFoldedTreeNode={isFoldedNode}
|
||||||
checkCanModifyRecord={this.props.checkCanModifyRecord}
|
checkCanModifyRecord={this.props.checkCanModifyRecord}
|
||||||
checkCellValueChanged={this.props.checkCellValueChanged}
|
checkCellValueChanged={this.props.checkCellValueChanged}
|
||||||
hasSelectedCell={hasSelectedCell}
|
hasSelectedCell={hasSelectedCell}
|
||||||
selectedPosition={selectedPosition}
|
selectedPosition={selectedPosition}
|
||||||
selectNoneCells={this.selectNoneCells}
|
selectNoneCells={this.selectNoneCells}
|
||||||
onSelectRecord={this.props.onSelectRecord}
|
onSelectRecord={this.props.onSelectRecord}
|
||||||
toggleExpandNode={() => this.toggleExpandNode(node_key)}
|
toggleExpandTreeNode={() => this.toggleExpandNode(node_key)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -22,7 +22,7 @@ export const checkCellValueChanged = (oldVal, newVal) => {
|
|||||||
export const cellCompare = (props, nextProps) => {
|
export const cellCompare = (props, nextProps) => {
|
||||||
const {
|
const {
|
||||||
record: oldRecord, column, isCellSelected, isLastCell, highlightClassName, height, bgColor,
|
record: oldRecord, column, isCellSelected, isLastCell, highlightClassName, height, bgColor,
|
||||||
showRecordAsTree, nodeDepth, hasChildNodes, isFoldedNode,
|
showRecordAsTree, treeNodeIndex, treeNodeDepth, hasChildNodes, isFoldedTreeNode,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
record: newRecord, highlightClassName: newHighlightClassName, height: newHeight, column: newColumn, bgColor: newBgColor,
|
record: newRecord, highlightClassName: newHighlightClassName, height: newHeight, column: newColumn, bgColor: newBgColor,
|
||||||
@@ -49,9 +49,10 @@ export const cellCompare = (props, nextProps) => {
|
|||||||
!ObjectUtils.isSameObject(column.data, newColumn.data) ||
|
!ObjectUtils.isSameObject(column.data, newColumn.data) ||
|
||||||
bgColor !== newBgColor ||
|
bgColor !== newBgColor ||
|
||||||
showRecordAsTree !== nextProps.showRecordAsTree ||
|
showRecordAsTree !== nextProps.showRecordAsTree ||
|
||||||
nodeDepth !== nextProps.nodeDepth ||
|
treeNodeIndex !== nextProps.treeNodeIndex ||
|
||||||
|
treeNodeDepth !== nextProps.treeNodeDepth ||
|
||||||
hasChildNodes !== nextProps.hasChildNodes ||
|
hasChildNodes !== nextProps.hasChildNodes ||
|
||||||
isFoldedNode !== nextProps.isFoldedNode ||
|
isFoldedTreeNode !== nextProps.isFoldedTreeNode ||
|
||||||
props.groupRecordIndex !== nextProps.groupRecordIndex ||
|
props.groupRecordIndex !== nextProps.groupRecordIndex ||
|
||||||
props.recordIndex !== nextProps.recordIndex
|
props.recordIndex !== nextProps.recordIndex
|
||||||
);
|
);
|
||||||
|
@@ -27,7 +27,6 @@ export const generateKeyTreeNodeRowIdMap = (tree) => {
|
|||||||
return tree_node_key_row_id_map;
|
return tree_node_key_row_id_map;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getValidKeyTreeNodeFoldedMap = (keyTreeNodeFoldedMap, treeNodeKeyRecordIdMap) => {
|
export const getValidKeyTreeNodeFoldedMap = (keyTreeNodeFoldedMap, treeNodeKeyRecordIdMap) => {
|
||||||
if (!keyTreeNodeFoldedMap) return {};
|
if (!keyTreeNodeFoldedMap) return {};
|
||||||
|
|
||||||
@@ -71,6 +70,21 @@ export const checkIsTreeNodeShown = (nodeKey, keyFoldedNodeMap) => {
|
|||||||
return !foldedNodeKeys.some((foldedNodeKey) => nodeKey !== foldedNodeKey && nodeKey.includes(foldedNodeKey));
|
return !foldedNodeKeys.some((foldedNodeKey) => nodeKey !== foldedNodeKey && nodeKey.includes(foldedNodeKey));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updatedKeyTreeNodeMap = (nodeKey, node, keyTreeNodeMap) => {
|
||||||
|
if (!nodeKey || !node || !keyTreeNodeMap) return;
|
||||||
|
keyTreeNodeMap[nodeKey] = node;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTreeNodeByKey = (nodeKey, keyTreeNodeMap) => {
|
||||||
|
if (!nodeKey || !keyTreeNodeMap) return null;
|
||||||
|
return keyTreeNodeMap[nodeKey];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTreeNodeById = (nodeId, tree) => {
|
||||||
|
if (!nodeId || !Array.isArray(tree) || tree.length === 0) return null;
|
||||||
|
return tree.find((node) => getTreeNodeId(node) === nodeId);
|
||||||
|
};
|
||||||
|
|
||||||
export const getTreeNodeId = (node) => {
|
export const getTreeNodeId = (node) => {
|
||||||
return node ? node[TREE_NODE_KEY.ID] : '';
|
return node ? node[TREE_NODE_KEY.ID] : '';
|
||||||
};
|
};
|
||||||
@@ -95,7 +109,7 @@ export const resetTreeHasChildNodesStatus = (tree) => {
|
|||||||
const nextNode = tree[index + 1];
|
const nextNode = tree[index + 1];
|
||||||
const nextNodeKey = getTreeNodeKey(nextNode);
|
const nextNodeKey = getTreeNodeKey(nextNode);
|
||||||
const currentNodeKey = getTreeNodeKey(node);
|
const currentNodeKey = getTreeNodeKey(node);
|
||||||
if (nextNode && checkTreeNodeHasChildNodes(node) && !nextNodeKey.includes(currentNodeKey)) {
|
if (checkTreeNodeHasChildNodes(node) && (!nextNode || !nextNodeKey.includes(currentNodeKey))) {
|
||||||
tree[index][TREE_NODE_KEY.HAS_CHILD_NODES] = false;
|
tree[index][TREE_NODE_KEY.HAS_CHILD_NODES] = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -131,3 +145,20 @@ export const addTreeChildNode = (newChildNode, parentNode, tree) => {
|
|||||||
}
|
}
|
||||||
tree.splice(lastChildNodeIndex + 1, 0, newChildNode);
|
tree.splice(lastChildNodeIndex + 1, 0, newChildNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getAllSubTreeNodes = (nodeIndex, tree) => {
|
||||||
|
const treeLen = Array.isArray(tree) ? tree.length : 0;
|
||||||
|
const parentNode = tree[nodeIndex];
|
||||||
|
const parentNodeKey = getTreeNodeKey(parentNode);
|
||||||
|
if (!parentNodeKey || nodeIndex === treeLen - 1) return [];
|
||||||
|
|
||||||
|
let subNodes = [];
|
||||||
|
for (let i = nodeIndex + 1, len = treeLen; i < len; i++) {
|
||||||
|
const currNodeKey = getTreeNodeKey(tree[i]);
|
||||||
|
if (!currNodeKey || !currNodeKey.includes(parentNodeKey)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
subNodes.push(tree[i]);
|
||||||
|
}
|
||||||
|
return subNodes;
|
||||||
|
};
|
||||||
|
@@ -96,6 +96,12 @@ class TagsManagerAPI {
|
|||||||
return this.req.get(url);
|
return this.req.get(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getTagsFiles = (repoID, tags_ids) => {
|
||||||
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/tags-files/';
|
||||||
|
const params = { tags_ids };
|
||||||
|
return this.req.post(url, params);
|
||||||
|
};
|
||||||
|
|
||||||
// file tags
|
// file tags
|
||||||
updateFileTags = (repoID, data) => {
|
updateFileTags = (repoID, data) => {
|
||||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/file-tags/';
|
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/file-tags/';
|
||||||
|
@@ -1,34 +1,36 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import tagsAPI from '../api';
|
import tagsAPI from '../api';
|
||||||
import { useTags } from './tags';
|
import { useTags } from './tags';
|
||||||
import { PRIVATE_COLUMN_KEY } from '../constants';
|
import { getTreeNodeByKey } from '../../components/sf-table/utils/tree';
|
||||||
import { getRecordIdFromRecord } from '../../metadata/utils/cell';
|
import { getAllChildTagsIdsFromNode } from '../utils/tree';
|
||||||
|
|
||||||
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
|
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
|
||||||
const TagViewContext = React.createContext(null);
|
const TagViewContext = React.createContext(null);
|
||||||
|
|
||||||
export const TagViewProvider = ({ repoID, tagID, children, ...params }) => {
|
export const TagViewProvider = ({ repoID, tagID, nodeKey, children, ...params }) => {
|
||||||
const [isLoading, setLoading] = useState(true);
|
const [isLoading, setLoading] = useState(true);
|
||||||
const [tagFiles, setTagFiles] = useState(null);
|
const [tagFiles, setTagFiles] = useState(null);
|
||||||
const [errorMessage, setErrorMessage] = useState(null);
|
const [errorMessage, setErrorMessage] = useState(null);
|
||||||
|
|
||||||
const { updateLocalTag } = useTags();
|
const { tagsData } = useTags();
|
||||||
|
|
||||||
|
const getChildTagsIds = useCallback((nodeKey) => {
|
||||||
|
if (!nodeKey) return [];
|
||||||
|
const displayNode = getTreeNodeByKey(nodeKey, tagsData.key_tree_node_map);
|
||||||
|
return getAllChildTagsIdsFromNode(displayNode);
|
||||||
|
}, [tagsData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
tagsAPI.getTagFiles(repoID, tagID).then(res => {
|
const childTagsIds = getChildTagsIds(nodeKey);
|
||||||
|
let tagsIds = [tagID];
|
||||||
|
if (Array.isArray(childTagsIds) && childTagsIds.length > 0) {
|
||||||
|
tagsIds.push(...childTagsIds);
|
||||||
|
}
|
||||||
|
tagsAPI.getTagsFiles(repoID, tagsIds).then(res => {
|
||||||
const rows = res.data?.results || [];
|
const rows = res.data?.results || [];
|
||||||
setTagFiles({ columns: res.data?.metadata || [], rows: res.data?.results || [] });
|
setTagFiles({ columns: res.data?.metadata || [], rows });
|
||||||
updateLocalTag(tagID, {
|
|
||||||
[PRIVATE_COLUMN_KEY.TAG_FILE_LINKS]: rows.map(r => {
|
|
||||||
const recordId = getRecordIdFromRecord(r);
|
|
||||||
return {
|
|
||||||
row_id: recordId,
|
|
||||||
display_value: recordId
|
|
||||||
};
|
|
||||||
})
|
|
||||||
});
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
const errorMessage = Utils.getErrorMsg(error);
|
const errorMessage = Utils.getErrorMsg(error);
|
||||||
@@ -36,7 +38,7 @@ export const TagViewProvider = ({ repoID, tagID, children, ...params }) => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [repoID, tagID]);
|
}, [repoID, tagID, nodeKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TagViewContext.Provider value={{
|
<TagViewContext.Provider value={{
|
||||||
|
@@ -3,12 +3,12 @@ import { getColumnByKey } from '../../metadata/utils/column';
|
|||||||
import { getGroupRows } from '../../metadata/utils/group';
|
import { getGroupRows } from '../../metadata/utils/group';
|
||||||
import { getRowsByIds } from '../../metadata/utils/table';
|
import { getRowsByIds } from '../../metadata/utils/table';
|
||||||
import { OPERATION_TYPE } from './operations';
|
import { OPERATION_TYPE } from './operations';
|
||||||
import { buildTagsTree } from '../utils/tree';
|
import { buildTagsTree, setNodeAllChildTagsIds } from '../utils/tree';
|
||||||
import { getRecordIdFromRecord } from '../../metadata/utils/cell';
|
import { getRecordIdFromRecord } from '../../metadata/utils/cell';
|
||||||
import { TREE_NODE_KEY } from '../../components/sf-table/constants/tree';
|
|
||||||
import {
|
import {
|
||||||
addTreeChildNode, createTreeNode, generateNodeKey, getTreeNodeDepth, getTreeNodeId, getTreeNodeKey,
|
addTreeChildNode, checkTreeNodeHasChildNodes, createTreeNode, generateNodeKey, getTreeNodeDepth, getTreeNodeId, getTreeNodeKey,
|
||||||
resetTreeHasChildNodesStatus,
|
resetTreeHasChildNodesStatus,
|
||||||
|
updatedKeyTreeNodeMap,
|
||||||
} from '../../components/sf-table/utils/tree';
|
} from '../../components/sf-table/utils/tree';
|
||||||
|
|
||||||
// const DEFAULT_COMPUTER_PROPERTIES_CONTROLLER = {
|
// const DEFAULT_COMPUTER_PROPERTIES_CONTROLLER = {
|
||||||
@@ -21,7 +21,9 @@ import {
|
|||||||
class DataProcessor {
|
class DataProcessor {
|
||||||
|
|
||||||
static buildTagsTree(rows, table) {
|
static buildTagsTree(rows, table) {
|
||||||
table.rows_tree = buildTagsTree(rows, table);
|
const { tree, key_tree_node_map } = buildTagsTree(rows, table);
|
||||||
|
table.rows_tree = tree;
|
||||||
|
table.key_tree_node_map = key_tree_node_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateTagsTreeWithNewTags(tags, table) {
|
static updateTagsTreeWithNewTags(tags, table) {
|
||||||
@@ -33,6 +35,7 @@ class DataProcessor {
|
|||||||
const nodeKey = generateNodeKey('', tagId);
|
const nodeKey = generateNodeKey('', tagId);
|
||||||
const node = createTreeNode(tagId, nodeKey, 0, false);
|
const node = createTreeNode(tagId, nodeKey, 0, false);
|
||||||
updated_rows_tree.push(node);
|
updated_rows_tree.push(node);
|
||||||
|
updatedKeyTreeNodeMap(nodeKey, node, table.key_tree_node_map);
|
||||||
});
|
});
|
||||||
table.rows_tree = updated_rows_tree;
|
table.rows_tree = updated_rows_tree;
|
||||||
}
|
}
|
||||||
@@ -41,7 +44,7 @@ class DataProcessor {
|
|||||||
if (!Array.isArray(deletedTagsIds) || deletedTagsIds.length === 0) return;
|
if (!Array.isArray(deletedTagsIds) || deletedTagsIds.length === 0) return;
|
||||||
const { rows_tree } = table;
|
const { rows_tree } = table;
|
||||||
const idTagDeletedMap = deletedTagsIds.reduce((currIdTagDeletedMap, tagId) => ({ ...currIdTagDeletedMap, [tagId]: true }), {});
|
const idTagDeletedMap = deletedTagsIds.reduce((currIdTagDeletedMap, tagId) => ({ ...currIdTagDeletedMap, [tagId]: true }), {});
|
||||||
const hasDeletedParentNode = rows_tree.some((node) => idTagDeletedMap[node[TREE_NODE_KEY.ID]] && node[TREE_NODE_KEY.HAS_CHILD_NODES]);
|
const hasDeletedParentNode = rows_tree.some((node) => idTagDeletedMap[getTreeNodeId(node)] && checkTreeNodeHasChildNodes(node));
|
||||||
if (hasDeletedParentNode) {
|
if (hasDeletedParentNode) {
|
||||||
// need re-build tree if some parent nodes deleted
|
// need re-build tree if some parent nodes deleted
|
||||||
this.buildTagsTree(table.rows, table);
|
this.buildTagsTree(table.rows, table);
|
||||||
@@ -51,13 +54,21 @@ class DataProcessor {
|
|||||||
// remove the nodes which has no child nodes directly
|
// remove the nodes which has no child nodes directly
|
||||||
let updated_rows_tree = [];
|
let updated_rows_tree = [];
|
||||||
rows_tree.forEach((node) => {
|
rows_tree.forEach((node) => {
|
||||||
if (!idTagDeletedMap[node[TREE_NODE_KEY.ID]]) {
|
if (!idTagDeletedMap[getTreeNodeId(node)]) {
|
||||||
updated_rows_tree.push(node);
|
updated_rows_tree.push(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// update has_child_nodes status(all child nodes may be deleted)
|
// update has_child_nodes status(all child nodes may be deleted)
|
||||||
resetTreeHasChildNodesStatus(updated_rows_tree);
|
resetTreeHasChildNodesStatus(updated_rows_tree);
|
||||||
|
|
||||||
|
// update tag all files links
|
||||||
|
setNodeAllChildTagsIds(updated_rows_tree);
|
||||||
|
|
||||||
|
table.key_tree_node_map = {};
|
||||||
|
updated_rows_tree.forEach((node) => {
|
||||||
|
updatedKeyTreeNodeMap(getTreeNodeKey(node), node, table.key_tree_node_map);
|
||||||
|
});
|
||||||
table.rows_tree = updated_rows_tree;
|
table.rows_tree = updated_rows_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +142,7 @@ class DataProcessor {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.buildTagsTree(table.rows, table);
|
||||||
this.updateDataWithModifyRecords();
|
this.updateDataWithModifyRecords();
|
||||||
this.updateSummaries();
|
this.updateSummaries();
|
||||||
}
|
}
|
||||||
@@ -149,6 +161,7 @@ class DataProcessor {
|
|||||||
});
|
});
|
||||||
table.rows = table.rows.filter((record) => !idRecordNotExistMap[record._id]);
|
table.rows = table.rows.filter((record) => !idRecordNotExistMap[record._id]);
|
||||||
|
|
||||||
|
this.buildTagsTree(table.rows, table);
|
||||||
this.updateSummaries();
|
this.updateSummaries();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,6 +230,7 @@ class DataProcessor {
|
|||||||
const subNodeKey = generateNodeKey(parentNodeKey, tagId);
|
const subNodeKey = generateNodeKey(parentNodeKey, tagId);
|
||||||
const childNode = createTreeNode(tagId, subNodeKey, parentNodeDepth + 1, false);
|
const childNode = createTreeNode(tagId, subNodeKey, parentNodeDepth + 1, false);
|
||||||
addTreeChildNode(childNode, node, rows_tree);
|
addTreeChildNode(childNode, node, rows_tree);
|
||||||
|
updatedKeyTreeNodeMap(subNodeKey, childNode, table.key_tree_node_map);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@@ -7,7 +7,7 @@ import { username } from '../../../utils/constants';
|
|||||||
import { addRowLinks, removeRowLinks } from '../../utils/link';
|
import { addRowLinks, removeRowLinks } from '../../utils/link';
|
||||||
import { getRecordIdFromRecord } from '../../../metadata/utils/cell';
|
import { getRecordIdFromRecord } from '../../../metadata/utils/cell';
|
||||||
import { getRowById, getRowsByIds } from '../../../metadata/utils/table';
|
import { getRowById, getRowsByIds } from '../../../metadata/utils/table';
|
||||||
import { getChildLinks, getParentLinks, getTagFileLinks } from '../../utils/cell';
|
import { getChildLinks, getParentLinks, getTagFilesLinks } from '../../utils/cell';
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ export default function apply(data, operation) {
|
|||||||
const opTagsIds = [target_tag_id, ...merged_tags_ids];
|
const opTagsIds = [target_tag_id, ...merged_tags_ids];
|
||||||
const parentLinks = getParentLinks(targetTag);
|
const parentLinks = getParentLinks(targetTag);
|
||||||
const childLinks = getChildLinks(targetTag);
|
const childLinks = getChildLinks(targetTag);
|
||||||
const fileLinks = getTagFileLinks(targetTag);
|
const fileLinks = getTagFilesLinks(targetTag);
|
||||||
const idParentLinkExistMap = parentLinks.reduce((currIdParentLinkExist, link) => ({ ...currIdParentLinkExist, [link.row_id]: true }), {});
|
const idParentLinkExistMap = parentLinks.reduce((currIdParentLinkExist, link) => ({ ...currIdParentLinkExist, [link.row_id]: true }), {});
|
||||||
const idChildLinkExistMap = childLinks.reduce((currIdChildLinkExist, link) => ({ ...currIdChildLinkExist, [link.row_id]: true }), {});
|
const idChildLinkExistMap = childLinks.reduce((currIdChildLinkExist, link) => ({ ...currIdChildLinkExist, [link.row_id]: true }), {});
|
||||||
const idFileLinkExistMap = fileLinks.reduce((currIdFileLinkExistMap, link) => ({ ...currIdFileLinkExistMap, [link.row_id]: true }), {});
|
const idFileLinkExistMap = fileLinks.reduce((currIdFileLinkExistMap, link) => ({ ...currIdFileLinkExistMap, [link.row_id]: true }), {});
|
||||||
@@ -241,7 +241,7 @@ export default function apply(data, operation) {
|
|||||||
mergedTags.forEach((mergedTag) => {
|
mergedTags.forEach((mergedTag) => {
|
||||||
const currParentLinks = getParentLinks(mergedTag);
|
const currParentLinks = getParentLinks(mergedTag);
|
||||||
const currChildLinks = getChildLinks(mergedTag);
|
const currChildLinks = getChildLinks(mergedTag);
|
||||||
const currFileLinks = getTagFileLinks(mergedTag);
|
const currFileLinks = getTagFilesLinks(mergedTag);
|
||||||
currParentLinks.forEach((parentLink) => {
|
currParentLinks.forEach((parentLink) => {
|
||||||
const parentLinkedTagId = parentLink.row_id;
|
const parentLinkedTagId = parentLink.row_id;
|
||||||
if (!opTagsIds.includes(parentLinkedTagId) && !idParentLinkExistMap[parentLinkedTagId]) {
|
if (!opTagsIds.includes(parentLinkedTagId) && !idParentLinkExistMap[parentLinkedTagId]) {
|
||||||
|
@@ -32,14 +32,15 @@ export const getChildLinks = (tag) => {
|
|||||||
return (tag && tag[PRIVATE_COLUMN_KEY.SUB_LINKS]) || [];
|
return (tag && tag[PRIVATE_COLUMN_KEY.SUB_LINKS]) || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTagFileLinks = (tag) => {
|
export const getTagFilesLinks = (tag) => {
|
||||||
return (tag && tag[PRIVATE_COLUMN_KEY.TAG_FILE_LINKS]) || [];
|
return (tag && tag[PRIVATE_COLUMN_KEY.TAG_FILE_LINKS]) || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTagFilesCount = (tag) => {
|
export const getTagFilesCount = (tag) => {
|
||||||
const links = getTagFileLinks(tag);
|
const links = getTagFilesLinks(tag);
|
||||||
return Array.isArray(links) ? links.length : 0;
|
return Array.isArray(links) ? links.length : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTagsByNameOrColor = (tags, nameOrColor) => {
|
export const getTagsByNameOrColor = (tags, nameOrColor) => {
|
||||||
if (!Array.isArray(tags) || tags.length === 0) return [];
|
if (!Array.isArray(tags) || tags.length === 0) return [];
|
||||||
if (!nameOrColor) return tags;
|
if (!nameOrColor) return tags;
|
||||||
|
@@ -1,8 +1,27 @@
|
|||||||
import { createTreeNode, generateNodeKey } from '../../components/sf-table/utils/tree';
|
import { checkTreeNodeHasChildNodes, createTreeNode, generateNodeKey, getAllSubTreeNodes, getTreeNodeId, getTreeNodeKey } from '../../components/sf-table/utils/tree';
|
||||||
import { getRecordIdFromRecord } from '../../metadata/utils/cell';
|
import { getRecordIdFromRecord } from '../../metadata/utils/cell';
|
||||||
import { getRowsByIds } from '../../metadata/utils/table';
|
import { getRowsByIds } from '../../metadata/utils/table';
|
||||||
import { getParentLinks, getChildLinks } from './cell';
|
import { getParentLinks, getChildLinks } from './cell';
|
||||||
|
|
||||||
|
const KEY_ALL_CHILD_TAGS_IDS = 'all_child_tags_ids';
|
||||||
|
|
||||||
|
const findAllChildTagIds = (nodeIndex, tree) => {
|
||||||
|
const targetNode = tree[nodeIndex];
|
||||||
|
if (!checkTreeNodeHasChildNodes(targetNode)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let allChildTagsIds = [];
|
||||||
|
const allSubNodes = getAllSubTreeNodes(nodeIndex, tree);
|
||||||
|
allSubNodes.forEach((subNode) => {
|
||||||
|
const nodeId = getTreeNodeId(subNode);
|
||||||
|
if (!allChildTagsIds.includes(nodeId)) {
|
||||||
|
allChildTagsIds.push(nodeId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allChildTagsIds;
|
||||||
|
};
|
||||||
|
|
||||||
const setChildNodes = (row, parentDepth, parentKey, idNodeInCurrentTreeMap, idNodeCreatedMap, tree, table) => {
|
const setChildNodes = (row, parentDepth, parentKey, idNodeInCurrentTreeMap, idNodeCreatedMap, tree, table) => {
|
||||||
const nodeId = getRecordIdFromRecord(row);
|
const nodeId = getRecordIdFromRecord(row);
|
||||||
|
|
||||||
@@ -28,6 +47,12 @@ const setChildNodes = (row, parentDepth, parentKey, idNodeInCurrentTreeMap, idNo
|
|||||||
delete idNodeInCurrentTreeMap[nodeId];
|
delete idNodeInCurrentTreeMap[nodeId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setNodeAllChildTagsIds = (tree) => {
|
||||||
|
tree.forEach((node, nodeIndex) => {
|
||||||
|
node[KEY_ALL_CHILD_TAGS_IDS] = findAllChildTagIds(nodeIndex, tree);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate tree for display in table
|
* generate tree for display in table
|
||||||
* @param {array} rows tags
|
* @param {array} rows tags
|
||||||
@@ -57,5 +82,17 @@ export const buildTagsTree = (rows, table) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return tree;
|
// set node all file links
|
||||||
|
setNodeAllChildTagsIds(tree);
|
||||||
|
|
||||||
|
let key_tree_node_map = {};
|
||||||
|
tree.forEach((node) => {
|
||||||
|
key_tree_node_map[getTreeNodeKey(node)] = node;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { tree, key_tree_node_map };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllChildTagsIdsFromNode = (node) => {
|
||||||
|
return (node && node[KEY_ALL_CHILD_TAGS_IDS]) || [];
|
||||||
};
|
};
|
||||||
|
@@ -21,6 +21,8 @@ const AllTags = ({ updateCurrentPath, ...params }) => {
|
|||||||
|
|
||||||
const { isLoading, isReloading, tagsData, store, context, currentPath } = useTags();
|
const { isLoading, isReloading, tagsData, store, context, currentPath } = useTags();
|
||||||
|
|
||||||
|
const displayNodeKey = useRef('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const eventBus = context.eventBus;
|
const eventBus = context.eventBus;
|
||||||
eventBus.dispatch(EVENT_BUS_TYPE.RELOAD_DATA);
|
eventBus.dispatch(EVENT_BUS_TYPE.RELOAD_DATA);
|
||||||
@@ -34,11 +36,11 @@ const AllTags = ({ updateCurrentPath, ...params }) => {
|
|||||||
const pathList = currentPath.split('/');
|
const pathList = currentPath.split('/');
|
||||||
const [, , currentTagId, children] = pathList;
|
const [, , currentTagId, children] = pathList;
|
||||||
if (currentTagId === ALL_TAGS_ID && !children) {
|
if (currentTagId === ALL_TAGS_ID && !children) {
|
||||||
setDisplayTag('');
|
setDisplayTag();
|
||||||
}
|
}
|
||||||
}, [currentPath]);
|
}, [currentPath]);
|
||||||
|
|
||||||
const onChangeDisplayTag = useCallback((tagID = '') => {
|
const onChangeDisplayTag = useCallback((tagID = '', nodeKey = '') => {
|
||||||
if (displayTag === tagID) return;
|
if (displayTag === tagID) return;
|
||||||
|
|
||||||
const tag = tagID && getRowById(tagsData, tagID);
|
const tag = tagID && getRowById(tagsData, tagID);
|
||||||
@@ -46,6 +48,8 @@ const AllTags = ({ updateCurrentPath, ...params }) => {
|
|||||||
if (tag) {
|
if (tag) {
|
||||||
path += `/${getTagName(tag)}`;
|
path += `/${getTagName(tag)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
displayNodeKey.current = nodeKey || '';
|
||||||
updateCurrentPath(path);
|
updateCurrentPath(path);
|
||||||
|
|
||||||
setDisplayTag(tagID);
|
setDisplayTag(tagID);
|
||||||
@@ -84,7 +88,7 @@ const AllTags = ({ updateCurrentPath, ...params }) => {
|
|||||||
if (displayTag) {
|
if (displayTag) {
|
||||||
return (
|
return (
|
||||||
<div className="sf-metadata-all-tags-tag-files">
|
<div className="sf-metadata-all-tags-tag-files">
|
||||||
<TagViewProvider { ...params } tagID={displayTag} updateCurrentPath={updateCurrentPath} >
|
<TagViewProvider { ...params } tagID={displayTag} nodeKey={displayNodeKey.current} updateCurrentPath={updateCurrentPath} >
|
||||||
<View />
|
<View />
|
||||||
</TagViewProvider>
|
</TagViewProvider>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,12 +1,41 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { NumberFormatter } from '@seafile/sf-metadata-ui-component';
|
import { NumberFormatter } from '@seafile/sf-metadata-ui-component';
|
||||||
|
import { useTags } from '../../../../hooks';
|
||||||
|
import { getTagFilesLinks } from '../../../../utils/cell';
|
||||||
|
import { getAllChildTagsIdsFromNode } from '../../../../utils/tree';
|
||||||
|
import { getRowById } from '../../../../../metadata/utils/table';
|
||||||
|
import { getTreeNodeId } from '../../../../../components/sf-table/utils/tree';
|
||||||
|
|
||||||
const TagFilesFormatter = ({ record, column }) => {
|
const TagFilesFormatter = ({ treeNodeIndex }) => {
|
||||||
|
const { tagsData } = useTags();
|
||||||
|
|
||||||
|
const tree = useMemo(() => {
|
||||||
|
return tagsData.rows_tree || [];
|
||||||
|
}, [tagsData]);
|
||||||
|
|
||||||
|
const currentNode = useMemo(() => {
|
||||||
|
return tree[treeNodeIndex];
|
||||||
|
}, [tree, treeNodeIndex]);
|
||||||
|
|
||||||
|
const currentTag = useMemo(() => {
|
||||||
|
const nodeId = getTreeNodeId(currentNode);
|
||||||
|
return getRowById(tagsData, nodeId);
|
||||||
|
}, [currentNode, tagsData]);
|
||||||
|
|
||||||
const tagFileLinksCount = useMemo(() => {
|
const tagFileLinksCount = useMemo(() => {
|
||||||
const tagFileLinks = record[column.key];
|
const filesLinks = getTagFilesLinks(currentTag);
|
||||||
return Array.isArray(tagFileLinks) ? tagFileLinks.length : 0;
|
let allFilesLinks = [...filesLinks];
|
||||||
}, [record, column]);
|
const childTagsIds = getAllChildTagsIdsFromNode(currentNode);
|
||||||
|
childTagsIds.forEach((childTagId) => {
|
||||||
|
const childTag = getRowById(tagsData, childTagId);
|
||||||
|
const childFilesLinks = getTagFilesLinks(childTag);
|
||||||
|
if (childFilesLinks && childFilesLinks.length > 0) {
|
||||||
|
allFilesLinks.push(...childFilesLinks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allFilesLinks.length;
|
||||||
|
}, [currentNode, currentTag, tagsData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sf-table-tag-files-formatter sf-table-cell-formatter sf-metadata-ui cell-formatter-container">
|
<div className="sf-table-tag-files-formatter sf-table-cell-formatter sf-metadata-ui cell-formatter-container">
|
||||||
@@ -15,4 +44,8 @@ const TagFilesFormatter = ({ record, column }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TagFilesFormatter.propTypes = {
|
||||||
|
treeNodeIndex: PropTypes.number,
|
||||||
|
};
|
||||||
|
|
||||||
export default TagFilesFormatter;
|
export default TagFilesFormatter;
|
||||||
|
@@ -1,9 +1,21 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTags } from '../../../../hooks';
|
||||||
import { PRIVATE_COLUMN_KEY } from '../../../../constants';
|
import { PRIVATE_COLUMN_KEY } from '../../../../constants';
|
||||||
import { getRecordIdFromRecord } from '../../../../../metadata/utils/cell';
|
import { getRecordIdFromRecord } from '../../../../../metadata/utils/cell';
|
||||||
|
import { getTreeNodeKey } from '../../../../../components/sf-table/utils/tree';
|
||||||
|
|
||||||
|
const TagNameFormatter = ({ record, isCellSelected, setDisplayTag, treeNodeIndex }) => {
|
||||||
|
const { tagsData } = useTags();
|
||||||
|
|
||||||
|
const tree = useMemo(() => {
|
||||||
|
return tagsData.rows_tree || [];
|
||||||
|
}, [tagsData]);
|
||||||
|
|
||||||
|
const currentNode = useMemo(() => {
|
||||||
|
return tree[treeNodeIndex];
|
||||||
|
}, [tree, treeNodeIndex]);
|
||||||
|
|
||||||
const TagNameFormatter = ({ record, isCellSelected, setDisplayTag }) => {
|
|
||||||
const tagColor = useMemo(() => {
|
const tagColor = useMemo(() => {
|
||||||
return record[PRIVATE_COLUMN_KEY.TAG_COLOR];
|
return record[PRIVATE_COLUMN_KEY.TAG_COLOR];
|
||||||
}, [record]);
|
}, [record]);
|
||||||
@@ -15,8 +27,9 @@ const TagNameFormatter = ({ record, isCellSelected, setDisplayTag }) => {
|
|||||||
const onClickName = useCallback(() => {
|
const onClickName = useCallback(() => {
|
||||||
if (!isCellSelected) return;
|
if (!isCellSelected) return;
|
||||||
const tagId = getRecordIdFromRecord(record);
|
const tagId = getRecordIdFromRecord(record);
|
||||||
setDisplayTag(tagId);
|
const nodeKey = getTreeNodeKey(currentNode);
|
||||||
}, [isCellSelected, record, setDisplayTag]);
|
setDisplayTag(tagId, nodeKey);
|
||||||
|
}, [isCellSelected, record, currentNode, setDisplayTag]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sf-table-tag-name-formatter sf-table-cell-formatter sf-metadata-ui cell-formatter-container">
|
<div className="sf-table-tag-name-formatter sf-table-cell-formatter sf-metadata-ui cell-formatter-container">
|
||||||
@@ -29,6 +42,7 @@ const TagNameFormatter = ({ record, isCellSelected, setDisplayTag }) => {
|
|||||||
TagNameFormatter.propTypes = {
|
TagNameFormatter.propTypes = {
|
||||||
record: PropTypes.object,
|
record: PropTypes.object,
|
||||||
isCellSelected: PropTypes.bool,
|
isCellSelected: PropTypes.bool,
|
||||||
|
treeNodeIndex: PropTypes.number,
|
||||||
setDisplayTag: PropTypes.func,
|
setDisplayTag: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -2324,6 +2324,70 @@ class MetadataTagFiles(APIView):
|
|||||||
return Response(tag_files_query)
|
return Response(tag_files_query)
|
||||||
|
|
||||||
|
|
||||||
|
class MetadataTagsFiles(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def post(self, request, repo_id):
|
||||||
|
tags_ids = request.data.get('tags_ids', None)
|
||||||
|
|
||||||
|
if not tags_ids:
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, 'tags_ids is invalid.')
|
||||||
|
|
||||||
|
metadata = RepoMetadata.objects.filter(repo_id=repo_id).first()
|
||||||
|
if not metadata or not metadata.enabled or not metadata.tags_enabled:
|
||||||
|
error_msg = f'The tags is disabled for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo = seafile_api.get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
if not can_read_metadata(request, repo_id):
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
metadata_server_api = MetadataServerAPI(repo_id, request.user.username)
|
||||||
|
|
||||||
|
from seafevents.repo_metadata.constants import TAGS_TABLE, METADATA_TABLE
|
||||||
|
|
||||||
|
tags_ids_str = ', '.join([f'"{id}"' for id in tags_ids])
|
||||||
|
sql = f'SELECT * FROM {TAGS_TABLE.name} WHERE `{TAGS_TABLE.columns.id.name}` in ({tags_ids_str})'
|
||||||
|
try:
|
||||||
|
query_new_rows = metadata_server_api.query_rows(sql)
|
||||||
|
found_tags = query_new_rows.get('results', [])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
|
||||||
|
|
||||||
|
if not found_tags:
|
||||||
|
return Response([])
|
||||||
|
|
||||||
|
tags_files_ids = []
|
||||||
|
for tag in found_tags:
|
||||||
|
tags_files_ids.extend(tag.get(TAGS_TABLE.columns.file_links.name, []))
|
||||||
|
|
||||||
|
if not tags_files_ids:
|
||||||
|
return Response([])
|
||||||
|
|
||||||
|
tags_files_sql = 'SELECT `%s`, `%s`, `%s`, `%s`, `%s`, `%s` FROM %s WHERE `%s` IN (%s)' % (METADATA_TABLE.columns.id.name, METADATA_TABLE.columns.file_name.name, \
|
||||||
|
METADATA_TABLE.columns.parent_dir.name, METADATA_TABLE.columns.size.name, \
|
||||||
|
METADATA_TABLE.columns.file_mtime.name, METADATA_TABLE.columns.tags.name, \
|
||||||
|
METADATA_TABLE.name, METADATA_TABLE.columns.id.name, \
|
||||||
|
', '.join(["'%s'" % id.get('row_id') for id in tags_files_ids]))
|
||||||
|
try:
|
||||||
|
tags_files_query = metadata_server_api.query_rows(tags_files_sql)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
return Response(tags_files_query)
|
||||||
|
|
||||||
|
|
||||||
class MetadataMergeTags(APIView):
|
class MetadataMergeTags(APIView):
|
||||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
@@ -2,7 +2,8 @@ from django.urls import re_path
|
|||||||
from .apis import MetadataRecords, MetadataManage, MetadataColumns, MetadataRecord, \
|
from .apis import MetadataRecords, MetadataManage, MetadataColumns, MetadataRecord, \
|
||||||
MetadataFolders, MetadataViews, MetadataViewsMoveView, MetadataViewsDetailView, MetadataViewsDuplicateView, FacesRecords, \
|
MetadataFolders, MetadataViews, MetadataViewsMoveView, MetadataViewsDetailView, MetadataViewsDuplicateView, FacesRecords, \
|
||||||
FaceRecognitionManage, FacesRecord, MetadataExtractFileDetails, PeoplePhotos, MetadataTagsStatusManage, MetadataTags, \
|
FaceRecognitionManage, FacesRecord, MetadataExtractFileDetails, PeoplePhotos, MetadataTagsStatusManage, MetadataTags, \
|
||||||
MetadataTagsLinks, MetadataFileTags, MetadataTagFiles, MetadataMergeTags, MetadataDetailsSettingsView, MetadataOCRManageView
|
MetadataTagsLinks, MetadataFileTags, MetadataTagFiles, MetadataMergeTags, MetadataTagsFiles, MetadataDetailsSettingsView, \
|
||||||
|
MetadataOCRManageView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r'^$', MetadataManage.as_view(), name='api-v2.1-metadata'),
|
re_path(r'^$', MetadataManage.as_view(), name='api-v2.1-metadata'),
|
||||||
@@ -38,4 +39,5 @@ urlpatterns = [
|
|||||||
re_path(r'^file-tags/$', MetadataFileTags.as_view(), name='api-v2.1-metadata-file-tags'),
|
re_path(r'^file-tags/$', MetadataFileTags.as_view(), name='api-v2.1-metadata-file-tags'),
|
||||||
re_path(r'^tag-files/(?P<tag_id>.+)/$', MetadataTagFiles.as_view(), name='api-v2.1-metadata-tag-files'),
|
re_path(r'^tag-files/(?P<tag_id>.+)/$', MetadataTagFiles.as_view(), name='api-v2.1-metadata-tag-files'),
|
||||||
re_path(r'^merge-tags/$', MetadataMergeTags.as_view(), name='api-v2.1-metadata-merge-tags'),
|
re_path(r'^merge-tags/$', MetadataMergeTags.as_view(), name='api-v2.1-metadata-merge-tags'),
|
||||||
|
re_path(r'^tags-files/$', MetadataTagsFiles.as_view(), name='api-v2.1-metadata-tags-files'),
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user