1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 15:53:28 +00:00

feat: metadata shooting time

This commit is contained in:
杨国璇
2024-09-18 11:08:14 +08:00
parent 25f12a252d
commit 69be0e8c25
11 changed files with 84 additions and 41 deletions

View File

@@ -17,9 +17,9 @@
"@seafile/react-image-lightbox": "3.0.0", "@seafile/react-image-lightbox": "3.0.0",
"@seafile/resumablejs": "1.1.16", "@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "1.0.81", "@seafile/sdoc-editor": "1.0.81",
"@seafile/seafile-calendar": "0.0.12", "@seafile/seafile-calendar": "0.0.25",
"@seafile/seafile-editor": "^1.0.116", "@seafile/seafile-editor": "^1.0.116",
"@seafile/sf-metadata-ui-component": "^0.0.32", "@seafile/sf-metadata-ui-component": "^0.0.33",
"@uiw/codemirror-extensions-langs": "^4.19.4", "@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4", "@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.4", "axios": "^1.7.4",
@@ -4969,19 +4969,24 @@
} }
}, },
"node_modules/@seafile/seafile-calendar": { "node_modules/@seafile/seafile-calendar": {
"version": "0.0.12", "version": "0.0.25",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.12.tgz", "resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.25.tgz",
"integrity": "sha512-TvttWwuZqDz2+Tvy4k3SeXDRbAjWmIw5BvJVeZkEzwuuFz2DB9TYucwT0tAWSSdzuRcD5jr42Y0V09D54Lig+Q==", "integrity": "sha512-d6whp7NfKTjD021AEZYaTTrAipKsWqE736b3r1s6ID6XLxPsBitoGgbzeMK3wDLq0iBPQi9MokHif+8L3Kxq7g==",
"dependencies": { "dependencies": {
"babel-runtime": "6.x", "babel-runtime": "6.x",
"classnames": "2.x", "classnames": "2.x",
"moment": "2.x", "dayjs": "1.10.7",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"rc-trigger": "^2.2.0", "rc-trigger": "^2.2.0",
"rc-util": "^4.1.1", "rc-util": "^4.1.1",
"react-lifecycles-compat": "^3.0.4" "react-lifecycles-compat": "^3.0.4"
} }
}, },
"node_modules/@seafile/seafile-calendar/node_modules/dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
},
"node_modules/@seafile/seafile-editor": { "node_modules/@seafile/seafile-editor": {
"version": "1.0.116", "version": "1.0.116",
"resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-1.0.116.tgz", "resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-1.0.116.tgz",
@@ -5093,11 +5098,11 @@
} }
}, },
"node_modules/@seafile/sf-metadata-ui-component": { "node_modules/@seafile/sf-metadata-ui-component": {
"version": "0.0.32", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.32.tgz", "resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.33.tgz",
"integrity": "sha512-7Np8syp2YoauqaDhd6i6VXalA1XLAmvertdz/Uo1tvFl5xZTECjbi1ywjEosJqM6ej5W6TfvXjc/yvGDwxXoxQ==", "integrity": "sha512-utCWgOCNPGr5w56+I+SfB60XJA1Amxfo8jfHELHiJG+hzp5bKNXvq6XQ9ampS/rVzzvwUFuCahOURyiQOx9hKw==",
"dependencies": { "dependencies": {
"@seafile/seafile-calendar": "0.0.24", "@seafile/seafile-calendar": "0.0.25",
"@seafile/seafile-editor": "~1.0.102", "@seafile/seafile-editor": "~1.0.102",
"classnames": "2.3.2", "classnames": "2.3.2",
"dayjs": "1.10.7", "dayjs": "1.10.7",
@@ -5120,20 +5125,6 @@
"react-dom": "17.0.0" "react-dom": "17.0.0"
} }
}, },
"node_modules/@seafile/sf-metadata-ui-component/node_modules/@seafile/seafile-calendar": {
"version": "0.0.24",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.24.tgz",
"integrity": "sha512-q1efVDcHAxJ2foMgsR8mQPD6Fbd6ISu2WHRM82P7tO0KPiQNS5pz9V0YVCblgi7da085jaog2iAplJM+vH7xLQ==",
"dependencies": {
"babel-runtime": "6.x",
"classnames": "2.x",
"dayjs": "1.10.7",
"prop-types": "^15.5.8",
"rc-trigger": "^2.2.0",
"rc-util": "^4.1.1",
"react-lifecycles-compat": "^3.0.4"
}
},
"node_modules/@seafile/sf-metadata-ui-component/node_modules/classnames": { "node_modules/@seafile/sf-metadata-ui-component/node_modules/classnames": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",

View File

@@ -12,9 +12,9 @@
"@seafile/react-image-lightbox": "3.0.0", "@seafile/react-image-lightbox": "3.0.0",
"@seafile/resumablejs": "1.1.16", "@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "1.0.81", "@seafile/sdoc-editor": "1.0.81",
"@seafile/seafile-calendar": "0.0.12", "@seafile/seafile-calendar": "0.0.25",
"@seafile/seafile-editor": "^1.0.116", "@seafile/seafile-editor": "^1.0.116",
"@seafile/sf-metadata-ui-component": "^0.0.32", "@seafile/sf-metadata-ui-component": "^0.0.33",
"@uiw/codemirror-extensions-langs": "^4.19.4", "@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4", "@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.4", "axios": "^1.7.4",

View File

@@ -103,10 +103,15 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType }) => {
if (isLoading) return null; if (isLoading) return null;
const { fields, record } = metadata; const { fields, record } = metadata;
if (!record._id) return null; if (!record._id) return null;
const fileName = record[PRIVATE_COLUMN_KEY.FILE_NAME];
const isImage = record && (Utils.imageCheck(fileName) || Utils.videoCheck(fileName));
return ( return (
<> <>
{fields.map(field => { {fields.map(field => {
const canEdit = permission === 'rw' && field.editable; let canEdit = permission === 'rw' && field.editable;
if (!isImage && canEdit && field.key === PRIVATE_COLUMN_KEY.SHOOTING_TIME) {
canEdit = false;
}
const value = getCellValueByColumn(record, field); const value = getCellValueByColumn(record, field);
return ( return (
<DetailItem key={field.key} field={field} readonly={!canEdit}> <DetailItem key={field.key} field={field} readonly={!canEdit}>

View File

@@ -40,11 +40,17 @@ const DateData = ({ value, onChange }) => {
const onMinuteChange = useCallback((v) => { const onMinuteChange = useCallback((v) => {
let newFormat = format || 'YYYY-MM-DD'; let newFormat = format || 'YYYY-MM-DD';
const formats = format.split(' '); const formats = format.split(' ');
if (formats.length === 1) newFormat = format + ' HH:mm'; if (formats.length === 1) newFormat = formats[0] + ' HH:mm';
if (formats.length === 2) newFormat = formats[0]; if (formats.length === 2) newFormat = formats[0];
onChange({ format: newFormat }); onChange({ format: newFormat });
}, [format, onChange]); }, [format, onChange]);
const onSecondChange = useCallback((v) => {
let newFormat = format || 'YYYY-MM-DD HH:mm';
newFormat = format.indexOf('ss') === -1 ? newFormat + ':ss' : newFormat.slice(0, -3);
onChange({ format: newFormat });
}, [format, onChange]);
useEffect(() => { useEffect(() => {
if (format) return; if (format) return;
onChange({ format: 'YYYY-MM-DD' }); onChange({ format: 'YYYY-MM-DD' });
@@ -52,6 +58,7 @@ const DateData = ({ value, onChange }) => {
}, []); }, []);
const selectedValue = options.find(o => o.value === format) || options[0]; const selectedValue = options.find(o => o.value === format) || options[0];
const showMinute = format ? format.indexOf('HH:mm') > -1 : false;
return ( return (
<div className="sf-metadata-column-data-settings sf-metadata-date-column-data-settings"> <div className="sf-metadata-column-data-settings sf-metadata-date-column-data-settings">
@@ -61,13 +68,23 @@ const DateData = ({ value, onChange }) => {
</FormGroup> </FormGroup>
<div className="pb-4"> <div className="pb-4">
<Switch <Switch
checked={format ? format.indexOf('HH:mm') > -1 : false} checked={showMinute}
size="large" size="large"
textPosition="right" textPosition="right"
className="sf-metadata-date-column-data-minute w-100" className="sf-metadata-date-column-data-minute w-100"
onChange={onMinuteChange} onChange={onMinuteChange}
placeholder={gettext('Accurate to minute')} /> placeholder={gettext('Accurate to minute')} />
</div> </div>
<div className="pb-4">
<Switch
disabled={!showMinute}
checked={format ? format.indexOf('HH:mm:ss') > -1 : false}
size="large"
textPosition="right"
className="sf-metadata-date-column-data-minute w-100"
onChange={onSecondChange}
placeholder={gettext('Accurate to second')} />
</div>
</div> </div>
); );
}; };

View File

@@ -59,6 +59,14 @@ const COLUMNS = [
key: PRIVATE_COLUMN_KEY.FILE_STATUS, key: PRIVATE_COLUMN_KEY.FILE_STATUS,
canChangeName: false, canChangeName: false,
groupby: 'predefined' groupby: 'predefined'
}, {
icon: COLUMNS_ICON_CONFIG[CellType.DATE],
type: CellType.DATE,
name: getColumnDisplayName(PRIVATE_COLUMN_KEY.SHOOTING_TIME),
unique: true,
key: PRIVATE_COLUMN_KEY.SHOOTING_TIME,
canChangeName: false,
groupby: 'predefined'
}, { }, {
icon: COLUMNS_ICON_CONFIG[CellType.TEXT], icon: COLUMNS_ICON_CONFIG[CellType.TEXT],
type: CellType.TEXT, type: CellType.TEXT,

View File

@@ -26,6 +26,7 @@ export const PRIVATE_COLUMN_KEY = {
SIZE: '_size', SIZE: '_size',
SUFFIX: '_suffix', SUFFIX: '_suffix',
FILE_DETAILS: '_file_details', FILE_DETAILS: '_file_details',
SHOOTING_TIME: '_shooting_time',
}; };
export const PRIVATE_COLUMN_KEYS = [ export const PRIVATE_COLUMN_KEYS = [
@@ -53,6 +54,7 @@ export const PRIVATE_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.SIZE, PRIVATE_COLUMN_KEY.SIZE,
PRIVATE_COLUMN_KEY.SUFFIX, PRIVATE_COLUMN_KEY.SUFFIX,
PRIVATE_COLUMN_KEY.FILE_DETAILS, PRIVATE_COLUMN_KEY.FILE_DETAILS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
]; ];
export const EDITABLE_PRIVATE_COLUMN_KEYS = [ export const EDITABLE_PRIVATE_COLUMN_KEYS = [
@@ -62,10 +64,11 @@ export const EDITABLE_PRIVATE_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.FILE_DESCRIPTION, PRIVATE_COLUMN_KEY.FILE_DESCRIPTION,
PRIVATE_COLUMN_KEY.FILE_EXPIRED, PRIVATE_COLUMN_KEY.FILE_EXPIRED,
PRIVATE_COLUMN_KEY.FILE_STATUS, PRIVATE_COLUMN_KEY.FILE_STATUS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
]; ];
export const EDITABLE_DATA_PRIVATE_COLUMN_KEYS = [ export const EDITABLE_DATA_PRIVATE_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
]; ];
export const DELETABLE_PRIVATE_COLUMN_KEY = [ export const DELETABLE_PRIVATE_COLUMN_KEY = [
@@ -75,4 +78,5 @@ export const DELETABLE_PRIVATE_COLUMN_KEY = [
PRIVATE_COLUMN_KEY.FILE_DESCRIPTION, PRIVATE_COLUMN_KEY.FILE_DESCRIPTION,
PRIVATE_COLUMN_KEY.FILE_EXPIRED, PRIVATE_COLUMN_KEY.FILE_EXPIRED,
PRIVATE_COLUMN_KEY.FILE_STATUS, PRIVATE_COLUMN_KEY.FILE_STATUS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
]; ];

View File

@@ -1,6 +1,6 @@
import metadataAPI from './api'; import metadataAPI from './api';
import { import {
PRIVATE_COLUMN_KEYS, EDITABLE_DATA_PRIVATE_COLUMN_KEYS, EDITABLE_PRIVATE_COLUMN_KEYS, PREDEFINED_COLUMN_KEYS, PRIVATE_COLUMN_KEYS, EDITABLE_DATA_PRIVATE_COLUMN_KEYS, EDITABLE_PRIVATE_COLUMN_KEYS, DELETABLE_PRIVATE_COLUMN_KEY,
} from './constants'; } from './constants';
import LocalStorage from './utils/local-storage'; import LocalStorage from './utils/local-storage';
import EventBus from '../components/common/event-bus'; import EventBus from '../components/common/event-bus';
@@ -133,7 +133,7 @@ class Context {
canDeleteColumn = (column) => { canDeleteColumn = (column) => {
if (this.permission === 'r') return false; if (this.permission === 'r') return false;
const { key } = column; const { key } = column;
if (PRIVATE_COLUMN_KEYS.includes(key)) return PREDEFINED_COLUMN_KEYS.includes(key); if (PRIVATE_COLUMN_KEYS.includes(key)) return DELETABLE_PRIVATE_COLUMN_KEY.includes(key);
return true; return true;
}; };

View File

@@ -27,10 +27,19 @@ const getDateDisplayString = (date, format) => {
const formatDateList = formatValuesList[0].split('-'); const formatDateList = formatValuesList[0].split('-');
return `${formatDateList[2]}/${formatDateList[1]}/${formatDateList[0]} ${formatValuesList[1]}`; return `${formatDateList[2]}/${formatDateList[1]}/${formatDateList[0]} ${formatValuesList[1]}`;
} }
case 'D/M/YYYY HH:mm:ss':
case 'DD/MM/YYYY HH:mm:ss': {
const formatValues = dateObj.format('YYYY-MM-DD HH:mm:ss');
const formatValuesList = formatValues.split(' ');
const formatDateList = formatValuesList[0].split('-');
return `${formatDateList[2]}/${formatDateList[1]}/${formatDateList[0]} ${formatValuesList[1]}`;
}
case 'M/D/YYYY': case 'M/D/YYYY':
return dateObj.format('M/D/YYYY'); return dateObj.format('M/D/YYYY');
case 'M/D/YYYY HH:mm': case 'M/D/YYYY HH:mm':
return dateObj.format('M/D/YYYY HH:mm'); return dateObj.format('M/D/YYYY HH:mm');
case 'M/D/YYYY HH:mm:ss':
return dateObj.format('M/D/YYYY HH:mm:ss');
case 'YYYY-MM-DD': case 'YYYY-MM-DD':
return dateObj.format('YYYY-MM-DD'); return dateObj.format('YYYY-MM-DD');
case 'YYYY-MM-DD HH:mm': case 'YYYY-MM-DD HH:mm':
@@ -42,6 +51,8 @@ const getDateDisplayString = (date, format) => {
return dateObj.format('DD.MM.YYYY'); return dateObj.format('DD.MM.YYYY');
case 'DD.MM.YYYY HH:mm': case 'DD.MM.YYYY HH:mm':
return dateObj.format('DD.MM.YYYY HH:mm'); return dateObj.format('DD.MM.YYYY HH:mm');
case 'DD.MM.YYYY HH:mm:ss':
return dateObj.format('DD.MM.YYYY HH:mm:ss');
case 'YYYY': case 'YYYY':
return dateObj.format('YYYY'); return dateObj.format('YYYY');
case 'YYYY-MM': case 'YYYY-MM':

View File

@@ -200,6 +200,8 @@ export const getColumnDisplayName = (key, name) => {
return gettext('Size'); return gettext('Size');
case PRIVATE_COLUMN_KEY.FILE_DETAILS: case PRIVATE_COLUMN_KEY.FILE_DETAILS:
return gettext('File details'); return gettext('File details');
case PRIVATE_COLUMN_KEY.SHOOTING_TIME:
return gettext('Shooting time');
default: default:
return name; return name;
} }

View File

@@ -100,6 +100,7 @@ const HeaderDropdownMenu = ({ column, view, renameColumn, modifyColumnData, dele
}, [column, renameColumn]); }, [column, renameColumn]);
const renderDateFormat = useCallback((canModifyColumnData) => { const renderDateFormat = useCallback((canModifyColumnData) => {
const { data = {} } = column;
if (!canModifyColumnData) { if (!canModifyColumnData) {
return ( return (
<DropdownItem <DropdownItem
@@ -111,15 +112,14 @@ const HeaderDropdownMenu = ({ column, view, renameColumn, modifyColumnData, dele
/> />
); );
} }
const { data = {} } = column;
const { format = DEFAULT_DATE_FORMAT } = data; const { format = DEFAULT_DATE_FORMAT } = data;
const withMinutes = format.indexOf('HH:mm') > -1; let timeUnit = format.split(' ')[1];
const options = [ const options = [
{ label: `${gettext('ISO')} (${getDateDisplayString(today, classnames('YYYY-MM-DD', { 'HH:mm': withMinutes }))})`, value: classnames('YYYY-MM-DD', { 'HH:mm': withMinutes }) }, { label: `${gettext('ISO')} (${getDateDisplayString(today, classnames('YYYY-MM-DD', timeUnit))})`, value: classnames('YYYY-MM-DD', timeUnit) },
{ label: `${gettext('US')} (${getDateDisplayString(today, classnames('M/D/YYYY', { 'HH:mm': withMinutes }))})`, value: classnames('M/D/YYYY', { 'HH:mm': withMinutes }) }, { label: `${gettext('US')} (${getDateDisplayString(today, classnames('M/D/YYYY', timeUnit))})`, value: classnames('M/D/YYYY', timeUnit) },
{ label: `${gettext('European')} (${getDateDisplayString(today, classnames('DD/MM/YYYY', { 'HH:mm': withMinutes }))})`, value: classnames('DD/MM/YYYY', { 'HH:mm': withMinutes }) }, { label: `${gettext('European')} (${getDateDisplayString(today, classnames('DD/MM/YYYY', timeUnit))})`, value: classnames('DD/MM/YYYY', timeUnit) },
{ label: `${gettext('Germany Russia etc')} (${getDateDisplayString(today, classnames('DD.MM.YYYY', { 'HH:mm': withMinutes }))})`, value: classnames('DD.MM.YYYY', { 'HH:mm': withMinutes }) } { label: `${gettext('Germany Russia etc')} (${getDateDisplayString(today, classnames('DD.MM.YYYY', timeUnit))})`, value: classnames('DD.MM.YYYY', timeUnit) }
]; ];
return ( return (

View File

@@ -2,7 +2,7 @@ import { Utils } from '../../../../utils/utils';
import { getCellValueByColumn } from '../../../utils/cell'; import { getCellValueByColumn } from '../../../utils/cell';
import { getGroupByPath } from '../../../utils/view'; import { getGroupByPath } from '../../../utils/view';
import { getColumnByIndex, canEditCell } from '../../../utils/column'; import { getColumnByIndex, canEditCell } from '../../../utils/column';
import { SUPPORT_PREVIEW_COLUMN_TYPES, metadataZIndexes } from '../../../constants'; import { PRIVATE_COLUMN_KEY, SUPPORT_PREVIEW_COLUMN_TYPES, metadataZIndexes } from '../../../constants';
import { getGroupRecordByIndex } from './group-metrics'; import { getGroupRecordByIndex } from './group-metrics';
const SELECT_DIRECTION = { const SELECT_DIRECTION = {
@@ -45,8 +45,13 @@ export const isSelectedCellEditable = ({ enableCellSelect, selectedPosition, col
const column = getSelectedColumn({ selectedPosition, columns }); const column = getSelectedColumn({ selectedPosition, columns });
const row = getSelectedRow({ selectedPosition, isGroupView, recordGetterByIndex }); const row = getSelectedRow({ selectedPosition, isGroupView, recordGetterByIndex });
if (!window.sfMetadataContext.canModifyRow(row)) return false; if (!window.sfMetadataContext.canModifyRow(row)) return false;
const isCellEditable = Utils.isFunction(onCheckCellIsEditable) ? onCheckCellIsEditable({ row, column, ...selectedPosition }) : true; let isCellEditable = Utils.isFunction(onCheckCellIsEditable) ? onCheckCellIsEditable({ row, column, ...selectedPosition }) : true;
return isCellEditable && canEditCell(column, row, enableCellSelect); const fileName = row ? row[PRIVATE_COLUMN_KEY.FILE_NAME] : '';
const imageRow = row && (Utils.imageCheck(fileName) || Utils.videoCheck(fileName));
isCellEditable = isCellEditable && canEditCell(column, row, enableCellSelect);
if (imageRow) return isCellEditable;
if (column?.key === PRIVATE_COLUMN_KEY.SHOOTING_TIME) return false;
return isCellEditable;
}; };
export function selectedRangeIsSingleCell(selectedRange) { export function selectedRangeIsSingleCell(selectedRange) {