Explorar o código

feat:修改农事规划页面和添加编辑弹窗

wangsisi hai 1 semana
pai
achega
c5a926802d

+ 455 - 89
src/components/popup/activeUploadPopup.vue

@@ -1,44 +1,113 @@
 <template>
     <popup
         class="active-upload-popup"
+        :class="{ 'interact-mode': mode === 'interact' }"
         v-model:show="show"
         closeable
         :close-on-click-overlay="false"
         @closed="handleClosed"
     >
-        <div class="header">
-            <div class="title">
-                <span class="required">*</span>
-                {{ problemTitle }}
+        <!-- 互动设置模式 -->
+        <template v-if="mode === 'interact'">
+            <div class="interact-header">
+                <div class="interact-title">{{ interactTitle }}</div>
             </div>
-            <div class="date-input">
-                <el-date-picker
-                    v-model="uploadDate"
-                    size="large"
-                    style="width: 100%"
-                    type="date"
-                    placeholder="请选择日期"
-                    :editable="false"
-                />
+            <div class="interact-form">
+                <div class="form-item">
+                    <div class="form-label">
+                        <span class="required">*</span>
+                        请选择互动时间
+                    </div>
+                    <div class="form-input-wrapper">
+                        <el-date-picker
+                            v-model="interactTime"
+                            size="large"
+                            style="width: 100%"
+                            type="date"
+                            placeholder="请选择日期"
+                            :editable="false"
+                        />
+                    </div>
+                </div>
+                <div class="form-item">
+                    <div class="form-label">
+                        <span class="required">*</span>
+                        请选择强制触发互动时间
+                    </div>
+                    <div class="form-input-wrapper">
+                        <el-date-picker
+                            v-model="forceTriggerTime"
+                            size="large"
+                            style="width: 100%"
+                            type="date"
+                            placeholder="请选择日期"
+                            :editable="false"
+                        />
+                    </div>
+                </div>
+                <div class="form-item">
+                    <div class="form-label">
+                        <span class="required">*</span>
+                        请设置互动问题
+                    </div>
+                    <el-input
+                        v-model="interactQuestion"
+                        type="textarea"
+                        :rows="4"
+                        placeholder="请设置互动问题"
+                        class="question-textarea"
+                    />
+                </div>
             </div>
-        </div>
-        <div class="header" v-if="needExecutor">
-            <div class="title">
-                <span class="required">*</span>
-                请确认执行人
+            <div class="interact-buttons">
+                <div class="btn-delete" @click="handleDeleteInteract">删除互动</div>
+                <div class="btn-save" :class="{ disabled: isUploading }" @click="handleSaveInteract">
+                    {{ saveButtonText }}
+                </div>
             </div>
-            <div class="date-input">
-                <el-select v-model="executorId" placeholder="请选择执行人">
-                    <el-option v-for="item in executorList" :key="item.id" :label="item.name" :value="item.id"></el-option>
-                </el-select>
+        </template>
+
+        <!-- 原有上传模式 -->
+        <template v-else>
+            <div class="header">
+                <div class="title">
+                    <span class="required">*</span>
+                    {{ problemTitle }}
+                </div>
+                <div class="date-input">
+                    <el-date-picker
+                        v-model="uploadDate"
+                        size="large"
+                        style="width: 100%"
+                        type="date"
+                        placeholder="请选择日期"
+                        :editable="false"
+                    />
+                </div>
             </div>
-        </div>
-        <div class="tips-text">上传照片,诊断更准确哦~</div>
-        <upload :textShow="true" class="upload-wrap" exampleImg>
-            <img class="example" src="@/assets/img/home/example-4.png" alt="" />
-            <img class="example" src="@/assets/img/home/plus.png" alt="" />
-        </upload>
-        <div class="btn" :class="{ 'disabled': isUploading }" @click="handleUpload">{{ isUploading ? '提交中...' : '确认' }}</div>
+            <div class="header" v-if="needExecutor">
+                <div class="title">
+                    <span class="required">*</span>
+                    请确认执行人
+                </div>
+                <div class="date-input">
+                    <el-select v-model="executorId" placeholder="请选择执行人">
+                        <el-option
+                            v-for="item in executorList"
+                            :key="item.id"
+                            :label="item.name"
+                            :value="item.id"
+                        ></el-option>
+                    </el-select>
+                </div>
+            </div>
+            <div class="tips-text">上传照片,诊断更准确哦~</div>
+            <upload :textShow="true" class="upload-wrap" exampleImg>
+                <img class="example" src="@/assets/img/home/example-4.png" alt="" />
+                <img class="example" src="@/assets/img/home/plus.png" alt="" />
+            </upload>
+            <div class="btn" :class="{ disabled: isUploading }" @click="handleUpload">{{ uploadButtonText }}</div>
+        </template>
     </popup>
 
     <!-- 上传成功提示弹窗 -->
@@ -54,10 +123,18 @@
 
 <script setup>
 import { Popup } from "vant";
-import { onMounted, onUnmounted, ref } from "vue";
+import { onMounted, onUnmounted, ref, computed } from "vue";
 import upload from "@/components/upload";
 import eventBus from "@/api/eventBus";
-import { ElMessage } from "element-plus";
+import { ElMessage, ElMessageBox } from "element-plus";
+
+// 常量定义
+const MODE = {
+    UPLOAD: "upload",
+    INTERACT: "interact",
+};
+
+// Props
 const props = defineProps({
     needExecutor: {
         type: Boolean,
@@ -65,103 +142,241 @@ const props = defineProps({
     },
 });
 
+// Emits
+const emit = defineEmits(["handleUploadSuccess", "handleDeleteInteract"]);
+
+// 响应式数据
 const show = ref(false);
 const gardenId = ref(null);
 const images = ref([]);
 const uploadDate = ref("");
 const problemTitle = ref("请选择问题");
 const successShow = ref(false);
-const isUploading = ref(false); // 标记是否正在上传中
-onMounted(() => {
-    eventBus.off("upload:changeArr", uploadChange);
-    eventBus.on("upload:changeArr", uploadChange);
-    eventBus.on("activeUpload:show", handleShow);
-    eventBus.on("activeUpload:success", handleSuccess);
-});
-
-function uploadChange(arr) {
-    images.value = arr;
-}
-
-function formatDate(date) {
-    let year = date.getFullYear();
-    let month = String(date.getMonth() + 1).padStart(2, "0");
-    let day = String(date.getDate()).padStart(2, "0");
-    return `${year}-${month}-${day}`;
-}
-
+const isUploading = ref(false);
 const type = ref(null);
 const arrangeId = ref(null);
 const executorId = ref(null);
 const executorList = ref([]);
-function handleShow({ gardenIdVal, problemTitleVal, typeVal ,arrangeIdVal, executorListVal}) {
+
+// 互动设置模式相关数据
+const mode = ref(MODE.UPLOAD);
+const interactTitle = ref("梢期杀虫");
+const interactTime = ref("");
+const forceTriggerTime = ref("");
+const interactQuestion = ref("");
+
+// 计算属性
+const isInteractMode = computed(() => mode.value === MODE.INTERACT);
+const isUploadMode = computed(() => mode.value === MODE.UPLOAD);
+const uploadButtonText = computed(() => (isUploading.value ? "提交中..." : "确认"));
+const saveButtonText = computed(() => (isUploading.value ? "保存中..." : "保存修改"));
+
+// 工具函数
+const formatDate = (date) => {
+    if (!date) return "";
+    const d = date instanceof Date ? date : new Date(date);
+    const year = d.getFullYear();
+    const month = String(d.getMonth() + 1).padStart(2, "0");
+    const day = String(d.getDate()).padStart(2, "0");
+    return `${year}-${month}-${day}`;
+};
+
+const resetInteractData = () => {
+    interactTime.value = "";
+    forceTriggerTime.value = "";
+    interactQuestion.value = "";
+};
+
+const resetUploadData = () => {
     images.value = [];
+    uploadDate.value = new Date();
+    executorId.value = null;
+};
+
+// 验证函数
+const validateInteractForm = () => {
+    if (!interactTime.value) {
+        ElMessage.warning("请选择互动时间");
+        return false;
+    }
+    if (!forceTriggerTime.value) {
+        ElMessage.warning("请选择强制触发互动时间");
+        return false;
+    }
+    if (!interactQuestion.value?.trim()) {
+        ElMessage.warning("请设置互动问题");
+        return false;
+    }
+    return true;
+};
+
+const validateUploadForm = () => {
+    if (images.value.length === 0) {
+        ElMessage.warning("请上传图片");
+        return false;
+    }
+    return true;
+};
+
+// 事件处理函数
+const uploadChange = (arr) => {
+    images.value = arr;
+};
+
+const handleShow = ({
+    gardenIdVal,
+    problemTitleVal,
+    typeVal,
+    arrangeIdVal,
+    executorListVal,
+    modeVal,
+    interactTitleVal,
+    interactTimeVal,
+    forceTriggerTimeVal,
+    interactQuestionVal,
+}) => {
+    // 重置数据
+    resetUploadData();
+    if (modeVal === MODE.INTERACT) {
+        resetInteractData();
+    }
+
+    // 设置基础数据
     gardenId.value = gardenIdVal;
     problemTitle.value = problemTitleVal || "请选择问题";
-    uploadDate.value = (new Date());
-    show.value = true;
     type.value = typeVal;
     arrangeId.value = arrangeIdVal;
-    executorList.value = executorListVal;
-    // 重置上传状态
+    executorList.value = executorListVal || [];
+    mode.value = modeVal || MODE.UPLOAD;
     isUploading.value = false;
-}
 
-function handleSuccess() {
-    successShow.value = true;
-}
+    // 设置互动模式数据
+    if (mode.value === MODE.INTERACT) {
+        interactTitle.value = interactTitleVal || "梢期杀虫";
+        interactTime.value = interactTimeVal || "";
+        forceTriggerTime.value = forceTriggerTimeVal || "";
+        interactQuestion.value = interactQuestionVal || "";
+    }
 
-const emit = defineEmits(["handleUploadSuccess"]);
+    show.value = true;
+};
+
+const handleSuccess = () => {
+    successShow.value = true;
+};
 
 const handleUpload = () => {
-    // 如果正在上传中,直接返回,防止重复调用
-    if (isUploading.value) return;
-    
-    if (images.value.length === 0) return ElMessage.warning("请上传图片");
+    if (isUploading.value || !validateUploadForm()) return;
+
     const paramsObj = {
         farmId: gardenId.value,
         arrangeId: arrangeId.value,
         executeDate: formatDate(uploadDate.value),
         imagePaths: images.value,
     };
-    if(type.value === "question"){
+
+    if (type.value === "question") {
         show.value = false;
         emit("handleUploadSuccess", paramsObj);
-        return
+        return;
     }
-    triggerFarmWork(paramsObj,true)
+
+    triggerFarmWork(paramsObj, true);
 };
 
-function triggerFarmWork(paramsObj,showSuccess) {
-    // 如果正在上传中,直接返回,防止重复调用
+const triggerFarmWork = (paramsObj, showSuccess = false) => {
     if (isUploading.value) return;
-    
-    // 设置上传状态为 true
+
     isUploading.value = true;
-    
-    VE_API.monitor.triggerFarmWork(paramsObj).then((res) => {
-        if (res.code === 0) {
-            if(showSuccess){
+
+    VE_API.monitor
+        .triggerFarmWork(paramsObj)
+        .then((res) => {
+            if (res.code === 0 && showSuccess) {
                 show.value = false;
                 successShow.value = true;
                 emit("handleUploadSuccess", paramsObj);
             }
-        }
-    }).catch((error) => {
-        console.error("触发农事失败:", error);
-    }).finally(() => {
-        // 无论成功或失败,都重置上传状态
-        isUploading.value = false;
-    });
-}
+        })
+        .catch((error) => {
+            console.error("触发农事失败:", error);
+            ElMessage.error("操作失败,请重试");
+        })
+        .finally(() => {
+            isUploading.value = false;
+        });
+};
 
-defineExpose({
-    triggerFarmWork
-})
+const handleDeleteInteract = () => {
+    ElMessageBox.confirm("确定要删除该互动设置吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+    })
+        .then(() => {
+            emit("handleDeleteInteract", { arrangeId: arrangeId.value });
+            show.value = false;
+            ElMessage.success("删除成功");
+        })
+        .catch(() => {
+            // 用户取消,不做任何操作
+        });
+};
+
+const handleSaveInteract = () => {
+    if (isUploading.value || !validateInteractForm()) return;
+
+    isUploading.value = true;
+
+    const paramsObj = {
+        arrangeId: arrangeId.value,
+        interactTime: interactTime.value,
+        forceTriggerTime: forceTriggerTime.value,
+        interactQuestion: interactQuestion.value.trim(),
+    };
+
+    // TODO: 调用保存互动设置的API
+    // VE_API.monitor.saveInteractSetting(paramsObj)
+    //     .then((res) => {
+    //         if (res.code === 0) {
+    //             ElMessage.success("保存成功");
+    //             show.value = false;
+    //             emit("handleUploadSuccess", paramsObj);
+    //         } else {
+    //             ElMessage.error(res.message || "保存失败");
+    //         }
+    //     })
+    //     .catch((error) => {
+    //         console.error("保存互动设置失败:", error);
+    //         ElMessage.error("保存失败,请重试");
+    //     })
+    //     .finally(() => {
+    //         isUploading.value = false;
+    //     });
+
+    // 临时模拟保存成功
+    setTimeout(() => {
+        ElMessage.success("保存成功");
+        show.value = false;
+        emit("handleUploadSuccess", paramsObj);
+        isUploading.value = false;
+    }, 500);
+};
 
-function handleClosed() {
+const handleClosed = () => {
     eventBus.emit("upload:reset");
-}
+    if (mode.value === MODE.INTERACT) {
+        resetInteractData();
+    }
+};
+
+// 生命周期
+onMounted(() => {
+    eventBus.off("upload:changeArr", uploadChange);
+    eventBus.on("upload:changeArr", uploadChange);
+    eventBus.on("activeUpload:show", handleShow);
+    eventBus.on("activeUpload:success", handleSuccess);
+});
 
 onUnmounted(() => {
     eventBus.off("activeUpload:show", handleShow);
@@ -169,6 +384,11 @@ onUnmounted(() => {
     eventBus.off("upload:changeArr", uploadChange);
     show.value = false;
 });
+
+// 暴露方法
+defineExpose({
+    triggerFarmWork,
+});
 </script>
 
 <style lang="scss" scoped>
@@ -183,6 +403,141 @@ onUnmounted(() => {
             color: #000;
         }
     }
+
+    // 互动设置模式样式
+    &.interact-mode {
+        background: #ffffff;
+        padding: 0;
+
+        .interact-header {
+            background: linear-gradient(180deg, #d1ebff 0%, #ffffff 100%);
+            padding: 20px 18px;
+            text-align: center;
+            border-radius: 10px 10px 0 0;
+
+            .interact-title {
+                font-size: 18px;
+                font-weight: 600;
+                color: #000;
+            }
+        }
+
+        .interact-form {
+            padding: 0 18px 20px;
+
+            .form-item {
+                margin-bottom: 20px;
+
+                &:last-child {
+                    margin-bottom: 0;
+                }
+
+                .form-label {
+                    font-size: 14px;
+                    color: #000;
+                    margin-bottom: 12px;
+                    display: flex;
+                    align-items: center;
+
+                    .required {
+                        color: #ff4d4f;
+                        margin-right: 4px;
+                    }
+                }
+
+                .form-input-wrapper {
+                    position: relative;
+
+                    ::v-deep {
+                        .el-input__inner {
+                            caret-color: transparent;
+                            padding-right: 40px;
+                        }
+
+                        .el-input__suffix {
+                            display: none;
+                        }
+                    }
+
+                    .time-icon {
+                        position: absolute;
+                        right: 12px;
+                        top: 50%;
+                        transform: translateY(-50%);
+                        color: #909399;
+                        pointer-events: none;
+                        z-index: 1;
+                        font-size: 16px;
+                    }
+                }
+
+                .question-textarea {
+                    ::v-deep {
+                        .el-textarea__inner {
+                            resize: none;
+                            line-height: 1.5;
+                            min-height: 80px;
+                        }
+                    }
+                }
+            }
+        }
+
+        .interact-buttons {
+            display: flex;
+            gap: 12px;
+            padding: 0 18px 20px;
+
+            .btn-delete,
+            .btn-save {
+                flex: 1;
+                padding: 8px;
+                border-radius: 25px;
+                font-size: 16px;
+                text-align: center;
+                cursor: pointer;
+                transition: all 0.3s;
+                user-select: none;
+            }
+
+            .btn-delete {
+                background: #ffffff;
+                border: 1px solid #ff4d4f;
+                color: #ff4d4f;
+
+                &:hover {
+                    background: #fff5f5;
+                }
+
+                &:active {
+                    opacity: 0.8;
+                    transform: scale(0.98);
+                }
+            }
+
+            .btn-save {
+                background: #2199f8;
+                color: #fff;
+                border: none;
+
+                &:hover:not(.disabled) {
+                    background: #1a8ae6;
+                }
+
+                &:active:not(.disabled) {
+                    opacity: 0.9;
+                    transform: scale(0.98);
+                }
+
+                &.disabled {
+                    opacity: 0.6;
+                    cursor: not-allowed;
+                    pointer-events: none;
+                }
+            }
+        }
+    }
+
     .header {
         .title {
             font-size: 16px;
@@ -226,7 +581,18 @@ onUnmounted(() => {
     font-size: 16px;
     text-align: center;
     cursor: pointer;
-    transition: opacity 0.3s;
+    transition: all 0.3s;
+    user-select: none;
+
+    &:hover:not(.disabled) {
+        background: #1a8ae6;
+    }
+
+    &:active:not(.disabled) {
+        opacity: 0.9;
+        transform: scale(0.98);
+    }
+
     &.disabled {
         opacity: 0.6;
         cursor: not-allowed;

+ 1 - 16
src/views/old_mini/mine/pages/serviceDetail.vue

@@ -23,7 +23,6 @@
                     v-for="(section, index) in detailList"
                     :key="index"
                     class="content-section"
-                    @click="handleClick(section)"
                 >
                     <record-item
                         :record-item-data="section"
@@ -54,14 +53,12 @@ import customHeader from "@/components/customHeader.vue";
 import FarmInfoCard from "@/components/pageComponents/FarmInfoCard.vue";
 import StatsBox from "@/components/pageComponents/StatsBox.vue";
 import { ref, onMounted, computed } from "vue";
-import { useRoute, useRouter } from "vue-router";
+import { useRoute } from "vue-router";
 import { base_img_url2 } from "@/api/config";
 import recordItem from "@/components/recordItem.vue";
-import { ElMessage } from "element-plus";
 import reviewPopup from "@/views/old_mini/task_condition/components/reviewPopup.vue";
 import { Empty } from "vant";
 const route = useRoute();
-const router = useRouter();
 const farmIdVal = ref(null);
 onMounted(() => {
     farmIdVal.value = route.query.farmId;
@@ -112,18 +109,6 @@ const getDetailList = () => {
     });
 };
 
-const handleClick = (section) => {
-    router.push(`/review_work?miniJson=${JSON.stringify({ id: section.id, goBack: true })}`);
-};
-
-const handleChatFarm = () => {
-    if (farmDetail.value.farmersMiniUserId != null) {
-        router.push(`/chat_frame?userId=${farmDetail.value.farmersMiniUserId}&farmId=${farmIdVal.value}`);
-    } else {
-        ElMessage.warning("尚未绑定用户,暂时无法沟通");
-    }
-};
-
 const reviewPopupRef = ref(null);
 const handleTitleRightClick = ({ id, reviewImage }) => {
     VE_API.z_farm_work_record.getTriggerImg({ farmWorkRecordId: id }).then(({ data }) => {

+ 1 - 1
src/views/old_mini/mine/pages/serviceRecords.vue

@@ -75,7 +75,7 @@ const renderList = computed(() =>
 );
 // 处理列表项点击
 const handleItemClick = (data) => {
-    router.push(`/service_detail?farmId=${data.farmId}`);
+    router.push(`/review_work?miniJson={"id":"275579","goBack":true}`);
 };
 const handleImgError = (e) => {
     e.target.src = defaultThumb;

+ 19 - 4
src/views/old_mini/modify_work/reviewWork.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="work-wrap">
-        <custom-header name="农事成效" :isClose="paramsPage.goBack ? false : true"></custom-header>
+        <custom-header name="农事详情" :isClose="paramsPage.goBack ? false : true"></custom-header>
         <div class="work-content recheck-title" :class="{ 'no-bottom': curRole == '0' && (!workItem.reviewImage ||!workItem.reviewImage.length) }" v-loading="loading">
             <div class="tabs-content-item">
                 <div class="common-card-title">
@@ -235,11 +235,16 @@
             </div>
 
             <div
-                class="fixed-btn-wrap center"
+                class="fixed-btn-wrap"
                  v-if="curRole == '2'"
             >
-                <div class="fixed-btn excute" @click="handleShare" v-if="workItem.reviewImage && workItem.reviewImage.length">分享成果</div>
-                <div class="fixed-btn second" @click="handleRemindUser" v-else>提醒农户拍照</div>
+                <template v-if="workItem.reviewImage && workItem.reviewImage.length">
+                    <div class="fixed-btn more" @click="handleMore">查看更多农事</div>
+                    <div class="fixed-btn excute" @click="handleShare">生成成果报告</div>
+                </template>
+                <template v-else>
+                    <div class="fixed-btn second" @click="handleRemindUser">提醒农户拍照</div>
+                </template>
             </div>
             <div
                 class="fixed-btn-wrap center"
@@ -272,6 +277,7 @@ import reviewPopup from "@/views/old_mini/task_condition/components/reviewPopup.
 import uploadExecute from "@/views/old_mini/task_condition/components/uploadExecute.vue";
 
 const route = useRoute();
+const router = useRouter();
 const uploadExecuteRef = ref(null);
 const workItem = ref({});
 const curRole = ref("");
@@ -348,6 +354,10 @@ const handleRemindUser = () => {
     uploadExecuteRef.value.showPopup({...workItem.value, type: 'remindUser'});
 }
 
+const handleMore = () => {
+    router.push(`/service_detail?farmId=${workItem.value.farmId}`);
+}
+
 // 清理数据的函数
 const clearData = () => {
     workItem.value = {};
@@ -480,6 +490,11 @@ const handleUpload = ({ imgArr }) => {
                 &.excute {
                     background: linear-gradient(180deg, #FFD887, #ED9E1E);
                 }
+                &.more {
+                    background: #FFFFFF;
+                    border: 1px solid rgba(153, 153, 153, 0.5);
+                    color: #666666;
+                }
                 &.second {
                     background: #FFFFFF;
                     border: 1px solid #2199F8;

+ 12 - 28
src/views/old_mini/monitor/subPages/plan.vue

@@ -88,37 +88,22 @@
     <!-- 农事信息弹窗 -->
     <detail-dialog ref="detailDialogRef" @triggerFarmWork="triggerFarmWork"></detail-dialog>
     <!-- 新增:激活上传弹窗 -->
-    <active-upload-popup @handleUploadSuccess="getFarmWorkPlan"></active-upload-popup>
+    <active-upload-popup ref="activeUploadPopupRef" :needExecutor="true" @handleUploadSuccess="getFarmWorkPlan"></active-upload-popup>
 </template>
 
 <script setup>
-import { reactive, ref, onMounted, computed, nextTick } from "vue";
+import { ref, onMounted, computed, nextTick } from "vue";
 import customHeader from "@/components/customHeader.vue";
 import { useRouter, useRoute } from "vue-router";
 import detailDialog from "@/components/detailDialog.vue";
 import eventBus from "@/api/eventBus";
 import activeUploadPopup from "@/components/popup/activeUploadPopup.vue";
 import { ElMessage } from "element-plus";
-import { SuccessFilled, WarningFilled } from "@element-plus/icons-vue";
 const router = useRouter();
 const route = useRoute();
 
-// 状态列表数据
-const seasons = reactive([
-    { value: "spring", label: "春季" },
-    { value: "summer", label: "夏季" },
-    { value: "autumn", label: "秋季" },
-    { value: "winter", label: "冬季" },
-]);
 const activeSeason = ref("");
 
-const statusList = reactive([
-    { value: "pending", label: "待触发", color: "gray" },
-    { value: "executing", label: "待完成", color: "blue" },
-    { value: "completed", label: "已完成", color: "green" },
-    { value: "expired", label: "已过期", color: "orange" },
-]);
-
 const solarTerms = ref([]);
 const phenologyList = ref([]);
 
@@ -242,18 +227,17 @@ const handleRowClick = (item) => {
     }
 };
 
+const activeUploadPopupRef = ref(null);
 const handleEdit = (item) => {
-    // 处理编辑逻辑
-    ElMessage.info("编辑功能开发中");
-    // 可以在这里添加编辑逻辑,比如打开编辑弹窗等
-};
-
-const manageTask = () => {
-    router.push({
-        path: "/agri_services_manage",
-        query: {
-            type: "manage",
-        },
+    eventBus.emit("activeUpload:show", {
+        gardenIdVal: route.query.farmId,
+        problemTitleVal: '请选择 ' + item.farmWorkName + ' 执行截至时间',
+        arrangeIdVal: item.id,
+        modeVal: 'interact', // 设置为互动设置模式
+        interactTitleVal: item.farmWorkName || '梢期杀虫', // 使用农事名称作为标题
+        interactTimeVal: item.interactTime || '', // 如果有已保存的互动时间
+        forceTriggerTimeVal: item.forceTriggerTime || '', // 如果有已保存的强制触发时间
+        interactQuestionVal: item.interactQuestion, // 如果有已保存的互动问题
     });
 };
 

+ 231 - 536
src/views/old_mini/plan/index copy.vue

@@ -1,581 +1,276 @@
 <template>
-    <div class="plan-page">
+    <div class="farm-card-page">
         <custom-header name="农事方案"></custom-header>
-        <div class="plan-title">
-            <div class="tabs">
-                <div class="tab" :class="{ active: activeTab === 'left' }" @click="setActiveTab('left')">专家方案</div>
-                <div class="tab" :class="{ active: activeTab === 'right' }" @click="setActiveTab('right')">
-                    我的方案
-                    <span class="badge-dot">2</span>
+        <!-- <Tabs v-model:active="activeTab" class="tabs-wrap" v-if="!route.query.containerId">
+            <Tab title="专家方案">
+                <expert-list :isShowHeader="true"></expert-list>
+            </Tab>
+            <Tab title="我的方案">
+                <div class="farm-card-content">
+                    <tab-list
+                        v-if="curRole == 2"
+                        type="light"
+                        v-model="active"
+                        :tabs="tabs"
+                        @change="handleTabChange"
+                        class="tabs-list"
+                    />
+                    <plan-list :schemeId="active" :farmId="route.query.farmId" :containerId="containerId" :isEdit="isEditVal"> </plan-list>
                 </div>
-                <div class="slider" :style="sliderStyle"></div>
+            </Tab>
+        </Tabs>
+        <div v-else class="system-generated">
+            <div class="tip-box">
+                <el-icon size="18"><CircleCheckFilled /></el-icon>
+                <span>系统已生成多套方案,请选择最佳方案</span>
             </div>
+            <tab-list
+                v-if="curRole == 2"
+                type="light"
+                v-model="active"
+                :tabs="tabs"
+                @change="handleTabChange"
+                class="tabs-list"
+            />
+            <plan-list :schemeId="active" :containerId="containerId" :isEdit="isEditVal"> </plan-list>
+        </div> -->
+        <div class="farm-card-content" v-if="!route.query.containerId">
+            <tab-list
+                v-if="curRole == 2"
+                type="light"
+                v-model="active"
+                :tabs="tabs"
+                @change="handleTabChange"
+                class="tabs-list"
+            />
+            <plan-list :schemeId="active" :farmId="route.query.farmId" :containerId="containerId" :isEdit="true"> </plan-list>
         </div>
-        <div class="plan-content" v-if="activeTab === 'left'">
-            <div class="filter-wrap">
-                <div class="filter-l">指导专家:韦帮稳</div>
-                <div class="filter-r" @click="toPage">更多专家<el-icon><ArrowRightBold /></el-icon></div>
+        <div v-else class="system-generated">
+            <div class="tip-box">
+                <el-icon size="18"><CircleCheckFilled /></el-icon>
+                <span>系统已生成多套方案,请选择最佳方案</span>
             </div>
-            <div class="expert-prescription">
-                <div class="plan-menu">
-                    <el-anchor :container="containerRef" direction="vertical" type="default" @click="handleClick">
-                        <el-menu :default-active="defaultActive" class="el-menu-vertical-demo">
-                            <el-sub-menu v-for="(menu, index) in menuData" :key="index" :index="String(menu.id)">
-                                <template #title>
-                                    <img class="menu-icon" :src="require(`@/assets/img/gallery/icon-${index}.png`)" />
-                                    <span class="menu-text">{{ menu.name }}</span>
-                                </template>
-                                <el-menu-item v-for="item in menu.farmWorkArrangeList" :key="item.id" :index="`${menu.id}-${item.id}`">
-                                    <el-anchor-link :href="'#'+menu.name+item.farmWorkDetail?.name" :title="item.farmWorkDetail?.name || '摇花落花'" />
-                                </el-menu-item>
-                            </el-sub-menu>
-                        </el-menu>
-                    </el-anchor>
-                </div>
-                <div class="expert-content" ref="containerRef">
-                    <div v-for="(section, index) in menuData" :key="index" class="content-section">
-                        <div class="section-item" v-for="(sub, subI) in section.farmWorkArrangeList" :key="index+'-'+subI">
-                            <div class="section-id" :id="section.name+sub.farmWorkDetail?.name"></div>
-                            <record-item :record-item-data="sub">
-                                <template #title>
-                                    <div class="box-title">
-                                        <div class="title-l">
-                                            {{ sub.farmWorkDetail?.name }}
-                                            <span class="parent-text">{{ section.name }}</span>
-                                        </div>
-                                        <!-- <div class="title-btn" v-if="sub.selected == 0" @click="addToMyPlan">
-                                            <el-icon color="#fff" size="14"><Plus /></el-icon>
-                                        </div> -->
-                                    </div>
-                                </template>
-                            </record-item>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <!-- 底部 -->
-            <div class="fixed-bottom">
-                <div class="bottom-l">
-                    <div class="l-btn">
-                        <el-icon color="#666666" class="btn-icon" size="16"><Download /></el-icon>
-                        下载处方
-                    </div>
-                    <div class="l-btn">
-                        <el-icon color="#666666" class="btn-icon" size="16"><ChatDotRound /></el-icon>
-                        咨询专家
-                    </div>
-                </div>
-                <div class="bottom-r">全部添加</div>
-            </div>
-        </div>
-        <div class="plan-content my-recipe" v-if="activeTab === 'right'">
-            <my-prescription :isSubPage="true"></my-prescription>
-            <!-- <div class="fixed-bottom">
-                <div class="bottom-l">
-                    <div class="l-btn">
-                        <img class="btn-icon calculator-icon" src="@/assets/img/home/calculator.png" alt="" />
-                        投入产出计算器
-                    </div>
-                </div>
-                <div class="bottom-r">新增农事</div>
-            </div> -->
+            <tab-list
+                v-if="curRole == 2"
+                type="light"
+                v-model="active"
+                :tabs="tabs"
+                @change="handleTabChange"
+                class="tabs-list"
+            />
+            <plan-list :schemeId="active" :containerId="containerId" :isEdit="false"> </plan-list>
         </div>
     </div>
-    <add-group ref="addGroupRef" />
+    <div v-if="route.query.containerId" class="custom-bottom-fixed-btns">
+        <div class="bottom-btn primary-btn" @click="handleConfirmPlan">确认方案</div>
+    </div>
+
+    <tip-popup
+        v-model:show="showFarmPopup"
+        type="success"
+        text="农场创建成功"
+        buttonText="分享微信"
+        @confirm="handleShareFarm"
+        @handleClickOverlay="handleClickOverlay"
+    />
 </template>
 
 <script setup>
-import { computed, onMounted, ref } from "vue";
-import recordItem from "@/components/recordItem.vue";
-import addGroup from "./components/addGroup.vue";
-import myPrescription from "./components/myPrescription.vue";
 import customHeader from "@/components/customHeader.vue";
-import { useStore } from "vuex";
-import { useRouter, useRoute } from "vue-router";
-const store = useStore();
-const router = useRouter()
-const route = useRoute()
+import { onActivated, ref,onDeactivated } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import { Tab, Tabs } from "vant";
+import wx from "weixin-js-sdk";
+import expertList from "@/views/old_mini/home/subPages/expertList.vue";
+import tabList from "@/components/pageComponents/TabList.vue";
+import PlanList from "@/components/pageComponents/PlanList.vue";
+import tipPopup from "@/components/popup/tipPopup.vue";
+import { ElMessage } from "element-plus";
 
-const tabBarHeight = computed(() => store.state.home.tabBarHeight);
+const activeTab = ref(1);
+const router = useRouter();
+const route = useRoute();
 
-const containerRef = ref(null);
-const handleClick = (e) => {
-    e.preventDefault();
-};
-const activeTab = ref("left");
-// const tabBarHeight = computed(() => store.state.home.tabBarHeight);
-const sliderStyle = computed(() => {
-    // 根据当前激活的选项卡计算滑动条位置
-    const position = activeTab.value === "left" ? "25%" : "75%";
-    return {
-        left: `calc(${position} - 12px)`, // 减去滑动条宽度的一半以实现居中
-    };
-});
-function setActiveTab(tab) {
-    activeTab.value = tab;
-}
-const typeVal = ref([1, 3]);
+const curRole = localStorage.getItem("SET_USER_CUR_ROLE");
 
-const typeOptions = ref([
-    {
-        value: 1,
-        label: "荔枝",
-        children: [
-            {
-                value: 2,
-                label: "井岗红糯",
-            },
-            {
-                value: 3,
-                label: "桂味",
-            },
-            {
-                value: 4,
-                label: "妃子笑",
-            },
-            {
-                value: 5,
-                label: "黑叶",
-            },
-        ],
-    },
-    {
-        value: 6,
-        label: "龙眼",
-        children: [
-            {
-                value: 7,
-                label: "龙眼1",
-            },
-            {
-                value: 8,
-                label: "龙眼2",
-            },
-            {
-                value: 9,
-                label: "龙眼3",
-            },
-            {
-                value: 10,
-                label: "龙眼4",
-            },
-        ],
-    },
-    {
-        value: 11,
-        label: "枇杷",
-        children: [
-            {
-                value: 12,
-                label: "枇杷1",
-            },
-            {
-                value: 13,
-                label: "枇杷2",
-            },
-            {
-                value: 14,
-                label: "枇杷3",
-            },
-            {
-                value: 15,
-                label: "枇杷4",
-            },
-        ],
-    },
-]);
-
-const proviceVal = ref("广东");
-const proviceOptions = ref([
-    {
-        value: "广东",
-        label: "广东省",
-    },
-    {
-        value: "广西",
-        label: "广西省",
-    },
-    {
-        value: "福建",
-        label: "福建省",
-    },
-    {
-        value: "海南",
-        label: "海南省",
-    },
-]);
-
-// 菜单
-const defaultActive = ref("1-1");
-
-const menuData = ref([])
-
-const activePlanIndex = ref(0);
-const handlePlanClick = (index) => {
-    activePlanIndex.value = index;
+const tabs = ref([]);
+const active = ref(null);
+const containerId = ref(null);
+const handleTabChange = (id, item) => {
+    containerId.value = item.containerId;
+    active.value = id;
 };
 
-const addGroupRef = ref(null);
-// 新增方案
-function newPlan() {
-    addGroupRef.value.openClientPopup();
-}
-
-// 将专家处方添加到我的处方
-function addToMyPlan() {
-    addGroupRef.value.openClientPopup({type: "edit"});
-}
-
-function toPage() {
-    router.push("/expert_list?isToSelect=true")
-}
-
-function getWorkList() {
-    VE_API.home.getPhenologyFarmWorkList({farmId: 93301, containerId: route.query.containerId || 2}).then(({data}) => {
-        menuData.value = data
-    })
-}
-
-onMounted(() => {
-    getWorkList()
-})
-</script>
-
-<style lang="scss" scoped>
-.plan-page {
-    position: relative;
-    height: 100vh;
-    .plan-title {
-        width: 158px;
-        margin: 0 auto;
-        padding: 6px 0;
-        .tabs {
-            display: flex;
-            position: relative;
-            height: 36px;
-            line-height: 36px;
-            .tab {
-                flex: 1;
-                text-align: center;
-                cursor: pointer;
-                color: rgba(0, 0, 0, 0.5);
-                z-index: 2;
-                transition: color 0.3s ease;
-                position: relative;
-                &.active {
-                    color: #000000;
-                    font-weight: bold;
-                }
-                .badge-dot {
-                    position: absolute;
-                    top: 0;
-                    right: -6px;
-                    width: 16px;
-                    height: 16px;
-                    background-color: #ff0000;
-                    border-radius: 50%;
-                    font-size: 12px;
-                    line-height: 16px;
-                    color: #fff;
-                }
-            }
-            .slider {
-                position: absolute;
-                height: 4px;
-                width: 24px;
-                background: #2199f8;
-                border-radius: 16px;
-                bottom: 0;
-                left: calc(25% - 12px); /* 初始位置在第一个选项卡中间 */
-                transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-            }
-        }
+onActivated(() => {
+    if (route.query.containerId || curRole == 2) {
+        getListMySchemes();
+    }
+});
 
-        .content {
-            padding: 20px;
-            background: #f8f9fa;
-            border-radius: 8px;
-            min-height: 200px;
-        }
+onDeactivated(() => {
+    active.value = null;
+    containerId.value = null;
+});
 
-        .content-section {
-            display: none;
-            animation: fadeIn 0.5s ease;
-            
-            .section-item {
-                position: relative;
+const getListMySchemes = () => {
+    VE_API.home.listMySchemes().then(({ data }) => {
+        if (data.length) {
+            tabs.value = data || [];
+            if (route.query.containerId) {
+                const index = data.findIndex((item) => item.containerId == 9);
+                active.value = data[index].id;
+                containerId.value = 9;
+            } else {
+                active.value = data[0].id;
+                containerId.value = data[0].containerId;
             }
+        } else {
+            getSchemes();
         }
+    });
+};
 
-        .content-section.active {
-            display: block;
+const getSchemes = () => {
+    VE_API.home.batchInitSchemes({ containerIds: [3], schemeName: "农资荔枝方案" }).then(({ code }) => {
+        if (code == 0) {
+            getListMySchemes();
         }
+    });
+};
 
-        @keyframes fadeIn {
-            from {
-                opacity: 0;
-                transform: translateY(10px);
-            }
-            to {
-                opacity: 1;
-                transform: translateY(0);
+const showFarmPopup = ref(false);
+const shareData = ref({});
+const handleConfirmPlan = () => {
+    // 从路由参数中获取农场数据
+    let geomValue = route.query.geom;
+    
+    // 处理 geom 参数,可能是 JSON 字符串或数组
+    if (typeof geomValue === 'string') {
+        try {
+            // 尝试解析 JSON 字符串
+            const parsed = JSON.parse(geomValue);
+            if (Array.isArray(parsed)) {
+                geomValue = parsed;
             }
+        } catch (e) {
+            // 如果不是 JSON 字符串,保持原值
+            console.warn('geom 参数解析失败,使用原值:', e);
         }
     }
-    .plan-content {
-        background: #f5f7fb;
-        .filter-wrap {
-            padding: 10px 12px;
-            width: 100%;
-            box-sizing: border-box;
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            .filter-r {
-                height: 32px;
-                display: flex;
-                align-items: center;
-                padding: 0 18px;
-                background: rgba(33, 153, 248, 0.1);
-                border-radius: 20px;
-                color: #2199F8;
-                font-size: 14px;
-            }
-        }
+    
+    const farmParams = {
+        ...route.query,
+        containerId: containerId.value,
+        geom: geomValue,
+        defaultFarm: Boolean(route.query.defaultFarm),
+        agriculturalCreate: route.query.agriculturalCreate * 1,
+    };
+    
+    // 验证必填字段
+    if (!farmParams.wkt || !farmParams.speciesId || !farmParams.containerId || !farmParams.address || !farmParams.mu || !farmParams.name || !farmParams.fzr || !farmParams.tel) {
+        ElMessage.error('农场信息不完整,请返回重新填写');
+        return;
     }
-    .fixed-bottom {
-        position: absolute;
-        bottom: 0;
-        left: 0;
-        width: 100%;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        padding: 14px 12px;
-        background: #fff;
-        box-sizing: border-box;
-        border-top: 1px solid rgba(153, 153, 153, 0.2);
-        .bottom-l {
-            display: flex;
-            align-items: center;
-            .l-btn {
-                border: 1px solid rgba(153, 153, 153, 0.5);
-                border-radius: 30px;
-                padding: 0 8px 0 12px;
-                height: 32px;
-                line-height: 32px;
-                box-sizing: border-box;
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                color: #666666;
-                .btn-icon {
-                    padding-right: 3px;
+    
+    delete farmParams.from;
+    // 调用创建农场接口
+    VE_API.farm.saveFarm(farmParams).then((res) => {
+        if (res.code === 0) {
+            shareData.value = res.data;
+            //选择方案
+            VE_API.home.selectSchemes({ schemeId: active.value ,farmId:res.data.id}).then(({code}) => {
+                if (code === 0) {
+                    showFarmPopup.value = true;
+                } else {
+                    ElMessage.error(res.msg || '创建失败');
                 }
-                .calculator-icon {
-                    width: 12px;
-                }
-            }
-            .l-btn + .l-btn {
-                margin-left: 10px;
-            }
-        }
-        .bottom-r {
-            height: 32px;
-            line-height: 32px;
-            background: #2199f8;
-            border-radius: 20px;
-            color: #fff;
-            padding: 0 12px;
+            });
+        } else {
+            ElMessage.error(res.msg || '创建失败');
         }
+    }).catch((err) => {
+        console.error('创建农场失败:', err);
+        ElMessage.error('创建失败,请稍后重试');
+    });
+};
+
+const handleShareFarm = () => {
+    const query = {
+        agriculturalStoreId: shareData.value.agriculturalStoreId,
+        farmId: shareData.value.id,
+        speciesName: route.query.speciesName,
+        containerId: shareData.value.containerId,
+    };
+    wx.miniProgram.navigateTo({
+        url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify(query)}&type=shareFarm`,
+    });
+};
+
+const handleClickOverlay = () => {
+    // 根据 from 参数跳转回原页面
+    const fromPage = route.query.from;
+    if (fromPage) {
+        router.replace(`/${fromPage}`);
+    } else {
+        // 如果没有 from 参数,默认跳转到首页
+        router.replace('/home');
     }
-    .expert-prescription {
-        display: flex;
-        width: 100%;
-        height: calc(100vh - 52px - 48px - 50px);
-        .plan-menu {
-            width: 100px;
-            height: 100%;
-            overflow: auto;
-            padding: 10px 0;
+};
+</script>
+
+<style scoped lang="scss">
+.farm-card-page {
+    width: 100%;
+    height: 100vh;
+    background: #f5f7fb;
+    .system-generated{
+        padding: 17px 10px 10px;
+        height: calc(100vh - 150px);
+        .tip-box {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            padding: 10px;
+            background: rgba(33, 153, 248, 0.1);
+            border-radius: 8px;
+            color: #2199f8;
             box-sizing: border-box;
-            background: #fff;
-            border-radius: 0 10px 10px 0;
-            .menu-icon {
-                width: 13px;
-            }
-            .menu-text {
-                padding: 0 4px;
-            }
-            ::v-deep {
-                .el-anchor {
-                    height: 100%;
-                    background: none;
-                }
-                .el-anchor__marker {
-                    display: none;
-                }
-                .el-menu {
-                    background: none;
-                    border: none;
-                    .el-sub-menu__title {
-                        background: none;
-                        padding: 0 2px;
-                        justify-content: center;
-                    }
-                    .el-sub-menu__title {
-                        height: 32px;
-                    }
-                    .el-sub-menu .el-sub-menu__icon-arrow {
-                        position: static;
-                        padding-top: 6px;
-                    }
-                    .el-sub-menu {
-                        margin-bottom: 16px;
-                        &.is-opened {
-                            .el-sub-menu__icon-arrow {
-                                padding-bottom: 6px;
-                                padding-top: 0;
-                            }
-                        }
-                        .el-menu-item {
-                            height: 32px;
-                            line-height: 32px;
-                            margin: 4px 8px;
-                            padding: 0 2px;
-                            justify-content: center;
-                            background: none;
-                        }
-                        .el-menu-item.is-active {
-                            background: none;
-                            color: #fff;
-                        }
-                        .el-anchor__item {
-                            width: 100%;
-                            text-align: center;
-                        }
-                        .el-anchor__link {
-                            color: #666666;
-                        }
-                        .el-anchor__link.is-active {
-                            background: rgba(33, 153, 248, 0.1);
-                            border-radius: 20px;
-                            color: #2199F8;
-                            border: 1px solid #2199F8;
-                        }
-                    }
-                }
-                .el-anchor__list {
-                    padding-left: 0;
-                }
+            span {
+                margin-left: 5px;
             }
         }
-        .expert-content {
-            width: calc(100% - 100px);
-            height: 100%;
-            overflow: auto;
-            padding-bottom: 80px;
-            box-sizing: border-box;
-            .content-section {
-                position: relative;
-                .section-item {
-                    position: relative;
-                }
-                .section-id {
-                    position: absolute;
-                    // top: -6px;
-                    top: 0;
-                    width: 100%;
-                    height: 1px;
-                }
-            }
-            .box-title {
-                display: flex;
-                align-items: center;
-                justify-content: space-between;
-                padding-bottom: 8px;
-                border-bottom: 1px solid #f5f5f5;
-                margin-bottom: 8px;
-                .title-l {
-                    font-size: 16px;
-                    font-weight: 600;
-                    color: #000;
-                    .parent-text {
-                        margin-left: 5px;
-                        font-size: 12px;
-                        font-weight: normal;
-                        padding: 4px 6px;
-                        border-radius: 14px;
-                        background: rgba(119, 119, 119, 0.1);
-                    }
-                }
-                .title-btn {
-                    width: 24px;
-                    height: 24px;
-                    border-radius: 50%;
-                    background: #2199f8;
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                }
-            }
+        .tabs-list{
+            margin: 10px 0;
         }
     }
-
-    .my-recipe {
-        .filter-wrap {
-            .plan-box {
-                display: flex;
-                overflow: auto;
-                white-space: nowrap;
-                align-items: center;
-                padding-left: 12px;
-                .plan-item {
-                    color: #000000;
-                    background: #f1f1f1;
-                    padding: 0 12px;
-                    height: 32px;
-                    line-height: 32px;
-                    border-radius: 20px;
-                    &.active {
-                        background: rgba(33, 153, 248, 0.2);
-                        color: #2199f8;
-                    }
-                }
-                .plan-item + .plan-item {
-                    margin-left: 10px;
-                }
+    .tabs-wrap {
+        ::v-deep {
+            .van-tabs__line {
+                width: 24px;
+                height: 4px;
             }
-            .plan-add {
-                width: 80px;
-                height: 30px;
-                border: 1px solid #2199f8;
-                border-radius: 20px;
+            .van-tab {
+                width: 100px;
                 flex: none;
-                line-height: 32px;
-                text-align: center;
-                margin: 0 12px;
-                color: #2199f8;
-                font-size: 14px;
             }
-        }
-        .title-r {
-            display: flex;
-            align-items: center;
-            .btn-item {
-                width: 24px;
-                height: 24px;
-                border-radius: 50%;
-                display: flex;
-                align-items: center;
+            .van-tabs__nav {
                 justify-content: center;
-                background: #2199f8;
-                &.del-btn {
-                    margin-right: 5px;
-                    background: #ff953d;
-                }
             }
         }
     }
+    .farm-card-content {
+        width: 100%;
+        height: calc(100vh - 40px);
+        padding-top: 10px;
+        .tabs-list {
+            margin: 0 0 10px 10px;
+        }
+    }
+}
+.custom-bottom-fixed-btns{
+    justify-content: center;
+    .bottom-btn{
+        padding: 10px 40px;
+    }
 }
 </style>