1
0
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:
杨国璇
2024-10-24 18:21:01 +08:00
committed by GitHub
parent 6bac5d1537
commit 9f3158f8f4
9 changed files with 290 additions and 15 deletions

View File

@@ -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>
</> </>
); );

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 };

View File

@@ -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 : '';

View 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();
};

View File

@@ -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 #

View File

@@ -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>

View File

@@ -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)