|
|
@@ -0,0 +1,384 @@
|
|
|
+<template>
|
|
|
+ <div class="weather-detail">
|
|
|
+ <custom-header name="气象详情"></custom-header>
|
|
|
+
|
|
|
+ <div class="weather-detail-content">
|
|
|
+ <!-- 顶部农场信息 -->
|
|
|
+ <div class="farm-card">
|
|
|
+ <div class="farm-right">
|
|
|
+ <img class="farm-point" src="@/assets/img/home/farm-point.png" alt="">
|
|
|
+ </div>
|
|
|
+ <div class="farm-left">
|
|
|
+ <div class="farm-name-row">
|
|
|
+ <span class="farm-name">{{ farmInfo.name }}</span>
|
|
|
+ <span class="farm-tag">桂味</span>
|
|
|
+ </div>
|
|
|
+ <div class="farm-address">
|
|
|
+ {{ farmInfo.address }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="weather-chart-container">
|
|
|
+ <div class="weather-chart-wrapper">
|
|
|
+ <div
|
|
|
+ class="weather-chart-slider"
|
|
|
+ :style="{ transform: `translateX(-${currentChartIndex * 50}%)` }"
|
|
|
+ >
|
|
|
+ <div class="weather-chart-item">
|
|
|
+ <weather-chart :key="1" class="weather-chart" :weather-data="pastWeatherData"></weather-chart>
|
|
|
+ </div>
|
|
|
+ <div class="weather-chart-item">
|
|
|
+ <weather-chart :key="2" class="weather-chart" :weather-data="weatherData"></weather-chart>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 左箭头 -->
|
|
|
+ <div
|
|
|
+ v-if="currentChartIndex > 0"
|
|
|
+ class="weather-arrow weather-arrow-left"
|
|
|
+ @click="prevChart"
|
|
|
+ >
|
|
|
+ <el-icon><ArrowLeftBold /></el-icon>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右箭头 -->
|
|
|
+ <div
|
|
|
+ v-if="currentChartIndex < 1"
|
|
|
+ class="weather-arrow weather-arrow-right"
|
|
|
+ @click="nextChart"
|
|
|
+ >
|
|
|
+ <el-icon><ArrowRightBold /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 指标 Tab -->
|
|
|
+ <div class="tab-row">
|
|
|
+ <div
|
|
|
+ v-for="tab in tabs"
|
|
|
+ :key="tab.key"
|
|
|
+ class="tab-item"
|
|
|
+ :class="{ active: currentTab === tab.key }"
|
|
|
+ @click="currentTab = tab.key"
|
|
|
+ >
|
|
|
+ {{ tab.label }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 风险列表 -->
|
|
|
+ <div class="risk-list">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in currentRiskList"
|
|
|
+ :key="index"
|
|
|
+ class="risk-item"
|
|
|
+ >
|
|
|
+ <div class="risk-title-row">
|
|
|
+ <span class="risk-title">{{ item.title }}</span>
|
|
|
+ <span class="risk-level">{{ item.level }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="risk-desc">
|
|
|
+ {{ item.desc }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { computed, ref, onMounted } from "vue";
|
|
|
+import customHeader from "@/components/customHeader.vue";
|
|
|
+import weatherChart from "@/components/weatherChart.vue";
|
|
|
+import { useStore } from "vuex";
|
|
|
+import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue";
|
|
|
+const store = useStore();
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getWeatherData();
|
|
|
+});
|
|
|
+
|
|
|
+const farmInfo = ref({
|
|
|
+ name: "从化荔博园合作社",
|
|
|
+ address: "广东省广州市从化区某某街道",
|
|
|
+});
|
|
|
+
|
|
|
+const tabs = [
|
|
|
+ { key: "temp", label: "温度" },
|
|
|
+ { key: "hum", label: "湿度" },
|
|
|
+ { key: "light", label: "光照" },
|
|
|
+];
|
|
|
+
|
|
|
+const currentTab = ref("temp");
|
|
|
+
|
|
|
+// 简单静态示例数据,后续可替换为接口返回
|
|
|
+const riskMap = ref({
|
|
|
+ temp: [
|
|
|
+ {
|
|
|
+ title: "阴雨寡照",
|
|
|
+ level: "一级",
|
|
|
+ desc: "阴雨寡照湿度大,荔枝易染疾病,需加强田间通风防",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "阴雨寡照",
|
|
|
+ level: "一级",
|
|
|
+ desc: "阴雨寡照湿度大,荔枝易染疾病,需加强田间通风防",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "阴雨寡照",
|
|
|
+ level: "一级",
|
|
|
+ desc: "阴雨寡照湿度大,荔枝易染疾病,需加强田间通风防",
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ hum: [],
|
|
|
+ light: [],
|
|
|
+});
|
|
|
+
|
|
|
+const currentRiskList = computed(() => {
|
|
|
+ return riskMap.value[currentTab.value] || [];
|
|
|
+});
|
|
|
+
|
|
|
+const weatherData = ref(null);
|
|
|
+// 过去7天天气数据
|
|
|
+const pastWeatherData = ref(null);
|
|
|
+const currentChartIndex = ref(0); // 当前显示的图表索引,0表示第一个,1表示第二个
|
|
|
+
|
|
|
+// 切换到上一个图表(向左滑动)
|
|
|
+const prevChart = () => {
|
|
|
+ if (currentChartIndex.value > 0) {
|
|
|
+ currentChartIndex.value--;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 切换到下一个图表(向右滑动)
|
|
|
+const nextChart = () => {
|
|
|
+ if (currentChartIndex.value < 1) {
|
|
|
+ currentChartIndex.value++;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取天气数据
|
|
|
+function getWeatherData() {
|
|
|
+ const point = store.state.home.miniUserLocationPoint;
|
|
|
+ if (!point) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ VE_API.old_mini_map.get7d({ point }).then(({ data }) => {
|
|
|
+ if (data && data.daily && data.daily.length > 0) {
|
|
|
+ weatherData.value = data;
|
|
|
+ pastWeatherData.value = data;
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ // 获取天气数据失败,使用默认值
|
|
|
+ });
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.weather-detail {
|
|
|
+ width: 100%;
|
|
|
+ height: 100vh;
|
|
|
+ overflow: hidden;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: #F2F4F5;
|
|
|
+
|
|
|
+ .weather-detail-content {
|
|
|
+ height: calc(100% - 40px);
|
|
|
+ overflow: auto;
|
|
|
+ padding: 10px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.farm-card {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 12px 14px;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(15, 35, 52, 0.06);
|
|
|
+ margin-bottom: 10px;
|
|
|
+
|
|
|
+ .farm-left {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .farm-name-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+
|
|
|
+ .farm-name {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1d2129;
|
|
|
+ }
|
|
|
+
|
|
|
+ .farm-tag {
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 2px 8px;
|
|
|
+ border-radius: 2px;
|
|
|
+ background: #E8F3FF;
|
|
|
+ color: #2199f8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .farm-address {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(32, 32, 32, 0.45);
|
|
|
+ }
|
|
|
+
|
|
|
+ .farm-right {
|
|
|
+ margin-right: 10px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(37, 47, 56, 0.6);
|
|
|
+ flex: none;
|
|
|
+ .farm-point {
|
|
|
+ width: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.weather-chart-container {
|
|
|
+ position: relative;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 10px;
|
|
|
+ box-shadow: 0 2px 8px rgba(15, 35, 52, 0.06);
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .weather-chart-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-chart-slider {
|
|
|
+ display: flex;
|
|
|
+ transition: transform 0.3s ease-in-out;
|
|
|
+ width: 200%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-chart-item {
|
|
|
+ flex: 0 0 50%;
|
|
|
+ width: 50%;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 180px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-arrow {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: 16px;
|
|
|
+ height: 42px;
|
|
|
+ background: rgba(0, 0, 0, 0.27);
|
|
|
+ border-radius: 2px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ z-index: 10;
|
|
|
+ transition: background 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(130, 130, 130, 0.8);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ background: rgba(130, 130, 130, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-icon {
|
|
|
+ color: #ffffff;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-arrow-left {
|
|
|
+ left: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .weather-arrow-right {
|
|
|
+ right: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.tab-row {
|
|
|
+ display: inline-flex;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 3px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #f2f3f5;
|
|
|
+ margin: 10px 0;
|
|
|
+
|
|
|
+ .tab-item {
|
|
|
+ min-width: 52px;
|
|
|
+ padding: 3px 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #767676;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ background: #FFFFFF;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #2199F8;
|
|
|
+ color: #ffffff;
|
|
|
+ box-shadow: 0 2px 6px rgba(29, 125, 247, 0.45);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.risk-list {
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.risk-item {
|
|
|
+// background: #ffffff;
|
|
|
+// border-radius: 10px;
|
|
|
+// padding: 10px 12px;
|
|
|
+// box-shadow: 0 2px 8px rgba(15, 35, 52, 0.03);
|
|
|
+ margin-bottom: 12px;
|
|
|
+
|
|
|
+ .risk-title-row {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 6px;
|
|
|
+
|
|
|
+ .risk-title {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #1d2129;
|
|
|
+ }
|
|
|
+
|
|
|
+ .risk-level {
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 2px 10px;
|
|
|
+ border-radius: 2px;
|
|
|
+ background: #E8F3FF;
|
|
|
+ color: #2199F8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .risk-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #4e5969;
|
|
|
+ line-height: 1.5;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|