1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 07:27:04 +00:00

feat: detail support tag (#7119)

* feat: detail support tag

* feat: optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
杨国璇
2024-11-29 09:53:51 +08:00
committed by GitHub
parent af0ad3e81c
commit 7d206bc691
25 changed files with 283 additions and 128 deletions

View File

@@ -19,7 +19,7 @@
"@seafile/sdoc-editor": "1.0.155",
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "1.0.126",
"@seafile/sf-metadata-ui-component": "^0.0.53",
"@seafile/sf-metadata-ui-component": "^0.0.56",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/codemirror-themes": "^4.23.5",
"@uiw/react-codemirror": "^4.19.4",
@@ -5024,9 +5024,9 @@
}
},
"node_modules/@seafile/sf-metadata-ui-component": {
"version": "0.0.53",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.53.tgz",
"integrity": "sha512-RzogKUvy0RkziPMkdjizPOfrMFFtkaZUaqvZT3RYggrZsPcJ68rbak2tI+4kIH7MUOQB4s43y5ygku0oxK7OEg==",
"version": "0.0.56",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.56.tgz",
"integrity": "sha512-sw3mbgUtXjEeqmi03azqTWzAaoW12CkT5FvB+bYa2wC1Re0o3t1kTFzqHVd/2NckOzHR2Qe9HFnQ8A0+5FOqGw==",
"dependencies": {
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "^1.0.122",

View File

@@ -14,7 +14,7 @@
"@seafile/sdoc-editor": "1.0.155",
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "1.0.126",
"@seafile/sf-metadata-ui-component": "^0.0.53",
"@seafile/sf-metadata-ui-component": "^0.0.56",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/codemirror-themes": "^4.23.5",
"@uiw/react-codemirror": "^4.19.4",

View File

@@ -27,6 +27,11 @@
flex-shrink: 0;
}
.dirent-detail-item .dirent-detail-item-name .sf-metadata-icon-tag {
position: relative;
top: 1px;
}
.dirent-detail-item .dirent-detail-item-value {
width: 200px;
display: flex;

View File

@@ -35,7 +35,7 @@ const FileDetails = ({ repoID, repoInfo, path, direntDetail }) => {
<DetailItem field={lastModifiedTimeField} className="sf-metadata-property-detail-formatter">
<Formatter field={lastModifiedTimeField} value={direntDetail.last_modified}/>
</DetailItem>
{window.app.pageOptions.enableMetadataManagement && enableMetadata && (
{enableMetadata && (
<MetadataDetails repoID={repoID} filePath={path} repoInfo={repoInfo} direntType="file" />
)}
</>

View File

@@ -14,6 +14,7 @@ import OnlyofficeFileToolbar from './onlyoffice-file-toolbar';
import EmbeddedFileDetails from '../dirent-detail/embedded-file-details';
import { MetadataStatusProvider } from '../../hooks';
import { CollaboratorsProvider } from '../../metadata';
import { TagsProvider } from '../../tag/hooks';
import Loading from '../loading';
import '../../css/file-view.css';
@@ -153,6 +154,7 @@ class FileView extends React.Component {
{isDetailsPanelOpen && (
<MetadataStatusProvider repoID={repoID} >
<CollaboratorsProvider repoID={repoID}>
<TagsProvider repoID={repoID} repoInfo={{ permission: filePerm }}>
<EmbeddedFileDetails
repoID={repoID}
path={filePath}
@@ -160,6 +162,7 @@ class FileView extends React.Component {
repoInfo={{ permission: filePerm }}
onClose={this.toggleDetailsPanel}
/>
</TagsProvider>
</CollaboratorsProvider>
</MetadataStatusProvider>
)}

View File

@@ -5,6 +5,8 @@
min-height: 35px;
padding: 2px 10px;
line-height: 1;
max-height: 150px;
overflow-y: scroll;
}
.sf-metadata-delete-select-tags .sf-metadata-delete-select-tag {

View File

@@ -28,6 +28,12 @@
padding: 10px;
}
.sf-metadata-tags-editor .sf-metadata-tags-editor-container .none-search-result {
font-size: 14px;
opacity: 0.5;
display: inline-block;
}
.sf-metadata-tags-editor .sf-metadata-tags-editor-tag-container {
align-items: center;
border-radius: 2px;

View File

@@ -24,7 +24,9 @@ const TagsEditor = forwardRef(({
onPressTab,
updateFileTags,
}, ref) => {
const { tagsData, addTag } = useTags();
const { tagsData, addTag, context } = useTags();
const canAddTag = context.canAddTag();
const [value, setValue] = useState((oldValue || []).map(item => item.row_id).filter(item => getRowById(tagsData, item)));
const [searchValue, setSearchValue] = useState('');
@@ -44,9 +46,10 @@ const TagsEditor = forwardRef(({
const displayTags = useMemo(() => getTagsByNameOrColor(tags, searchValue), [searchValue, tags]);
const isShowCreateBtn = useMemo(() => {
if (!canAddTag) return false;
if (!canEditData || !searchValue) return false;
return !getTagByNameOrColor(displayTags, searchValue);
}, [canEditData, displayTags, searchValue]);
}, [canEditData, displayTags, searchValue, canAddTag]);
const style = useMemo(() => {
return { width: column.width };

View File

@@ -1,45 +0,0 @@
.sf-metadata-tags-formatter .sf-metadata-tags-operation-container {
display: flex;
align-items: center;
}
.sf-metadata-tags-formatter .sf-metadata-tags-operation-container .sf-metadata-tags-operation-add-btn {
display: flex;
align-items: center;
justify-content: center;
height: 20px;
width: 20px;
background: #eceff4;
border-radius: 3px;
cursor: pointer;
}
.sf-metadata-tags-formatter .sf-metadata-tags-operation-container .sf-metadata-tags-operation-add-btn:hover {
background-color: #f2f2f2;
}
.sf-metadata-tags-formatter .sf-metadata-tags-operation-add-btn .sf-metadata-icon-add-table {
font-size: 12px;
fill: #212519;
}
.sf-metadata-tags-formatter .sf-metadata-tags-formatter-container {
display: flex;
align-items: center;
width: max-content;
min-height: 1rem;
position: relative;
justify-content: flex-end;
}
.sf-metadata-tags-formatter .sf-metadata-tags-formatter-container .sf-metadata-tag-formatter {
margin-right: -0.5rem;
border: 0.125rem solid #fff;
cursor: pointer;
position: relative;
top: 1px;
display: inline-block;
width: 18px;
height: 18px;
border-radius: 50%;
}

View File

@@ -1,39 +0,0 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { useTags } from '../../../../tag/hooks';
import { getRowById } from '../../../utils/table';
import { getTagColor, getTagName } from '../../../../tag/utils/cell/core';
import './index.css';
const FileTagsFormatter = ({ value: oldValue }) => {
const { tagsData } = useTags();
const value = useMemo(() => {
if (!Array.isArray(oldValue)) return [];
return oldValue.filter(item => getRowById(tagsData, item.row_id)).map(item => item.row_id);
}, [oldValue, tagsData]);
return (
<div className="sf-metadata-ui cell-formatter-container link-formatter sf-metadata-link-formatter sf-metadata-tags-formatter">
{value.length > 0 && (
<div className="sf-metadata-tags-formatter-container">
{value.map((item) => {
const tag = getRowById(tagsData, item);
const tagColor = getTagColor(tag);
const tagName = getTagName(tag);
return (
<span className="sf-metadata-tag-formatter" key={item} style={{ backgroundColor: tagColor }} title={tagName}></span>
);
})}
</div>
)}
</div>
);
};
FileTagsFormatter.propTypes = {
value: PropTypes.array,
};
export default FileTagsFormatter;

View File

@@ -4,6 +4,7 @@ import { Formatter } from '@seafile/sf-metadata-ui-component';
import { useCollaborators } from '../../hooks';
import { CellType } from '../../constants';
import FileName from './file-name';
import { useTags } from '../../../tag/hooks';
const CellFormatter = ({ readonly, value, field, record, ...params }) => {
const { collaborators, collaboratorsCache, updateCollaboratorsCache, queryUser } = useCollaborators();
@@ -18,13 +19,14 @@ const CellFormatter = ({ readonly, value, field, record, ...params }) => {
queryUserAPI: queryUser,
};
}, [readonly, value, field, collaborators, collaboratorsCache, updateCollaboratorsCache, queryUser]);
const { tagsData } = useTags();
if (field.type === CellType.FILE_NAME) {
return (<FileName { ...props } { ...params } record={record} />);
}
return (
<Formatter { ...props } { ...params } />
<Formatter { ...props } { ...params } tagsData={tagsData} />
);
};

View File

@@ -9,6 +9,7 @@ import CollaboratorEditor from './collaborator-editor';
import DateEditor from './date-editor';
import LongTextEditor from './long-text-editor';
import RateEditor from './rate-editor';
import TagsEditor from './tags-editor';
import { lang } from '../../../utils/constants';
import { CellType } from '../../constants';
@@ -47,6 +48,9 @@ const DetailEditor = ({ field, onChange: onChangeAPI, ...props }) => {
case CellType.RATE: {
return (<RateEditor { ...props } field={field} onChange={onChange} />);
}
case CellType.TAGS: {
return (<TagsEditor { ...props } field={field} />);
}
default: {
return null;
}

View File

@@ -3,3 +3,7 @@
width: 100%;
line-height: 1;
}
.sf-metadata-rate-property-detail-editor .sf-metadata-rate-editor {
flex-wrap: wrap;
}

View File

@@ -0,0 +1,46 @@
.sf-metadata-tags-property-detail-editor {
padding: 0 6px;
min-height: 34px;
width: 100%;
height: auto;
}
.sf-metadata-tags-property-detail-editor:empty {
line-height: 34px;
}
.sf-metadata-tags-property-detail-editor .sf-metadata-delete-select-tags {
min-height: 34px;
border-bottom: none;
background-color: inherit;
border-radius: 0;
border-radius: initial;
padding: 2px 0px;
max-height: fit-content;
}
.sf-metadata-tags-property-detail-editor .sf-metadata-delete-select-tags .sf-metadata-delete-select-tag {
margin-top: 5px;
margin-bottom: 5px;
margin-right: 10px;
}
.sf-metadata-tags-property-editor-popover .sf-metadata-delete-select-tags {
display: none;
}
.sf-metadata-tags-property-editor-popover .popover {
max-width: unset;
}
.sf-metadata-tags-property-editor-popover .sf-metadata-tags-editor {
position: unset;
min-width: 200px;
padding: 0;
overflow: hidden;
opacity: 1;
background-color: #ffffff;
border: none;
border-radius: none;
box-shadow: none;
}

View File

@@ -0,0 +1,117 @@
import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Popover } from 'reactstrap';
import { getRowById } from '../../../utils/table';
import { getRecordIdFromRecord } from '../../../utils/cell';
import { gettext } from '../../../../utils/constants';
import DeleteTag from '../../cell-editors/tags-editor/delete-tags';
import { KeyCodes } from '../../../../constants';
import { getEventClassName } from '../../../utils/common';
import Editor from '../../cell-editors/tags-editor';
import { useTags } from '../../../../tag/hooks';
import './index.css';
const TagsEditor = ({ record, value, field, updateFileTags }) => {
const ref = useRef(null);
const [showEditor, setShowEditor] = useState(false);
const { tagsData } = useTags();
const validValue = useMemo(() => {
if (!Array.isArray(value) || value.length === 0) return [];
return value.filter(item => getRowById(tagsData, item.row_id)).map(item => item.row_id);
}, [value, tagsData]);
const onClick = useCallback((event) => {
if (!event.target) return;
const className = getEventClassName(event);
if (className.indexOf('sf-metadata-search-tags') > -1) return;
const dom = document.querySelector('.sf-metadata-tags-editor');
if (!dom) return;
if (dom.contains(event.target)) return;
if (ref.current && !ref.current.contains(event.target) && showEditor) {
setShowEditor(false);
}
}, [showEditor]);
const onHotKey = useCallback((event) => {
if (event.keyCode === KeyCodes.Esc) {
if (showEditor) {
setShowEditor(false);
}
}
}, [showEditor]);
useEffect(() => {
document.addEventListener('mousedown', onClick);
document.addEventListener('keydown', onHotKey, true);
return () => {
document.removeEventListener('mousedown', onClick);
document.removeEventListener('keydown', onHotKey, true);
};
}, [onClick, onHotKey]);
const openEditor = useCallback(() => {
setShowEditor(true);
}, []);
const onDeleteTag = useCallback((tagId, event) => {
event && event.stopPropagation();
event && event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
const newValue = validValue.slice(0);
let optionIdx = validValue.indexOf(tagId);
if (optionIdx > -1) {
newValue.splice(optionIdx, 1);
}
const recordId = getRecordIdFromRecord(record);
updateFileTags([{ record_id: recordId, tags: newValue, old_tags: value }]);
}, [validValue, value, record, updateFileTags]);
const renderEditor = useCallback(() => {
if (!showEditor) return null;
const { width } = ref.current.getBoundingClientRect();
return (
<Popover
target={ref}
isOpen={true}
placement="bottom-end"
hideArrow={true}
fade={false}
className="sf-metadata-property-editor-popover sf-metadata-tags-property-editor-popover"
boundariesElement={document.body}
>
<Editor
saveImmediately={true}
value={value}
column={{ ...field, width: Math.max(width - 2, 200) }}
record={record}
updateFileTags={updateFileTags}
/>
</Popover>
);
}, [showEditor, field, record, value, updateFileTags]);
return (
<div
className="sf-metadata-property-detail-editor sf-metadata-tags-property-detail-editor"
placeholder={gettext('Empty')}
ref={ref}
onClick={openEditor}
>
{validValue.length > 0 && (<DeleteTag value={validValue} tags={tagsData} onDelete={onDeleteTag} />)}
{renderEditor()}
</div>
);
};
TagsEditor.propTypes = {
record: PropTypes.object,
value: PropTypes.array,
field: PropTypes.object,
updateFileTags: PropTypes.func,
};
export default TagsEditor;

View File

@@ -21,7 +21,6 @@ export const NOT_DISPLAY_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.LOCATION,
PRIVATE_COLUMN_KEY.FACE_LINKS,
PRIVATE_COLUMN_KEY.FACE_VECTORS,
PRIVATE_COLUMN_KEY.TAGS,
];
export const SYSTEM_FOLDERS = [

View File

@@ -74,3 +74,17 @@
.dirent-detail-item-value:not(.editable) .sf-metadata-rate-formatter .sf-metadata-rate-item {
cursor: default;
}
.dirent-detail-item-value:not(.editable) .sf-metadata-tags-formatter {
padding: 8px 6px;
}
.dirent-detail-item-value:not(.editable) .sf-metadata-tags-formatter .sf-metadata-ui-tags-container {
width: 100%;
overflow: hidden;
}
.dirent-detail-item-value:not(.editable) .sf-metadata-tags-formatter .sf-metadata-ui-tags-container .sf-metadata-ui-tag {
top: 0;
cursor: default;
}

View File

@@ -11,9 +11,11 @@ import { getCellValueByColumn, getOptionName, getColumnOptionNamesByIds, getColu
import { normalizeFields } from './utils';
import { gettext } from '../../../utils/constants';
import { CellType, EVENT_BUS_TYPE, PREDEFINED_COLUMN_KEYS, PRIVATE_COLUMN_KEY } from '../../constants';
import { getColumnOptions, getColumnOriginName } from '../../utils/column';
import { getColumnByKey, getColumnOptions, getColumnOriginName } from '../../utils/column';
import { SYSTEM_FOLDERS } from './constants';
import Location from './location';
import { checkIsDir } from '../../utils/row';
import tagsAPI from '../../../tag/api';
import './index.css';
@@ -24,7 +26,7 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, updateRecord
const onChange = useCallback((fieldKey, newValue) => {
const { record, fields } = metadata;
const field = fields.find(f => f.key === fieldKey);
const field = getColumnByKey(fields, fieldKey);
const fileName = getColumnOriginName(field);
const recordId = getRecordIdFromRecord(record);
const fileObjId = getFileObjIdFromRecord(record);
@@ -82,6 +84,24 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, updateRecord
setMetadata(newMetadata);
}, [metadata]);
const updateFileTags = useCallback((updateRecords) => {
const { record } = metadata;
const { record_id, tags } = updateRecords[0];
tagsAPI.updateFileTags(repoID, [{ record_id, tags }]).then(res => {
const newValue = tags ? tags.map(id => ({ row_id: id, display_value: id })) : [];
const update = { [PRIVATE_COLUMN_KEY.TAGS]: newValue };
const newMetadata = { ...metadata, record: { ...record, ...update } };
setMetadata(newMetadata);
if (window?.sfMetadataContext?.eventBus) {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, record_id, update);
}
}).catch(error => {
const errorMsg = Utils.getErrorMsg(error);
toaster.danger(errorMsg);
});
}, [repoID, metadata]);
useEffect(() => {
setLoading(true);
if (SYSTEM_FOLDERS.find(folderPath => filePath.startsWith(folderPath))) {
@@ -98,7 +118,11 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, updateRecord
metadataAPI.getMetadataRecordInfo(repoID, parentDir, fileName).then(res => {
const { results, metadata } = res.data;
const record = Array.isArray(results) && results.length > 0 ? results[0] : {};
const fields = normalizeFields(metadata).map(field => new Column(field));
let fields = normalizeFields(metadata).map(field => new Column(field));
const isDir = checkIsDir(record);
if (isDir) {
fields = fields.filter(field => field.type !== CellType.TAGS);
}
updateRecord && updateRecord(record);
setMetadata({ record, fields });
setLoading(false);
@@ -134,9 +158,17 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType, updateRecord
return (
<DetailItem key={field.key} field={field} readonly={!canEdit}>
{canEdit ? (
<DetailEditor field={field} value={value} onChange={onChange} fields={fields} record={record} modifyColumnData={modifyColumnData} />
<DetailEditor
field={field}
value={value}
fields={fields}
record={record}
modifyColumnData={modifyColumnData}
onChange={onChange}
updateFileTags={updateFileTags}
/>
) : (
<CellFormatter field={field} value={value} emptyTip={gettext('Empty')} className="sf-metadata-property-detail-formatter" />
<CellFormatter readonly={true} field={field} value={value} emptyTip={gettext('Empty')} className="sf-metadata-property-detail-formatter" />
)}
</DetailItem>
);

View File

@@ -5,7 +5,6 @@ import CheckboxEditor from '../../../../../../components/cell-editors/checkbox-e
import RateEditor from '../../../../../../components/cell-editors/rate-editor';
import { canEditCell } from '../../../../../../utils/column';
import { CellType } from '../../../../../../constants';
import FileTagsFormatter from '../../../../../../components/cell-formatter/file-tags-formatter';
const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
const { type } = field;
@@ -17,10 +16,6 @@ const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
return (<RateEditor isCellSelected={isCellSelected} value={value} field={field} onChange={onChange} />);
}
if (field.type === CellType.TAGS) {
return (<FileTagsFormatter isCellSelected={isCellSelected} field={field} readonly={!cellEditAble} value={value} record={record} />);
}
return (<CellFormatter readonly={true} isCellSelected={isCellSelected} value={value} field={field} record={record} />);
};

View File

@@ -183,6 +183,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
}, [tagsData, modifyLocalTags]);
useEffect(() => {
if (!handelSelectTag) return;
if (isLoading) return;
const { search } = window.location;
const urlParams = new URLSearchParams(search);
@@ -206,6 +207,7 @@ export const TagsProvider = ({ repoID, currentPath, selectTagsView, children, ..
}, [isLoading]);
useEffect(() => {
if (!currentPath) return;
if (!currentPath.includes('/' + PRIVATE_FILE_TYPE.TAGS_PROPERTIES + '/')) return;
const currentTagId = currentPath.split('/').pop();
if (currentTagId === ALL_TAGS_ID) {

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useState, useRef, useEffect } from 'react';
import { useTagView } from '../../hooks';
import { useTagView, useTags } from '../../hooks';
import { gettext } from '../../../utils/constants';
import TagFile from './tag-file';
import { getRecordIdFromRecord } from '../../../metadata/utils/cell';
@@ -10,6 +10,7 @@ import './index.css';
const TagFiles = () => {
const { tagFiles, repoID, repoInfo } = useTagView();
const { tagsData } = useTags();
const [selectedFiles, setSelectedFiles] = useState(null);
const [isImagePreviewerVisible, setImagePreviewerVisible] = useState(false);
const [containerWidth, setContainerWidth] = useState(0);
@@ -119,6 +120,7 @@ const TagFiles = () => {
repoID={repoID}
isSelected={selectedFiles && selectedFiles.includes(fileId)}
file={file}
tagsData={tagsData}
onSelectFile={onSelectFile}
reSelectFiles={reSelectFiles}
openImagePreview={openImagePreview}

View File

@@ -1,10 +1,15 @@
.tag-list-title .sf-metadata-tags-formatter .sf-metadata-tag-formatter {
height: 16px;
width: 16px;
top: 0;
.sf-metadata-tags-main .tag-list-title .sf-metadata-ui-tags-container {
width: fit-content;
max-width: 100%;
}
.tag-list-title .sf-metadata-tags-formatter .sf-metadata-tag-formatter:last-child {
.tag-list-title .sf-metadata-ui-tags-container .sf-metadata-ui-tag {
height: 16px !important;
width: 16px !important;
top: 0 !important;
}
.tag-list-title .sf-metadata-ui-tags-container .sf-metadata-ui-tag:last-child {
margin-right: 0;
}
@@ -18,8 +23,3 @@
.sf-metadata-tags-main .table-container td.name a {
word-break: break-word;
}
.sf-metadata-tags-main .tag-list-title .sf-metadata-tags-formatter {
width: fit-content;
max-width: 100%;
}

View File

@@ -3,19 +3,19 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { FileTagsFormatter } from '@seafile/sf-metadata-ui-component';
import { gettext, siteRoot, thumbnailDefaultSize } from '../../../../utils/constants';
import { getParentDirFromRecord, getRecordIdFromRecord, getFileNameFromRecord, getFileSizedFromRecord,
getFileMTimeFromRecord, getTagsFromRecord, getFilePathByRecord,
} from '../../../../metadata/utils/cell';
import { Utils } from '../../../../utils/utils';
import FileTagsFormatter from '../../../../metadata/components/cell-formatter/file-tags-formatter';
import { openFile } from '../../../../metadata/utils/open-file';
import './index.css';
dayjs.extend(relativeTime);
const TagFile = ({ isSelected, repoID, file, onSelectFile, reSelectFiles, openImagePreview }) => {
const TagFile = ({ isSelected, repoID, file, tagsData, onSelectFile, reSelectFiles, openImagePreview }) => {
const [highlight, setHighlight] = useState(false);
const [isIconLoadError, setIconLoadError] = useState(false);
@@ -112,7 +112,7 @@ const TagFile = ({ isSelected, repoID, file, onSelectFile, reSelectFiles, openIm
<a href={path} onClick={handelClickFileName}>{name}</a>
</td>
<td className="tag-list-title">
<FileTagsFormatter value={tags} />
<FileTagsFormatter value={tags} tagsData={tagsData} className="sf-metadata-tags-formatter" />
</td>
<td className="operation"></td>
<td className="file-size">{size || ''}</td>

View File

@@ -7,6 +7,7 @@ import Loading from './components/loading';
import SdocEditor from './pages/sdoc/sdoc-editor';
import { MetadataStatusProvider } from './hooks';
import { CollaboratorsProvider } from './metadata';
import { TagsProvider } from './tag/hooks';
const { serviceURL, avatarURL, siteRoot, lang, mediaUrl, isPro } = window.app.config;
const { username, name } = window.app.userInfo;
@@ -55,7 +56,9 @@ ReactDom.render(
<Suspense fallback={<Loading />}>
<MetadataStatusProvider repoID={repoID}>
<CollaboratorsProvider repoID={repoID}>
<TagsProvider repoID={repoID} repoInfo={{ permission: filePerm }}>
<SdocEditor />
</TagsProvider>
</CollaboratorsProvider>
</MetadataStatusProvider>
</Suspense>

View File

@@ -231,12 +231,12 @@ def remove_tags_table(metadata_server_api):
tables = metadata.get('tables', [])
for table in tables:
if table['name'] == TAGS_TABLE.name:
metadata_server_api.delete_table(table['id'])
metadata_server_api.delete_table(table['id'], True)
elif table['name'] == METADATA_TABLE.name:
columns = table.get('columns', [])
for column in columns:
if column['key'] in [METADATA_TABLE.columns.tags.key]:
metadata_server_api.delete_column(table['id'], column['key'])
metadata_server_api.delete_column(table['id'], column['key'], True)
def get_file_download_token(repo_id, file_id, username):