mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 02:42:47 +00:00
open file
This commit is contained in:
@@ -135,3 +135,9 @@ export const GALLERY_DATE_MODE = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const UNCATEGORIZED = '_uncategorized';
|
export const UNCATEGORIZED = '_uncategorized';
|
||||||
|
|
||||||
|
export const FILE_TYPE = {
|
||||||
|
MARKDOWN: 'markdown',
|
||||||
|
SDOC: 'sdoc',
|
||||||
|
IMAGE: 'image',
|
||||||
|
};
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card:hover {
|
.sf-metadata-kanban-card:hover {
|
||||||
@@ -16,6 +17,10 @@
|
|||||||
box-shadow: 0 0 6px #0000002e;
|
box-shadow: 0 0 6px #0000002e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sf-metadata-kanban-card.readonly {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header {
|
.sf-metadata-kanban-card .sf-metadata-kanban-card-header {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -34,10 +39,6 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-record .file-name-formatter-wrapper {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-record .sf-metadata-kanban-card-record-name {
|
.sf-metadata-kanban-card .sf-metadata-kanban-card-record .sf-metadata-kanban-card-record-name {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
color: #666;
|
color: #666;
|
||||||
@@ -76,7 +77,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-special-file-name-formatter:not(.sf-metadata-image-file-formatter) {
|
.sf-metadata-kanban-card .sf-metadata-special-file-name-formatter:not(.sf-metadata-image-file-formatter) {
|
||||||
margin-left: -2px;
|
transform: translateX(-2px)
|
||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .file-name-formatter .sf-metadata-file-icon {
|
.sf-metadata-kanban-card .file-name-formatter .sf-metadata-file-icon {
|
||||||
@@ -89,6 +90,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sf-metadata-kanban-card .sf-metadata-ui.file-name-formatter .sf-metadata-file-name:hover {
|
.sf-metadata-kanban-card .sf-metadata-ui.file-name-formatter .sf-metadata-file-name:hover {
|
||||||
|
cursor: pointer;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-decoration-color: #212529;
|
text-decoration-color: #212529;
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { getCellValueByColumn, isValidCellValue } from '../../../../../utils/cell';
|
|
||||||
import Formatter from '../formatter';
|
import Formatter from '../formatter';
|
||||||
import { PRIVATE_COLUMN_KEY } from '../../../../../constants';
|
import { FILE_TYPE } from '../../../../../constants';
|
||||||
import { useMetadataView } from '../../../../../hooks/metadata-view';
|
import { useMetadataView } from '../../../../../hooks/metadata-view';
|
||||||
|
import { Utils } from '../../../../../../utils/utils';
|
||||||
|
import { geRecordIdFromRecord, getCellValueByColumn, getFileNameFromRecord, getParentDirFromRecord, isValidCellValue } from '../../../../../utils/cell';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -16,7 +17,10 @@ const Card = ({
|
|||||||
titleColumn,
|
titleColumn,
|
||||||
displayColumns,
|
displayColumns,
|
||||||
onCloseSettings,
|
onCloseSettings,
|
||||||
|
onOpenFile,
|
||||||
}) => {
|
}) => {
|
||||||
|
const cardRef = useRef(null);
|
||||||
|
|
||||||
const { updateCurrentDirent, showDirentDetail } = useMetadataView();
|
const { updateCurrentDirent, showDirentDetail } = useMetadataView();
|
||||||
|
|
||||||
const titleValue = getCellValueByColumn(record, titleColumn);
|
const titleValue = getCellValueByColumn(record, titleColumn);
|
||||||
@@ -24,8 +28,8 @@ const Card = ({
|
|||||||
const handleClick = useCallback((e) => {
|
const handleClick = useCallback((e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const name = record[PRIVATE_COLUMN_KEY.FILE_NAME];
|
const name = getFileNameFromRecord(record);
|
||||||
const path = record[PRIVATE_COLUMN_KEY.PARENT_DIR];
|
const path = getParentDirFromRecord(record);
|
||||||
updateCurrentDirent({
|
updateCurrentDirent({
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name,
|
name,
|
||||||
@@ -36,14 +40,48 @@ const Card = ({
|
|||||||
showDirentDetail();
|
showDirentDetail();
|
||||||
}, [record, updateCurrentDirent, showDirentDetail, onCloseSettings]);
|
}, [record, updateCurrentDirent, showDirentDetail, onCloseSettings]);
|
||||||
|
|
||||||
|
const getFileType = useCallback((fileName) => {
|
||||||
|
if (!fileName) return '';
|
||||||
|
const index = fileName.lastIndexOf('.');
|
||||||
|
if (index === -1) return '';
|
||||||
|
const suffix = fileName.slice(index).toLowerCase();
|
||||||
|
if (suffix.indexOf(' ') > -1) return '';
|
||||||
|
if (Utils.imageCheck(fileName)) return FILE_TYPE.IMAGE;
|
||||||
|
if (Utils.isMarkdownFile(fileName)) return FILE_TYPE.MARKDOWN;
|
||||||
|
if (Utils.isSdocFile(fileName)) return FILE_TYPE.SDOC;
|
||||||
|
return '';
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleClickFilename = useCallback((e) => {
|
const handleClickFilename = useCallback((e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
console.log('click filename');
|
|
||||||
}, []);
|
const fileName = getFileNameFromRecord(record);
|
||||||
|
const fileType = getFileType(fileName);
|
||||||
|
const parentDir = getParentDirFromRecord(record);
|
||||||
|
const recordId = geRecordIdFromRecord(record);
|
||||||
|
onOpenFile(fileType, fileName, parentDir, recordId);
|
||||||
|
}, [record, getFileType, onOpenFile]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cardElement = cardRef.current;
|
||||||
|
if (!cardElement) return;
|
||||||
|
|
||||||
|
const filenameElement = cardElement.querySelector('.file-name-formatter .sf-metadata-file-name');
|
||||||
|
if (filenameElement) {
|
||||||
|
filenameElement.addEventListener('click', handleClickFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (filenameElement) {
|
||||||
|
filenameElement.removeEventListener('click', handleClickFilename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [handleClickFilename]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
|
ref={cardRef}
|
||||||
data-id={record._id}
|
data-id={record._id}
|
||||||
className={classnames('sf-metadata-kanban-card', { 'readonly': readonly })}
|
className={classnames('sf-metadata-kanban-card', { 'readonly': readonly })}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
@@ -70,9 +108,7 @@ const Card = ({
|
|||||||
return (
|
return (
|
||||||
<div className="sf-metadata-kanban-card-record" key={column.key}>
|
<div className="sf-metadata-kanban-card-record" key={column.key}>
|
||||||
{displayColumnName && (<div className="sf-metadata-kanban-card-record-name">{column.name}</div>)}
|
{displayColumnName && (<div className="sf-metadata-kanban-card-record-name">{column.name}</div>)}
|
||||||
<div className='file-name-formatter-wrapper' onClick={handleClickFilename}>
|
<Formatter value={value} column={column} record={record}/>
|
||||||
<Formatter value={value} column={column} record={record}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -89,6 +125,7 @@ Card.propTypes = {
|
|||||||
titleColumn: PropTypes.object,
|
titleColumn: PropTypes.object,
|
||||||
displayColumns: PropTypes.array,
|
displayColumns: PropTypes.array,
|
||||||
onCloseSettings: PropTypes.func.isRequired,
|
onCloseSettings: PropTypes.func.isRequired,
|
||||||
|
onOpenFile: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Card;
|
export default Card;
|
||||||
|
@@ -24,6 +24,7 @@ const Board = ({
|
|||||||
onFreezed,
|
onFreezed,
|
||||||
onUnFreezed,
|
onUnFreezed,
|
||||||
onCloseSettings,
|
onCloseSettings,
|
||||||
|
onOpenFile,
|
||||||
}) => {
|
}) => {
|
||||||
const [isDraggingOver, setDraggingOver] = useState(false);
|
const [isDraggingOver, setDraggingOver] = useState(false);
|
||||||
const boardName = useMemo(() => `sf_metadata_kanban_board_${board.key}`, [board]);
|
const boardName = useMemo(() => `sf_metadata_kanban_board_${board.key}`, [board]);
|
||||||
@@ -84,6 +85,7 @@ const Board = ({
|
|||||||
titleColumn={titleColumn}
|
titleColumn={titleColumn}
|
||||||
displayColumns={displayColumns}
|
displayColumns={displayColumns}
|
||||||
onCloseSettings={onCloseSettings}
|
onCloseSettings={onCloseSettings}
|
||||||
|
onOpenFile={onOpenFile}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if (readonly) return CardElement;
|
if (readonly) return CardElement;
|
||||||
@@ -113,6 +115,7 @@ Board.propTypes = {
|
|||||||
onFreezed: PropTypes.func,
|
onFreezed: PropTypes.func,
|
||||||
onUnFreezed: PropTypes.func,
|
onUnFreezed: PropTypes.func,
|
||||||
onCloseSettings: PropTypes.func.isRequired,
|
onCloseSettings: PropTypes.func.isRequired,
|
||||||
|
onOpenFile: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Board;
|
export default Board;
|
||||||
|
@@ -3,19 +3,25 @@ import PropTypes from 'prop-types';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { useMetadataView } from '../../../hooks/metadata-view';
|
import { useMetadataView } from '../../../hooks/metadata-view';
|
||||||
import { useCollaborators } from '../../../hooks';
|
import { useCollaborators } from '../../../hooks';
|
||||||
import { CellType, KANBAN_SETTINGS_KEYS, UNCATEGORIZED } from '../../../constants';
|
import { CellType, KANBAN_SETTINGS_KEYS, UNCATEGORIZED, FILE_TYPE } from '../../../constants';
|
||||||
import { COLUMN_DATA_OPERATION_TYPE } from '../../../store/operations';
|
import { COLUMN_DATA_OPERATION_TYPE } from '../../../store/operations';
|
||||||
import { gettext } from '../../../../utils/constants';
|
import { gettext, siteRoot } from '../../../../utils/constants';
|
||||||
import { checkIsPredefinedOption, getCellValueByColumn, isValidCellValue, geRecordIdFromRecord } from '../../../utils/cell';
|
import { checkIsPredefinedOption, getCellValueByColumn, isValidCellValue, geRecordIdFromRecord } from '../../../utils/cell';
|
||||||
import { getColumnOptions, getColumnOriginName } from '../../../utils/column';
|
import { getColumnOptions, getColumnOriginName } from '../../../utils/column';
|
||||||
import AddBoard from '../add-board';
|
import AddBoard from '../add-board';
|
||||||
import EmptyTip from '../../../../components/empty-tip';
|
import EmptyTip from '../../../../components/empty-tip';
|
||||||
import Board from './board';
|
import Board from './board';
|
||||||
|
import { Utils } from '../../../../utils/utils';
|
||||||
|
import { EVENT_BUS_TYPE } from '../../../../components/common/event-bus-type';
|
||||||
|
import ImagePreviewer from '../../../components/cell-formatter/image-previewer';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
import { getRowById } from '../../../utils/table';
|
||||||
|
|
||||||
const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
||||||
const [haveFreezed, setHaveFreezed] = useState(false);
|
const [haveFreezed, setHaveFreezed] = useState(false);
|
||||||
|
const [isShowImagePreviewer, setIsShowImagePreviewer] = useState(false);
|
||||||
|
const [record, setRecord] = useState(null);
|
||||||
|
|
||||||
const { metadata, store } = useMetadataView();
|
const { metadata, store } = useMetadataView();
|
||||||
const { collaborators } = useCollaborators();
|
const { collaborators } = useCollaborators();
|
||||||
@@ -166,6 +172,61 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
|||||||
|
|
||||||
const isEmpty = boards.length === 0;
|
const isEmpty = boards.length === 0;
|
||||||
|
|
||||||
|
const generateUrl = (fileName, parentDir) => {
|
||||||
|
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||||
|
const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
|
||||||
|
return `${siteRoot}lib/${repoID}/file${path}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openMarkdown = (name, parentDir) => {
|
||||||
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, parentDir, name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openByNewWindow = (fileType, fileName, parentDir) => {
|
||||||
|
if (!fileType) {
|
||||||
|
const url = generateUrl(fileName, parentDir);
|
||||||
|
window.open(url);
|
||||||
|
} else {
|
||||||
|
let pathname = window.location.pathname;
|
||||||
|
if (pathname.endsWith('/')) {
|
||||||
|
pathname = pathname.slice(0, -1);
|
||||||
|
}
|
||||||
|
window.open(window.location.origin + pathname + Utils.encodePath(Utils.joinPath(parentDir, fileName)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openSdoc = (fileName, parentDir) => {
|
||||||
|
const url = generateUrl(fileName, parentDir);
|
||||||
|
window.open(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openFile = (type, fileName, parentDir, recordId = null) => {
|
||||||
|
switch (type) {
|
||||||
|
case FILE_TYPE.MARKDOWN: {
|
||||||
|
openMarkdown(fileName, parentDir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FILE_TYPE.SDOC: {
|
||||||
|
openSdoc(fileName, parentDir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FILE_TYPE.IMAGE: {
|
||||||
|
const record = getRowById(metadata, recordId);
|
||||||
|
setRecord(record);
|
||||||
|
setIsShowImagePreviewer(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
openByNewWindow(type, fileName, parentDir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeImagePreviewer = () => {
|
||||||
|
setIsShowImagePreviewer(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('sf-metadata-view-kanban-boards', {
|
<div className={classnames('sf-metadata-view-kanban-boards', {
|
||||||
'sf-metadata-view-kanban-boards-text-wrap': textWrap,
|
'sf-metadata-view-kanban-boards-text-wrap': textWrap,
|
||||||
@@ -194,6 +255,7 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
|||||||
onFreezed={onFreezed}
|
onFreezed={onFreezed}
|
||||||
onUnFreezed={onUnFreezed}
|
onUnFreezed={onUnFreezed}
|
||||||
onCloseSettings={onCloseSettings}
|
onCloseSettings={onCloseSettings}
|
||||||
|
onOpenFile={openFile}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -201,6 +263,7 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
|||||||
)}
|
)}
|
||||||
{!readonly && (<AddBoard groupByColumn={groupByColumn}/>)}
|
{!readonly && (<AddBoard groupByColumn={groupByColumn}/>)}
|
||||||
</div>
|
</div>
|
||||||
|
{isShowImagePreviewer && (<ImagePreviewer record={record} table={metadata} closeImagePopup={closeImagePreviewer} />)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user