mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-07 01:41:39 +00:00
feat: map location (#6935)
* feat: map location * feat: optimize display --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
@@ -5,17 +5,16 @@ import { Formatter } from '@seafile/sf-metadata-ui-component';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { getDirentPath } from '../utils';
|
import { getDirentPath } from '../utils';
|
||||||
import DetailItem from '../../detail-item';
|
import DetailItem from '../../detail-item';
|
||||||
import { CellType, GEOLOCATION_FORMAT, PRIVATE_COLUMN_KEY } from '../../../../metadata/constants';
|
import { CellType, PRIVATE_COLUMN_KEY } from '../../../../metadata/constants';
|
||||||
import { gettext } from '../../../../utils/constants';
|
import { gettext } from '../../../../utils/constants';
|
||||||
import EditFileTagPopover from '../../../popover/edit-filetag-popover';
|
import EditFileTagPopover from '../../../popover/edit-filetag-popover';
|
||||||
import FileTagList from '../../../file-tag-list';
|
import FileTagList from '../../../file-tag-list';
|
||||||
import { Utils } from '../../../../utils/utils';
|
import { Utils } from '../../../../utils/utils';
|
||||||
import { MetadataDetails, useMetadata } from '../../../../metadata';
|
import { MetadataDetails, useMetadata } from '../../../../metadata';
|
||||||
import ObjectUtils from '../../../../metadata/utils/object-utils';
|
import ObjectUtils from '../../../../metadata/utils/object-utils';
|
||||||
import { getCellValueByColumn, getDateDisplayString, getGeolocationDisplayString,
|
import { getCellValueByColumn, getDateDisplayString, decimalToExposureTime } from '../../../../metadata/utils/cell';
|
||||||
decimalToExposureTime,
|
|
||||||
} from '../../../../metadata/utils/cell';
|
|
||||||
import Collapse from './collapse';
|
import Collapse from './collapse';
|
||||||
|
import Location from './location';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -143,14 +142,7 @@ const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail,
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{fileLocation && (
|
{record && (<Location position={fileLocation} />)}
|
||||||
<div className="dirent-detail-item sf-metadata-property-detail-capture-information-item" key={'location'}>
|
|
||||||
<div className="dirent-detail-item-name">{gettext('Location')}</div>
|
|
||||||
<div className="dirent-detail-item-value" placeholder={gettext('Empty')}>
|
|
||||||
{getGeolocationDisplayString(fileLocation, { geo_format: GEOLOCATION_FORMAT.LNG_LAT })}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
.dirent-detail-item-value-map {
|
||||||
|
height: 100px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dirent-detail-item-value-map.error-message {
|
||||||
|
word-break: break-word;
|
||||||
|
padding: 8px;
|
||||||
|
margin: auto;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
@@ -0,0 +1,209 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { initMapInfo, loadMapSource } from '../../../../../utils/map-utils';
|
||||||
|
import { MAP_TYPE, DOMESTIC_MAP_TYPE } from '../../../../../constants';
|
||||||
|
import Loading from '../../../../loading';
|
||||||
|
import { gettext, baiduMapKey, googleMapKey, googleMapId } from '../../../../../utils/constants';
|
||||||
|
import { GEOLOCATION_FORMAT } from '../../../../../metadata/constants';
|
||||||
|
import { getGeolocationDisplayString } from '../../../../../metadata/utils/cell';
|
||||||
|
import { isValidPosition } from '../../../../../metadata/utils/validate';
|
||||||
|
import toaster from '../../../../toast';
|
||||||
|
import ObjectUtils from '../../../../../metadata/utils/object-utils';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
class Location extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const { type, key } = initMapInfo({ baiduMapKey, googleMapKey });
|
||||||
|
this.mapType = type;
|
||||||
|
this.mapKey = key;
|
||||||
|
this.map = null;
|
||||||
|
this.currentPosition = {};
|
||||||
|
this.state = {
|
||||||
|
address: '',
|
||||||
|
isLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.initMap(this.props.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
|
const { position } = nextProps;
|
||||||
|
if (!ObjectUtils.isSameObject(position, this.props.position)) {
|
||||||
|
this.initMap(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.map && DOMESTIC_MAP_TYPE.includes(this.mapType)) {
|
||||||
|
this.mineMapMarker = null;
|
||||||
|
} else if (this.map && this.mapType === MAP_TYPE.G_MAP) {
|
||||||
|
this.googleMarker = null;
|
||||||
|
}
|
||||||
|
this.map = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
initMap = (position) => {
|
||||||
|
this.setState({ isLoading: true });
|
||||||
|
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||||
|
if (!window.BMap) {
|
||||||
|
window.renderBaiduMap = () => this.renderBaiduMap(position);
|
||||||
|
loadMapSource(this.mapType, this.mapKey);
|
||||||
|
} else {
|
||||||
|
this.renderBaiduMap(position);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mapType === MAP_TYPE.G_MAP) {
|
||||||
|
if (!window.google) {
|
||||||
|
window.renderGoogleMap = () => this.renderGoogleMap(position);
|
||||||
|
loadMapSource(this.mapType, this.mapKey);
|
||||||
|
} else {
|
||||||
|
this.renderGoogleMap(position);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ isLoading: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
addMarkerByPosition = (lng, lat) => {
|
||||||
|
if (this.mapType === MAP_TYPE.B_MAP) {
|
||||||
|
let point = new window.BMap.Point(lng, lat);
|
||||||
|
const marker = new window.BMap.Marker(point, { offset: new window.BMap.Size(-2, -5) });
|
||||||
|
this.map && this.map.clearOverlays();
|
||||||
|
this.map && this.map.addOverlay(marker);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mapType === MAP_TYPE.G_MAP) {
|
||||||
|
if (!this.googleMarker) {
|
||||||
|
this.googleMarker = new window.google.maps.marker.AdvancedMarkerElement({
|
||||||
|
position: { lng, lat },
|
||||||
|
map: this.map,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.googleMarker.setPosition({ lng, lat });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.mapType === MAP_TYPE.M_MAP) {
|
||||||
|
if (!this.mineMapMarker) {
|
||||||
|
this.mineMapMarker = new window.minemap.Marker({
|
||||||
|
draggable: false,
|
||||||
|
anchor: 'top-left',
|
||||||
|
color: 'red',
|
||||||
|
rotation: 0,
|
||||||
|
pitchAlignment: 'map',
|
||||||
|
rotationAlignment: 'map',
|
||||||
|
scale: 0.8
|
||||||
|
}).setLngLat({ lng, lat }).addTo(this.map);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mineMapMarker.setLngLat({ lng, lat });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderBaiduMap = (position = {}) => {
|
||||||
|
this.setState({ isLoading: false }, () => {
|
||||||
|
if (!window.BMap.Map) return;
|
||||||
|
const { lng, lat } = position || {};
|
||||||
|
if (!isValidPosition(lng, lat)) return;
|
||||||
|
this.map = new window.BMap.Map('sf-geolocation-map-container', { enableMapClick: false });
|
||||||
|
this.addMarkerByPosition(lng, lat);
|
||||||
|
const point = new window.BMap.Point(lng, lat);
|
||||||
|
this.map.centerAndZoom(point, 14);
|
||||||
|
this.map.enableScrollWheelZoom(true);
|
||||||
|
const geocoder = new window.BMap.Geocoder();
|
||||||
|
geocoder.getLocation(point, (res) => {
|
||||||
|
const addressRes = res.addressComponents;
|
||||||
|
const addressArray = [addressRes.province, addressRes.city, addressRes.district, addressRes.street, addressRes.streetNumber];
|
||||||
|
const address = addressArray.filter(item => item).join('');
|
||||||
|
this.setState({ address });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderGoogleMap = (position) => {
|
||||||
|
this.setState({ isLoading: false }, () => {
|
||||||
|
if (!window.google.maps.Map) return;
|
||||||
|
const { lng, lat } = position || {};
|
||||||
|
const isValid = isValidPosition(lng, lat);
|
||||||
|
if (!isValid) return;
|
||||||
|
this.map = new window.google.maps.Map(this.ref, {
|
||||||
|
zoom: 14,
|
||||||
|
center: position,
|
||||||
|
mapId: googleMapId,
|
||||||
|
zoomControl: false,
|
||||||
|
mapTypeControl: false,
|
||||||
|
scaleControl: false,
|
||||||
|
streetViewControl: false,
|
||||||
|
rotateControl: false,
|
||||||
|
fullscreenControl: false
|
||||||
|
});
|
||||||
|
this.addMarkerByPosition(lng, lat);
|
||||||
|
this.map.setCenter(position);
|
||||||
|
var geocoder = new window.google.maps.Geocoder();
|
||||||
|
var latLng = new window.google.maps.LatLng(lat, lng);
|
||||||
|
geocoder.geocode({ 'location': latLng }, (results, status) => {
|
||||||
|
if (status === 'OK') {
|
||||||
|
if (results[0]) {
|
||||||
|
var address = results[0].formatted_address;
|
||||||
|
this.setState({ address });
|
||||||
|
} else {
|
||||||
|
toaster.warning(gettext('No address found for the given coordinates.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isLoading, address } = this.state;
|
||||||
|
const { position } = this.props;
|
||||||
|
const isValid = isValidPosition(position?.lng, position?.lat);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="dirent-detail-item sf-metadata-property-detail-capture-information-item" key={'location'}>
|
||||||
|
<div className="dirent-detail-item-name">{gettext('Location')}</div>
|
||||||
|
<div className="dirent-detail-item-value" placeholder={gettext('Empty')}>
|
||||||
|
{isValid && (
|
||||||
|
<>
|
||||||
|
{!isLoading && this.mapType && address && (
|
||||||
|
<>
|
||||||
|
<span>{address}</span>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span>{getGeolocationDisplayString(position, { geo_format: GEOLOCATION_FORMAT.LNG_LAT })}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isLoading ? (<Loading />) : (
|
||||||
|
<>
|
||||||
|
{this.mapType ? (
|
||||||
|
<div className="dirent-detail-item-value-map">
|
||||||
|
{!isLoading && (<div className={classnames('w-100 h-100', { 'd-none': !isValid })} ref={ref => this.ref = ref} id="sf-geolocation-map-container"></div>)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="dirent-detail-item-value-map alert-danger error-message d-flex justify-content-center">
|
||||||
|
{gettext('The map plugin is not properly configured. Contact the administrator.')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Location.propTypes = {
|
||||||
|
location: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Location;
|
@@ -14,4 +14,12 @@ const TAG_COLORS = ['#FBD44A', '#EAA775', '#F4667C', '#DC82D2', '#9860E5', '#9F8
|
|||||||
export const SIDE_PANEL_FOLDED_WIDTH = 71;
|
export const SIDE_PANEL_FOLDED_WIDTH = 71;
|
||||||
export const SUB_NAV_ITEM_HEIGHT = 28;
|
export const SUB_NAV_ITEM_HEIGHT = 28;
|
||||||
|
|
||||||
|
export const MAP_TYPE = {
|
||||||
|
B_MAP: 'b_map', // baidu
|
||||||
|
G_MAP: 'g_map', // google
|
||||||
|
};
|
||||||
|
|
||||||
|
// domestic map's format: [lng, lat], foreign map's format: [lat, lng]
|
||||||
|
export const DOMESTIC_MAP_TYPE = [MAP_TYPE.B_MAP];
|
||||||
|
|
||||||
export { KeyCodes, zIndexes, TAG_COLORS };
|
export { KeyCodes, zIndexes, TAG_COLORS };
|
||||||
|
@@ -110,6 +110,12 @@ export const additionalShareDialogNote = window.app.pageOptions.additionalShareD
|
|||||||
export const additionalAppBottomLinks = window.app.pageOptions.additionalAppBottomLinks;
|
export const additionalAppBottomLinks = window.app.pageOptions.additionalAppBottomLinks;
|
||||||
export const additionalAboutDialogLinks = window.app.pageOptions.additionalAboutDialogLinks;
|
export const additionalAboutDialogLinks = window.app.pageOptions.additionalAboutDialogLinks;
|
||||||
|
|
||||||
|
// map settings
|
||||||
|
export const baiduMapKey = window.app.pageOptions.baiduMapKey;
|
||||||
|
export const googleMapKey = window.app.pageOptions.googleMapKey;
|
||||||
|
export const googleMapId = window.app.pageOptions.googleMapId;
|
||||||
|
export const mineMapKey = window.app.pageOptions.mineMapKey;
|
||||||
|
|
||||||
// wiki
|
// wiki
|
||||||
export const slug = window.wiki ? window.wiki.config.slug : '';
|
export const slug = window.wiki ? window.wiki.config.slug : '';
|
||||||
export const wikiId = window.wiki ? window.wiki.config.wikiId : '';
|
export const wikiId = window.wiki ? window.wiki.config.wikiId : '';
|
||||||
|
27
frontend/src/utils/map-utils.js
Normal file
27
frontend/src/utils/map-utils.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { MAP_TYPE } from '../constants';
|
||||||
|
|
||||||
|
export const initMapInfo = ({ baiduMapKey, googleMapKey, mineMapKey }) => {
|
||||||
|
if (baiduMapKey) return { type: MAP_TYPE.B_MAP, key: baiduMapKey };
|
||||||
|
if (googleMapKey) return { type: MAP_TYPE.G_MAP, key: googleMapKey };
|
||||||
|
return { type: '', key: '' };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadMapSource = (type, key, callback) => {
|
||||||
|
if (!type || !key) return;
|
||||||
|
let scriptUrl = '';
|
||||||
|
const sourceId = 'map-source-script';
|
||||||
|
if (document.getElementById(sourceId)) return;
|
||||||
|
let script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.id = sourceId;
|
||||||
|
if (type === MAP_TYPE.B_MAP) {
|
||||||
|
scriptUrl = `https://api.map.baidu.com/api?v=3.0&ak=${key}&callback=renderBaiduMap`;
|
||||||
|
} else if (type === MAP_TYPE.G_MAP) {
|
||||||
|
scriptUrl = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=renderGoogleMap&libraries=marker&v=weekly`;
|
||||||
|
}
|
||||||
|
if (scriptUrl) {
|
||||||
|
script.src = scriptUrl;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}
|
||||||
|
callback && callback();
|
||||||
|
};
|
@@ -925,6 +925,17 @@ del d
|
|||||||
if not os.path.exists(EVENTS_CONFIG_FILE):
|
if not os.path.exists(EVENTS_CONFIG_FILE):
|
||||||
del EVENTS_CONFIG_FILE
|
del EVENTS_CONFIG_FILE
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Map settings #
|
||||||
|
#####################
|
||||||
|
|
||||||
|
# baidu map
|
||||||
|
BAIDU_MAP_KEY = ''
|
||||||
|
|
||||||
|
# google map
|
||||||
|
GOOGLE_MAP_KEY = ''
|
||||||
|
GOOGLE_MAP_ID = ''
|
||||||
|
|
||||||
|
|
||||||
#####################
|
#####################
|
||||||
# External settings #
|
# External settings #
|
||||||
|
@@ -155,7 +155,10 @@
|
|||||||
enableSeadoc: {% if enable_seadoc %} true {% else %} false {% endif %},
|
enableSeadoc: {% if enable_seadoc %} true {% else %} false {% endif %},
|
||||||
isOrgContext: {% if org is not None %} true {% else %} false {% endif %},
|
isOrgContext: {% if org is not None %} true {% else %} false {% endif %},
|
||||||
enableMetadataManagement: {% if enable_metadata_management %} true {% else %} false {% endif %},
|
enableMetadataManagement: {% if enable_metadata_management %} true {% else %} false {% endif %},
|
||||||
enableFileTags: {% if enable_file_tags %} true {% else %} false {% endif %}
|
enableFileTags: {% if enable_file_tags %} true {% else %} false {% endif %},
|
||||||
|
baiduMapKey: '{{ baidu_map_key }}',
|
||||||
|
googleMapKey: '{{ google_map_key }}',
|
||||||
|
googleMapId: '{{ google_map_id }}',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1108,7 +1108,7 @@ def react_fake_view(request, **kwargs):
|
|||||||
if enable_clean_trash:
|
if enable_clean_trash:
|
||||||
enable_clean_trash = int(not org_setting[DISABLE_ORG_USER_CLEAN_TRASH])
|
enable_clean_trash = int(not org_setting[DISABLE_ORG_USER_CLEAN_TRASH])
|
||||||
|
|
||||||
return render(request, "react_app.html", {
|
return_dict = {
|
||||||
"guide_enabled": guide_enabled,
|
"guide_enabled": guide_enabled,
|
||||||
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
||||||
'max_upload_file_size': max_upload_file_size,
|
'max_upload_file_size': max_upload_file_size,
|
||||||
@@ -1149,4 +1149,11 @@ def react_fake_view(request, **kwargs):
|
|||||||
'enable_sso_to_thirdpart_website': settings.ENABLE_SSO_TO_THIRDPART_WEBSITE,
|
'enable_sso_to_thirdpart_website': settings.ENABLE_SSO_TO_THIRDPART_WEBSITE,
|
||||||
'enable_metadata_management': settings.ENABLE_METADATA_MANAGEMENT,
|
'enable_metadata_management': settings.ENABLE_METADATA_MANAGEMENT,
|
||||||
'enable_file_tags': settings.ENABLE_FILE_TAGS
|
'enable_file_tags': settings.ENABLE_FILE_TAGS
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if settings.ENABLE_METADATA_MANAGEMENT:
|
||||||
|
return_dict['baidu_map_key'] = settings.BAIDU_MAP_KEY
|
||||||
|
return_dict['google_map_key'] = settings.GOOGLE_MAP_KEY
|
||||||
|
return_dict['google_map_id'] = settings.GOOGLE_MAP_ID
|
||||||
|
|
||||||
|
return render(request, "react_app.html", return_dict)
|
||||||
|
Reference in New Issue
Block a user