mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-10 19:29:56 +00:00
root path show file_tag && tagged_files_count (#2715)
This commit is contained in:
@@ -12,6 +12,7 @@ import DirentDetail from '../dirent-detail/dirent-details';
|
|||||||
import FileUploader from '../file-uploader/file-uploader';
|
import FileUploader from '../file-uploader/file-uploader';
|
||||||
import ModalPortal from '../modal-portal';
|
import ModalPortal from '../modal-portal';
|
||||||
import LibDecryptDialog from '../dialog/lib-decrypt-dialog';
|
import LibDecryptDialog from '../dialog/lib-decrypt-dialog';
|
||||||
|
import FileTagsViewer from '../../components/filetags-viewer';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
currentRepoInfo: PropTypes.object,
|
currentRepoInfo: PropTypes.object,
|
||||||
@@ -46,6 +47,7 @@ const propTypes = {
|
|||||||
updateDirent: PropTypes.func.isRequired,
|
updateDirent: PropTypes.func.isRequired,
|
||||||
onSearchedClick: PropTypes.func.isRequired,
|
onSearchedClick: PropTypes.func.isRequired,
|
||||||
onFileUploadSuccess: PropTypes.func.isRequired,
|
onFileUploadSuccess: PropTypes.func.isRequired,
|
||||||
|
usedRepoTags: PropTypes.array.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DirPanel extends React.Component {
|
class DirPanel extends React.Component {
|
||||||
@@ -171,6 +173,15 @@ class DirPanel extends React.Component {
|
|||||||
onTabNavClick={this.props.onTabNavClick}
|
onTabNavClick={this.props.onTabNavClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{(this.props.usedRepoTags.length > 0 && this.props.path === '/') && (
|
||||||
|
<div className="tags-summary-bar">
|
||||||
|
<FileTagsViewer
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
currentPath={this.props.path}
|
||||||
|
usedRepoTags={this.props.usedRepoTags}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="cur-view-content">
|
<div className="cur-view-content">
|
||||||
{this.props.errorMsg ?
|
{this.props.errorMsg ?
|
||||||
<p className="error text-center">{this.props.errorMsg}</p> :
|
<p className="error text-center">{this.props.errorMsg}</p> :
|
||||||
|
@@ -9,6 +9,7 @@ import toaster from '../toast';
|
|||||||
import DirPanel from './dir-panel';
|
import DirPanel from './dir-panel';
|
||||||
import Dirent from '../../models/dirent';
|
import Dirent from '../../models/dirent';
|
||||||
import FileTag from '../../models/file-tag';
|
import FileTag from '../../models/file-tag';
|
||||||
|
import RepoTag from '../../models/repo-tag';
|
||||||
import RepoInfo from '../../models/repo-info';
|
import RepoInfo from '../../models/repo-info';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
@@ -37,6 +38,7 @@ class DirView extends React.Component {
|
|||||||
selectedDirentList: [],
|
selectedDirentList: [],
|
||||||
dirID: '',
|
dirID: '',
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
|
usedRepoTags: [],
|
||||||
};
|
};
|
||||||
window.onpopstate = this.onpopstate;
|
window.onpopstate = this.onpopstate;
|
||||||
this.lastModifyTime = new Date();
|
this.lastModifyTime = new Date();
|
||||||
@@ -54,6 +56,16 @@ class DirView extends React.Component {
|
|||||||
let location = decodeURIComponent(window.location.href);
|
let location = decodeURIComponent(window.location.href);
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
collabServer.watchRepo(repoID, this.onRepoUpdateEvent);
|
collabServer.watchRepo(repoID, this.onRepoUpdateEvent);
|
||||||
|
seafileAPI.listRepoTags(repoID).then(res => {
|
||||||
|
let usedRepoTags = [];
|
||||||
|
res.data.repo_tags.forEach(item => {
|
||||||
|
let usedRepoTag = new RepoTag(item);
|
||||||
|
if (usedRepoTag.fileCount > 0) {
|
||||||
|
usedRepoTags.push(usedRepoTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({usedRepoTags: usedRepoTags});
|
||||||
|
});
|
||||||
seafileAPI.getRepoInfo(repoID).then(res => {
|
seafileAPI.getRepoInfo(repoID).then(res => {
|
||||||
let repoInfo = new RepoInfo(res.data);
|
let repoInfo = new RepoInfo(res.data);
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -122,6 +134,10 @@ class DirView extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUsedRepoTags = (newUsedRepoTags) => {
|
||||||
|
this.setState({usedRepoTags: newUsedRepoTags});
|
||||||
|
}
|
||||||
|
|
||||||
updateDirentList = (filePath) => {
|
updateDirentList = (filePath) => {
|
||||||
let repoID = this.state.repoID;
|
let repoID = this.state.repoID;
|
||||||
this.setState({isDirentListLoading: true});
|
this.setState({isDirentListLoading: true});
|
||||||
@@ -368,6 +384,17 @@ class DirView extends React.Component {
|
|||||||
});
|
});
|
||||||
this.updateDirent(dirent, 'file_tags', fileTags);
|
this.updateDirent(dirent, 'file_tags', fileTags);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
seafileAPI.listRepoTags(repoID).then(res => {
|
||||||
|
let usedRepoTags = [];
|
||||||
|
res.data.repo_tags.forEach(item => {
|
||||||
|
let usedRepoTag = new RepoTag(item);
|
||||||
|
if (usedRepoTag.fileCount > 0) {
|
||||||
|
usedRepoTags.push(usedRepoTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.updateUsedRepoTags(usedRepoTags);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuClick = () => {
|
onMenuClick = () => {
|
||||||
@@ -549,6 +576,7 @@ class DirView extends React.Component {
|
|||||||
onFileUploadSuccess={this.onFileUploadSuccess}
|
onFileUploadSuccess={this.onFileUploadSuccess}
|
||||||
libNeedDecrypt={this.state.libNeedDecrypt}
|
libNeedDecrypt={this.state.libNeedDecrypt}
|
||||||
onLibDecryptDialog={this.onLibDecryptDialog}
|
onLibDecryptDialog={this.onLibDecryptDialog}
|
||||||
|
usedRepoTags={this.state.usedRepoTags}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -426,7 +426,7 @@ class DirentListItem extends React.Component {
|
|||||||
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
|
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="tag-list-title">
|
||||||
{(dirent.type !== 'dir' && dirent.file_tags) && (
|
{(dirent.type !== 'dir' && dirent.file_tags) && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked">
|
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked">
|
||||||
|
74
frontend/src/components/filetags-viewer.js
Normal file
74
frontend/src/components/filetags-viewer.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ModalPortal from './modal-portal';
|
||||||
|
import { Modal } from 'reactstrap';
|
||||||
|
import ListTaggedFilesDialog from './dialog/list-taggedfiles-dialog';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
repoID: PropTypes.string.isRequired,
|
||||||
|
currentPath: PropTypes.string.isRequired,
|
||||||
|
usedRepoTags: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileTagsViewer extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
usedRepoTags: [],
|
||||||
|
currentTag: null,
|
||||||
|
isListTaggedFileShow: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onListTaggedFiles = (currentTag) => {
|
||||||
|
this.setState({
|
||||||
|
currentTag: currentTag,
|
||||||
|
isListTaggedFileShow: !this.state.isListTaggedFileShow,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
isListTaggedFileShow: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let {usedRepoTags, repoID} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<ul className="used-tag-list">
|
||||||
|
{usedRepoTags.map((usedRepoTag) => {
|
||||||
|
return (
|
||||||
|
<li key={usedRepoTag.id} className="used-tag-item">
|
||||||
|
<span className={`used-tag bg-${usedRepoTag.color}`}></span>
|
||||||
|
<span className="used-tag-name" title={usedRepoTag.name}>{usedRepoTag.name}</span>
|
||||||
|
<span className="used-tag-files" onClick={this.onListTaggedFiles.bind(this, usedRepoTag)}>
|
||||||
|
{usedRepoTag.fileCount > 1 ? usedRepoTag.fileCount + ' files' : usedRepoTag.fileCount + ' file'}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
{this.state.isListTaggedFileShow && (
|
||||||
|
<ModalPortal>
|
||||||
|
<Modal isOpen={true}>
|
||||||
|
<ListTaggedFilesDialog
|
||||||
|
repoID={repoID}
|
||||||
|
currentTag={this.state.currentTag}
|
||||||
|
onClose={this.onCloseDialog}
|
||||||
|
toggleCancel={this.onListTaggedFiles}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTagsViewer.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default FileTagsViewer;
|
@@ -44,6 +44,8 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dirent-info .img {
|
.dirent-info .img {
|
||||||
@@ -103,11 +105,16 @@
|
|||||||
.file-tag-list li {
|
.file-tag-list li {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
max-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-tag-list .tag-name {
|
.file-tag-list .tag-name {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.5rem;
|
margin-left: 5px;
|
||||||
|
width: 100px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-related-files th {
|
.file-related-files th {
|
||||||
|
@@ -23,3 +23,7 @@
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tag-list-title {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
@@ -31,6 +31,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-tag-selected {
|
.show-tag-selected {
|
||||||
@@ -92,3 +93,35 @@
|
|||||||
.tag-dialog-back:focus {
|
.tag-dialog-back:focus {
|
||||||
color: #444;
|
color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags-summary-bar {
|
||||||
|
margin: 16px 16px 4px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border: 1px solid #e6e6dd;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.used-tag-list {
|
||||||
|
list-style: none;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
.used-tag-item {
|
||||||
|
display: inline-block;
|
||||||
|
margin: auto 15px;
|
||||||
|
}
|
||||||
|
.used-tag {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.used-tag-name {
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
}
|
||||||
|
.used-tag-files {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.used-tag-files:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
@@ -14,6 +14,7 @@ import MarkdownContentViewer from '../../components/markdown-viewer';
|
|||||||
import DirentListView from '../../components/dirent-list-view/dirent-list-view';
|
import DirentListView from '../../components/dirent-list-view/dirent-list-view';
|
||||||
import DirentDetail from '../../components/dirent-detail/dirent-details';
|
import DirentDetail from '../../components/dirent-detail/dirent-details';
|
||||||
import FileUploader from '../../components/file-uploader/file-uploader';
|
import FileUploader from '../../components/file-uploader/file-uploader';
|
||||||
|
import FileTagsViewer from '../../components/filetags-viewer';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
@@ -55,6 +56,7 @@ const propTypes = {
|
|||||||
goReviewPage: PropTypes.func,
|
goReviewPage: PropTypes.func,
|
||||||
goDraftPage: PropTypes.func,
|
goDraftPage: PropTypes.func,
|
||||||
reviewID: PropTypes.any,
|
reviewID: PropTypes.any,
|
||||||
|
usedRepoTags: PropTypes.array.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class MainPanel extends Component {
|
class MainPanel extends Component {
|
||||||
@@ -229,6 +231,15 @@ class MainPanel extends Component {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{(this.props.usedRepoTags.length > 0 && this.props.path === '/') && (
|
||||||
|
<div className="tags-summary-bar">
|
||||||
|
<FileTagsViewer
|
||||||
|
repoID={repoID}
|
||||||
|
currentPath={this.props.path}
|
||||||
|
usedRepoTags={this.props.usedRepoTags}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="cur-view-content" onScroll={this.handlePageScroll} ref="curViewContent">
|
<div className="cur-view-content" onScroll={this.handlePageScroll} ref="curViewContent">
|
||||||
{!this.props.pathExist ?
|
{!this.props.pathExist ?
|
||||||
ErrMessage :
|
ErrMessage :
|
||||||
|
@@ -14,6 +14,7 @@ import LibDecryptDialog from './components/dialog/lib-decrypt-dialog';
|
|||||||
import ModalPortal from './components/modal-portal';
|
import ModalPortal from './components/modal-portal';
|
||||||
import Dirent from './models/dirent';
|
import Dirent from './models/dirent';
|
||||||
import FileTag from './models/file-tag';
|
import FileTag from './models/file-tag';
|
||||||
|
import RepoTag from './models/repo-tag';
|
||||||
import './assets/css/fa-solid.css';
|
import './assets/css/fa-solid.css';
|
||||||
import './assets/css/fa-regular.css';
|
import './assets/css/fa-regular.css';
|
||||||
import './assets/css/fontawesome.css';
|
import './assets/css/fontawesome.css';
|
||||||
@@ -50,6 +51,7 @@ class Wiki extends Component {
|
|||||||
reviewID: '',
|
reviewID: '',
|
||||||
draftFilePath: '',
|
draftFilePath: '',
|
||||||
dirID: '',
|
dirID: '',
|
||||||
|
usedRepoTags: [],
|
||||||
};
|
};
|
||||||
window.onpopstate = this.onpopstate;
|
window.onpopstate = this.onpopstate;
|
||||||
this.hash = '';
|
this.hash = '';
|
||||||
@@ -65,6 +67,16 @@ class Wiki extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
collabServer.watchRepo(repoID, this.onRepoUpdateEvent);
|
collabServer.watchRepo(repoID, this.onRepoUpdateEvent);
|
||||||
|
seafileAPI.listRepoTags(repoID).then(res => {
|
||||||
|
let usedRepoTags = [];
|
||||||
|
res.data.repo_tags.forEach(item => {
|
||||||
|
let usedRepoTag = new RepoTag(item);
|
||||||
|
if (usedRepoTag.fileCount > 0) {
|
||||||
|
usedRepoTags.push(usedRepoTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({usedRepoTags: usedRepoTags});
|
||||||
|
});
|
||||||
seafileAPI.getRepoInfo(repoID).then(res => {
|
seafileAPI.getRepoInfo(repoID).then(res => {
|
||||||
this.setState({
|
this.setState({
|
||||||
libNeedDecrypt: res.data.lib_need_decrypt,
|
libNeedDecrypt: res.data.lib_need_decrypt,
|
||||||
@@ -325,6 +337,10 @@ class Wiki extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUsedRepoTags = (newUsedRepoTags) => {
|
||||||
|
this.setState({usedRepoTags: newUsedRepoTags});
|
||||||
|
}
|
||||||
|
|
||||||
updateDirent = (dirent, paramKey, paramValue) => {
|
updateDirent = (dirent, paramKey, paramValue) => {
|
||||||
let newDirentList = this.state.direntList.map(item => {
|
let newDirentList = this.state.direntList.map(item => {
|
||||||
if (item.name === dirent.name) {
|
if (item.name === dirent.name) {
|
||||||
@@ -595,6 +611,17 @@ class Wiki extends Component {
|
|||||||
});
|
});
|
||||||
this.updateDirent(dirent, 'file_tags', fileTags);
|
this.updateDirent(dirent, 'file_tags', fileTags);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
seafileAPI.listRepoTags(repoID).then(res => {
|
||||||
|
let usedRepoTags = [];
|
||||||
|
res.data.repo_tags.forEach(item => {
|
||||||
|
let usedRepoTag = new RepoTag(item);
|
||||||
|
if (usedRepoTag.fileCount > 0) {
|
||||||
|
usedRepoTags.push(usedRepoTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.updateUsedRepoTags(usedRepoTags);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onTreeNodeClick = (node) => {
|
onTreeNodeClick = (node) => {
|
||||||
@@ -904,6 +931,7 @@ class Wiki extends Component {
|
|||||||
reviewID={this.state.reviewID}
|
reviewID={this.state.reviewID}
|
||||||
goDraftPage={this.goDraftPage}
|
goDraftPage={this.goDraftPage}
|
||||||
goReviewPage={this.goReviewPage}
|
goReviewPage={this.goReviewPage}
|
||||||
|
usedRepoTags={this.state.usedRepoTags}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user