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/resumablejs": "1.1.16",
"@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/sf-metadata-ui-component": "^0.0.32",
"@seafile/sf-metadata-ui-component": "^0.0.33",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.4",
@@ -4969,19 +4969,24 @@
}
},
"node_modules/@seafile/seafile-calendar": {
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.12.tgz",
"integrity": "sha512-TvttWwuZqDz2+Tvy4k3SeXDRbAjWmIw5BvJVeZkEzwuuFz2DB9TYucwT0tAWSSdzuRcD5jr42Y0V09D54Lig+Q==",
"version": "0.0.25",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.25.tgz",
"integrity": "sha512-d6whp7NfKTjD021AEZYaTTrAipKsWqE736b3r1s6ID6XLxPsBitoGgbzeMK3wDLq0iBPQi9MokHif+8L3Kxq7g==",
"dependencies": {
"babel-runtime": "6.x",
"classnames": "2.x",
"moment": "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/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": {
"version": "1.0.116",
"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": {
"version": "0.0.32",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.32.tgz",
"integrity": "sha512-7Np8syp2YoauqaDhd6i6VXalA1XLAmvertdz/Uo1tvFl5xZTECjbi1ywjEosJqM6ej5W6TfvXjc/yvGDwxXoxQ==",
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.33.tgz",
"integrity": "sha512-utCWgOCNPGr5w56+I+SfB60XJA1Amxfo8jfHELHiJG+hzp5bKNXvq6XQ9ampS/rVzzvwUFuCahOURyiQOx9hKw==",
"dependencies": {
"@seafile/seafile-calendar": "0.0.24",
"@seafile/seafile-calendar": "0.0.25",
"@seafile/seafile-editor": "~1.0.102",
"classnames": "2.3.2",
"dayjs": "1.10.7",
@@ -5120,20 +5125,6 @@
"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": {
"version": "2.3.2",
"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/resumablejs": "1.1.16",
"@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/sf-metadata-ui-component": "^0.0.32",
"@seafile/sf-metadata-ui-component": "^0.0.33",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4",
"axios": "^1.7.4",

View File

@@ -103,10 +103,15 @@ const MetadataDetails = ({ repoID, filePath, repoInfo, direntType }) => {
if (isLoading) return null;
const { fields, record } = metadata;
if (!record._id) return null;
const fileName = record[PRIVATE_COLUMN_KEY.FILE_NAME];
const isImage = record && (Utils.imageCheck(fileName) || Utils.videoCheck(fileName));
return (
<>
{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);
return (
<DetailItem key={field.key} field={field} readonly={!canEdit}>

View File

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

View File

@@ -59,6 +59,14 @@ const COLUMNS = [
key: PRIVATE_COLUMN_KEY.FILE_STATUS,
canChangeName: false,
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],
type: CellType.TEXT,

View File

@@ -26,6 +26,7 @@ export const PRIVATE_COLUMN_KEY = {
SIZE: '_size',
SUFFIX: '_suffix',
FILE_DETAILS: '_file_details',
SHOOTING_TIME: '_shooting_time',
};
export const PRIVATE_COLUMN_KEYS = [
@@ -53,6 +54,7 @@ export const PRIVATE_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.SIZE,
PRIVATE_COLUMN_KEY.SUFFIX,
PRIVATE_COLUMN_KEY.FILE_DETAILS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
];
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_EXPIRED,
PRIVATE_COLUMN_KEY.FILE_STATUS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
];
export const EDITABLE_DATA_PRIVATE_COLUMN_KEYS = [
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
];
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_EXPIRED,
PRIVATE_COLUMN_KEY.FILE_STATUS,
PRIVATE_COLUMN_KEY.SHOOTING_TIME,
];

View File

@@ -1,6 +1,6 @@
import metadataAPI from './api';
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';
import LocalStorage from './utils/local-storage';
import EventBus from '../components/common/event-bus';
@@ -133,7 +133,7 @@ class Context {
canDeleteColumn = (column) => {
if (this.permission === 'r') return false;
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;
};

View File

@@ -27,10 +27,19 @@ const getDateDisplayString = (date, format) => {
const formatDateList = formatValuesList[0].split('-');
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':
return dateObj.format('M/D/YYYY');
case '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':
return dateObj.format('YYYY-MM-DD');
case 'YYYY-MM-DD HH:mm':
@@ -42,6 +51,8 @@ const getDateDisplayString = (date, format) => {
return dateObj.format('DD.MM.YYYY');
case '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':
return dateObj.format('YYYY');
case 'YYYY-MM':

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ import { Utils } from '../../../../utils/utils';
import { getCellValueByColumn } from '../../../utils/cell';
import { getGroupByPath } from '../../../utils/view';
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';
const SELECT_DIRECTION = {
@@ -45,8 +45,13 @@ export const isSelectedCellEditable = ({ enableCellSelect, selectedPosition, col
const column = getSelectedColumn({ selectedPosition, columns });
const row = getSelectedRow({ selectedPosition, isGroupView, recordGetterByIndex });
if (!window.sfMetadataContext.canModifyRow(row)) return false;
const isCellEditable = Utils.isFunction(onCheckCellIsEditable) ? onCheckCellIsEditable({ row, column, ...selectedPosition }) : true;
return isCellEditable && canEditCell(column, row, enableCellSelect);
let isCellEditable = Utils.isFunction(onCheckCellIsEditable) ? onCheckCellIsEditable({ row, column, ...selectedPosition }) : true;
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) {