Browse Source

Merge branch 'farmer' of http://www.sysuimars.cn:3000/feiniao/feiniao-farm-h5 into farmer

wangsisi 6 days ago
parent
commit
974635634e

+ 106 - 0
src/views/old_mini/home/index.vue

@@ -20,6 +20,33 @@
                 </div>
             </div>
         </div>
+
+        <div class="task-list">
+            <div class="task-title">待办任务</div>
+            <div class="bottom-tag">
+                <div class="tag-card">
+                    <div class="card-content">
+                        <div class="card-main-text">干旱风险</div>
+                        <div class="card-sub-text">
+                            气象风险
+                        </div>
+                    </div>
+                </div>
+                <div class="tag-card active">
+                    <div class="card-content">
+                        <div class="card-main-text">当天</div>
+                        <div class="card-sub-text">新梢长势评估</div>
+                    </div>
+                </div>
+                <div class="tag-card">
+                    <div class="card-content">
+                        <div class="card-main-text">3天后</div>
+                        <div class="card-sub-text">白点催醒</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
         <knowledge-card />
         <!-- <template v-if="userType == 2">
         </template> -->
@@ -379,5 +406,84 @@ const handleBannerClick = () => {
             }
         }
     }
+
+    .task-list {
+        padding: 0 10px 10px;
+        .task-title {
+            color: #000;
+            margin: 6px 0 14px 0;
+            font-weight: 500;
+            font-size: 16px;
+            line-height: 22px;
+            position: relative;
+            padding-left: 9px;
+            &::after {
+                content: "";
+                position: absolute;
+                left: 0;
+                top: 50%;
+                transform: translateY(-50%);
+                width: 3px;
+                height: 15px;
+                background: #2199f8;
+                border-radius: 20px;
+            }
+        }
+        .bottom-tag {
+            display: flex;
+            gap: 4px;
+            .tag-card {
+                flex: 1;
+                border-radius: 6px;
+                padding: 8px 4px;
+                box-sizing: border-box;
+                background: rgba(201, 201, 201, 0.1);
+                border: 0.5px solid transparent;
+                .card-content {
+                    display: flex;
+                    flex-direction: column;
+                    align-items: center;
+                    justify-content: center;
+                    text-align: center;
+                    height: 100%;
+                    .card-main-text {
+                        font-size: 18px;
+                        line-height: 28px;
+                        font-weight: 400;
+                        color: #1D2129;
+                        margin-bottom: 2px;
+                    }
+                    .card-sub-text {
+                        font-size: 14px;
+                        line-height: 22px;
+                        color: #4E5969;
+                        display: flex;
+                        align-items: center;
+                        gap: 4px;
+                        .card-icon {
+                            display: inline-flex;
+                            align-items: center;
+                            justify-content: center;
+                            width: 12px;
+                            height: 12px;
+                            color: #2199f8;
+                        }
+                    }
+                }
+                &.active {
+                    background: rgba(33, 153, 248, 0.1);
+                    border-color: #2199f8;
+                    .card-content {
+                        .card-main-text {
+                            color: #2199f8;
+                        }
+                        .card-sub-text {
+                            color: #2199f8;
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
 </style>

+ 178 - 0
src/views/old_mini/interactionList/components/examplePopup.vue

@@ -0,0 +1,178 @@
+<template>
+    <popup v-model:show="localShow" class="example-popup" :overlay-style="{ backdropFilter: 'blur(4px)' }">
+        <div class="example-content">
+            <div class="example-swipe-wrap">
+                <Swipe
+                    ref="exampleSwipeRef"
+                    :show-indicators="false"
+                    v-model="activeIndex"
+                    :autoplay="0"
+                    indicator-color="#2199F8"
+                >
+                    <SwipeItem v-for="img in images" :key="img">
+                        <div class="img-tag">{{ title }}</div>
+                        <img class="example-img" :src="img" alt="" />
+                    </SwipeItem>
+                </Swipe>
+
+                <!-- 左箭头 -->
+                <div
+                    v-if="images.length > 1 && activeIndex > 0"
+                    class="example-arrow arrow-left"
+                    @click.stop="prevExample"
+                >
+                    <el-icon>
+                        <ArrowLeftBold color="#F0D09C" />
+                    </el-icon>
+                </div>
+                <!-- 右箭头 -->
+                <div
+                    v-if="images.length > 1 && activeIndex < images.length - 1"
+                    class="example-arrow arrow-right"
+                    @click.stop="nextExample"
+                >
+                    <el-icon>
+                        <ArrowRightBold color="#F0D09C" />
+                    </el-icon>
+                </div>
+            </div>
+
+            <div class="example-tips">
+                {{ tips }}
+            </div>
+        </div>
+    </popup>
+</template>
+
+<script setup>
+import { ref, watch, computed } from "vue";
+import { Popup, Swipe, SwipeItem } from "vant";
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+    images: {
+        type: Array,
+        default: () => [],
+    },
+    startIndex: {
+        type: Number,
+        default: 0,
+    },
+    title: {
+        type: String,
+        default: "示例图",
+    },
+    tips: {
+        type: String,
+        default: "拍摄要求:请采集代表农场作物物候期的照片,请采集代表农场作物物候期的照片。",
+    },
+});
+
+const emit = defineEmits(["update:show"]);
+
+const exampleSwipeRef = ref(null);
+const activeIndex = ref(0);
+
+// v-model:show 双向绑定
+const localShow = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+// 当打开弹窗或 startIndex 变化时,设置当前索引
+watch(
+    () => [props.show, props.startIndex],
+    ([show, start]) => {
+        if (show) {
+            activeIndex.value = start || 0;
+            if (exampleSwipeRef.value) {
+                exampleSwipeRef.value.swipeTo(activeIndex.value);
+            }
+        }
+    }
+);
+
+const prevExample = () => {
+    if (!props.images.length) return;
+    activeIndex.value = (activeIndex.value - 1 + props.images.length) % props.images.length;
+    exampleSwipeRef.value && exampleSwipeRef.value.swipeTo(activeIndex.value);
+};
+
+const nextExample = () => {
+    if (!props.images.length) return;
+    activeIndex.value = (activeIndex.value + 1) % props.images.length;
+    exampleSwipeRef.value && exampleSwipeRef.value.swipeTo(activeIndex.value);
+};
+</script>
+
+<style scoped lang="scss">
+.example-popup {
+    width: 100%;
+    border-radius: 0;
+    background: none;
+    max-width: 100%;
+
+    .example-content {
+        text-align: center;
+
+        .example-swipe-wrap {
+            position: relative;
+        }
+
+        .example-img {
+            width: 100%;
+        }
+
+        .img-tag {
+            position: absolute;
+            top: 0;
+            left: 0;
+            padding: 0 10px;
+            height: 28px;
+            line-height: 28px;
+            font-size: 12px;
+            color: #fff;
+            background: rgba(0, 0, 0, 0.7);
+            border-radius: 0 0 10px 0;
+            z-index: 2;
+        }
+
+        .example-arrow {
+            position: absolute;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 32px;
+            height: 32px;
+            border-radius: 50%;
+            background: rgba(0, 0, 0, 0.45);
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            z-index: 3;
+        }
+
+        .arrow-left {
+            left: 8px;
+        }
+
+        .arrow-right {
+            right: 8px;
+        }
+    }
+
+    .example-tips {
+        margin: 16px 12px 6px 12px;
+        background: #3d3d3d;
+        padding: 8px 10px;
+        border-radius: 4px;
+        backdrop-filter: blur(4px);
+        color: #fff;
+        font-size: 14px;
+        line-height: 21px;
+        text-align: left;
+    }
+}
+</style>

+ 257 - 0
src/views/old_mini/interactionList/components/morePopup.vue

@@ -0,0 +1,257 @@
+<template>
+    <popup v-model:show="showMore" class="more-popup" round>
+        <div class="more-content">
+            <!-- 搜索框 -->
+            <div class="search-bar">
+                <el-icon class="search-icon">
+                    <Search />
+                </el-icon>
+                <input
+                    v-model="keyword"
+                    class="search-input"
+                    type="text"
+                    placeholder="请输入病虫害名称"
+                />
+            </div>
+
+            <!-- 类型切换 -->
+            <div class="type-tabs">
+                <div
+                    class="tab-item all-tab"
+                    :class="{ active: activeType === 'all' }"
+                    @click="changeType('all')"
+                >
+                    全部
+                </div>
+                <div
+                    class="tab-item"
+                    :class="{ active: activeType === 'disease' }"
+                    @click="changeType('disease')"
+                >
+                    病害
+                </div>
+                <div
+                    class="tab-item"
+                    :class="{ active: activeType === 'pest' }"
+                    @click="changeType('pest')"
+                >
+                    虫害
+                </div>
+            </div>
+
+            <!-- 病虫害列表 -->
+            <div class="card-grid">
+                <div
+                    v-for="(item, key) in filteredList"
+                    :key="key"
+                    class="card-item"
+                    @click="showExample(filteredList, item, key)"
+                >
+                    <div class="thumb-wrap">
+                        <img class="thumb" :src="item" :alt="key" />
+                    </div>
+                    <div class="card-name">{{ item.name || "病害" }}</div>
+                </div>
+                <div v-if="!filteredList.length" class="empty-text">暂无数据</div>
+            </div>
+        </div>
+    </popup>
+
+    <!-- 示例照片轮播组件 -->
+    <example-popup
+        v-model:show="showExamplePopup"
+        :images="exampleList"
+        :start-index="exampleStartIndex"
+        :title="exampleTitle"
+    />
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Popup } from "vant";
+import { Search } from "@element-plus/icons-vue";
+import ExamplePopup from "./examplePopup.vue";
+
+const showMore = ref(false);
+const keyword = ref("");
+const activeType = ref("all"); // all / disease / pest
+
+// 病虫害数据(后续可通过接口或外部传入)
+const items = ref([
+    // 示例数据,占位用,后续可替换为接口返回
+    {
+        id: 1,
+        name: "蒂蛀虫",
+        type: "pest",
+        image: "https://birdseye-img-ali-cdn.sysuimars.com/birdseye-look-mini/94379/1768801082504.png",
+        images: [
+            "https://birdseye-img-ali-cdn.sysuimars.com/birdseye-look-mini/94379/1768801082504.png",
+        ],
+    },
+]);
+
+const filteredList = computed(() => {
+    let list = items.value || [];
+    if (activeType.value !== "all") {
+        list = list.filter((it) => it.type === activeType.value);
+    }
+    if (keyword.value.trim()) {
+        const k = keyword.value.trim().toLowerCase();
+        list = list.filter((it) => (it.name || "").toLowerCase().includes(k));
+    }
+    return list;
+});
+
+const openPopup = () => {
+    showMore.value = true;
+};
+
+const changeType = (type) => {
+    activeType.value = type;
+};
+
+// 预留给外部设置数据的能力
+const setItems = (list) => {
+    items.value = Array.isArray(list) ? list : [];
+};
+
+// 示例轮播相关
+const showExamplePopup = ref(false);
+const exampleList = ref([]);
+const exampleStartIndex = ref(0);
+const exampleTitle = ref("示例图");
+
+const showExample = (list, item, index) => {
+    exampleList.value = list;
+    exampleStartIndex.value = index;
+    exampleTitle.value = item.name || "示例图";
+    showExamplePopup.value = true;
+};
+
+defineExpose({
+    openPopup,
+    setItems,
+});
+</script>
+
+<style lang="scss" scoped>
+.more-popup {
+    width: calc(100% - 40px);
+    border-radius: 16px;
+    background: #fff;
+    padding: 16px 0 20px 16px;
+    box-sizing: border-box;
+    max-height: 90%;
+    overflow: hidden;
+}
+
+.more-content {
+    display: flex;
+    flex-direction: column;
+    max-height: 100%;
+}
+
+.search-bar {
+    display: flex;
+    align-items: center;
+    border: 0.5px solid rgba(0, 0, 0, 0.2);
+    border-radius: 20px;
+    padding: 6px 12px;
+    margin-bottom: 16px;
+    margin-right: 16px;
+
+    .search-icon {
+        font-size: 18px;
+        color: #c0c4cc;
+        margin-right: 6px;
+    }
+
+    .search-input {
+        flex: 1;
+        border: none;
+        outline: none;
+        background: transparent;
+        font-size: 14px;
+        color: rgba(0, 0, 0, 0.5);
+    }
+
+    .search-input::placeholder {
+        color: rgba(0, 0, 0, 0.5);
+    }
+}
+
+.type-tabs {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 16px;
+    padding-right: 16px;
+
+    .tab-item {
+        flex: 1;
+        text-align: center;
+        height: 32px;
+        line-height: 32px;
+        border-radius: 999px;
+        font-size: 15px;
+        background: rgba(218, 218, 218, 0.2);
+        color: #000;
+        &.all-tab {
+            flex: none;
+            width: 73px;
+        }
+    }
+
+    .tab-item.active {
+        background: #2199f8;
+        color: #fff;
+    }
+}
+
+.card-grid {
+    padding-right: 16px;
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    grid-row-gap: 12px;
+    grid-column-gap: 5px;
+    margin-top: 4px;
+    padding-bottom: 4px;
+    overflow-y: auto;
+    /* 让列表区域在内容较多时内部滚动 */
+    max-height: 70vh;
+
+    .card-item {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+    }
+
+    .thumb-wrap {
+        width: 100%;
+        border-radius: 8px;
+        overflow: hidden;
+        background: #f5f5f5;
+    }
+
+    .thumb {
+        width: 100%;
+        height: 76px;
+        object-fit: cover;
+        display: block;
+    }
+
+    .card-name {
+        margin-top: 5px;
+        font-size: 12px;
+        color: #000;
+        text-align: center;
+    }
+
+    .empty-text {
+        grid-column: 1 / -1;
+        text-align: center;
+        font-size: 14px;
+        color: #999;
+        padding: 16px 0;
+    }
+}
+</style>

+ 28 - 46
src/views/old_mini/interactionList/index.vue

@@ -36,11 +36,11 @@
                     <div class="example-wrapper">
                         <div class="example-header">
                             <div>示例照片</div>
-                            <div class="more">查看更多</div>
+                            <div class="more" @click="openMorePopup(item.exampleImagesJson)">查看更多</div>
                         </div>
                         <div class="example-list" v-if="item.exampleImagesJson.length > 0">
-                            <div class="image-item-wrapper" v-for="example in item.exampleImagesJson" :key="example"
-                                @click="showExample(example)">
+                            <div class="image-item-wrapper" v-for="(example, exIndex) in item.exampleImagesJson"
+                                :key="example" @click="showExample(item.exampleImagesJson, exIndex)">
                                 <img class="image-item" :src="example" alt="" />
                             </div>
                         </div>
@@ -93,15 +93,13 @@
     <!-- 农场信息完善弹窗 -->
     <farm-info-popup :expertMiniUserId="query.expertMiniUserId" v-model:show="showFarmInfoPopup" />
 
-    <!-- 示例照片 -->
-    <popup v-model:show="showExamplePopup" class="example-popup" :overlay-style="{ backdropFilter: 'blur(4px)' }">
-        <div class="example-content">
-            <img class="example-img" :src="exampleImgData" alt="" />
-            <div class="example-tips">
-                拍摄要求:请采集代表农场作物物候期的照片,请采集代表农场作物物候期的照片。
-            </div>
-        </div>
-    </popup>
+    <!-- 示例照片轮播组件 -->
+    <example-popup
+        v-model:show="showExamplePopup"
+        :images="exampleList"
+        :start-index="exampleStartIndex"
+        title="蒂蛀虫示例图"
+    />
     <!-- 照片上传进度 -->
     <popup v-model:show="showUploadProgressPopup" round class="upload-progress-popup">
         <div class="upload-progress-title">
@@ -130,6 +128,9 @@
         </div>
         <div class="confirm-btn" @click="handleConfirmUpload">确认上传</div>
     </popup>
+
+    <!-- 查看更多弹窗 -->
+    <more-popup ref="morePopupRef" />
 </template>
 <script setup>
 import { ref, onMounted, onActivated } from "vue";
@@ -138,11 +139,13 @@ import { Uploader, Popup } from "vant";
 import customHeader from "@/components/customHeader.vue";
 import upload from "@/components/upload.vue";
 import FarmInfoPopup from "@/components/popup/farmInfoPopup.vue";
+import ExamplePopup from "./components/examplePopup.vue";
 import { useRouter, useRoute } from "vue-router";
 import { base_img_url2 } from "@/api/config";
 import DrawRegionMap from "./map/drawRegionMap.js";
 import UploadFile from "@/utils/upliadFile";
 import { getFileExt } from "@/utils/util";
+import MorePopup from "./components/morePopup.vue";
 
 
 const showFarmInfoPopup = ref(false);
@@ -150,7 +153,7 @@ const loading = ref(false);
 const router = useRouter();
 const listData = ref([]);
 const query = ref(useRoute().query);
-
+const morePopupRef = ref(null);
 //照片上传进度
 const showUploadProgressPopup = ref(false);
 const uploadFormData = ref({
@@ -216,11 +219,13 @@ const afterReadUpload = async (data) => {
         }, 100);
     }
 };
-//示例照片
+// 示例照片轮播
 const showExamplePopup = ref(false);
-const exampleImgData = ref(null);
-const showExample = (example) => {
-    exampleImgData.value = example;
+const exampleList = ref([]);
+const exampleStartIndex = ref(0);
+const showExample = (list, index) => {
+    exampleList.value = list || [];
+    exampleStartIndex.value = index || 0;
     showExamplePopup.value = true;
 };
 
@@ -241,7 +246,6 @@ const loadData = async () => {
                         item.exampleImagesJson = [];
                     }
                 } catch (e) {
-                    console.error('解析 exampleImagesJson 失败:', e);
                     item.exampleImagesJson = [];
                 }
             } else {
@@ -253,7 +257,7 @@ const loadData = async () => {
             };
         });
     } catch (error) {
-        console.error("加载数据失败", error);
+        // 加载数据失败时静默处理或在需要时提示
     } finally {
         loading.value = false;
     }
@@ -345,6 +349,11 @@ const handleUploadSuccess = (data) => {
     uploadData.value = data.imgArr;
 };
 
+const openMorePopup = (images) => {
+    morePopupRef.value.setItems([...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images, ...images]);
+    morePopupRef.value.openPopup();
+}
+
 onMounted(() => {
     // 初始化加载
     getFarmList();
@@ -651,33 +660,6 @@ const getFarmList = async () => {
     }
 }
 
-.example-popup {
-    width: 100%;
-    border-radius: 0;
-    background: none;
-    max-width: 100%;
-
-    .example-content {
-        text-align: center;
-
-        .example-img {
-            width: 100%;
-        }
-    }
-
-    .example-tips {
-        margin: 16px 12px 6px 12px;
-        background: #3d3d3d;
-        padding: 8px 10px;
-        border-radius: 4px;
-        backdrop-filter: blur(4px);
-        color: #fff;
-        font-size: 14px;
-        line-height: 21px;
-        text-align: left;
-    }
-}
-
 .upload-progress-popup {
     width: 100%;
     padding: 20px 16px;