|
@@ -1,187 +1,523 @@
|
|
|
<template>
|
|
|
<div>
|
|
|
- <!-- 三个按钮 -->
|
|
|
- <div class="button-container">
|
|
|
- <!-- 优先保护类按钮 -->
|
|
|
- <el-button class="custom-button priority" @click="handleClick('优先保护类', '#4E94F4')">优先保护类</el-button>
|
|
|
- <!-- 安全利用类按钮 -->
|
|
|
- <el-button class="custom-button safe" @click="handleClick('安全利用类', '#26BB91')">安全利用类</el-button>
|
|
|
- <!-- 严格管控类按钮 -->
|
|
|
- <el-button class="custom-button strict" @click="handleClick('严格管控类', '#77B369')">严格管控类</el-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 文字和图表区域,只有选中按钮后才显示 -->
|
|
|
- <div v-show="selectedText" class="info-section">
|
|
|
- <!-- 显示选中的类别文字 -->
|
|
|
- <div class="selected-text" :style="{ color: selectedColor }">{{ selectedText }}</div>
|
|
|
-
|
|
|
- <!-- 图表区域 -->
|
|
|
- <div class="charts">
|
|
|
- <!-- 柱状图容器 -->
|
|
|
- <div ref="barChart" class="chart"></div>
|
|
|
- <!-- 饼图容器 -->
|
|
|
- <div ref="pieChart" class="chart"></div>
|
|
|
+ <!-- 顶部信息卡片区域 -->
|
|
|
+ <div class="dashboard">
|
|
|
+ <!-- 合并的统计与分布卡片 -->
|
|
|
+ <div class="dashboard-card combined-card">
|
|
|
+ <div class="card-title">统计与分布</div>
|
|
|
+ <div class="combined-content">
|
|
|
+ <!-- 左侧:单元统计 -->
|
|
|
+ <div class="statistics-section">
|
|
|
+ <h3>单元统计</h3>
|
|
|
+ <div class="statistics">
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-value">{{ statistics.total_units }}</div>
|
|
|
+ <div class="stat-label">总单元数</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-value">{{ statistics.units_with_data }}</div>
|
|
|
+ <div class="stat-label">有数据单元</div>
|
|
|
+ </div>
|
|
|
+ <!-- 仅当无数据单元数不为0时显示 -->
|
|
|
+ <div class="stat-item" v-if="statistics.units_without_data !== 0">
|
|
|
+ <div class="stat-value">{{ statistics.units_without_data }}</div>
|
|
|
+ <div class="stat-label">无数据单元</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右侧:分类分布 -->
|
|
|
+ <div class="distribution-section">
|
|
|
+ <h3>分类分布</h3>
|
|
|
+ <div class="distribution">
|
|
|
+ <div v-for="(count, category) in statistics.category_distribution" :key="category"
|
|
|
+ class="category-dist" :style="{ backgroundColor: categoryColors[category] }">
|
|
|
+ <div class="dist-category">{{ category }}</div>
|
|
|
+ <div class="dist-count">{{ count }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 饼图卡片 -->
|
|
|
+ <div class="dashboard-card chart-card">
|
|
|
+ <div class="card-title">点位分类分布</div>
|
|
|
+ <div ref="pointPieChart" class="chart"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 地图区域 -->
|
|
|
+ <div class="map-area">
|
|
|
+ <div class="map-title">土壤分类分布地图</div>
|
|
|
+ <div ref="mapContainer" class="map-container"></div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import * as echarts from 'echarts'; // 引入echarts库
|
|
|
+import * as echarts from 'echarts';
|
|
|
+
|
|
|
+// 分类颜色配置
|
|
|
+const categoryColors = {
|
|
|
+ '优先保护类': 'rgba(255, 214, 0, 0.7)', // #FFD600 转换为RGBA
|
|
|
+ '安全利用类': 'rgba(0, 200, 83, 0.7)', // #00C853 转换为RGBA
|
|
|
+ '严格管控类': 'rgba(213, 0, 0, 0.7)' // #D50000 转换为RGBA
|
|
|
+};
|
|
|
|
|
|
export default {
|
|
|
- name: 'CategoryButtons',
|
|
|
+ name: 'CategoryMap',
|
|
|
data() {
|
|
|
return {
|
|
|
- selectedText: '', // 当前选中的类别文字
|
|
|
- selectedColor: '', // 当前选中的类别颜色
|
|
|
- barChartInstance: null, // 柱状图实例
|
|
|
- pieChartInstance: null // 饼图实例
|
|
|
+ map: null,
|
|
|
+ geoJSONLayer: null,
|
|
|
+ multiPolygon: null,
|
|
|
+ categoryColors,
|
|
|
+ statistics: {
|
|
|
+ total_units: 0,
|
|
|
+ units_with_data: 0,
|
|
|
+ units_without_data: 0,
|
|
|
+ category_distribution: {
|
|
|
+ '优先保护类': 0,
|
|
|
+ '安全利用类': 0,
|
|
|
+ '严格管控类': 0
|
|
|
+ },
|
|
|
+ point_distribution: {
|
|
|
+ '优先保护类': 0,
|
|
|
+ '安全利用类': 0,
|
|
|
+ '严格管控类': 0
|
|
|
+ }
|
|
|
+ },
|
|
|
+ groupingData: [] // 存储接口数据
|
|
|
};
|
|
|
},
|
|
|
- mounted() {
|
|
|
- // 默认选中“优先保护类”
|
|
|
- this.handleClick('优先保护类', '#4E94F4');
|
|
|
-},
|
|
|
+ async mounted() {
|
|
|
+ // 获取分类数据
|
|
|
+ await this.fetchGroupingData();
|
|
|
+
|
|
|
+ // 初始化地图
|
|
|
+ await this.initMap();
|
|
|
+
|
|
|
+ // 初始化点位分布饼图
|
|
|
+ this.initPointPieChart();
|
|
|
+ },
|
|
|
methods: {
|
|
|
- // 按钮点击事件,设置类别和颜色,并生成随机数据
|
|
|
- handleClick(category, color) {
|
|
|
- this.selectedText = category;
|
|
|
- this.selectedColor = color;
|
|
|
-
|
|
|
- // 模拟柱状图数据
|
|
|
- const barData = [
|
|
|
- { name: 'A', value: Math.floor(Math.random() * 100) },
|
|
|
- { name: 'B', value: Math.floor(Math.random() * 100) },
|
|
|
- { name: 'C', value: Math.floor(Math.random() * 100) }
|
|
|
- ];
|
|
|
-
|
|
|
- // 模拟饼图数据
|
|
|
- const pieData = [
|
|
|
- { name: 'X', value: Math.floor(Math.random() * 100) },
|
|
|
- { name: 'Y', value: Math.floor(Math.random() * 100) },
|
|
|
- { name: 'Z', value: Math.floor(Math.random() * 100) }
|
|
|
- ];
|
|
|
-
|
|
|
- // 等DOM更新后初始化图表
|
|
|
- this.$nextTick(() => {
|
|
|
- this.initBarChart(barData);
|
|
|
- this.initPieChart(pieData);
|
|
|
+ // 获取分类数据
|
|
|
+ async fetchGroupingData() {
|
|
|
+ try {
|
|
|
+ const response = await fetch(`http://localhost:8000/api/unit-grouping/h_xtfx`);
|
|
|
+ const result = await response.json();
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ this.groupingData = result.data;
|
|
|
+ this.statistics = result.statistics;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取分类数据失败:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化地图
|
|
|
+ async initMap() {
|
|
|
+ // 加载TMap SDK
|
|
|
+ const TMap = await this.loadSDK();
|
|
|
+
|
|
|
+ // 创建地图实例
|
|
|
+ this.map = new TMap.Map(this.$refs.mapContainer, {
|
|
|
+ center: new TMap.LatLng(24.81088, 113.59762),
|
|
|
+ zoom: 12,
|
|
|
+ mapStyleId: 'style1'
|
|
|
});
|
|
|
+
|
|
|
+ // 加载GeoJSON数据
|
|
|
+ const geojsonData = await this.loadGeoJSON('https://soilgd.com/data/单元格.geojson');
|
|
|
+
|
|
|
+ // 初始化GeoJSON图层 - 传递TMap对象
|
|
|
+ this.initMapWithGeoJSON(geojsonData, TMap);
|
|
|
},
|
|
|
-
|
|
|
- // 初始化柱状图
|
|
|
- initBarChart(data) {
|
|
|
- // 如果实例不存在则初始化
|
|
|
- if (!this.barChartInstance) {
|
|
|
- this.barChartInstance = echarts.init(this.$refs.barChart);
|
|
|
- }
|
|
|
- // 柱状图配置项
|
|
|
- const option = {
|
|
|
- title: { text: '柱状图', left: 'center' },
|
|
|
- xAxis: { type: 'category', data: data.map(d => d.name) },
|
|
|
- yAxis: { type: 'value' },
|
|
|
- series: [
|
|
|
- {
|
|
|
- data: data.map(d => d.value),
|
|
|
- type: 'bar',
|
|
|
- itemStyle: {
|
|
|
- color: this.selectedColor // 使用选中的颜色
|
|
|
- }
|
|
|
+
|
|
|
+ // 加载SDK
|
|
|
+ loadSDK() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ if (window.TMap) return resolve(window.TMap);
|
|
|
+
|
|
|
+ const script = document.createElement('script');
|
|
|
+ script.src = `https://map.qq.com/api/gljs?v=2.exp&libraries=basic,service,vector&key=${import.meta.env.VITE_TMAP_KEY}&callback=initTMap`;
|
|
|
+
|
|
|
+ window.initTMap = () => {
|
|
|
+ if (!window.TMap) {
|
|
|
+ reject(new Error('TMap SDK 加载失败'));
|
|
|
+ return;
|
|
|
}
|
|
|
- ]
|
|
|
- };
|
|
|
- // 设置配置项
|
|
|
- this.barChartInstance.setOption(option);
|
|
|
+ resolve(window.TMap);
|
|
|
+ };
|
|
|
+
|
|
|
+ script.onerror = (err) => {
|
|
|
+ reject(new Error('加载地图SDK失败'));
|
|
|
+ document.head.removeChild(script);
|
|
|
+ };
|
|
|
+
|
|
|
+ document.head.appendChild(script);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载GeoJSON数据
|
|
|
+ async loadGeoJSON(url) {
|
|
|
+ try {
|
|
|
+ const response = await fetch(url);
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`加载GeoJSON失败: ${response.statusText}`);
|
|
|
+ }
|
|
|
+ return await response.json();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载GeoJSON数据失败:', error);
|
|
|
+ return { type: 'FeatureCollection', features: [] };
|
|
|
+ }
|
|
|
},
|
|
|
+
|
|
|
+ // 初始化GeoJSON图层 - 使用MultiPolygon的setStyles方法
|
|
|
+ initMapWithGeoJSON(geojsonData, TMap) {
|
|
|
+ try {
|
|
|
+ // 创建分类映射表
|
|
|
+ const categoryMap = {};
|
|
|
+ this.groupingData.forEach(item => {
|
|
|
+ categoryMap[item.OBJECTID] = item.h_xtfx;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 处理GeoJSON特征
|
|
|
+ geojsonData.features.forEach(feature => {
|
|
|
+ const objectId = feature.properties.OBJECTID;
|
|
|
+ const category = categoryMap[objectId];
|
|
|
+
|
|
|
+ // 添加分类属性
|
|
|
+ feature.properties.category = category;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 检查TMap对象是否有效
|
|
|
+ if (!TMap || !TMap.PolygonStyle) {
|
|
|
+ throw new Error('TMap对象无效,缺少PolygonStyle');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建GeoJSON图层
|
|
|
+ this.geoJSONLayer = new TMap.vector.GeoJSONLayer({
|
|
|
+ map: this.map,
|
|
|
+ data: geojsonData,
|
|
|
+ polygonStyle: new TMap.PolygonStyle({
|
|
|
+ color: 'rgba(0,0,0,0)',
|
|
|
+ showBorder: false
|
|
|
+ })
|
|
|
+ });
|
|
|
|
|
|
- // 初始化饼图
|
|
|
- initPieChart(data) {
|
|
|
- // 如果实例不存在则初始化
|
|
|
- if (!this.pieChartInstance) {
|
|
|
- this.pieChartInstance = echarts.init(this.$refs.pieChart);
|
|
|
+
|
|
|
+ // 获取多边形覆盖层
|
|
|
+ this.multiPolygon = this.geoJSONLayer.getGeometryOverlay('polygon');
|
|
|
+ this.multiPolygon.setMap(this.map);
|
|
|
+ const polygons = this.multiPolygon.getGeometries();
|
|
|
+
|
|
|
+ // 创建样式映射
|
|
|
+ const styles = {};
|
|
|
+
|
|
|
+ // 遍历所有多边形,为每个多边形设置样式和唯一ID
|
|
|
+ // 遍历所有多边形,为每个多边形设置样式
|
|
|
+ polygons.forEach((polygon) => {
|
|
|
+ // 直接访问properties属性
|
|
|
+ const properties = polygon.properties;
|
|
|
+ const category = properties.category;
|
|
|
+
|
|
|
+ // 使用多边形的id作为样式ID的键
|
|
|
+ const styleId = `style_${polygon.id}`;
|
|
|
+
|
|
|
+ // 根据分类设置颜色
|
|
|
+ const color = category ? this.categoryColors[category] : '#CCCCCC';
|
|
|
+
|
|
|
+ // 添加样式到映射表
|
|
|
+ styles[styleId] = new TMap.PolygonStyle({
|
|
|
+ color: color,
|
|
|
+ showBorder: true,
|
|
|
+ borderColor: '#000000',
|
|
|
+ borderWidth: 2
|
|
|
+ });
|
|
|
+
|
|
|
+ // 关键修复:为每个多边形设置样式ID(正确方式)
|
|
|
+ polygon.styleId = styleId; // 直接设置属性
|
|
|
+ });
|
|
|
+ // 使用setStyles方法一次性设置所有样式
|
|
|
+ this.multiPolygon.setStyles(styles);
|
|
|
+
|
|
|
+ // 更新几何体以应用新样式
|
|
|
+ this.multiPolygon.updateGeometries(polygons);
|
|
|
+
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('初始化GeoJSON图层失败:', error);
|
|
|
}
|
|
|
- // 饼图配置项
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化点位分布饼图
|
|
|
+ initPointPieChart() {
|
|
|
+ const chartDom = this.$refs.pointPieChart;
|
|
|
+ if (!chartDom) return;
|
|
|
+
|
|
|
+ const chart = echarts.init(chartDom);
|
|
|
+
|
|
|
+ // 准备饼图数据
|
|
|
+ const pieData = Object.entries(this.statistics.category_distribution).map(([name, value]) => ({
|
|
|
+ name,
|
|
|
+ value,
|
|
|
+ itemStyle: { color: this.categoryColors[name] || '#CCCCCC' }
|
|
|
+ }));
|
|
|
+
|
|
|
const option = {
|
|
|
- title: { text: '饼图', left: 'center' },
|
|
|
- tooltip: { trigger: 'item' },
|
|
|
+ title: {
|
|
|
+ text: '点位分类分布',
|
|
|
+ left: 'center',
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 18,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{b}: {c} ({d}%)'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 10,
|
|
|
+ data: Object.keys(this.statistics.point_distribution)
|
|
|
+ },
|
|
|
series: [
|
|
|
{
|
|
|
+ name: '点位分布',
|
|
|
type: 'pie',
|
|
|
- radius: '60%',
|
|
|
- data,
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ center: ['50%', '45%'],
|
|
|
+ avoidLabelOverlap: true,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 10,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: '{b}: {c}\n({d}%)',
|
|
|
+ fontSize: 14
|
|
|
+ },
|
|
|
emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ },
|
|
|
itemStyle: {
|
|
|
shadowBlur: 10,
|
|
|
shadowOffsetX: 0,
|
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
+ data: pieData
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
- // 设置配置项
|
|
|
- this.pieChartInstance.setOption(option);
|
|
|
+
|
|
|
+ chart.setOption(option);
|
|
|
+
|
|
|
+ // 响应式调整
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ chart.resize();
|
|
|
+ });
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
+ }
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.button-container {
|
|
|
+/* 顶部信息卡片区域 */
|
|
|
+.dashboard {
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 56px 350px 0 350px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 20px;
|
|
|
+ margin-bottom: 20px;
|
|
|
}
|
|
|
|
|
|
-.custom-button {
|
|
|
- width: 320px;
|
|
|
- height: 120px;
|
|
|
- font-size: 36px;
|
|
|
- font-weight: bold;
|
|
|
- color: #fff;
|
|
|
- border: none;
|
|
|
- border-radius: 8px;
|
|
|
+.dashboard-card {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 280px;
|
|
|
+ background: white;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ padding: 20px;
|
|
|
+ transition: transform 0.3s ease;
|
|
|
}
|
|
|
|
|
|
-.priority {
|
|
|
- background-color: #4E94F4; /* 优先保护类按钮颜色 */
|
|
|
+.dashboard-card:hover {
|
|
|
+ transform: translateY(-3px);
|
|
|
}
|
|
|
|
|
|
-.safe {
|
|
|
- background-color: #26BB91; /* 安全利用类按钮颜色 */
|
|
|
+.chart-card {
|
|
|
+ min-width: 350px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
|
|
|
|
-.strict {
|
|
|
- background-color: #77B369; /* 严格管控类按钮颜色 */
|
|
|
+.combined-card {
|
|
|
+ min-width: 500px;
|
|
|
}
|
|
|
|
|
|
-.info-section {
|
|
|
- margin-top: 30px;
|
|
|
- margin-left: 50px;
|
|
|
+.card-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1a1a1a;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
-.selected-text {
|
|
|
- font-size: 36px;
|
|
|
- font-weight: bold;
|
|
|
- margin-bottom: 20px;
|
|
|
+/* 合并内容样式 */
|
|
|
+.combined-content {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-section, .distribution-section {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-section h3, .distribution-section h3 {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ color: #409EFF;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
|
|
|
-.charts {
|
|
|
+/* 统计信息样式 */
|
|
|
+.statistics {
|
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
- gap: 100px; /* 图表之间的间距 */
|
|
|
- flex-wrap: wrap; /* 可选:小屏幕时换行显示 */
|
|
|
- margin-top: 20px;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-item {
|
|
|
+ text-align: center;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f8fafc;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-item:hover {
|
|
|
+ transform: translateY(-3px);
|
|
|
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-value {
|
|
|
+ font-size: 26px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #409EFF;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+/* 分类分布样式 */
|
|
|
+.distribution {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.category-dist {
|
|
|
+ padding: 15px;
|
|
|
+ border-radius: 8px;
|
|
|
+ color: white;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
|
|
+ transition: transform 0.2s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.category-dist:hover {
|
|
|
+ transform: scale(1.02);
|
|
|
}
|
|
|
|
|
|
+.dist-category {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.dist-count {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+/* 饼图样式 */
|
|
|
.chart {
|
|
|
- width: 600px;
|
|
|
- height: 450px;
|
|
|
- background-color: #fff;
|
|
|
+ height: 300px;
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 地图区域样式 */
|
|
|
+.map-area {
|
|
|
+ margin-top: 20px;
|
|
|
+ background: white;
|
|
|
border-radius: 12px;
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.map-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ padding: 15px 25px;
|
|
|
+ background-color: #f8fafc;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
}
|
|
|
|
|
|
+.map-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 60vh; /* 调整为60vh,更紧凑 */
|
|
|
+ min-height: 400px; /* 降低最小高度 */
|
|
|
+ max-height: 700px; /* 添加最大高度限制 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式调整 */
|
|
|
+@media (max-width: 992px) {
|
|
|
+ .dashboard {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .dashboard-card {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .combined-content {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .combined-card, .chart-card {
|
|
|
+ min-width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 移动设备上地图高度调整 */
|
|
|
+ .map-container {
|
|
|
+ height: 55vh;
|
|
|
+ min-height: 350px;
|
|
|
+ max-height: 600px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 小屏幕设备进一步调整 */
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .map-container {
|
|
|
+ height: 50vh;
|
|
|
+ min-height: 300px;
|
|
|
+ max-height: 500px;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|