From 7d206bc691cb5a830be467ca4ed94fbd2285ffb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=9B=BD=E7=92=87?= <37972689+YangGuoXuan-0503@users.noreply.github.com> Date: Fri, 29 Nov 2024 09:53:51 +0800 Subject: [PATCH] feat: detail support tag (#7119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: detail support tag * feat: optimize code * feat: optimize code * feat: optimize code --------- Co-authored-by: 杨国璇 --- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- .../dirent-detail/detail-item/index.css | 5 + .../embedded-file-details/file-details.js | 2 +- .../src/components/file-view/file-view.js | 17 +-- .../tags-editor/delete-tags/index.css | 2 + .../cell-editors/tags-editor/index.css | 6 + .../cell-editors/tags-editor/index.js | 7 +- .../file-tags-formatter/index.css | 45 ------- .../file-tags-formatter/index.js | 39 ------ .../components/cell-formatter/index.js | 4 +- .../components/detail-editor/index.js | 4 + .../detail-editor/rate-editor/index.css | 4 + .../detail-editor/tags-editor/index.css | 46 +++++++ .../detail-editor/tags-editor/index.js | 117 ++++++++++++++++++ .../components/metadata-details/constants.js | 1 - .../components/metadata-details/index.css | 14 +++ .../components/metadata-details/index.js | 42 ++++++- .../records/record/cell/formatter.js | 5 - frontend/src/tag/hooks/tags.js | 2 + frontend/src/tag/views/tag-files/index.js | 4 +- .../tag/views/tag-files/tag-file/index.css | 20 +-- .../src/tag/views/tag-files/tag-file/index.js | 6 +- frontend/src/view-file-sdoc.js | 5 +- seahub/repo_metadata/utils.py | 4 +- 25 files changed, 283 insertions(+), 128 deletions(-) delete mode 100644 frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.css delete mode 100644 frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.js create mode 100644 frontend/src/metadata/components/detail-editor/tags-editor/index.css create mode 100644 frontend/src/metadata/components/detail-editor/tags-editor/index.js diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 187b080690..ac28f13fc2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/package.json b/frontend/package.json index f81f630a9a..69cc053ca8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/components/dirent-detail/detail-item/index.css b/frontend/src/components/dirent-detail/detail-item/index.css index 72b731ab55..65882e3931 100644 --- a/frontend/src/components/dirent-detail/detail-item/index.css +++ b/frontend/src/components/dirent-detail/detail-item/index.css @@ -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; diff --git a/frontend/src/components/dirent-detail/embedded-file-details/file-details.js b/frontend/src/components/dirent-detail/embedded-file-details/file-details.js index 35f494a1b4..300d36f7f2 100644 --- a/frontend/src/components/dirent-detail/embedded-file-details/file-details.js +++ b/frontend/src/components/dirent-detail/embedded-file-details/file-details.js @@ -35,7 +35,7 @@ const FileDetails = ({ repoID, repoInfo, path, direntDetail }) => { - {window.app.pageOptions.enableMetadataManagement && enableMetadata && ( + {enableMetadata && ( )} diff --git a/frontend/src/components/file-view/file-view.js b/frontend/src/components/file-view/file-view.js index 8fdc4e4d9e..f161af7b79 100644 --- a/frontend/src/components/file-view/file-view.js +++ b/frontend/src/components/file-view/file-view.js @@ -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,13 +154,15 @@ class FileView extends React.Component { {isDetailsPanelOpen && ( - + + + )} diff --git a/frontend/src/metadata/components/cell-editors/tags-editor/delete-tags/index.css b/frontend/src/metadata/components/cell-editors/tags-editor/delete-tags/index.css index 0470c74c4f..0cac13cba2 100644 --- a/frontend/src/metadata/components/cell-editors/tags-editor/delete-tags/index.css +++ b/frontend/src/metadata/components/cell-editors/tags-editor/delete-tags/index.css @@ -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 { diff --git a/frontend/src/metadata/components/cell-editors/tags-editor/index.css b/frontend/src/metadata/components/cell-editors/tags-editor/index.css index ac0b7bbe70..404971804e 100644 --- a/frontend/src/metadata/components/cell-editors/tags-editor/index.css +++ b/frontend/src/metadata/components/cell-editors/tags-editor/index.css @@ -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; diff --git a/frontend/src/metadata/components/cell-editors/tags-editor/index.js b/frontend/src/metadata/components/cell-editors/tags-editor/index.js index 572137f3fe..4a6b945aa5 100644 --- a/frontend/src/metadata/components/cell-editors/tags-editor/index.js +++ b/frontend/src/metadata/components/cell-editors/tags-editor/index.js @@ -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 }; diff --git a/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.css b/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.css deleted file mode 100644 index 1e54638657..0000000000 --- a/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.css +++ /dev/null @@ -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%; -} diff --git a/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.js b/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.js deleted file mode 100644 index c1876f8d1c..0000000000 --- a/frontend/src/metadata/components/cell-formatter/file-tags-formatter/index.js +++ /dev/null @@ -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 ( -
- {value.length > 0 && ( -
- {value.map((item) => { - const tag = getRowById(tagsData, item); - const tagColor = getTagColor(tag); - const tagName = getTagName(tag); - - return ( - - ); - })} -
- )} -
- ); -}; - -FileTagsFormatter.propTypes = { - value: PropTypes.array, -}; - -export default FileTagsFormatter; diff --git a/frontend/src/metadata/components/cell-formatter/index.js b/frontend/src/metadata/components/cell-formatter/index.js index d702acce66..c9b399096f 100644 --- a/frontend/src/metadata/components/cell-formatter/index.js +++ b/frontend/src/metadata/components/cell-formatter/index.js @@ -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 (); } return ( - + ); }; diff --git a/frontend/src/metadata/components/detail-editor/index.js b/frontend/src/metadata/components/detail-editor/index.js index 0a7ccd536b..fb9df9f41f 100644 --- a/frontend/src/metadata/components/detail-editor/index.js +++ b/frontend/src/metadata/components/detail-editor/index.js @@ -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 (); } + case CellType.TAGS: { + return (); + } default: { return null; } diff --git a/frontend/src/metadata/components/detail-editor/rate-editor/index.css b/frontend/src/metadata/components/detail-editor/rate-editor/index.css index 43fbeef403..44b79d76cc 100644 --- a/frontend/src/metadata/components/detail-editor/rate-editor/index.css +++ b/frontend/src/metadata/components/detail-editor/rate-editor/index.css @@ -3,3 +3,7 @@ width: 100%; line-height: 1; } + +.sf-metadata-rate-property-detail-editor .sf-metadata-rate-editor { + flex-wrap: wrap; +} diff --git a/frontend/src/metadata/components/detail-editor/tags-editor/index.css b/frontend/src/metadata/components/detail-editor/tags-editor/index.css new file mode 100644 index 0000000000..0d95605ca0 --- /dev/null +++ b/frontend/src/metadata/components/detail-editor/tags-editor/index.css @@ -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; +} diff --git a/frontend/src/metadata/components/detail-editor/tags-editor/index.js b/frontend/src/metadata/components/detail-editor/tags-editor/index.js new file mode 100644 index 0000000000..fe8e4a1348 --- /dev/null +++ b/frontend/src/metadata/components/detail-editor/tags-editor/index.js @@ -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 ( + + + + ); + }, [showEditor, field, record, value, updateFileTags]); + + return ( +
+ {validValue.length > 0 && ()} + {renderEditor()} +
+ ); + +}; + +TagsEditor.propTypes = { + record: PropTypes.object, + value: PropTypes.array, + field: PropTypes.object, + updateFileTags: PropTypes.func, +}; + +export default TagsEditor; diff --git a/frontend/src/metadata/components/metadata-details/constants.js b/frontend/src/metadata/components/metadata-details/constants.js index 0ca1dca88b..12d7876bf6 100644 --- a/frontend/src/metadata/components/metadata-details/constants.js +++ b/frontend/src/metadata/components/metadata-details/constants.js @@ -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 = [ diff --git a/frontend/src/metadata/components/metadata-details/index.css b/frontend/src/metadata/components/metadata-details/index.css index 73863ffc83..84ee9e6ca7 100644 --- a/frontend/src/metadata/components/metadata-details/index.css +++ b/frontend/src/metadata/components/metadata-details/index.css @@ -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; +} diff --git a/frontend/src/metadata/components/metadata-details/index.js b/frontend/src/metadata/components/metadata-details/index.js index 5f626c0a85..db7db7e68e 100644 --- a/frontend/src/metadata/components/metadata-details/index.js +++ b/frontend/src/metadata/components/metadata-details/index.js @@ -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 ( {canEdit ? ( - + ) : ( - + )} ); diff --git a/frontend/src/metadata/views/table/table-main/records/record/cell/formatter.js b/frontend/src/metadata/views/table/table-main/records/record/cell/formatter.js index 957baad556..0ae7a57f5a 100644 --- a/frontend/src/metadata/views/table/table-main/records/record/cell/formatter.js +++ b/frontend/src/metadata/views/table/table-main/records/record/cell/formatter.js @@ -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 (); } - if (field.type === CellType.TAGS) { - return (); - } - return (); }; diff --git a/frontend/src/tag/hooks/tags.js b/frontend/src/tag/hooks/tags.js index cae49bae05..6c945468a4 100644 --- a/frontend/src/tag/hooks/tags.js +++ b/frontend/src/tag/hooks/tags.js @@ -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) { diff --git a/frontend/src/tag/views/tag-files/index.js b/frontend/src/tag/views/tag-files/index.js index 7faf2aa4d7..eacf2c2407 100644 --- a/frontend/src/tag/views/tag-files/index.js +++ b/frontend/src/tag/views/tag-files/index.js @@ -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} diff --git a/frontend/src/tag/views/tag-files/tag-file/index.css b/frontend/src/tag/views/tag-files/tag-file/index.css index cc3b27e493..8f8aecf8ac 100644 --- a/frontend/src/tag/views/tag-files/tag-file/index.css +++ b/frontend/src/tag/views/tag-files/tag-file/index.css @@ -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%; -} diff --git a/frontend/src/tag/views/tag-files/tag-file/index.js b/frontend/src/tag/views/tag-files/tag-file/index.js index 8e1d1103ba..3a1176c6fc 100644 --- a/frontend/src/tag/views/tag-files/tag-file/index.js +++ b/frontend/src/tag/views/tag-files/tag-file/index.js @@ -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 {name} - + {size || ''} diff --git a/frontend/src/view-file-sdoc.js b/frontend/src/view-file-sdoc.js index 505b0c1849..f113a99145 100644 --- a/frontend/src/view-file-sdoc.js +++ b/frontend/src/view-file-sdoc.js @@ -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( }> - + + + diff --git a/seahub/repo_metadata/utils.py b/seahub/repo_metadata/utils.py index 4532999f93..eb36f28d1e 100644 --- a/seahub/repo_metadata/utils.py +++ b/seahub/repo_metadata/utils.py @@ -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):