|
|
@@ -50,16 +50,25 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
+ <!-- 地图图层切换 -->
|
|
|
+ <div class="layer-switch">
|
|
|
+ <el-radio-group v-model="currentLayer" size="small" @change="switchLayer">
|
|
|
+ <el-radio-button label="vec">矢量图</el-radio-button>
|
|
|
+ <el-radio-button label="img">影像图</el-radio-button>
|
|
|
+ <el-radio-button label="ter">地形图</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+
|
|
|
<div class="coord-display">
|
|
|
经度: {{ coordDisplay.lng }} 纬度: {{ coordDisplay.lat }}
|
|
|
</div>
|
|
|
|
|
|
<div v-if="tooltipData.visible" class="map-tooltip" :style="tooltipStyle">
|
|
|
<div class="tooltip-title">{{ tooltipData.stnm }}</div>
|
|
|
- <div class="tooltip-row">站码:<b>{{ tooltipData.stcd }}</b></div>
|
|
|
<div class="tooltip-row">共享单位:{{ tooltipData.unit }}</div>
|
|
|
<div class="tooltip-row">监测时间:{{ tooltipData.loading ? '加载中...' : tooltipData.tm }}</div>
|
|
|
<template v-if="!tooltipData.loading && tooltipData.values.length">
|
|
|
+ <div class="tooltip-divider"></div>
|
|
|
<div class="tooltip-row" v-for="v in tooltipData.values" :key="v.label">
|
|
|
{{ v.label }}:<b>{{ v.value }}</b> {{ v.unit }}
|
|
|
</div>
|
|
|
@@ -77,11 +86,56 @@
|
|
|
</div>
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
+
|
|
|
+ <!-- 历史数据弹框 -->
|
|
|
+ <el-dialog v-model="historyDialogVisible" :title="historyDialogTitle" width="85%" append-to-body>
|
|
|
+ <div class="history-content">
|
|
|
+ <!-- 左侧表格 -->
|
|
|
+ <div class="history-left">
|
|
|
+ <el-table :data="historyData" v-loading="historyLoading" stripe border style="width: 100%" height="100%">
|
|
|
+ <el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
|
|
|
+ <template v-for="col in currentHistoryColumns" :key="col.prop">
|
|
|
+ <el-table-column
|
|
|
+ :prop="col.prop"
|
|
|
+ :label="col.label"
|
|
|
+ :width="col.width"
|
|
|
+ :min-width="col.minWidth"
|
|
|
+ :formatter="col.formatter"
|
|
|
+ :align="col.align || 'center'"
|
|
|
+ show-overflow-tooltip
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table>
|
|
|
+ <div v-if="historyData.length === 0 && !historyLoading" class="empty-state">
|
|
|
+ <el-icon class="empty-icon"><DataAnalysis /></el-icon>
|
|
|
+ <p>暂无历史数据</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 右侧图表 -->
|
|
|
+ <div class="history-right">
|
|
|
+ <div class="chart-header">
|
|
|
+ <span class="toolbar-label">时间范围:</span>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="historyDateRange"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="-"
|
|
|
+ start-placeholder="开始"
|
|
|
+ end-placeholder="结束"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ @change="loadHistoryData"
|
|
|
+ size="default"
|
|
|
+ style="width: 260px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div ref="historyChartRef" class="history-chart"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup name="GisModule">
|
|
|
-import { ref, reactive, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
|
|
+import { ref, reactive, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
|
|
|
import { Map as OlMap, View } from 'ol'
|
|
|
import { XYZ, Vector as VectorSource } from 'ol/source'
|
|
|
import TileLayer from 'ol/layer/Tile'
|
|
|
@@ -89,13 +143,14 @@ import VectorLayer from 'ol/layer/Vector'
|
|
|
import { fromLonLat, toLonLat } from 'ol/proj'
|
|
|
import { Feature } from 'ol'
|
|
|
import { Point } from 'ol/geom'
|
|
|
-import { Style, Fill, Stroke, Icon, Circle as CircleStyle } from 'ol/style'
|
|
|
+import { Style, Fill, Stroke, Icon, Circle as CircleStyle, Text as TextStyle } from 'ol/style'
|
|
|
import GeoJSON from 'ol/format/GeoJSON'
|
|
|
import 'ol/ol.css'
|
|
|
-import { DataLine } from '@element-plus/icons-vue'
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
+import * as echarts from 'echarts'
|
|
|
+import { DataLine, Search, DataAnalysis } from '@element-plus/icons-vue'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import { xajfqGeoJson } from '@/assets/mapJSON/xajfq_geojson'
|
|
|
-import { listMapStations, getLatestData } from '@/api/xajgl/station'
|
|
|
+import { listMapStations, getLatestData, listData } from '@/api/xajgl/station'
|
|
|
|
|
|
const props = defineProps({
|
|
|
active: { type: Boolean, default: false }
|
|
|
@@ -106,6 +161,37 @@ const emit = defineEmits(['open-station-dialog'])
|
|
|
const activeTab = ref('situation')
|
|
|
const mapContainer = ref(null)
|
|
|
|
|
|
+// 历史数据弹框相关
|
|
|
+const historyDialogVisible = ref(false)
|
|
|
+const historyDialogTitle = ref('')
|
|
|
+const historyData = ref([])
|
|
|
+const historyLoading = ref(false)
|
|
|
+const historyDateRange = ref([])
|
|
|
+const currentStation = ref(null)
|
|
|
+const historyChartRef = ref(null)
|
|
|
+let historyChart = null
|
|
|
+
|
|
|
+// 站点类型对应的业务域配置
|
|
|
+const STATION_BIZ_CONFIG = {
|
|
|
+ DRP: { bizCode: 'AH_SL_YL', columns: ['stnm', 'tm', 'drp', 'prov', 'city', 'county'], historyChart: ['drp'] },
|
|
|
+ ZZ: { bizCode: 'AH_SL_JL', columns: ['stnm', 'tm', 'z', 'q', 'prov', 'city', 'county'], historyChart: ['z', 'q'] },
|
|
|
+ DYP: { bizCode: 'AH_SL_ZF', columns: ['stnm', 'tm', 'dyp', 'prov', 'city', 'county'], historyChart: ['dyp'] },
|
|
|
+ SK: { bizCode: 'FD_XAJ', columns: ['stnm', 'tm', 'z', 'dwz', 'inq', 'outq'], historyChart: ['z', 'inq', 'outq'] }
|
|
|
+}
|
|
|
+
|
|
|
+// 历史数据列配置
|
|
|
+const historyColumns = [
|
|
|
+ { prop: 'stnm', label: '站点名称', width: 150, fixed: 'left' },
|
|
|
+ { prop: 'tm', label: '时间', width: 150, formatter: fmtDateTime },
|
|
|
+ { prop: 'z', label: '水位(m)', width: 100 },
|
|
|
+ { prop: 'dwz', label: '坝下水位(m)', width: 110 },
|
|
|
+ { prop: 'q', label: '流量(m³/s)', width: 110 },
|
|
|
+ { prop: 'inq', label: '入库流量(m³/s)', width: 120 },
|
|
|
+ { prop: 'outq', label: '出库流量(m³/s)', width: 120 },
|
|
|
+ { prop: 'drp', label: '降雨量(mm)', width: 110 },
|
|
|
+ { prop: 'dyp', label: '蒸发量(mm)', width: 110 }
|
|
|
+]
|
|
|
+
|
|
|
const stationColorMap = {
|
|
|
DRP: '#17c2c7',
|
|
|
ZZ: '#1c97e7',
|
|
|
@@ -131,8 +217,27 @@ const typeLegend = [
|
|
|
let olMap = null
|
|
|
let areaLayer = null
|
|
|
let stationLayer = null
|
|
|
+let baseLayer = null
|
|
|
+let labelLayer = null
|
|
|
let resizeObserver = null
|
|
|
let currentHoverStcd = null
|
|
|
+const currentLayer = ref('vec')
|
|
|
+
|
|
|
+// 天地图图层配置
|
|
|
+const layerConfig = {
|
|
|
+ vec: {
|
|
|
+ base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
|
|
|
+ label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
+ },
|
|
|
+ img: {
|
|
|
+ base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
|
|
|
+ label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
+ },
|
|
|
+ ter: {
|
|
|
+ base: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=ter_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65',
|
|
|
+ label: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cta_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
const coordDisplay = reactive({ lng: '119.0500', lat: '29.5000' })
|
|
|
|
|
|
@@ -170,6 +275,14 @@ function createStationStyle(tp) {
|
|
|
function initMap() {
|
|
|
if (!mapContainer.value) return
|
|
|
|
|
|
+ // 创建底图和注记图层
|
|
|
+ baseLayer = new TileLayer({
|
|
|
+ source: new XYZ({ url: layerConfig.vec.base })
|
|
|
+ })
|
|
|
+ labelLayer = new TileLayer({
|
|
|
+ source: new XYZ({ url: layerConfig.vec.label })
|
|
|
+ })
|
|
|
+
|
|
|
olMap = new OlMap({
|
|
|
target: mapContainer.value,
|
|
|
view: new View({
|
|
|
@@ -178,18 +291,7 @@ function initMap() {
|
|
|
minZoom: 8,
|
|
|
maxZoom: 18
|
|
|
}),
|
|
|
- layers: [
|
|
|
- new TileLayer({
|
|
|
- source: new XYZ({
|
|
|
- url: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
- })
|
|
|
- }),
|
|
|
- new TileLayer({
|
|
|
- source: new XYZ({
|
|
|
- url: 'https://t{0-7}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
- })
|
|
|
- })
|
|
|
- ]
|
|
|
+ layers: [baseLayer, labelLayer]
|
|
|
})
|
|
|
|
|
|
areaLayer = new VectorLayer({ source: new VectorSource() })
|
|
|
@@ -204,6 +306,14 @@ function initMap() {
|
|
|
olMap.on('pointermove', handlePointerMove)
|
|
|
}
|
|
|
|
|
|
+// 切换地图图层
|
|
|
+function switchLayer(type) {
|
|
|
+ if (!baseLayer || !labelLayer) return
|
|
|
+ const config = layerConfig[type]
|
|
|
+ baseLayer.setSource(new XYZ({ url: config.base }))
|
|
|
+ labelLayer.setSource(new XYZ({ url: config.label }))
|
|
|
+}
|
|
|
+
|
|
|
function loadAreaGeoJson() {
|
|
|
const source = areaLayer.getSource()
|
|
|
try {
|
|
|
@@ -213,9 +323,17 @@ function loadAreaGeoJson() {
|
|
|
featureProjection: 'EPSG:3857'
|
|
|
})
|
|
|
features.forEach(f => {
|
|
|
+ const name = f.get('name') || ''
|
|
|
f.setStyle(new Style({
|
|
|
- fill: new Fill({ color: 'rgba(28, 151, 231, 0.12)' }),
|
|
|
- stroke: new Stroke({ color: 'rgba(28, 151, 231, 0.7)', width: 2 })
|
|
|
+ fill: new Fill({ color: 'rgba(128, 128, 128, 0.1)' }),
|
|
|
+ stroke: new Stroke({ color: 'rgba(0, 51, 102, 0.9)', width: 2.5 }),
|
|
|
+ text: name ? new TextStyle({
|
|
|
+ text: name,
|
|
|
+ font: '12px Microsoft YaHei',
|
|
|
+ fill: new Fill({ color: '#303133' }),
|
|
|
+ stroke: new Stroke({ color: '#fff', width: 3 }),
|
|
|
+ overflow: true
|
|
|
+ }) : undefined
|
|
|
}))
|
|
|
})
|
|
|
source.addFeatures(features)
|
|
|
@@ -266,29 +384,147 @@ async function getLatestDataWithCache(stcd) {
|
|
|
function buildLatestValues(tp, data) {
|
|
|
const values = []
|
|
|
if (tp === 'DRP') {
|
|
|
- values.push({ label: '降雨量', value: data.drp, unit: 'mm' })
|
|
|
+ if (data.drp != null && data.drp !== '') values.push({ label: '降雨量', value: data.drp, unit: 'mm' })
|
|
|
} else if (tp === 'ZZ') {
|
|
|
- values.push({ label: '水位', value: data.z, unit: 'm' })
|
|
|
- values.push({ label: '流量', value: data.q, unit: 'm³/s' })
|
|
|
+ if (data.z != null && data.z !== '') values.push({ label: '水位', value: data.z, unit: 'm' })
|
|
|
+ if (data.q != null && data.q !== '') values.push({ label: '流量', value: data.q, unit: 'm³/s' })
|
|
|
} else if (tp === 'DYP') {
|
|
|
- values.push({ label: '蒸发量', value: data.dyp, unit: 'mm' })
|
|
|
+ if (data.dyp != null && data.dyp !== '') values.push({ label: '蒸发量', value: data.dyp, unit: 'mm' })
|
|
|
} else if (tp === 'SK') {
|
|
|
- values.push({ label: '水位', value: data.z, unit: 'm' })
|
|
|
- values.push({ label: '下泄水量', value: data.ww, unit: '万m³' })
|
|
|
+ if (data.z != null && data.z !== '') values.push({ label: '坝上水位', value: data.z, unit: 'm' })
|
|
|
+ if (data.inq != null && data.inq !== '') values.push({ label: '入库流量', value: data.inq, unit: 'm³/s' })
|
|
|
+ if (data.outq != null && data.outq !== '') values.push({ label: '出库流量', value: data.outq, unit: 'm³/s' })
|
|
|
+ if (data.ww != null && data.ww !== '') values.push({ label: '下泄水量', value: data.ww, unit: '万m³' })
|
|
|
}
|
|
|
- return values.filter(v => v.value != null && v.value !== '')
|
|
|
+ return values
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化时间为 YYYY-MM-DD
|
|
|
+function fmtDate(val) {
|
|
|
+ if (!val) return '-'
|
|
|
+ const d = new Date(val)
|
|
|
+ if (isNaN(d)) return String(val)
|
|
|
+ const pad = n => n < 10 ? '0' + n : String(n)
|
|
|
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化日期时间
|
|
|
+function fmtDateTime(row, col, val) {
|
|
|
+ if (!val) return '-'
|
|
|
+ const d = new Date(val)
|
|
|
+ if (isNaN(d)) return String(val)
|
|
|
+ const pad = n => n < 10 ? '0' + n : String(n)
|
|
|
+ return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化日期
|
|
|
+function formatDate(date) {
|
|
|
+ const y = date.getFullYear()
|
|
|
+ const m = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
+ const d = String(date.getDate()).padStart(2, '0')
|
|
|
+ return `${y}-${m}-${d}`
|
|
|
}
|
|
|
|
|
|
+// 计算当前历史数据列配置
|
|
|
+const currentHistoryColumns = computed(() => {
|
|
|
+ if (!currentStation.value) return historyColumns
|
|
|
+ const config = STATION_BIZ_CONFIG[currentStation.value.tp]
|
|
|
+ if (!config) return historyColumns
|
|
|
+ return historyColumns.filter(col => config.columns.includes(col.prop))
|
|
|
+})
|
|
|
+
|
|
|
function handleMapClick(event) {
|
|
|
const feature = olMap.forEachFeatureAtPixel(event.pixel, f => f, {
|
|
|
layerFilter: l => l === stationLayer
|
|
|
})
|
|
|
if (feature) {
|
|
|
const station = feature.get('station')
|
|
|
- if (station) emit('open-station-dialog', station)
|
|
|
+ if (station) {
|
|
|
+ openHistoryDialog(station)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 打开历史数据弹框
|
|
|
+function openHistoryDialog(station) {
|
|
|
+ currentStation.value = station
|
|
|
+ historyDialogTitle.value = `${station.stnm} - 历史数据`
|
|
|
+ // 默认7天
|
|
|
+ const end = new Date()
|
|
|
+ const start = new Date()
|
|
|
+ start.setDate(start.getDate() - 7)
|
|
|
+ historyDateRange.value = [formatDate(start), formatDate(end)]
|
|
|
+ historyData.value = []
|
|
|
+ historyDialogVisible.value = true
|
|
|
+ loadHistoryData()
|
|
|
+}
|
|
|
+
|
|
|
+// 加载历史数据
|
|
|
+function loadHistoryData() {
|
|
|
+ if (!currentStation.value || !historyDateRange.value || historyDateRange.value.length !== 2) return
|
|
|
+ historyLoading.value = true
|
|
|
+ const [startTm, endTm] = historyDateRange.value
|
|
|
+ const config = STATION_BIZ_CONFIG[currentStation.value.tp]
|
|
|
+ const bizCode = config ? config.bizCode : null
|
|
|
+ listData(currentStation.value.stcd, startTm, endTm, bizCode).then(res => {
|
|
|
+ if (res.code === 200) {
|
|
|
+ historyData.value = res.data || []
|
|
|
+ } else {
|
|
|
+ historyData.value = []
|
|
|
+ }
|
|
|
+ historyLoading.value = false
|
|
|
+ nextTick(() => initHistoryChart())
|
|
|
+ }).catch(() => {
|
|
|
+ historyData.value = []
|
|
|
+ historyLoading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化历史数据图表
|
|
|
+function initHistoryChart() {
|
|
|
+ if (!historyChartRef.value) return
|
|
|
+ if (!historyChart) {
|
|
|
+ historyChart = echarts.init(historyChartRef.value)
|
|
|
+ }
|
|
|
+ if (!historyData.value || historyData.value.length === 0) {
|
|
|
+ historyChart.clear()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const config = STATION_BIZ_CONFIG[currentStation.value.tp]
|
|
|
+ if (!config || !config.historyChart || config.historyChart.length === 0) {
|
|
|
+ historyChart.clear()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const chartFields = config.historyChart
|
|
|
+ const sortedData = [...historyData.value].sort((a, b) => new Date(a.tm) - new Date(b.tm))
|
|
|
+ const xData = sortedData.map(d => fmtDateTime(null, null, d.tm))
|
|
|
+ const series = chartFields.map(field => {
|
|
|
+ const col = historyColumns.find(c => c.prop === field)
|
|
|
+ return {
|
|
|
+ name: col ? col.label : field,
|
|
|
+ type: 'line',
|
|
|
+ data: sortedData.map(d => d[field] ?? null),
|
|
|
+ smooth: true,
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 4,
|
|
|
+ lineStyle: { width: 2 }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const option = {
|
|
|
+ tooltip: { trigger: 'axis' },
|
|
|
+ legend: { data: series.map(s => s.name), bottom: 0 },
|
|
|
+ grid: { left: '10%', right: '5%', top: '10%', bottom: '15%' },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: xData,
|
|
|
+ axisLabel: { rotate: 30, fontSize: 11 }
|
|
|
+ },
|
|
|
+ yAxis: { type: 'value' },
|
|
|
+ series
|
|
|
+ }
|
|
|
+ historyChart.setOption(option, true)
|
|
|
+}
|
|
|
+
|
|
|
function handlePointerMove(event) {
|
|
|
const coord = toLonLat(event.coordinate)
|
|
|
coordDisplay.lng = coord[0].toFixed(4)
|
|
|
@@ -330,7 +566,7 @@ async function loadTooltipData(station) {
|
|
|
const data = await getLatestDataWithCache(station.stcd)
|
|
|
if (currentHoverStcd !== station.stcd) return
|
|
|
if (data) {
|
|
|
- tooltipData.tm = data.tm || '-'
|
|
|
+ tooltipData.tm = fmtDate(data.tm)
|
|
|
tooltipData.values = buildLatestValues(station.tp, data)
|
|
|
} else {
|
|
|
tooltipData.tm = '暂无数据'
|
|
|
@@ -554,6 +790,27 @@ watch(activeTab, (val) => {
|
|
|
border-radius: 50%;
|
|
|
}
|
|
|
|
|
|
+.layer-switch {
|
|
|
+ position: absolute;
|
|
|
+ top: 12px;
|
|
|
+ left: 12px;
|
|
|
+ z-index: 100;
|
|
|
+}
|
|
|
+
|
|
|
+.layer-switch :deep(.el-radio-button__inner) {
|
|
|
+ background: rgba(255, 255, 255, 0.9);
|
|
|
+ border-color: #dcdfe6;
|
|
|
+ color: #606266;
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 6px 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.layer-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
|
|
|
+ background: #1c97e7;
|
|
|
+ border-color: #1c97e7;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
.coord-display {
|
|
|
position: absolute;
|
|
|
bottom: 8px;
|
|
|
@@ -596,6 +853,12 @@ watch(activeTab, (val) => {
|
|
|
color: #e8e8e8;
|
|
|
}
|
|
|
|
|
|
+.map-tooltip .tooltip-divider {
|
|
|
+ height: 1px;
|
|
|
+ background: rgba(255, 255, 255, 0.15);
|
|
|
+ margin: 4px 0;
|
|
|
+}
|
|
|
+
|
|
|
.map-tooltip .tooltip-row b {
|
|
|
color: #fff;
|
|
|
margin: 0 2px;
|
|
|
@@ -631,4 +894,59 @@ watch(activeTab, (val) => {
|
|
|
gap: 10px;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+.history-content {
|
|
|
+ display: flex;
|
|
|
+ gap: 16px;
|
|
|
+ height: 520px;
|
|
|
+}
|
|
|
+
|
|
|
+.history-left {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.history-right {
|
|
|
+ width: 45%;
|
|
|
+ flex-shrink: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-header .toolbar-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.history-chart {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-state {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 40px 0;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-icon {
|
|
|
+ font-size: 48px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
</style>
|