diff --git a/src/assets/images/station-operation/icon-route-active.png b/src/assets/images/station-operation/icon-route-active.png new file mode 100644 index 0000000..dc3feb6 Binary files /dev/null and b/src/assets/images/station-operation/icon-route-active.png differ diff --git a/src/assets/images/station-operation/icon-route.png b/src/assets/images/station-operation/icon-route.png new file mode 100644 index 0000000..34ad7fb Binary files /dev/null and b/src/assets/images/station-operation/icon-route.png differ diff --git a/src/store/modules/sample.js b/src/store/modules/sample.js index 04bdb20..c13d6dd 100644 --- a/src/store/modules/sample.js +++ b/src/store/modules/sample.js @@ -32,7 +32,9 @@ const sample = { REMOVE_SAMPLE_DATA: (state, inputFileName) => { const findIndex = state.sampleList.findIndex(item => item.inputFileName == inputFileName) - state.sampleList.splice(findIndex, 1) + if(-1 !== findIndex) { + state.sampleList.splice(findIndex, 1) + } }, CLEAR_SAMPLE_DATA: (state) => { diff --git a/src/views/spectrumAnalysis/gamma-analysis.vue b/src/views/spectrumAnalysis/gamma-analysis.vue index bc75927..dd3e522 100644 --- a/src/views/spectrumAnalysis/gamma-analysis.vue +++ b/src/views/spectrumAnalysis/gamma-analysis.vue @@ -525,7 +525,10 @@ export default { } } catch (error) { console.error(error) - this.isLoading = false + const isCancel = axios.isCancel(error) + if(!isCancel) { + this.isLoading = false + } } }, @@ -561,6 +564,7 @@ export default { cancelLastRequest() { if (this._cancelToken && typeof this._cancelToken == 'function') { this._cancelToken() + this._cancelToken = undefined } }, @@ -2058,6 +2062,8 @@ export default { this.handleResetState() const sampleData = await this.$store.dispatch('GET_SAMPLE_DATA', newVal.inputFileName) if (sampleData) { + this.cancelLastRequest() + this.isLoading = false this.dataProcess(sampleData.data, sampleData.from) } else { if (newVal.sampleId) { diff --git a/src/views/stationOperation/components/Map.vue b/src/views/stationOperation/components/Map.vue index 8cc3564..ad45525 100644 --- a/src/views/stationOperation/components/Map.vue +++ b/src/views/stationOperation/components/Map.vue @@ -11,6 +11,14 @@ import Map from 'ol/Map' import XYZ from 'ol/source/XYZ' import View from 'ol/View' import { fromLonLat } from 'ol/proj' +import Stroke from 'ol/style/Stroke' +import { MarkerIcon, MarkerType } from './markerEnum' +import Icon from 'ol/style/Icon' +import VectorSource from 'ol/source/Vector' +import VectorLayer from 'ol/layer/Vector' +import { LineString, Point } from 'ol/geom' +import { Feature } from 'ol' +import Style from 'ol/style/Style' const mapSourceUrl = process.env.VUE_APP_MAP_BASE_URL export default { @@ -57,21 +65,29 @@ export default { }), }) - const layers = [this.tileLayer] + this.animationSource = new VectorSource({ + features: [], + }) - const view = new View({ + this.animationLayer = new VectorLayer({ + source: this.animationSource, + }) + + const layers = [this.tileLayer, this.animationLayer] + + this.view = new View({ projection: 'EPSG:3857', // 使用这个坐标系 center: fromLonLat([longitude, latitude]), zoom: this.zoom, maxZoom: this.maxZoom, minZoom: this.minZoom, - extent: [-20037508.34 - 3500000, -20037508.34 , 20037508.34 + 3500000, 20037508.34], + extent: [-20037508.34 - 3500000, -20037508.34, 20037508.34 + 3500000, 20037508.34], }) this.map = new Map({ target: this.$refs.mapContainerRef, layers, - view, + view: this.view, controls: [], }) }, @@ -120,6 +136,110 @@ export default { }) ) }, + + // 按路线运动 + animateByRoute(coordinates) { + if (!coordinates) { + return + } + + if (!coordinates.length) { + this.stopRouteAnimation() + this.removeAnimationFeatures() + return + } + + this.startRouteAnimation( + coordinates, + { + stroke: new Stroke({ + color: '#17f85c', + lineDash: [6], + width: 2, + }), + }, + { + image: new Icon({ + src: MarkerIcon[MarkerType.Car], + anchor: [0.65, 0.5], + }), + }, + [100, 430, 100, 450] + ) + }, + /** + * 沿轨迹运动 + * @param {Array} route 轨迹路线 + * @param {Object} marker 运动的物体 + */ + startRouteAnimation(route, lineStyleOptions, markerStyleOptions, padding = []) { + // 清理上次的动画 + this.stopRouteAnimation() + + // 清理动画图层 + this.removeAnimationFeatures() + + const lineString = new LineString(route.map((item) => fromLonLat(item))) + const extent = lineString.getExtent() + this.view.fit(extent, { + padding, + duration: 1000, + }) + + // 创建轨迹线 + const routeFeature = new Feature({ + geometry: lineString, + }) + + // 设置轨迹线样式,并添加到地图上 + routeFeature.setStyle(new Style(lineStyleOptions)) + this.animationSource.addFeature(routeFeature) + + // 创建标记物,并设置位置到轨迹路线的初始位置 + const markerFeature = new Feature({ + geometry: new Point(lineString.getFirstCoordinate()), + }) + + // 设置标记物样式,并添加到地图上 + markerFeature.setStyle(new Style(markerStyleOptions)) + this.animationSource.addFeature(markerFeature) + + let lastTime = Date.now(), + distance = 0 + this.postRenderCallback = (event) => { + const speed = 60 + const time = event.frameState.time + const elapsedTime = time - lastTime + distance = (distance + (speed * elapsedTime) / 1e6) % 2 + lastTime = time + + if (distance >= 1) { + this.stopRouteAnimation() + return + } + + const currentCoordinate = lineString.getCoordinateAt(distance > 1 ? 2 - distance : distance) + markerFeature.setGeometry(new Point(currentCoordinate)) + + // tell OpenLayers to continue the postrender animation + this.map.render() + } + + this.animationLayer.on('postrender', this.postRenderCallback) + }, + + // 移除动画层所有元素 + removeAnimationFeatures() { + this.animationSource.clear() + }, + + // 停止轨迹动画 + stopRouteAnimation() { + if (this.postRenderCallback) { + this.animationLayer.un('postrender', this.postRenderCallback) + this.postRenderCallback = null + } + }, }, } diff --git a/src/views/stationOperation/components/MapPane.vue b/src/views/stationOperation/components/MapPane.vue index 907df4a..e250db4 100644 --- a/src/views/stationOperation/components/MapPane.vue +++ b/src/views/stationOperation/components/MapPane.vue @@ -32,6 +32,14 @@ /> +
+ + +
@@ -149,6 +157,41 @@
+ + +
+
Route
+
+
+
Station
+ +
+
+
Start Date
+ +
+
+
End Date
+ +
+
+ Search +
+
+
+ @@ -274,6 +317,7 @@ import { MarkerType, FilterIcon } from './markerEnum' import { Vector as VectorLayer } from 'ol/layer' import VectorSource from 'ol/source/Vector' import { cloneDeep } from 'lodash' +import dayjs from 'dayjs' // Filter中的筛选列表 const filterList = [ @@ -393,10 +437,13 @@ export default { type: Number, default: 500, }, - treeData: { type: Array, }, + originalDataList: { + type: Array, + default: () => [], + }, }, components: { CustomModal, @@ -445,6 +492,14 @@ export default { stationInfo: undefined, mapSource: 'online', + + routeParams: { + stationCode: undefined, + startDate: dayjs().subtract(7, 'd').format('YYYY-MM-DD'), + endDate: dayjs().format('YYYY-MM-DD'), + }, + + isSearchingRoute: false, } }, created() { @@ -492,18 +547,33 @@ export default { // 面板改变 onPaneChange(active) { this.showPane = true + this.prevPane = this.active // 记录上次是哪个面板 this.active = active const source = this.circleLayer.getSource() source.clear() // 清理图层 - switch (active) { - case 1: // 核设施查询面板 + + // 如果切换到轨迹绘制页面,则使用上一次的轨迹先绘制一遍 + if (active == 3) { + if (this.lastRoute) { + this.$emit('drawRoute', this.lastRoute) + } + + this.$emit('changeMarker', []) + } else { + // 如果是从轨迹绘制切换到其他页面 + if (this.prevPane == 3) { + this.$emit('drawRoute', []) + } + + if (active == 1) { this.emitDrawCircle(2) this.emitStationChange() - break - case 2: // 筛选面板 + } + + if (active == 2) { this.emitDrawCircle(1) this.emitFilter() - break + } } }, @@ -822,6 +892,39 @@ export default { handleResize() { this.$refs.realtimeChartRef.resize() }, + + // 查询路径 + async handleRouteSearch() { + if (!this.routeParams.stationCode) { + this.$message.warn('Station Code Cannot Be Null') + return + } + + this.isSearchingRoute = true + + try { + const { success, result, message } = await getAction('/stationOperation/getSelfStationGPS', { + ...this.routeParams, + }) + + if (success) { + if (!result.length) { + this.$message.warn('No Route Found') + return + } + + this.lastRoute = result.map(({ lon, lat }) => [lon, lat]) + + this.$emit('drawRoute', this.lastRoute) + } else { + this.$message.error(message) + } + } catch (e) { + console.error(e) + } finally { + this.isSearchingRoute = false + } + }, }, watch: { async dataStatusModalVisible(val) { @@ -840,6 +943,16 @@ export default { } }, }, + computed: { + stationSelectOptions() { + return this.originalDataList + .filter(({ stationType }) => stationType == 'Car') + .map(({ stationName: label }) => ({ + label, + value: label, + })) + }, + }, }