1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-26 15:26:19 +00:00
This commit is contained in:
杨国璇
2025-01-08 14:40:28 +08:00
parent 89b5100102
commit ff44d97405
30 changed files with 563 additions and 604 deletions

View File

@@ -9,7 +9,7 @@ import { siteRoot, gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { PRIVATE_FILE_TYPE } from '../../constants';
import { debounce } from '../../metadata/utils/common';
import { EVENT_BUS_TYPE, FACE_RECOGNITION_VIEW_ID } from '../../metadata/constants';
import { EVENT_BUS_TYPE } from '../../metadata/constants';
import { ALL_TAGS_ID } from '../../tag/constants';
const propTypes = {
@@ -126,13 +126,12 @@ class DirPath extends React.Component {
turnViewPathToLink = (pathList) => {
if (!Array.isArray(pathList) || pathList.length === 0) return null;
const [, , viewId, children] = pathList;
const isViewSupportClick = viewId === FACE_RECOGNITION_VIEW_ID && children;
return (
<>
<span className="path-split">/</span>
<span className="path-item">{gettext('Views')}</span>
<span className="path-split">/</span>
<span className="path-item" role={isViewSupportClick ? 'button' : null} onClick={isViewSupportClick ? this.handleRefresh : () => {}}>
<span className="path-item" role={children ? 'button' : null} onClick={children ? this.handleRefresh : () => {}}>
<MetadataViewName id={viewId} />
</span>
{children && (

View File

@@ -0,0 +1,37 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, STORAGE_GALLERY_DATE_MODE_KEY } from '../../constants';
import { gettext } from '../../../utils/constants';
import RadioGroup from '../radio-group';
const DATE_MODES = [
{ value: GALLERY_DATE_MODE.YEAR, label: gettext('Year') },
{ value: GALLERY_DATE_MODE.MONTH, label: gettext('Month') },
{ value: GALLERY_DATE_MODE.DAY, label: gettext('Day') },
{ value: GALLERY_DATE_MODE.ALL, label: gettext('All') },
];
const GalleryGroupBySetter = ({ view }) => {
const [currentMode, setCurrentMode] = useState(GALLERY_DATE_MODE.DAY);
useEffect(() => {
const savedValue = window.sfMetadataContext.localStorage.getItem(STORAGE_GALLERY_DATE_MODE_KEY) || GALLERY_DATE_MODE.DAY;
setCurrentMode(savedValue);
}, [view?._id]);
const handleGroupByChange = useCallback((newMode) => {
if (currentMode === newMode) return;
setCurrentMode(newMode);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY, newMode);
}, [currentMode]);
return (<RadioGroup value={currentMode} options={DATE_MODES} onChange={handleGroupByChange} />);
};
GalleryGroupBySetter.propTypes = {
view: PropTypes.shape({
_id: PropTypes.string
})
};
export default GalleryGroupBySetter;

View File

@@ -1,57 +0,0 @@
.metadata-gallery-group-by-setter {
width: 272px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #e2e2e2;
border-radius: 3px;
}
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button {
width: 66px;
height: 28px;
color: #212529;
background-color: #fff;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
border: 0;
border-radius: 2px;
}
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button:hover {
background-color: #f0f0f0;
cursor: pointer;
}
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button.active {
background-color: #f5f5f5;
}
.metadata-gallery-group-by-setter .metadata-gallery-group-by-button span {
display: block;
text-align: center;
width: 100%;
}
.metadata-gallery-group-by-button:not(:first-child)::before {
content: '';
width: 1px;
height: 22px;
background-color: #e2e2e2;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: opacity 0.3s;
left: -1px;
}
.metadata-gallery-group-by-button:hover::before,
.metadata-gallery-group-by-button.active::before,
.metadata-gallery-group-by-button:hover + .metadata-gallery-group-by-button::before,
.metadata-gallery-group-by-button.active + .metadata-gallery-group-by-button::before {
opacity: 0;
}

View File

@@ -1,51 +0,0 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE } from '../../../constants';
import { gettext } from '../../../../utils/constants';
import './index.css';
const DATE_MODE_MAP = {
[GALLERY_DATE_MODE.YEAR]: gettext('Year'),
[GALLERY_DATE_MODE.MONTH]: gettext('Month'),
[GALLERY_DATE_MODE.DAY]: gettext('Day'),
[GALLERY_DATE_MODE.ALL]: gettext('All')
};
const GalleryGroupBySetter = ({ view }) => {
const [currentMode, setCurrentMode] = useState(GALLERY_DATE_MODE.DAY);
useEffect(() => {
const savedValue = window.sfMetadataContext.localStorage.getItem('gallery-group-by', GALLERY_DATE_MODE.DAY);
setCurrentMode(savedValue || GALLERY_DATE_MODE.DAY);
}, [view?._id]);
const handleGroupByChange = useCallback((newMode) => {
setCurrentMode(newMode);
window.sfMetadataContext.localStorage.setItem('gallery-group-by', newMode);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY, newMode);
}, []);
return (
<div className="metadata-gallery-group-by-setter">
{Object.entries(DATE_MODE_MAP).map(([dateMode, label]) => (
<button
key={dateMode}
className={classnames('metadata-gallery-group-by-button', { active: currentMode === dateMode })}
onClick={() => handleGroupByChange(dateMode)}
>
<span>{label}</span>
</button>
))}
</div>
);
};
GalleryGroupBySetter.propTypes = {
view: PropTypes.shape({
_id: PropTypes.string
})
};
export default GalleryGroupBySetter;

View File

@@ -1,4 +1,4 @@
import GalleryGroupBySetter from './gallery-group-by-setter/index';
import GalleryGroupBySetter from './gallery-group-by-setter';
import GallerySliderSetter from './gallery-slider-setter/index';
import FilterSetter from './filter-setter';
import SortSetter from './sort-setter';

View File

@@ -0,0 +1,35 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { EVENT_BUS_TYPE, MAP_TYPE, STORAGE_MAP_TYPE_KEY } from '../../constants';
import { gettext } from '../../../utils/constants';
import RadioGroup from '../radio-group';
const MAP_TYPES = [
{ value: MAP_TYPE.MAP, label: gettext('Map') },
{ value: MAP_TYPE.SATELLITE, label: gettext('Satellite') },
];
const MapTypeSetter = ({ view }) => {
const [currentType, setCurrentType] = useState(MAP_TYPE.MAP);
useEffect(() => {
const type = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_TYPE_KEY) || MAP_TYPE.MAP;
setCurrentType(type);
}, [view?._id]);
const onChange = useCallback((type) => {
if (currentType === type) return;
setCurrentType(type);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, type);
}, [currentType]);
return (<RadioGroup options={MAP_TYPES} value={currentType} onChange={onChange} />);
};
MapTypeSetter.propTypes = {
view: PropTypes.shape({
_id: PropTypes.string
})
};
export default MapTypeSetter;

View File

@@ -1,57 +0,0 @@
.metadata-map-type-setter {
width: fit-content;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #e2e2e2;
border-radius: 3px;
}
.metadata-map-type-setter .metadata-map-type-button {
width: 66px;
height: 28px;
color: #212529;
background-color: #fff;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
border: 0;
border-radius: 2px;
}
.metadata-map-type-setter .metadata-map-type-button:hover {
background-color: #f0f0f0;
cursor: pointer;
}
.metadata-map-type-setter .metadata-map-type-button.active {
background-color: #f5f5f5;
}
.metadata-map-type-setter .metadata-map-type-button span {
display: block;
text-align: center;
width: 100%;
}
.metadata-map-type-button:not(:first-child)::before {
content: '';
width: 1px;
height: 22px;
background-color: #e2e2e2;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: opacity 0.3s;
left: -1px;
}
.metadata-map-type-button:hover::before,
.metadata-map-type-button.active::before,
.metadata-map-type-button:hover + .metadata-map-type-button::before,
.metadata-map-type-button.active + .metadata-map-type-button::before {
opacity: 0;
}

View File

@@ -1,49 +0,0 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { EVENT_BUS_TYPE, MAP_TYPE } from '../../../constants';
import { gettext } from '../../../../utils/constants';
import './index.css';
const TYPE_MAP = {
[MAP_TYPE.MAP]: gettext('Map'),
[MAP_TYPE.SATELLITE]: gettext('Satellite')
};
const MapTypeSetter = ({ view }) => {
const [currentType, setCurrentType] = useState(MAP_TYPE.MAP);
useEffect(() => {
const savedValue = window.sfMetadataContext.localStorage.getItem('map-type', MAP_TYPE.MAP);
setCurrentType(savedValue || MAP_TYPE.MAP);
}, [view?._id]);
const handleTypeChange = useCallback((newType) => {
setCurrentType(newType);
window.sfMetadataContext.localStorage.setItem('map-type', newType);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, newType);
}, []);
return (
<div className="metadata-map-type-setter">
{Object.entries(TYPE_MAP).map(([type, label]) => (
<button
key={type}
className={classnames('metadata-map-type-button', { active: currentType === type })}
onClick={() => handleTypeChange(type)}
>
<span>{label}</span>
</button>
))}
</div>
);
};
MapTypeSetter.propTypes = {
view: PropTypes.shape({
_id: PropTypes.string
})
};
export default MapTypeSetter;

View File

@@ -0,0 +1,54 @@
.sf-metadata-radio-group {
width: fit-content;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #e2e2e2;
border-radius: 3px;
padding: 0 3px;
}
.sf-metadata-radio-group .sf-metadata-radio-group-option {
min-width: 66px;
width: fit-content;
height: 28px;
color: #212529;
background-color: #fff;
position: relative;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
border: 0;
border-radius: 2px;
}
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover {
background-color: #f0f0f0;
cursor: pointer;
}
.sf-metadata-radio-group .sf-metadata-radio-group-option.active {
background-color: #f5f5f5;
}
.sf-metadata-radio-group .sf-metadata-radio-group-option:not(:first-child)::before {
content: '';
width: 1px;
height: 22px;
background-color: #e2e2e2;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: opacity 0.3s;
left: -1px;
}
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover::before,
.sf-metadata-radio-group .sf-metadata-radio-group-option.active::before,
.sf-metadata-radio-group .sf-metadata-radio-group-option:hover + .sf-metadata-radio-group-option::before,
.sf-metadata-radio-group .sf-metadata-radio-group-option.active + .sf-metadata-radio-group-option::before {
opacity: 0;
}

View File

@@ -0,0 +1,46 @@
import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import './index.css';
const RadioGroup = ({ value, options, className, onChange: onChangeAPI }) => {
const selected = useMemo(() => {
const selectedOption = options.find(o => value === o.value) || options[0];
return selectedOption.value;
}, [value, options]);
const onChange = useCallback((event) => {
const newValue = event.target.dataset.option;
if (selected === newValue) return;
onChangeAPI(newValue);
}, [selected, onChangeAPI]);
return (
<div className={classnames('sf-metadata-radio-group', className)}>
{options.map(option => {
const { value, label } = option;
return (
<div
key={value}
data-option={value}
className={classnames('sf-metadata-radio-group-option', { 'active': value === selected })}
onClick={onChange}
>
{label}
</div>
);
})}
</div>
);
};
RadioGroup.propTypes = {
value: PropTypes.string,
options: PropTypes.array,
className: PropTypes.string,
onChange: PropTypes.func,
};
export default RadioGroup;

View File

@@ -17,17 +17,17 @@ const FaceRecognitionViewToolbar = ({ readOnly, isCustomPermission, onToggleDeta
setShow(isShow);
}, []);
const setRecognitionView = useCallback(view => {
const resetView = useCallback(view => {
setView(view);
}, []);
const modifySorts = useCallback((sorts) => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW_CHANGE, { sorts });
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, { sorts });
}, []);
useEffect(() => {
const unsubscribeToggle = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, onToggle);
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW, setRecognitionView);
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.RESET_VIEW, resetView);
return () => {
unsubscribeToggle && unsubscribeToggle();
unsubscribeView && unsubscribeView();

View File

@@ -94,8 +94,8 @@ const ViewToolBar = ({ viewId, isCustomPermission, onToggleDetail, onCloseDetail
)}
{viewType === VIEW_TYPE.FACE_RECOGNITION && (
<FaceRecognitionViewToolbar
readOnly={readOnly}
isCustomPermission={isCustomPermission}
view={view}
onToggleDetail={onToggleDetail}
/>
)}
@@ -113,8 +113,9 @@ const ViewToolBar = ({ viewId, isCustomPermission, onToggleDetail, onCloseDetail
)}
{viewType === VIEW_TYPE.MAP && (
<MapViewToolBar
isCustomPermission={isCustomPermission}
readOnly={readOnly}
isCustomPermission={isCustomPermission}
viewID={view._id}
collaborators={collaborators}
modifyFilters={modifyFilters}
onToggleDetail={onToggleDetail}

View File

@@ -7,6 +7,7 @@ import { gettext } from '../../../../utils/constants';
const MapViewToolBar = ({
isCustomPermission,
readOnly,
viewID,
collaborators,
modifyFilters,
onToggleDetail,
@@ -14,7 +15,7 @@ const MapViewToolBar = ({
const [showGalleryToolbar, setShowGalleryToolbar] = useState(false);
const [view, setView] = useState({});
const viewType = useMemo(() => view.type, [view]);
const viewType = useMemo(() => VIEW_TYPE.MAP, []);
const viewColumns = useMemo(() => {
if (!view) return [];
return view.columns;
@@ -29,33 +30,34 @@ const MapViewToolBar = ({
}, []);
const modifySorts = useCallback((sorts) => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_GALLERY_VIEW_CHANGE, { sorts });
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, { sorts });
}, []);
const setMapView = useCallback(view => setView(view), []);
const resetView = useCallback(view => {
setView(view);
}, []);
useEffect(() => {
const unsubscribeToggleViewToolbarMode = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, onToggle);
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MAP_VIEW, setMapView);
const unsubscribeToggle = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, onToggle);
const unsubscribeView = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.RESET_VIEW, resetView);
return () => {
unsubscribeToggleViewToolbarMode();
unsubscribeView();
unsubscribeToggle && unsubscribeToggle();
unsubscribeView && unsubscribeView();
};
}, [setMapView, onToggle]);
useEffect(() => {
setView(window.sfMetadataStore.data.view);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
setShowGalleryToolbar(false);
}, [viewID]);
if (showGalleryToolbar) {
return (
<>
{showGalleryToolbar ? (
<div className="sf-metadata-tool-left-operations">
<>
<GalleryGroupBySetter view={{ _id: view._id }} />
<GallerySliderSetter view={{ _id: view._id }} />
<GalleryGroupBySetter view={{ _id: viewID }} />
<GallerySliderSetter view={{ _id: viewID }} />
<SortSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
target="sf-metadata-sort-popover"
readOnly={readOnly}
@@ -64,14 +66,18 @@ const MapViewToolBar = ({
columns={viewColumns}
modifySorts={modifySorts}
/>
</>
{!isCustomPermission && (
<div className="cur-view-path-btn ml-2" onClick={onToggleDetail}>
<span className="sf3-font sf3-font-info" aria-label={gettext('Properties')} title={gettext('Properties')}></span>
</div>
)}
</div>
) :
<div className="sf-metadata-tool-right-operations"></div>
</>
);
}
return (
<>
<div className="sf-metadata-tool-left-operations">
<MapTypeSetter view={view} />
@@ -91,7 +97,6 @@ const MapViewToolBar = ({
/>
</div>
<div className="sf-metadata-tool-right-operations"></div>
</>}
</>
);
};

View File

@@ -60,22 +60,21 @@ export const EVENT_BUS_TYPE = {
SAVED: 'saved',
ERROR: 'error',
// view
TOGGLE_VIEW_TOOLBAR: 'toggle_view_toolbar',
RESET_VIEW: 'reset_view',
UPDATE_SERVER_VIEW: 'update_server_view',
// gallery
MODIFY_GALLERY_ZOOM_GEAR: 'modify_gallery_zoom_gear',
SWITCH_GALLERY_GROUP_BY: 'switch_gallery_group_by',
// face recognition
TOGGLE_VIEW_TOOLBAR: 'toggle_view_toolbar',
FACE_RECOGNITION_VIEW: 'face_recognition_view',
FACE_RECOGNITION_VIEW_CHANGE: 'face_recognition_view_change',
// kanban
TOGGLE_KANBAN_SETTINGS: 'toggle_kanban_settings',
OPEN_KANBAN_SETTINGS: 'open_kanban_settings',
CLOSE_KANBAN_SETTINGS: 'close_kanban_settings',
// map
SWITCH_MAP_TYPE: 'switch_map_type',
TOGGLE_MAP_VIEW_TOOLBAR: 'toggle_map_view_toolbar',
MODIFY_MAP_TYPE: 'modify_map_type',
MAP_VIEW: 'map_view',
};

View File

@@ -1,4 +1,3 @@
import CellType from './column/type';
import { EVENT_BUS_TYPE } from './event-bus-type';
import TRANSFER_TYPES from './TransferTypes';
import * as metadataZIndexes from './z-index';
@@ -13,147 +12,8 @@ export * from './sort';
export * from './error';
export * from './view';
export const CELL_NAVIGATION_MODE = {
NONE: 'none',
CHANGE_ROW: 'changeRow',
LOOP_OVER_ROW: 'loopOverRow',
};
export const SEQUENCE_COLUMN_WIDTH = 80;
export const ROW_HEIGHT = 32;
export const GRID_HEADER_DEFAULT_HEIGHT = 32;
export const GRID_HEADER_DOUBLE_HEIGHT = 56;
export const GROUP_VIEW_OFFSET = 16;
export const GROUP_HEADER_HEIGHT = 48;
export const TABLE_LEFT_MARGIN = 10;
export const TABLE_BORDER_WIDTH = 1;
export const UNABLE_TO_CALCULATE = '--';
export const FROZEN_COLUMN_SHADOW = '2px 0 5px -2px hsla(0,0%,53.3%,.3)';
export const TABLE_NOT_SUPPORT_EDIT_TYPE_MAP = {
[CellType.CREATOR]: true,
[CellType.LAST_MODIFIER]: true,
[CellType.CTIME]: true,
[CellType.MTIME]: true,
[CellType.FILE_NAME]: true,
};
export const TABLE_SUPPORT_EDIT_TYPE_MAP = {
[CellType.TEXT]: true,
[CellType.DATE]: true,
[CellType.NUMBER]: true,
[CellType.SINGLE_SELECT]: true,
[CellType.MULTIPLE_SELECT]: true,
[CellType.COLLABORATOR]: true,
[CellType.CHECKBOX]: true,
[CellType.LONG_TEXT]: true,
[CellType.LINK]: true,
[CellType.TAGS]: true,
};
export const TABLE_MOBILE_SUPPORT_EDIT_CELL_TYPE_MAP = {
[CellType.TEXT]: true,
};
export const CANVAS_RIGHT_INTERVAL = 44;
export const LEFT_NAV = 280;
export const ROW_DETAIL_PADDING = 40 * 2;
export const ROW_DETAIL_MARGIN = 20 * 2;
export const EDITOR_PADDING = 1.5 * 16; // 1.5: 0.75 * 2
export const COLUMN_RATE_MAX_NUMBER = [
{ name: 1 },
{ name: 2 },
{ name: 3 },
{ name: 4 },
{ name: 5 },
{ name: 6 },
{ name: 7 },
{ name: 8 },
{ name: 9 },
{ name: 10 },
];
export const GROUP_ROW_TYPE = {
GROUP_CONTAINER: 'group_container',
ROW: 'row',
BTN_INSERT_ROW: 'btn_insert_row',
};
export const INSERT_ROW_HEIGHT = 32;
export const CHANGE_HEADER_WIDTH = 'CHANGE_HEADER_WIDTH';
export const NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES = [
];
export const SUPPORT_PREVIEW_COLUMN_TYPES = [];
export const OVER_SCAN_COLUMNS = 10;
export const DELETED_OPTION_BACKGROUND_COLOR = '#eaeaea';
export const DELETED_OPTION_TIPS = 'deleted_option';
export const SUPPORT_BATCH_DOWNLOAD_TYPES = [];
export const PER_LOAD_NUMBER = 1000;
export const DEFAULT_RETRY_TIMES = 4;
export const DEFAULT_RETRY_INTERVAL = 1000;
export const MAX_LOAD_NUMBER = 10000;
export const EDITOR_TYPE = {
PREVIEWER: 'previewer',
ADDITION: 'addition',
};
export {
EVENT_BUS_TYPE,
TRANSFER_TYPES,
metadataZIndexes,
};
export const DATE_TAG_HEIGHT = 44;
export const GALLERY_ZOOM_GEAR_MIN = -2;
export const GALLERY_ZOOM_GEAR_MAX = 2;
export const GALLERY_IMAGE_GAP = 2;
export const GALLERY_DATE_MODE = {
YEAR: 'year',
MONTH: 'month',
DAY: 'day',
ALL: 'all',
};
export const MAP_TYPE = {
NORMAL_MAP: 'normal_map',
SATELLITE: 'satellite',
};
export const MAP_VIEW_TOOLBAR_MODE = {
MAP: 'map',
GALLERY: 'gallery',
};
export const UNCATEGORIZED = '_uncategorized';
export const PASTE_SOURCE = {
COPY: 'copy',
CUT: 'cut',
};

View File

@@ -0,0 +1,16 @@
export const DATE_TAG_HEIGHT = 44;
export const GALLERY_ZOOM_GEAR_MIN = -2;
export const GALLERY_ZOOM_GEAR_MAX = 2;
export const GALLERY_IMAGE_GAP = 2;
export const GALLERY_DATE_MODE = {
YEAR: 'year',
MONTH: 'month',
DAY: 'day',
ALL: 'all',
};
export const STORAGE_GALLERY_DATE_MODE_KEY = 'gallery_date_mode';

View File

@@ -1,8 +1,13 @@
import { PRIVATE_COLUMN_KEY } from './column';
import { FILTER_PREDICATE_TYPE } from './filter';
import { PRIVATE_COLUMN_KEY } from '../column';
import { FILTER_PREDICATE_TYPE } from '../filter';
import { SORT_COLUMN_OPTIONS, GALLERY_SORT_COLUMN_OPTIONS, GALLERY_FIRST_SORT_COLUMN_OPTIONS, SORT_TYPE,
GALLERY_SORT_PRIVATE_COLUMN_KEYS, GALLERY_FIRST_SORT_PRIVATE_COLUMN_KEYS,
} from './sort';
} from '../sort';
export * from './gallery';
export * from './kanban';
export * from './map';
export * from './table';
export const METADATA_VIEWS_KEY = 'sf-metadata-views';

View File

@@ -0,0 +1 @@
export const UNCATEGORIZED = '_uncategorized';

View File

@@ -0,0 +1,15 @@
export const MAP_TYPE = {
MAP: 'map',
SATELLITE: 'satellite',
};
export const STORAGE_MAP_TYPE_KEY = 'map_type';
export const STORAGE_MAP_CENTER_KEY = 'map_center';
export const STORAGE_MAP_ZOOM_KEY = 'map_zoom';
export const MAP_VIEW_TOOLBAR_MODE = {
MAP: 'map',
GALLERY: 'gallery',
};

View File

@@ -0,0 +1,101 @@
import { CellType } from '../column';
export const CELL_NAVIGATION_MODE = {
NONE: 'none',
CHANGE_ROW: 'changeRow',
LOOP_OVER_ROW: 'loopOverRow',
};
export const SEQUENCE_COLUMN_WIDTH = 80;
export const ROW_HEIGHT = 32;
export const GRID_HEADER_DEFAULT_HEIGHT = 32;
export const GRID_HEADER_DOUBLE_HEIGHT = 56;
export const GROUP_VIEW_OFFSET = 16;
export const GROUP_HEADER_HEIGHT = 48;
export const TABLE_LEFT_MARGIN = 10;
export const TABLE_BORDER_WIDTH = 1;
export const UNABLE_TO_CALCULATE = '--';
export const FROZEN_COLUMN_SHADOW = '2px 0 5px -2px hsla(0,0%,53.3%,.3)';
export const TABLE_NOT_SUPPORT_EDIT_TYPE_MAP = {
[CellType.CREATOR]: true,
[CellType.LAST_MODIFIER]: true,
[CellType.CTIME]: true,
[CellType.MTIME]: true,
[CellType.FILE_NAME]: true,
};
export const TABLE_SUPPORT_EDIT_TYPE_MAP = {
[CellType.TEXT]: true,
[CellType.DATE]: true,
[CellType.NUMBER]: true,
[CellType.SINGLE_SELECT]: true,
[CellType.MULTIPLE_SELECT]: true,
[CellType.COLLABORATOR]: true,
[CellType.CHECKBOX]: true,
[CellType.LONG_TEXT]: true,
[CellType.LINK]: true,
[CellType.TAGS]: true,
};
export const TABLE_MOBILE_SUPPORT_EDIT_CELL_TYPE_MAP = {
[CellType.TEXT]: true,
};
export const CANVAS_RIGHT_INTERVAL = 44;
export const LEFT_NAV = 280;
export const ROW_DETAIL_PADDING = 40 * 2;
export const ROW_DETAIL_MARGIN = 20 * 2;
export const EDITOR_PADDING = 1.5 * 16; // 1.5: 0.75 * 2
export const GROUP_ROW_TYPE = {
GROUP_CONTAINER: 'group_container',
ROW: 'row',
BTN_INSERT_ROW: 'btn_insert_row',
};
export const INSERT_ROW_HEIGHT = 32;
export const CHANGE_HEADER_WIDTH = 'CHANGE_HEADER_WIDTH';
export const NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES = [
];
export const SUPPORT_PREVIEW_COLUMN_TYPES = [];
export const OVER_SCAN_COLUMNS = 10;
export const DELETED_OPTION_BACKGROUND_COLOR = '#eaeaea';
export const DELETED_OPTION_TIPS = 'deleted_option';
export const SUPPORT_BATCH_DOWNLOAD_TYPES = [];
export const PER_LOAD_NUMBER = 1000;
export const DEFAULT_RETRY_TIMES = 4;
export const DEFAULT_RETRY_INTERVAL = 1000;
export const MAX_LOAD_NUMBER = 10000;
export const EDITOR_TYPE = {
PREVIEWER: 'previewer',
ADDITION: 'addition',
};
export const PASTE_SOURCE = {
COPY: 'copy',
CUT: 'cut',
};

View File

@@ -340,6 +340,8 @@ export const MetadataViewProvider = ({
<MetadataViewContext.Provider
value={{
isLoading,
repoID,
viewID,
isBeingBuilt,
errorMessage,
metadata,

View File

@@ -130,7 +130,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
metadata.hasMore = false;
}
setMetadata(metadata);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW, metadata.view);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RESET_VIEW, metadata.view);
setLoading(false);
}).catch(error => {
const errorMessage = Utils.getErrorMsg(error);
@@ -164,7 +164,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
}, []);
useEffect(() => {
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.FACE_RECOGNITION_VIEW_CHANGE, onViewChange);
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, onViewChange);
return () => {
unsubscribeViewChange && unsubscribeViewChange();
};

View File

@@ -30,17 +30,17 @@ const GalleryContextMenu = ({ metadata, selectedImages, onDelete, onDuplicate, a
const options = useMemo(() => {
let validOptions = [{ value: CONTEXT_MENU_KEY.DOWNLOAD, label: gettext('Download') }];
if (checkCanDeleteRow) {
if (onDelete && checkCanDeleteRow) {
validOptions.push({ value: CONTEXT_MENU_KEY.DELETE, label: selectedImages.length > 1 ? gettext('Delete') : gettext('Delete file') });
}
if (canDuplicateRow && selectedImages.length === 1) {
if (onDuplicate && canDuplicateRow && selectedImages.length === 1) {
validOptions.push({ value: CONTEXT_MENU_KEY.DUPLICATE, label: gettext('Duplicate') });
}
if (canRemovePhotoFromPeople) {
if (onRemoveImage && canRemovePhotoFromPeople) {
validOptions.push({ value: CONTEXT_MENU_KEY.REMOVE, label: gettext('Remove from this group') });
}
return validOptions;
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, selectedImages]);
}, [checkCanDeleteRow, canDuplicateRow, canRemovePhotoFromPeople, selectedImages, onDuplicate, onDelete, onRemoveImage]);
const closeZipDialog = () => {
setIsZipDialogOpen(false);

View File

@@ -8,7 +8,7 @@ import { useMetadataView } from '../../hooks/metadata-view';
import { Utils } from '../../../utils/utils';
import { getDateDisplayString, getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell';
import { siteRoot, fileServerRoot, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, GALLERY_IMAGE_GAP } from '../../constants';
import { EVENT_BUS_TYPE, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, GALLERY_IMAGE_GAP, STORAGE_GALLERY_DATE_MODE_KEY } from '../../constants';
import { getRowById } from '../../utils/table';
import { getEventClassName } from '../../utils/common';
import GalleryContextmenu from './context-menu';
@@ -129,14 +129,14 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
const gear = window.sfMetadataContext.localStorage.getItem('zoom-gear', 0) || 0;
setZoomGear(gear);
const mode = window.sfMetadataContext.localStorage.getItem('gallery-group-by', GALLERY_DATE_MODE.DAY) || GALLERY_DATE_MODE.DAY;
const mode = window.sfMetadataContext.localStorage.getItem(STORAGE_GALLERY_DATE_MODE_KEY, GALLERY_DATE_MODE.DAY) || GALLERY_DATE_MODE.DAY;
setMode(mode);
const switchGalleryModeSubscribe = window.sfMetadataContext.eventBus.subscribe(
EVENT_BUS_TYPE.SWITCH_GALLERY_GROUP_BY,
(mode) => {
setMode(mode);
window.sfMetadataContext.localStorage.setItem('gallery-group-by', mode);
window.sfMetadataContext.localStorage.setItem(STORAGE_GALLERY_DATE_MODE_KEY, mode);
}
);
@@ -193,7 +193,7 @@ const Main = ({ isLoadingMore, metadata, onDelete, onLoadMore, duplicateRecord,
if (!containerRef.current) return;
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
if (scrollTop + clientHeight >= scrollHeight - 10) {
onLoadMore();
onLoadMore && onLoadMore();
} else {
const { scrollTop, clientHeight } = containerRef.current;
const overScanTop = Math.max(0, scrollTop - (imageSize + GALLERY_IMAGE_GAP) * OVER_SCAN_ROWS);

View File

@@ -1,54 +1,4 @@
.sf-metadata-map-photos-container {
padding: 0 !important;
overflow-y: hidden !important;
}
.sf-metadata-map-photos-container .sf-metadata-map-photos-header {
height: 48px;
display: flex;
align-items: center;
padding: 0 16px;
}
.sf-metadata-map-photos-container .sf-metadata-icon-btn {
margin-left: -4px;
border-radius: 3px;
}
.sf-metadata-map-photos-container .sf-metadata-icon-btn:hover {
background-color: #EFEFEF;
cursor: pointer;
}
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-photos-header-back {
font-size: 14px;
height: 24px;
min-width: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-left: -5px;
border-radius: 3px;
}
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-photos-header-back:hover {
background-color: #EFEFEF;
cursor: pointer;
}
.sf-metadata-map-photos-container .sf-metadata-map-photos-header-back .sf3-font-arrow {
color: #666;
font-size: 14px !important;
}
.sf-metadata-map-photos-container .sf-metadata-map-photos-header .sf-metadata-map-location {
margin-left: 4px;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sf-metadata-map-photos-container .sf-metadata-gallery-container {
height: calc(100% - 48px);
overflow: hidden !important;
}

View File

@@ -1,67 +1,113 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import deepCopy from 'deep-copy';
import { CenteredLoading } from '@seafile/sf-metadata-ui-component';
import Gallery from '../../gallery/main';
import { gettext } from '../../../../utils/constants';
import { EVENT_BUS_TYPE } from '../../../constants';
import metadataAPI from '../../../api';
import { Utils } from '../../../../utils/utils';
import toaster from '../../../../components/toast';
import { useMetadataView } from '../../../hooks/metadata-view';
import { getRowsByIds } from '../../../utils/table';
import Metadata from '../../../model/metadata';
import { sortTableRows } from '../../../utils/sort';
import { useCollaborators } from '../../../hooks/collaborators';
import './index.css';
const ClusterPhotos = ({ metadata, markerIds, onClose }) => {
const { store, duplicateRecord, addFolder } = useMetadataView();
const ClusterPhotos = ({ markerIds, onClose }) => {
const [isLoading, setLoading] = useState(true);
const [metadata, setMetadata] = useState({ rows: [] });
const repoID = window.sfMetadataContext.getSetting('repoID');
const { repoID, viewID, metadata: allMetadata, store, addFolder, deleteRecords } = useMetadataView();
const { collaborators } = useCollaborators();
const clusterMetadata = useMemo(() => {
const filteredRows = metadata.rows.filter(row => markerIds.includes(row._id));
const rows = useMemo(() => getRowsByIds(allMetadata, markerIds), [allMetadata, markerIds]);
const columns = useMemo(() => allMetadata?.columns || [], [allMetadata]);
const loadData = useCallback((view) => {
setLoading(true);
const orderRows = sortTableRows({ columns }, rows, view?.sorts || [], { collaborators });
let metadata = new Metadata({ rows, columns, view });
metadata.hasMore = false;
metadata.row_ids = orderRows;
metadata.view.rows = orderRows;
setMetadata(metadata);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RESET_VIEW, metadata.view);
setLoading(false);
}, [rows, columns, collaborators]);
const deletedByIds = useCallback((ids) => {
if (!Array.isArray(ids) || ids.length === 0) return;
const newMetadata = deepCopy(metadata);
newMetadata.rows = filteredRows;
return newMetadata;
}, [metadata, markerIds]);
const idNeedDeletedMap = ids.reduce((currIdNeedDeletedMap, rowId) => ({ ...currIdNeedDeletedMap, [rowId]: true }), {});
newMetadata.rows = newMetadata.rows.filter((row) => !idNeedDeletedMap[row._id]);
newMetadata.row_ids = newMetadata.row_ids.filter((id) => !idNeedDeletedMap[id]);
// delete rows in id_row_map
ids.forEach(rowId => {
delete newMetadata.id_row_map[rowId];
});
newMetadata.recordsCount = newMetadata.row_ids.length;
setMetadata(newMetadata);
if (newMetadata.rows.length === 0) {
onClose && onClose();
}
}, [metadata, onClose]);
const handelDelete = useCallback((deletedImages, { success_callback } = {}) => {
if (!deletedImages.length) return;
let recordIds = [];
deletedImages.forEach((record) => recordIds.push(record.id));
store.deleteRecords(recordIds, { success_callback });
}, [store]);
const handleViewChange = useCallback((update) => {
metadataAPI.modifyView(repoID, metadata.view._id, update).then(res => {
deletedImages.forEach((record) => {
const { id, parentDir, name } = record || {};
if (parentDir && name) {
recordIds.push(id);
}
});
deleteRecords(recordIds, {
success_callback: () => {
success_callback();
deletedByIds(recordIds);
}
});
}, [deleteRecords, deletedByIds]);
const onViewChange = useCallback((update) => {
metadataAPI.modifyView(repoID, viewID, update).then(res => {
store.modifyLocalView(update);
const newView = { ...metadata.view, ...update };
loadData(newView);
}).catch(error => {
const errorMessage = Utils.getErrorMsg(error);
toaster.danger(errorMessage);
});
}, [metadata, repoID,]);
}, [metadata, repoID, viewID, store, loadData]);
useEffect(() => {
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MAP_GALLERY_VIEW_CHANGE, handleViewChange);
const unsubscribeViewChange = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SERVER_VIEW, onViewChange);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, true);
return () => {
unsubscribeViewChange();
window?.sfMetadataContext?.eventBus?.dispatch(EVENT_BUS_TYPE.TOGGLE_VIEW_TOOLBAR, false);
};
}, [onViewChange]);
useEffect(() => {
loadData({ sorts: allMetadata.view.sorts });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (isLoading) return (<CenteredLoading />);
return (
<div className="sf-metadata-view-map sf-metadata-map-photos-container">
<div className="sf-metadata-map-photos-header">
<div className="sf-metadata-map-photos-header-back" onClick={onClose}>
<i className="sf3-font sf3-font-arrow rotate-180"></i>
</div>
<div className="sf-metadata-map-location">{gettext('Location')}</div>
</div>
<Gallery metadata={clusterMetadata} onDelete={handelDelete} duplicateRecord={duplicateRecord} onAddFolder={addFolder} />
<Gallery metadata={metadata} onDelete={handelDelete} onAddFolder={addFolder} />
</div>
);
};
ClusterPhotos.propTypes = {
metadata: PropTypes.object,
markerIds: PropTypes.array,
onClose: PropTypes.func,
};

View File

@@ -1,24 +1,26 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { getFileNameFromRecord, getFileTypeFromRecord, getImageLocationFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../utils/cell';
import ClusterPhotos from './cluster-photos';
import Main from './main';
import { EVENT_BUS_TYPE, PREDEFINED_FILE_TYPE_OPTION_KEY } from '../../constants';
import MapView from './map';
import { PREDEFINED_FILE_TYPE_OPTION_KEY } from '../../constants';
import { useMetadataView } from '../../hooks/metadata-view';
import { Utils } from '../../../utils/utils';
import { siteRoot, thumbnailSizeForGrid } from '../../../utils/constants';
import { gettext, siteRoot, thumbnailSizeForGrid } from '../../../utils/constants';
import { isValidPosition } from '../../utils/validate';
import { gcj02_to_bd09, wgs84_to_gcj02 } from '../../../utils/coord-transform';
import { PRIVATE_FILE_TYPE } from '../../../constants';
import './index.css';
const Map = () => {
const [showGallery, setShowGallery] = useState(false);
const [markerIds, setMarkerIds] = useState([]);
const { metadata } = useMetadataView();
const [showCluster, setShowCluster] = useState(false);
const { metadata, viewID, updateCurrentPath } = useMetadataView();
const clusterRef = useRef([]);
const repoID = window.sfMetadataContext.getSetting('repoID');
const validImages = useMemo(() => {
const images = useMemo(() => {
return metadata.rows
.map(record => {
const recordType = getFileTypeFromRecord(record);
@@ -39,31 +41,28 @@ const Map = () => {
.filter(Boolean);
}, [repoID, metadata.rows]);
const openGallery = useCallback((cluster_marker_ids) => {
setMarkerIds(cluster_marker_ids);
setShowGallery(true);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, true);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_VIEW, metadata.view);
}, [metadata.view]);
const openCluster = useCallback((clusterIds) => {
clusterRef.current = clusterIds;
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}/${gettext('Location')}`);
setShowCluster(true);
}, [viewID, updateCurrentPath]);
const closeGallery = useCallback(() => {
setShowGallery(false);
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MAP_VIEW_TOOLBAR, false);
}, []);
const closeCluster = useCallback(() => {
clusterRef.current = [];
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
setShowCluster(false);
}, [viewID, updateCurrentPath]);
useEffect(() => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.MAP_VIEW, metadata.view);
}, [metadata.view]);
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${viewID}`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [viewID]);
return (
<>
{showGallery ? (
<ClusterPhotos metadata={metadata} markerIds={markerIds} onClose={closeGallery} />
) : (
<Main validImages={validImages} onOpen={openGallery} />
)}
</>
);
if (showCluster) {
return (<ClusterPhotos markerIds={clusterRef.current} onClose={closeCluster} />);
}
return (<MapView images={images} onOpenCluster={openCluster} />);
};
export default Map;

View File

@@ -5,7 +5,7 @@ import { appAvatarURL, baiduMapKey, gettext, googleMapKey, mediaUrl } from '../.
import { isValidPosition } from '../../utils/validate';
import { wgs84_to_gcj02, gcj02_to_bd09 } from '../../../utils/coord-transform';
import { MAP_TYPE as MAP_PROVIDER } from '../../../constants';
import { EVENT_BUS_TYPE, MAP_TYPE } from '../../constants';
import { EVENT_BUS_TYPE, MAP_TYPE, STORAGE_MAP_CENTER_KEY, STORAGE_MAP_TYPE_KEY, STORAGE_MAP_ZOOM_KEY } from '../../constants';
import { createBMapGeolocationControl, createBMapZoomControl } from './control';
import { customAvatarOverlay, customImageOverlay } from './overlay';
import toaster from '../../../components/toast';
@@ -14,7 +14,7 @@ const DEFAULT_POSITION = { lng: 104.195, lat: 35.861 };
const DEFAULT_ZOOM = 4;
const BATCH_SIZE = 500;
const Main = ({ validImages, onOpen }) => {
const Main = ({ images, onOpenCluster }) => {
const mapInfo = useMemo(() => initMapInfo({ baiduMapKey, googleMapKey }), []);
const mapRef = useRef(null);
@@ -61,27 +61,28 @@ const Main = ({ validImages, onOpen }) => {
);
}, []);
const getMapType = useCallback((type) => {
if (!mapRef.current) return;
const getBMapType = useCallback((type) => {
switch (type) {
case MAP_TYPE.SATELLITE:
case MAP_TYPE.SATELLITE: {
return window.BMAP_SATELLITE_MAP;
default:
}
default: {
return window.BMAP_NORMAL_MAP;
}
}
}, []);
const saveMapState = useCallback(() => {
if (!mapRef.current) return;
const point = mapRef.current.getCenter && mapRef.current.getCenter();
const zoom = mapRef.current.getZoom && mapRef.current.getZoom();
window.sfMetadataContext.localStorage.setItem('map-center', point);
window.sfMetadataContext.localStorage.setItem('map-zoom', zoom);
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, point);
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_ZOOM_KEY, zoom);
}, []);
const loadMapState = useCallback(() => {
const savedCenter = window.sfMetadataContext.localStorage.getItem('map-center') || DEFAULT_POSITION;
const savedZoom = window.sfMetadataContext.localStorage.getItem('map-zoom') || DEFAULT_ZOOM;
const savedCenter = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_CENTER_KEY) || DEFAULT_POSITION;
const savedZoom = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_ZOOM_KEY) || DEFAULT_ZOOM;
return { center: savedCenter, zoom: savedZoom };
}, []);
@@ -89,18 +90,18 @@ const Main = ({ validImages, onOpen }) => {
saveMapState();
const imageIds = markers.map(marker => marker._imageId);
onOpen(imageIds);
}, [onOpen, saveMapState]);
onOpenCluster(imageIds);
}, [onOpenCluster, saveMapState]);
const renderMarkersBatch = useCallback(() => {
if (!validImages.length || !clusterRef.current) return;
if (!images.length || !clusterRef.current) return;
const startIndex = batchIndexRef.current * BATCH_SIZE;
const endIndex = Math.min(startIndex + BATCH_SIZE, validImages.length);
const endIndex = Math.min(startIndex + BATCH_SIZE, images.length);
const batchMarkers = [];
for (let i = startIndex; i < endIndex; i++) {
const image = validImages[i];
const image = images[i];
const { lng, lat } = image;
const point = new window.BMap.Point(lng, lat);
const marker = customImageOverlay(point, image, {
@@ -110,15 +111,15 @@ const Main = ({ validImages, onOpen }) => {
}
clusterRef.current.addMarkers(batchMarkers);
if (endIndex < validImages.length) {
if (endIndex < images.length) {
batchIndexRef.current += 1;
setTimeout(renderMarkersBatch, 20); // Schedule the next batch
}
}, [validImages, onClickMarker]);
}, [images, onClickMarker]);
const initializeClusterer = useCallback(() => {
const initializeCluster = useCallback(() => {
if (mapRef.current && !clusterRef.current) {
clusterRef.current = new window.BMapLib.MarkerClusterer(mapRef.current, {
clusterRef.current = new window.BMapLib.MarkerCluster(mapRef.current, {
callback: (e, markers) => onClickMarker(e, markers)
});
}
@@ -131,7 +132,7 @@ const Main = ({ validImages, onOpen }) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((userInfo) => {
center = { lng: userInfo.coords.longitude, lat: userInfo.coords.latitude };
window.sfMetadataContext.localStorage.setItem('map-center', center);
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_CENTER_KEY, center);
});
}
if (!isValidPosition(center?.lng, center?.lat)) return;
@@ -145,28 +146,30 @@ const Main = ({ validImages, onOpen }) => {
mapRef.current.centerAndZoom(point, zoom);
mapRef.current.enableScrollWheelZoom(true);
const savedValue = window.sfMetadataContext.localStorage.getItem('map-type');
mapRef.current && mapRef.current.setMapType(getMapType(savedValue));
const savedValue = window.sfMetadataContext.localStorage.getItem(STORAGE_MAP_TYPE_KEY);
mapRef.current && mapRef.current.setMapType(getBMapType(savedValue));
addMapController();
initializeUserMarker();
initializeClusterer();
initializeCluster();
batchIndexRef.current = 0;
renderMarkersBatch();
}, [addMapController, initializeClusterer, initializeUserMarker, renderMarkersBatch, getMapType, loadMapState]);
}, [addMapController, initializeCluster, initializeUserMarker, renderMarkersBatch, getBMapType, loadMapState]);
useEffect(() => {
const switchMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.SWITCH_MAP_TYPE, (newType) => {
window.sfMetadataContext.localStorage.setItem('map-type', newType);
mapRef.current && mapRef.current.setMapType(getMapType(newType));
const modifyMapTypeSubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_MAP_TYPE, (newType) => {
window.sfMetadataContext.localStorage.setItem(STORAGE_MAP_TYPE_KEY, newType);
mapRef.current && mapRef.current.setMapType(getBMapType(newType));
});
return () => {
switchMapTypeSubscribe();
modifyMapTypeSubscribe();
};
}, [getMapType, saveMapState]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (mapInfo.type === MAP_PROVIDER.B_MAP) {
@@ -186,8 +189,8 @@ const Main = ({ validImages, onOpen }) => {
};
Main.propTypes = {
validImages: PropTypes.array,
onOpen: PropTypes.func,
images: PropTypes.array,
onOpenCluster: PropTypes.func,
};
export default Main;

View File

@@ -2,8 +2,7 @@ import dayjs from 'dayjs';
import { getCellValueByColumn, getFileNameFromRecord, getRecordIdFromRecord, isCellValueChanged } from '../../../utils/cell';
import { getColumnByIndex, getColumnOriginName } from '../../../utils/column';
import { CellType, NOT_SUPPORT_DRAG_COPY_COLUMN_TYPES, PRIVATE_COLUMN_KEY, TRANSFER_TYPES,
REG_NUMBER_DIGIT, REG_STRING_NUMBER_PARTS, COLUMN_RATE_MAX_NUMBER,
PASTE_SOURCE,
REG_NUMBER_DIGIT, REG_STRING_NUMBER_PARTS, RATE_MAX_NUMBER, PASTE_SOURCE,
} from '../../../constants';
import { getGroupRecordByIndex } from './group-metrics';
import { convertCellValue } from './convert-utils';
@@ -594,7 +593,7 @@ class GridUtils {
}
_getRatingLeastSquares(numberList, data) {
const { rate_max_number = COLUMN_RATE_MAX_NUMBER[4].name } = data || {};
const { rate_max_number = RATE_MAX_NUMBER[4].name } = data || {};
let slope;
let intercept;
let xAverage;

View File

@@ -1,6 +1,6 @@
/**
* @fileoverview MarkerClusterer标记聚合器用来解决加载大量点要素到地图上产生覆盖现象的问题并提高性能。
* 主入口类是<a href="symbols/BMapLib.MarkerClusterer.html">MarkerClusterer</a>
* 主入口类是<a href="symbols/BMapLib.MarkerCluster.html">MarkerCluster</a>
* 基于Baidu Map API 1.2。
*
* @author Baidu Map Api Group
@@ -97,11 +97,11 @@ var BMapLib = window.BMapLib = BMapLib || {};
};
/**
*@exports MarkerClusterer as BMapLib.MarkerClusterer
*@exports MarkerCluster as BMapLib.MarkerCluster
*/
var MarkerClusterer =
var MarkerCluster =
/**
* MarkerClusterer
* MarkerCluster
* @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
* @constructor
* @param {Map} map 地图的一个实例。
@@ -113,7 +113,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* isAverangeCenter {Boolean} 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点<br />
* styles {Array<IconStyle>} 自定义聚合后的图标风格请参考TextIconOverlay类<br />
*/
BMapLib.MarkerClusterer = function(map, options){
BMapLib.MarkerCluster = function(map, options){
if (!map){
return;
}
@@ -151,7 +151,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return 无返回值。
*/
MarkerClusterer.prototype.addMarkers = function(markers){
MarkerCluster.prototype.addMarkers = function(markers){
for(var i = 0, len = markers.length; i <len ; i++){
this._pushMarkerTo(markers[i]);
}
@@ -164,7 +164,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return 无返回值。
*/
MarkerClusterer.prototype._pushMarkerTo = function(marker){
MarkerCluster.prototype._pushMarkerTo = function(marker){
var index = indexOf(marker, this._markers);
if(index === -1){
marker.isInCluster = false;
@@ -177,7 +177,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* @param {BMap.Marker} marker 要聚合的单个标记。
* @return 无返回值。
*/
MarkerClusterer.prototype.addMarker = function(marker) {
MarkerCluster.prototype.addMarker = function(marker) {
this._pushMarkerTo(marker);
this._createClusters();
};
@@ -186,7 +186,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 根据所给定的标记,创建聚合点,并且遍历所有聚合点
* @return 无返回值
*/
MarkerClusterer.prototype._createClusters = function(){
MarkerCluster.prototype._createClusters = function(){
var mapBounds = this._map.getBounds();
var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize);
for(var i = 0, marker; marker = this._markers[i]; i++){
@@ -209,7 +209,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return 无返回值。
*/
MarkerClusterer.prototype._addToClosestCluster = function (marker){
MarkerCluster.prototype._addToClosestCluster = function (marker){
var distance = 4000000;
var clusterToAddTo = null;
var position = marker.getPosition();
@@ -237,7 +237,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 清除上一次的聚合的结果
* @return 无返回值。
*/
MarkerClusterer.prototype._clearLastClusters = function(){
MarkerCluster.prototype._clearLastClusters = function(){
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
cluster.remove();
}
@@ -249,7 +249,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 清除某个聚合中的所有标记
* @return 无返回值
*/
MarkerClusterer.prototype._removeMarkersFromCluster = function(){
MarkerCluster.prototype._removeMarkersFromCluster = function(){
for(var i = 0, marker; marker = this._markers[i]; i++){
marker.isInCluster = false;
}
@@ -259,7 +259,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 把所有的标记从地图上清除
* @return 无返回值
*/
MarkerClusterer.prototype._removeMarkersFromMap = function(){
MarkerCluster.prototype._removeMarkersFromMap = function(){
for(var i = 0, marker; marker = this._markers[i]; i++){
marker.isInCluster = false;
this._map.removeOverlay(marker);
@@ -272,7 +272,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return {Boolean} 删除成功返回true否则返回false
*/
MarkerClusterer.prototype._removeMarker = function(marker) {
MarkerCluster.prototype._removeMarker = function(marker) {
var index = indexOf(marker, this._markers);
if (index === -1) {
return false;
@@ -288,7 +288,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return {Boolean} 删除成功返回true否则返回false
*/
MarkerClusterer.prototype.removeMarker = function(marker) {
MarkerCluster.prototype.removeMarker = function(marker) {
var success = this._removeMarker(marker);
if (success) {
this._clearLastClusters();
@@ -303,7 +303,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
*
* @return {Boolean} 删除成功返回true否则返回false
*/
MarkerClusterer.prototype.removeMarkers = function(markers) {
MarkerCluster.prototype.removeMarkers = function(markers) {
var success = false;
for (var i = 0; i < markers.length; i++) {
var r = this._removeMarker(markers[i]);
@@ -321,7 +321,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 从地图上彻底清除所有的标记
* @return 无返回值
*/
MarkerClusterer.prototype.clearMarkers = function() {
MarkerCluster.prototype.clearMarkers = function() {
this._clearLastClusters();
this._removeMarkersFromMap();
this._markers = [];
@@ -331,7 +331,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 重新生成,比如改变了属性等
* @return 无返回值
*/
MarkerClusterer.prototype._redraw = function () {
MarkerCluster.prototype._redraw = function () {
this._clearLastClusters();
this._createClusters();
};
@@ -340,7 +340,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取网格大小
* @return {Number} 网格大小
*/
MarkerClusterer.prototype.getGridSize = function() {
MarkerCluster.prototype.getGridSize = function() {
return this._gridSize;
};
@@ -349,7 +349,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* @param {Number} size 网格大小
* @return 无返回值
*/
MarkerClusterer.prototype.setGridSize = function(size) {
MarkerCluster.prototype.setGridSize = function(size) {
this._gridSize = size;
this._redraw();
};
@@ -358,7 +358,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取聚合的最大缩放级别。
* @return {Number} 聚合的最大缩放级别。
*/
MarkerClusterer.prototype.getMaxZoom = function() {
MarkerCluster.prototype.getMaxZoom = function() {
return this._maxZoom;
};
@@ -367,7 +367,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* @param {Number} maxZoom 聚合的最大缩放级别
* @return 无返回值
*/
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
MarkerCluster.prototype.setMaxZoom = function(maxZoom) {
this._maxZoom = maxZoom;
this._redraw();
};
@@ -376,7 +376,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取聚合的样式风格集合
* @return {Array<IconStyle>} 聚合的样式风格集合
*/
MarkerClusterer.prototype.getStyles = function() {
MarkerCluster.prototype.getStyles = function() {
return this._styles;
};
@@ -385,7 +385,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* @param {Array<IconStyle>} styles 样式风格数组
* @return 无返回值
*/
MarkerClusterer.prototype.setStyles = function(styles) {
MarkerCluster.prototype.setStyles = function(styles) {
this._styles = styles;
this._redraw();
};
@@ -394,7 +394,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取单个聚合的最小数量。
* @return {Number} 单个聚合的最小数量。
*/
MarkerClusterer.prototype.getMinClusterSize = function() {
MarkerCluster.prototype.getMinClusterSize = function() {
return this._minClusterSize;
};
@@ -403,7 +403,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* @param {Number} size 单个聚合的最小数量。
* @return 无返回值。
*/
MarkerClusterer.prototype.setMinClusterSize = function(size) {
MarkerCluster.prototype.setMinClusterSize = function(size) {
this._minClusterSize = size;
this._redraw();
};
@@ -412,7 +412,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
* @return {Boolean} true或false。
*/
MarkerClusterer.prototype.isAverageCenter = function() {
MarkerCluster.prototype.isAverageCenter = function() {
return this._isAverageCenter;
};
@@ -420,7 +420,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取聚合的Map实例。
* @return {Map} Map的示例。
*/
MarkerClusterer.prototype.getMap = function() {
MarkerCluster.prototype.getMap = function() {
return this._map;
};
@@ -428,7 +428,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取所有的标记数组。
* @return {Array<Marker>} 标记数组。
*/
MarkerClusterer.prototype.getMarkers = function() {
MarkerCluster.prototype.getMarkers = function() {
return this._markers;
};
@@ -436,7 +436,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* 获取聚合的总数量。
* @return {Number} 聚合的总数量。
*/
MarkerClusterer.prototype.getClustersCount = function() {
MarkerCluster.prototype.getClustersCount = function() {
var count = 0;
for(var i = 0, cluster; cluster = this._clusters[i]; i++){
cluster.isReal() && count++;
@@ -444,7 +444,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
return count;
};
MarkerClusterer.prototype.getCallback = function() {
MarkerCluster.prototype.getCallback = function() {
return this._callback;
}
@@ -453,7 +453,7 @@ var BMapLib = window.BMapLib = BMapLib || {};
* Cluster
* @class 表示一个聚合对象该聚合包含有N个标记这N个标记组成的范围并有予以显示在Map上的TextIconOverlay等。
* @constructor
* @param {MarkerClusterer} markerClusterer 一个标记聚合器示例。
* @param {MarkerCluster} markerClusterer 一个标记聚合器示例。
*/
function Cluster(markerClusterer){
this._markerClusterer = markerClusterer;