Browse Source

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

lxf 1 tuần trước cách đây
mục cha
commit
690ddc5f1d
100 tập tin đã thay đổi với 506 bổ sung2776 xóa
  1. BIN
      src/assets/img/agricultural/notice.png
  2. BIN
      src/assets/img/agricultural/photo.png
  3. BIN
      src/assets/img/agricultural/report.png
  4. BIN
      src/assets/img/common/lz-chart-1.png
  5. BIN
      src/assets/img/common/lz-chart-2.png
  6. BIN
      src/assets/img/common/lz-chart-3.png
  7. BIN
      src/assets/img/common/sd-chart-1.png
  8. BIN
      src/assets/img/common/sd-chart-2.png
  9. BIN
      src/assets/img/common/sd-chart-3.png
  10. BIN
      src/assets/img/common/yw-en.png
  11. BIN
      src/assets/img/common/yw.png
  12. BIN
      src/assets/img/gallery/icon-0.png
  13. BIN
      src/assets/img/gallery/img-icon-act.png
  14. BIN
      src/assets/img/gallery/img-icon.png
  15. BIN
      src/assets/img/home/ask-icon.png
  16. BIN
      src/assets/img/home/book.png
  17. BIN
      src/assets/img/home/box-bg.png
  18. BIN
      src/assets/img/home/bulb-icon.png
  19. BIN
      src/assets/img/home/example-3.png
  20. BIN
      src/assets/img/home/expert-icon.png
  21. BIN
      src/assets/img/home/expert-text.png
  22. BIN
      src/assets/img/home/farm-bg-1.png
  23. BIN
      src/assets/img/home/farm-bg-2.png
  24. BIN
      src/assets/img/home/farm-bg-3.png
  25. BIN
      src/assets/img/home/farm-bg-4.png
  26. BIN
      src/assets/img/home/farm-bg-5.png
  27. BIN
      src/assets/img/home/farm-point.png
  28. BIN
      src/assets/img/home/file-icon.png
  29. BIN
      src/assets/img/home/garden-title.png
  30. BIN
      src/assets/img/home/good-fill.png
  31. BIN
      src/assets/img/home/home-user.png
  32. BIN
      src/assets/img/home/humidity.png
  33. BIN
      src/assets/img/home/link-icon.png
  34. BIN
      src/assets/img/home/man.png
  35. BIN
      src/assets/img/home/map.png
  36. BIN
      src/assets/img/home/mask-bg.png
  37. BIN
      src/assets/img/home/msg_icon.png
  38. BIN
      src/assets/img/home/nz.png
  39. BIN
      src/assets/img/home/offer-icon.png
  40. BIN
      src/assets/img/home/page-bg.png
  41. BIN
      src/assets/img/home/pest.png
  42. BIN
      src/assets/img/home/photo-icon.png
  43. BIN
      src/assets/img/home/report_bg.png
  44. BIN
      src/assets/img/home/task-icon.png
  45. BIN
      src/assets/img/home/temperature.png
  46. BIN
      src/assets/img/home/vs.png
  47. BIN
      src/assets/img/home/zj-1.png
  48. BIN
      src/assets/img/home/zj-2.png
  49. BIN
      src/assets/img/home/zj-3.png
  50. BIN
      src/assets/img/map/delete.png
  51. BIN
      src/assets/img/map/garden-border.png
  52. BIN
      src/assets/img/map/hall.png
  53. BIN
      src/assets/img/map/tool-1.png
  54. BIN
      src/assets/img/map/tool-2.png
  55. BIN
      src/assets/img/map/tool-3.png
  56. BIN
      src/assets/img/map/tool-4.png
  57. BIN
      src/assets/img/map/tool-5.png
  58. BIN
      src/assets/img/map/tool-6.png
  59. BIN
      src/assets/img/mine/agricultural.png
  60. BIN
      src/assets/img/mine/code-icon.png
  61. BIN
      src/assets/img/mine/drone-icon.png
  62. BIN
      src/assets/img/mine/expert-icon.png
  63. BIN
      src/assets/img/mine/expert.png
  64. BIN
      src/assets/img/mine/figure-icon.png
  65. BIN
      src/assets/img/mine/firend-icon.png
  66. BIN
      src/assets/img/mine/mine-bg.png
  67. BIN
      src/assets/img/monitor/approve.png
  68. BIN
      src/assets/img/monitor/arrow.png
  69. BIN
      src/assets/img/monitor/bell.png
  70. BIN
      src/assets/img/monitor/defalut-arrow.png
  71. BIN
      src/assets/img/monitor/example.png
  72. BIN
      src/assets/img/monitor/execute-icon.png
  73. BIN
      src/assets/img/monitor/grid-1.png
  74. BIN
      src/assets/img/monitor/grid-2.png
  75. BIN
      src/assets/img/monitor/grid-3.png
  76. BIN
      src/assets/img/monitor/grid-4.png
  77. BIN
      src/assets/img/monitor/image.png
  78. BIN
      src/assets/img/monitor/notice.png
  79. BIN
      src/assets/img/monitor/popup-header-bg.png
  80. BIN
      src/assets/img/monitor/report-icon.png
  81. BIN
      src/assets/img/monitor/speaker.png
  82. BIN
      src/assets/img/report/type-bg.png
  83. BIN
      src/assets/img/status/status_bcyc.png
  84. BIN
      src/assets/img/status/status_dfh.png
  85. BIN
      src/assets/img/status/status_szyc.png
  86. BIN
      src/assets/logo.png
  87. 0 1166
      src/components/chatWindow.vue
  88. 92 0
      src/components/popup/completeFarmInfoPopup.vue
  89. 88 0
      src/components/popup/diagnosisReportPopup.vue
  90. 145 0
      src/components/popup/farmCalendarPopup.vue
  91. 94 0
      src/components/popup/leaveConfirmPopup.vue
  92. 28 2
      src/i18n/messages.js
  93. 0 40
      src/router/globalRoutes.js
  94. 0 784
      src/views/old_mini/achievement_report/index.vue
  95. 0 6
      src/views/old_mini/agri_file/index copy.vue
  96. 36 5
      src/views/old_mini/agri_file/index.vue
  97. 21 1
      src/views/old_mini/agri_file/pages/growthTrack.vue
  98. 2 16
      src/views/old_mini/agri_record/index.vue
  99. 0 690
      src/views/old_mini/chat_frame/consult.vue
  100. 0 66
      src/views/old_mini/chat_frame/index.vue

BIN
src/assets/img/agricultural/notice.png


BIN
src/assets/img/agricultural/photo.png


BIN
src/assets/img/agricultural/report.png


BIN
src/assets/img/common/lz-chart-1.png


BIN
src/assets/img/common/lz-chart-2.png


BIN
src/assets/img/common/lz-chart-3.png


BIN
src/assets/img/common/sd-chart-1.png


BIN
src/assets/img/common/sd-chart-2.png


BIN
src/assets/img/common/sd-chart-3.png


BIN
src/assets/img/common/yw-en.png


BIN
src/assets/img/common/yw.png


BIN
src/assets/img/gallery/icon-0.png


BIN
src/assets/img/gallery/img-icon-act.png


BIN
src/assets/img/gallery/img-icon.png


BIN
src/assets/img/home/ask-icon.png


BIN
src/assets/img/home/book.png


BIN
src/assets/img/home/box-bg.png


BIN
src/assets/img/home/bulb-icon.png


BIN
src/assets/img/home/example-3.png


BIN
src/assets/img/home/expert-icon.png


BIN
src/assets/img/home/expert-text.png


BIN
src/assets/img/home/farm-bg-1.png


BIN
src/assets/img/home/farm-bg-2.png


BIN
src/assets/img/home/farm-bg-3.png


BIN
src/assets/img/home/farm-bg-4.png


BIN
src/assets/img/home/farm-bg-5.png


BIN
src/assets/img/home/farm-point.png


BIN
src/assets/img/home/file-icon.png


BIN
src/assets/img/home/garden-title.png


BIN
src/assets/img/home/good-fill.png


BIN
src/assets/img/home/home-user.png


BIN
src/assets/img/home/humidity.png


BIN
src/assets/img/home/link-icon.png


BIN
src/assets/img/home/man.png


BIN
src/assets/img/home/map.png


BIN
src/assets/img/home/mask-bg.png


BIN
src/assets/img/home/msg_icon.png


BIN
src/assets/img/home/nz.png


BIN
src/assets/img/home/offer-icon.png


BIN
src/assets/img/home/page-bg.png


BIN
src/assets/img/home/pest.png


BIN
src/assets/img/home/photo-icon.png


BIN
src/assets/img/home/report_bg.png


BIN
src/assets/img/home/task-icon.png


BIN
src/assets/img/home/temperature.png


BIN
src/assets/img/home/vs.png


BIN
src/assets/img/home/zj-1.png


BIN
src/assets/img/home/zj-2.png


BIN
src/assets/img/home/zj-3.png


BIN
src/assets/img/map/delete.png


BIN
src/assets/img/map/garden-border.png


BIN
src/assets/img/map/hall.png


BIN
src/assets/img/map/tool-1.png


BIN
src/assets/img/map/tool-2.png


BIN
src/assets/img/map/tool-3.png


BIN
src/assets/img/map/tool-4.png


BIN
src/assets/img/map/tool-5.png


BIN
src/assets/img/map/tool-6.png


BIN
src/assets/img/mine/agricultural.png


BIN
src/assets/img/mine/code-icon.png


BIN
src/assets/img/mine/drone-icon.png


BIN
src/assets/img/mine/expert-icon.png


BIN
src/assets/img/mine/expert.png


BIN
src/assets/img/mine/figure-icon.png


BIN
src/assets/img/mine/firend-icon.png


BIN
src/assets/img/mine/mine-bg.png


BIN
src/assets/img/monitor/approve.png


BIN
src/assets/img/monitor/arrow.png


BIN
src/assets/img/monitor/bell.png


BIN
src/assets/img/monitor/defalut-arrow.png


BIN
src/assets/img/monitor/example.png


BIN
src/assets/img/monitor/execute-icon.png


BIN
src/assets/img/monitor/grid-1.png


BIN
src/assets/img/monitor/grid-2.png


BIN
src/assets/img/monitor/grid-3.png


BIN
src/assets/img/monitor/grid-4.png


BIN
src/assets/img/monitor/image.png


BIN
src/assets/img/monitor/notice.png


BIN
src/assets/img/monitor/popup-header-bg.png


BIN
src/assets/img/monitor/report-icon.png


BIN
src/assets/img/monitor/speaker.png


BIN
src/assets/img/report/type-bg.png


BIN
src/assets/img/status/status_bcyc.png


BIN
src/assets/img/status/status_dfh.png


BIN
src/assets/img/status/status_szyc.png


BIN
src/assets/logo.png


+ 0 - 1166
src/components/chatWindow.vue

@@ -1,1166 +0,0 @@
-<template>
-    <div class="chat-container">
-        <!-- 聊天消息区域 -->
-        <div class="chat-messages" ref="messagesContainer">
-            <div v-for="(msg, index) in messages" :key="index" class="message" :class="msg.sender">
-                <!-- 对方消息 -->
-                <template v-if="msg.sender === 'received'">
-                    <!-- <div class="avatar">{{ msg.receiverName.charAt(0) }}</div> -->
-                    <el-avatar
-                        class="avatar"
-                        :size="40"
-                        :src="
-                            msg.receiverIcon ||
-                            'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'
-                        "
-                    />
-                    <img src="" alt="" />
-                    <div class="bubble" :class="{ 'no-bubble': msg.messageType === 'image' ,'card-bubble': msg.messageType === 'card'}">
-                        <!-- 文本消息 -->
-                        <div v-if="msg.messageType === 'text'" class="content">{{ msg.content }}</div>
-
-                        <!-- 图片消息 -->
-                        <div v-if="msg.messageType === 'image'" class="image-message">
-                            <img
-                                :src="msg.content + resize"
-                                @click="showImagePreview(msg.content)"
-                                @load="handleImageLoad"
-                                alt="图片"
-                            />
-                        </div>
-
-                        <!-- 语音消息 -->
-                        <div v-if="msg.messageType === 'audio'" class="audio-message" @click="playAudio(msg.content)">
-                            <span class="audio-icon">🎵</span>
-                            <span class="duration">{{ msg.duration }}"</span>
-                        </div>
-
-                        <!-- 对话样式消息 -->
-                        <div v-if="msg.messageType === 'report'" class="dialog-message" @click="handleReportClick(msg)">
-                            <template v-if="(msg.reportType || msg.content.reportType) === 'farm_report'">
-                                <div class="report-title">{{ msg.title ||msg.content.title }}</div>
-                                <div class="dialog-title">这是{{curRole == 2 ? '该农场' : '我'}}的果园情况,请查看~</div>
-                                <img src="https://birdseye-img.sysuimars.com/birdseye-look-mini/share-report-bg.png" alt="" class="monitor-image" />
-                            </template>
-                            <template v-else>
-                                <div class="dialog-title">{{ msg.title || msg.content.title}}</div>
-                                <img src="@/assets/img/monitor/image.png" alt="" class="monitor-image" />
-                            </template>
-                        </div>
-
-                         <!-- 对话样式消息 -->
-                        <div v-if="msg.messageType === 'card'" class="card-message" @click="handleCardClick(msg)">
-                            <template v-if="(msg.cardType || msg.content.cardType) === 'quotation'">
-                                <div class="card-title">{{ $t('向您发送了一张 服务报价单') }}</div>
-                                <img src="https://birdseye-img.sysuimars.com/temp/price.png" alt="" />
-                            </template>
-                            <template v-else>
-                                <div class="card-title">{{ msg.title || msg.content.title }}</div>
-                                <img :src="handleImgUrl(msg.coverUrl || msg.content.coverUrl)" alt="" />
-                            </template>
-                        </div>
-
-                        <!-- <div class="time">{{ msg.time }}</div> -->
-                    </div>
-                </template>
-
-                <!-- 我方消息 -->
-                <template v-else>
-                    <div class="bubble" :class="{ 'no-bubble': msg.messageType === 'image','card-bubble': msg.messageType === 'card' || msg.messageType === 'report' }">
-                        <!-- 文本消息 -->
-                        <div v-if="msg.messageType === 'text'" class="content">{{ msg.content }}</div>
-
-                        <!-- 图片消息 -->
-                        <div v-if="msg.messageType === 'image'" class="image-message">
-                            <img
-                                :src="msg.content + resize"
-                                @click="showImagePreview(msg.content)"
-                                @load="handleImageLoad"
-                                alt="图片"
-                            />
-                        </div>
-
-                        <!-- 语音消息 -->
-                        <div v-if="msg.messageType === 'audio'" class="audio-message" @click="playAudio(msg.content)">
-                            <span class="audio-icon">🎵</span>
-                            <span class="duration">{{ msg.duration }}"</span>
-                        </div>
-
-                        <!-- 对话样式消息 -->
-                        <div v-if="msg.messageType === 'report'" class="dialog-message" @click="handleReportClick(msg)">
-                            <template v-if="(msg.reportType || msg.content.reportType) === 'farm_report'">
-                                <div class="report-title">{{ msg.title ||msg.content.title }}</div>
-                                <div class="dialog-title">这是{{curRole == 2 ? '该农场' : '我'}}果园情况,请查看~</div>
-                                <img src="https://birdseye-img.sysuimars.com/birdseye-look-mini/share-report-bg.png" alt="" class="monitor-image" />
-                            </template>
-                            <template v-else>
-                                <div class="dialog-title">{{ msg.title || msg.content.title}}</div>
-                                <img src="@/assets/img/monitor/image.png" alt="" class="monitor-image" />
-                            </template>
-                        </div>
-
-                        <!-- 对话样式消息 -->
-                        <div v-if="msg.messageType === 'card'" class="card-message" @click="handleCardClick(msg)">
-                            <template v-if="(msg.cardType || msg.content.cardType) === 'quotation'">
-                                <div class="card-title">{{ $t('向您发送了一张 服务报价单') }}</div>
-                                <img src="https://birdseye-img.sysuimars.com/temp/price.png" alt="" />
-                            </template>
-                            <template v-else>
-                                <div class="card-title">{{ msg.title || msg.content.title }}</div>
-                                <img :src="handleImgUrl(msg.coverUrl || msg.content.coverUrl)" alt="" />
-                            </template>
-                        </div>
-
-                        <!-- <div class="time">{{ msg.time }}</div> -->
-                    </div>
-                    <!-- <div class="avatar avatar-r">{{ msg.senderName.charAt(0) }}</div> -->
-                    <el-avatar
-                        class="avatar avatar-r"
-                        :size="40"
-                        :src="
-                            msg.senderIcon ||
-                            'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'
-                        "
-                    />
-                </template>
-            </div>
-        </div>
-
-        <!-- 功能按钮区域 -->
-        <div class="function-buttons">
-            <el-select v-model="farmVal" size="large" @change="handleFarmChange">
-                <el-option
-                    v-for="item in options"
-                    :key="item.id"
-                    :label="item.name"
-                    :value="item.id"
-                />
-            </el-select>
-            <div v-for="(btn, index) in functionButtons" :key="index" class="function-btn" @click="btn.handler">
-                <span class="btn-text">{{ btn.text }}</span>
-            </div>
-        </div>
-
-        <!-- 输入框区域 -->
-        <div class="input-area">
-            <div class="toolbar">
-                <!-- <button @click="toggleEmojiPicker">😊</button> -->
-                <!-- <button @click="startImageUpload">📷</button> -->
-                <!-- <button
-                    @mousedown="startRecording"
-                    @mouseup="stopRecording"
-                    @touchstart="startRecording"
-                    @touchend="stopRecording"
-                >
-                    🎤
-                </button> -->
-                <el-icon class="link" @click="startImageUpload"><Link /></el-icon>
-                <input type="file" ref="fileInput" accept="image/*" style="display: none" @change="handleImageUpload" />
-            </div>
-
-            <input type="text" v-model="inputMessage" :placeholder="$t('请输入你想说的话~')" @keyup.enter="sendTextMessage" />
-            <div class="send" @click="sendTextMessage">{{ $t('发送') }}</div>
-
-            <!-- 录音指示器 -->
-            <div v-if="isRecording" class="recording-indicator">
-                <div class="pulse"></div>
-                录音中... {{ recordingDuration }}s
-            </div>
-        </div>
-
-        <!-- Emoji 选择器 -->
-        <div v-if="showEmojiPicker" class="emoji-picker">
-            <span v-for="emoji in emojis" :key="emoji" @click="addEmoji(emoji)">{{ emoji }}</span>
-        </div>
-
-        <!-- 图片预览模态框 -->
-        <div v-if="previewImage" class="image-preview" @click="previewImage = null">
-            <img :src="previewImage" alt="预览" />
-        </div>
-    </div>
-</template>
-
-<script setup>
-import { ref, onUnmounted, nextTick, watch, onActivated, onDeactivated } from "vue";
-import { useRouter ,useRoute} from "vue-router";
-import { base_img_url2 } from "@/api/config";
-import { getFileExt } from "@/utils/util";
-import UploadFile from "@/utils/upliadFile";
-import MqttClient from "@/plugins/MqttClient";
-import customHeader from "@/components/customHeader.vue";
-
-const resize = "?x-oss-process=image/resize,p_120/format,webp/quality,q_100";
-const router = useRouter();
-
-const props = defineProps({
-    text: {
-        type: String,
-        defalut: "",
-    },
-    img: {
-        type: String,
-        defalut: "",
-    },
-    userId: {
-        type: [String, Number],
-        defalut: "",
-    },
-});
-
-const emit = defineEmits(['update:name']);
-
-const defalutMsg = ref([
-    {
-        sender: "sent",
-        senderIcon: "王",
-        messageType: "text",
-        content:
-            "你好,我叫陈晓晓。有100亩荔枝,30亩桂味,70亩妃子笑,位置在广州市番禺区大学城110号,希望您可以来指导。",
-        time: "上午10:32",
-    },
-]);
-
-const curUserId = Number(localStorage.getItem("MINI_USER_ID"));
-const senderIcon = ref("");
-const receiverIcon = ref("");
-const receiverIdVal = ref(null);
-
-// 本地用户头像
-const localUserInfoIcon = (() => {
-    try {
-        const info = JSON.parse(localStorage.getItem("localUserInfo") || "{}");
-        return info?.icon || "";
-    } catch (e) {
-        return "";
-    }
-})();
-
-// 初始化本地头像为默认发送者头像
-senderIcon.value = localUserInfoIcon;
-const nameVal = ref('');
-
-// 监听 nameVal 变化,传递给父组件
-watch(nameVal, (newVal) => {
-    emit('update:name', newVal);
-});
-
-//聊天会话
-const createSession = (targetUserId, callback) => {
-    nameVal.value = '';
-    VE_API.bbs.createSession({ farmId: farmVal.value, targetUserId }).then(({ data, code }) => {
-        if (code === 0) {
-            nameVal.value = data.session.targetUserName;
-            senderIcon.value = localUserInfoIcon;
-            receiverIcon.value = data.session.targetUserAvatar;
-            receiverIdVal.value = data.session.targetUserId;
-            messages.value = data.messages.map((item) => {
-                let content = item.content;
-                if (item.messageType === "image") {
-                    // 优先读取后端的 image 字段,其次兼容旧的 content(JSON)
-                    if (item.image && (item.image.url || item.image.originUrl)) {
-                        content = item.image.url || item.image.originUrl;
-                    } else if (item.content) {
-                        try {
-                            const imgObj = JSON.parse(item.content);
-                            content = imgObj.url || imgObj.originUrl;
-                        } catch (e) {
-                            console.error(e, "e");
-                        }
-                    }
-                }else if(item.messageType !== 'text'){
-                    content = JSON.parse(item.content);
-                }
-                return {
-                    ...item,
-                    content,
-                    sender: item.senderId === curUserId ? "sent" : "received",
-                    senderIcon: item.senderId === curUserId ? localUserInfoIcon : data.session.targetUserAvatar,
-                    receiverIcon: data.session.targetUserAvatar,
-                };
-            });
-
-            setTimeout(() => {
-                scrollToBottom();
-            }, 300);
-            callback && callback();
-        }
-    });
-};
-
-const handleFarmChange = (e) => {
-    sessionStorage.setItem('selectedFarmId', e);
-    createSession(userId.value);
-    initMqtt();
-};
-
-//发送消息接口
-//类型 text ,file,image
-const sendMsg = (messageType = "text", content = "", obj = {}) => {
-    const params = {
-        farmId: farmVal.value,
-        senderId: curUserId,
-        receiverId: userId.value,
-        content,
-        [messageType]:obj,
-        messageType,
-    };
-    VE_API.bbs.sendMsg(params);
-};
-
-const userId = ref(null);
-
-const handleCardClick = (msg) => {
-    router.push(msg.linkUrl || msg.content.linkUrl);
-}
-
-const handleImgUrl = (url) => {
-    if (url && url.includes('https://')) {
-        return url;
-    } else {
-        return base_img_url2 + url + resize;
-    }
-}
-
-watch(
-    () => props.userId,
-    async (newValue) => {
-        if (newValue) {
-            await getFarmList();
-            userId.value = newValue;
-            createSession(newValue, () => {
-                if(route.query.pageParams) {
-                    const params = JSON.parse(route.query.pageParams);
-                    const message = {
-                        sender: "sent",
-                        messageType: "card",
-                        senderIcon: senderIcon.value,
-                        title: params.farmWorkName + ' 农事已完成,请您确认',
-                        cardType:'farm_work',
-                        linkUrl:`/completed_work?miniJson=${JSON.stringify({id:params.id})}`,
-                        time: getCurrentTime(),
-                    };
-                    if(params.executeEvidence && params.executeEvidence.length > 5) {
-                        const imgArr = JSON.parse(params.executeEvidence);
-                        message.coverUrl = imgArr[imgArr.length - 1];
-                    }
-
-                    if(params.imageList && params.imageList.length) {
-                        const img = params.imageList[params.imageList.length - 1];
-                        if (img.cloudFilename) {
-                            message.coverUrl = img.cloudFilename;
-                        } else {
-                            message.coverUrl = img;
-                        }
-                    }
-                    
-                    if(params.type === 'quotation') {
-                        message.cardType = 'quotation';
-                        message.title = '向您发送了一张服务报价单'
-                        const jsonParams = {
-                            id:params.id,
-                            farmWorkOrderId:params.farmWorkOrderId,
-                            isAssign: true
-                        }
-                        message.linkUrl = `/completed_work?miniJson=${JSON.stringify(jsonParams)}`;
-                    }
-
-                    if(params.type === 'reviewWork') {
-                        message.cardType = 'reviewWork';
-                        message.title = '向您分享了农事执行成果'
-                        message.linkUrl = `/review_work?miniJson=${JSON.stringify({id: params.id,goBack: true})}`;
-                    }
-
-                    if(params.type === 'remindExecute' || params.type === 'remindUser') {
-                        const jsonParams = {
-                            id:params.id,
-                            farmWorkOrderId:params.farmWorkOrderId
-                        }
-                        if(params.type === 'remindExecute') {
-                            message.title = '请您尽快执行 ' + params.farmWorkName + ' 农事';
-                            message.linkUrl = `/completed_work?miniJson=${JSON.stringify(jsonParams)}`;
-                        } else if(params.type === 'remindUser') {
-                            message.title = '请您尽快完成复核';
-                            message.linkUrl = `/review_work?miniJson=${JSON.stringify({id:params.id,goBack: true})}`;
-                        }
-                        message.cardType = params.type
-                    }
-
-                    sendMessage(message);
-                    
-                    // 清除路由中的 pageParams 参数
-                    const newQuery = { ...route.query };
-                    delete newQuery.pageParams;
-                    router.replace({
-                        path: route.path,
-                        query: newQuery
-                    });
-                }
-                if (route.query.selectedImgs) {
-                    const imgArr = JSON.parse(route.query.selectedImgs);
-                    if (imgArr && imgArr.length) {
-                        imgArr.forEach((item) => {
-                            sendImageMessage(item);
-                        });
-                        // 图片发送完成后,清除路由中的 selectedImgs 参数
-                        const newQuery = { ...route.query };
-                        delete newQuery.selectedImgs;
-                        router.replace({
-                            path: route.path,
-                            query: newQuery
-                        });
-                    }
-                }
-            });
-            initMqtt();
-        }
-    }
-);
-
-onDeactivated(() => {
-    if(clearSession.value){
-        sessionStorage.removeItem('selectedFarmId');
-    }
-    mqttClient.value && mqttClient.value.client.end(true);
-});
-
-// mqtt 连接
-const mqttClient = ref(null);
-const messagesContainer = ref(null);
-
-// 消息数据
-const messages = ref([]);
-
-// 输入相关
-const inputMessage = ref("");
-const fileInput = ref(null);
-const showEmojiPicker = ref(false);
-const emojis = ["😀", "😂", "😍", "👍", "👋", "🎉", "❤️", "🙏"];
-
-// 录音相关
-const isRecording = ref(false);
-const recordingDuration = ref(0);
-const audioChunks = ref([]);
-const mediaRecorder = ref(null);
-const audioContext = ref(null);
-
-function handleImageLoad() {
-    scrollToBottom();
-}
-
-// 图片预览
-const previewImage = ref(null);
-
-// 初始化 mqtt
-const initMqtt = () => {
-    const topics = [`user/chat/message/${farmVal.value}/${curUserId}`]; // 订阅的主题数组
-    mqttClient.value = new MqttClient(topics, (topic, message) => {
-        if (message && message.length > 10) {
-            const obj = JSON.parse(message);
-            console.log("message有值", obj);
-            if(obj.senderId === curUserId){
-                return;
-            }
-            if (obj.senderId === receiverIdVal.value) {
-                // 检查是否已存在相同 id 的消息,避免重复添加
-                if (obj.id && messages.value.some(msg => msg.id === obj.id)) {
-                    return;
-                }
-                if (obj.messageType === "image") {
-                    if (obj.image && (obj.image.url || obj.image.originUrl)) {
-                        obj.content = obj.image.url || obj.image.originUrl;
-                    } else if (obj.content) {
-                        try {
-                            const img = JSON.parse(obj.content);
-                            obj.content = img.url || img.originUrl;
-                        } catch (e) {
-                            console.error(e, "e");
-                        }
-                    }
-                }else if(obj.messageType !== 'text'){
-                    obj.content = JSON.parse(obj.content);
-                }
-                obj.receiverId = curUserId;
-                (obj.sender = obj.senderId === curUserId ? "sent" : "received"), (obj.senderIcon = senderIcon.value);
-                obj.receiverIcon = receiverIcon.value;
-                messages.value.push(obj);
-
-                scrollToBottom();
-            }
-        }
-    });
-
-    mqttClient.value.connect();
-};
-
-// 发送消息
-const sendMessage = (message) => {
-    if (message.messageType === "text") {
-        sendMsg("text", message.content);
-    } else if (message.messageType === "image") {
-        // 按新协议:不传 content,传 image 对象
-        sendMsg("image", "", { url: message.content, thumbnailUrl: message.content + resize });
-    } else if (message.messageType === "report") {
-        // 对话样式消息不发送到服务器,只显示在本地
-        console.log("发送对话样式消息:", message);
-        if(message.reportType === 'farm_report'){
-            sendMsg('report','',{
-                title: message.title,
-                reportId: message.reportId,
-                reportType: message.reportType,
-            });
-        }else{
-            sendMsg('report','',{
-                title: message.title,
-                reportId: message.reportId,
-                reportType: message.reportType,
-            });
-            console.log('其他文件1');
-        }
-    }else{
-        sendMsg('card','',{
-            title: message.title,
-            coverUrl: message.coverUrl,
-            cardType: message.cardType,
-            linkUrl: message.linkUrl
-        });
-    }
-    messages.value.push(message);
-    scrollToBottom();
-};
-
-// 发送文本消息
-const sendTextMessage = () => {
-    if (inputMessage.value.trim()) {
-        const message = {
-            sender: "sent",
-            messageType: "text",
-            senderIcon: senderIcon.value,
-            content: inputMessage.value,
-            time: getCurrentTime(),
-        };
-        sendMessage(message);
-        inputMessage.value = "";
-    }
-};
-
-// 发送图片消息
-const sendImageMessage = (thumbnailUrl) => {
-    const message = {
-        sender: "sent",
-        messageType: "image",
-        senderIcon: senderIcon.value,
-        content: thumbnailUrl,
-        time: getCurrentTime(),
-    };
-    sendMessage(message);
-};
-
-// 发送语音消息
-const sendAudioMessage = (audioUrl, duration) => {
-    const message = {
-        sender: "sent",
-        messageType: "audio",
-        senderIcon: senderIcon.value,
-        content: audioUrl,
-        duration: duration,
-        time: getCurrentTime(),
-    };
-    sendMessage(message);
-};
-
-// 图片处理
-const startImageUpload = () => {
-    fileInput.value.click();
-};
-
-const uploadFileObj = new UploadFile();
-
-const handleImageUpload = (event) => {
-    const file = event.target.files[0];
-    if (file) {
-        // 实际项目中应该上传到服务器,这里使用本地URL模拟
-        const miniUserId = localStorage.getItem("MINI_USER_ID");
-        let ext = getFileExt(file.name);
-        let key = `birdseye-look-mini/${miniUserId}/${new Date().getTime()}.${ext}`;
-        let imageUrl = "";
-        uploadFileObj.put(key, file).then((resFilename) => {
-            imageUrl = base_img_url2 + resFilename;
-            sendImageMessage(imageUrl);
-        });
-    }
-};
-
-const showImagePreview = (imageUrl) => {
-    previewImage.value = imageUrl;
-};
-
-// 语音处理
-const startRecording = async () => {
-    try {
-        audioChunks.value = [];
-        recordingDuration.value = 0;
-
-        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
-        mediaRecorder.value = new MediaRecorder(stream);
-        audioContext.value = new (window.AudioContext || window.webkitAudioContext)();
-
-        mediaRecorder.value.ondataavailable = (event) => {
-            if (event.data.size > 0) {
-                audioChunks.value.push(event.data);
-            }
-        };
-
-        mediaRecorder.value.onstop = async () => {
-            const audioBlob = new Blob(audioChunks.value, { type: "audio/wav" });
-            const audioUrl = URL.createObjectURL(audioBlob);
-
-            // 计算录音时长
-            const duration = Math.round(recordingDuration.value);
-
-            // 实际项目中应该上传到服务器,这里使用本地URL模拟
-            sendAudioMessage(audioUrl, duration);
-
-            // 释放资源
-            stream.getTracks().forEach((track) => track.stop());
-        };
-
-        mediaRecorder.value.start();
-        isRecording.value = true;
-
-        // 更新录音计时器
-        const timer = setInterval(() => {
-            recordingDuration.value += 0.1;
-            if (!isRecording.value) {
-                clearInterval(timer);
-            }
-        }, 100);
-    } catch (error) {
-        console.error("录音失败:", error);
-        alert("无法访问麦克风,请检查权限设置");
-    }
-};
-
-const stopRecording = () => {
-    if (mediaRecorder.value && isRecording.value) {
-        mediaRecorder.value.stop();
-        isRecording.value = false;
-    }
-};
-
-const playAudio = (audioUrl) => {
-    const audio = new Audio(audioUrl);
-    audio.play();
-};
-
-// Emoji 处理
-const toggleEmojiPicker = () => {
-    showEmojiPicker.value = !showEmojiPicker.value;
-};
-
-const addEmoji = (emoji) => {
-    inputMessage.value += emoji;
-    showEmojiPicker.value = false;
-};
-
-// 功能按钮配置
-const functionButtons = ref([
-    {
-        text: "农场报告",
-        handler: () => {
-            // 发送农场报告对话框消息
-            sendFarmReportDialog();
-        },
-    },
-    // {
-    //     text: "农事卡片",
-    //     handler: () => {
-    //         clearSession.value = false
-    //         // 跳转到农事卡片页面
-    //         router.push(`/farm_card?farmId=${farmVal.value}`);
-    //     },
-    // },
-    {
-        text: '农场相册',
-        handler: () => {
-            clearSession.value = false
-            // 跳转到农场相册页面
-            router.push(`/farm_photo?farmId=${farmVal.value}&isCheck=true&userId=${props.userId}`);
-        }
-    }
-]);
-
-// 辅助函数
-const getCurrentTime = () => {
-    return new Date().toLocaleTimeString("zh-CN", {
-        hour: "2-digit",
-        minute: "2-digit",
-    });
-};
-
-const scrollToBottom = () => {
-    nextTick(() => {
-        setTimeout(() => {
-            if (messagesContainer.value) {
-                messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
-            }
-        }, 300);
-    });
-};
-
-const farmVal = ref("");
-const options = ref([]);
-const route = useRoute();
-
-// 获取农场列表
-function getFarmList() {
-    let params = {
-        agriculturalQuery: false,
-    }
-    if(curRole.value == 2){
-        params.userId = props.userId;
-    }
-    return VE_API.farm.userFarmSelectOption(params).then(({ data }) => {
-        options.value = data || [];
-        if (data && data.length > 0) {
-            const defaultOption = data.find((item) => item.defaultOption === true);
-            // 优先使用 sessionStorage 中保存的农场ID(用户刚刚切换的农场)
-            const savedFarmId = sessionStorage.getItem('selectedFarmId');
-            if (savedFarmId) {
-                // 检查保存的农场是否还在当前列表中
-                const savedFarm = data.find((item) => item.id == savedFarmId);
-                if (savedFarm) {
-                    farmVal.value = Number(savedFarmId);
-                } else {
-                    // 如果保存的农场不在列表中,使用路由参数或默认农场
-                    farmVal.value = route.query.farmId ? Number(route.query.farmId) : (defaultOption ? defaultOption.id : data[0].id);
-                }
-            } else if(route.query.farmId) {
-                // 如果没有保存的农场,使用路由参数
-                farmVal.value = Number(route.query.farmId);
-            } else {
-                // 都没有则使用默认农场
-                farmVal.value = defaultOption ? defaultOption.id : data[0].id;
-            }
-        }
-    });
-}
-
-const curRole = ref(null);
-onActivated(() => {
-    curRole.value = localStorage.getItem("SET_USER_CUR_ROLE");
-    if (props.userId) {
-        scrollToBottom();
-    }
-    // 检查是否有选中的农事工作数据
-    checkSelectedFarmWork();
-});
-
-// 检查选中的农事工作数据
-const checkSelectedFarmWork = () => {
-    const selectedFarmWork = sessionStorage.getItem("selectedFarmWork");
-    if (selectedFarmWork) {
-        const data = JSON.parse(selectedFarmWork);
-        const message = {
-            ...data,
-            coverUrl:''
-        };
-        // 发送对话样式的消息
-        sendMessage(data);
-        // 清除localStorage中的数据
-        sessionStorage.removeItem("selectedFarmWork");
-    }
-};
-
-// 发送对话样式的消息
-const sendDialogMessage = (dialogData) => {
-    const message = {
-        sender: "sent",
-        messageType: "report",
-        senderIcon: senderIcon.value,
-        content: dialogData,
-        time: getCurrentTime(),
-    };
-    sendMessage(message);
-};
-
-const clearSession = ref(true);
-// 点击农场报告对话框
-const handleReportClick = (msg) => {
-    console.log(msg);
-    if(msg.reportType === 'farm_report' || msg.messageType === 'report'){
-        clearSession.value = false;
-        const params = {
-            farmId: msg.reportId || msg.content?.reportId,
-            showFilter: true,
-        }
-        router.push(`/farm_report?miniJson=${JSON.stringify(params)}`);
-    }else{
-        console.log('其他文件');
-    }
-}
-
-// 发送农场报告对话框
-const sendFarmReportDialog = () => {
-    const currentFarmName = options.value.find((opt) => opt.id === farmVal.value)?.name || "当前农场";
-
-    const message = {
-        sender: "sent",
-        messageType: "report",
-        senderIcon: senderIcon.value,
-        title: currentFarmName,
-        reportId:farmVal.value,
-        reportType:'farm_report',
-        time: getCurrentTime(),
-    };
-    sendMessage(message);
-};
-</script>
-
-<style scoped lang="scss">
-/* 基础样式(保持之前的不变) */
-.chat-container {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    width: 100%;
-    margin: 0 auto;
-    border: 1px solid #e6e6e6;
-    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
-    position: relative;
-    box-sizing: border-box;
-    .chat-messages {
-        flex: 1;
-        padding: 12px;
-        overflow-y: auto;
-        background-color: #f5f5f5;
-        box-sizing: border-box;
-        .message {
-            display: flex;
-            margin-bottom: 15px;
-        }
-        .received {
-            justify-content: flex-start;
-            .bubble {
-                background-color: white;
-                border-radius: 0 10px 10px 10px;
-                padding: 10px 12px;
-                box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
-            }
-        }
-        .sent {
-            justify-content: flex-end;
-            .bubble {
-                background-color: #07c160;
-                border-radius: 10px 0 10px 10px;
-                padding: 10px 15px;
-                color: #fff;
-                box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
-            }
-        }
-        .avatar {
-            width: 40px;
-            height: 40px;
-            border-radius: 50%;
-            background-color: #07c160;
-            color: white;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            margin-right: 10px;
-            font-weight: bold;
-        }
-
-        .avatar-r {
-            margin: 0 0 0 10px;
-        }
-        .bubble {
-            max-width: 70%;
-        }
-    }
-}
-
-.content {
-    font-size: 16px;
-    line-height: 1.4;
-}
-
-.time {
-    font-size: 12px;
-    color: #fff;
-    margin-top: 5px;
-    text-align: right;
-}
-
-.input-area {
-    display: flex;
-    align-items: center;
-    padding: 15px 10px;
-    border-top: 1px solid #e6e6e6;
-    background-color: white;
-    position: relative;
-    width: 100%;
-    box-sizing: border-box;
-    input {
-        flex: 1;
-        padding: 10px;
-        border: 1px solid #e6e6e6;
-        border-radius: 20px;
-        outline: none;
-    }
-    .send {
-        margin-left: 10px;
-        padding: 8px 20px;
-        background-color: #07c160;
-        color: white;
-        border: none;
-        border-radius: 20px;
-        cursor: pointer;
-    }
-}
-// .input-area button:hover {
-//     background-color: #06ad56;
-// }
-
-/* 新增的多媒体消息样式 */
-.image-message {
-    img {
-        max-width: 200px;
-        max-height: 200px;
-        border-radius: 8px;
-        cursor: pointer;
-    }
-}
-
-/* 图片消息不使用对话气泡样式 */
-.no-bubble {
-    background: transparent !important;
-    border-radius: 0 !important;
-    padding: 0 !important;
-    box-shadow: none !important;
-    color: inherit !important;
-}
-.card-bubble{
-    background: #fff !important;
-}
-
-.audio-message {
-    display: flex;
-    align-items: center;
-    padding: 10px 15px;
-    background-color: #f0f0f0;
-    border-radius: 20px;
-    cursor: pointer;
-}
-
-.audio-message .audio-icon {
-    margin-right: 8px;
-    font-size: 20px;
-}
-
-.audio-message .duration {
-    font-size: 14px;
-    color: #666;
-}
-
-.message.sent .audio-message {
-    background-color: #d8f1cb;
-}
-
-/* 工具栏样式 */
-.toolbar {
-    display: flex;
-    align-items: center;
-    button {
-        background: none;
-        border: none;
-        font-size: 20px;
-        margin-right: 10px;
-        cursor: pointer;
-        padding: 5px;
-    }
-    .link {
-        font-size: 24px;
-        margin-right: 10px;
-    }
-}
-
-/* 录音指示器 */
-.recording-indicator {
-    position: absolute;
-    top: -40px;
-    left: 0;
-    right: 0;
-    background-color: #ff4d4f;
-    color: white;
-    padding: 8px;
-    text-align: center;
-    border-radius: 4px;
-    font-size: 14px;
-}
-
-.recording-indicator .pulse {
-    display: inline-block;
-    width: 10px;
-    height: 10px;
-    border-radius: 50%;
-    background: white;
-    margin-right: 8px;
-    animation: pulse 1.5s infinite;
-}
-
-@keyframes pulse {
-    0% {
-        transform: scale(0.95);
-        opacity: 1;
-    }
-    50% {
-        transform: scale(1.1);
-        opacity: 0.7;
-    }
-    100% {
-        transform: scale(0.95);
-        opacity: 1;
-    }
-}
-
-/* Emoji 选择器 */
-.emoji-picker {
-    position: absolute;
-    bottom: 60px;
-    right: 10px;
-    background: white;
-    border: 1px solid #e6e6e6;
-    border-radius: 8px;
-    padding: 10px;
-    display: grid;
-    grid-template-columns: repeat(4, 1fr);
-    gap: 8px;
-    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
-    z-index: 100;
-}
-
-.emoji-picker span {
-    font-size: 24px;
-    text-align: center;
-}
-
-.emoji-picker span:hover {
-    transform: scale(1.2);
-}
-
-/* 功能按钮样式 */
-.function-buttons {
-    display: flex;
-    gap: 5px;
-    padding: 5px 10px;
-    box-sizing: border-box;
-    .function-btn {
-        background-color: white;
-        border-radius: 8px;
-        padding: 10px 10px;
-        min-width: 60px;
-        text-align: center;
-        .btn-text {
-            font-size: 14px;
-        }
-    }
-}
-
-/* 图片预览 */
-.image-preview {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: rgba(0, 0, 0, 0.8);
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    z-index: 1000;
-}
-
-.image-preview img {
-    max-width: 90%;
-    max-height: 90%;
-    object-fit: contain;
-}
-
-/* 对话样式消息 */
-.dialog-message {
-    max-width: 100%;
-    background: #fff !important;
-    border-radius: 10px;
-    .report-title {
-        font-size: 16px;
-        font-weight: 600;
-        color: #000;
-        margin-bottom: 5px;
-    }
-
-    .dialog-title {
-        font-size: 12px;
-        color: rgba(0, 0, 0, 0.6);
-        margin-bottom: 10px;
-    }
-    .monitor-image {
-        width: 222px;
-        height: 180px;
-        object-fit: cover;
-    }
-
-    .farm-report-content,
-    .farm-work-content {
-        .report-details,
-        .work-details {
-            background: #f8f9fa;
-            border-radius: 8px;
-            padding: 12px;
-            margin-top: 10px;
-
-            .detail-item {
-                display: flex;
-                margin-bottom: 6px;
-                font-size: 13px;
-
-                &:last-child {
-                    margin-bottom: 0;
-                }
-
-                .detail-label {
-                    color: #666;
-                    min-width: 80px;
-                }
-
-                .detail-value {
-                    color: #333;
-                    flex: 1;
-                }
-            }
-        }
-    }
-}
-
-.card-message{
-    .card-title{
-        font-size: 15px;
-        font-weight: 600;
-        color: #000;
-        margin-bottom: 5px;
-    }
-    img{
-        width: 222px;
-        height: 180px;
-        object-fit: cover;
-    }
-}
-
-/* 我方消息中的对话样式 */
-.message.sent .dialog-message {
-    background: #e3f2fd;
-
-    .work-details {
-        background: #f0f8ff;
-    }
-}
-</style>

+ 92 - 0
src/components/popup/completeFarmInfoPopup.vue

@@ -0,0 +1,92 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        teleport="body"
+        class="complete-farm-popup"
+        :close-on-click-overlay="true"
+    >
+        <div class="complete-farm-popup__title">{{ t("agriFile.completeFarmInfo") }}</div>
+        <div class="complete-farm-popup__subtitle">
+            {{ t("agriFile.getImmediately") }}
+            <span>{{ t("agriFile.patrolDiagnosisService") }}</span>
+        </div>
+        <div class="complete-farm-popup__image">
+            <slot name="image"></slot>
+        </div>
+        <div class="complete-farm-popup__btn" @click="handleOpen">{{ t("agriFile.goEnable") }}</div>
+    </popup>
+</template>
+
+<script setup>
+import { computed } from "vue";
+import { Popup } from "vant";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const emit = defineEmits(["update:show", "confirm"]);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+const handleOpen = () => {
+    emit("confirm");
+    showValue.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.complete-farm-popup {
+    width: 82%;
+    padding: 20px 10px;
+    text-align: center;
+
+    &__title,
+    &__subtitle {
+        font-size: 24px;
+        font-family: 'pangmenzhengdao';
+    }
+
+    &__subtitle {
+        margin-top: -4px;
+
+        span {
+            color: #2199f8;
+        }
+    }
+
+    &__image {
+        width: 100%;
+        height: 156px;
+        margin: 14px 0;
+        border-radius: 5px;
+        background: #f2f4f7;
+
+        :deep(img) {
+            display: block;
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    &__btn {
+        width: 162px;
+        border-radius: 25px;
+        background: #2199f8;
+        padding: 8px 0;
+        color: #fff;
+        font-size: 16px;
+        margin: auto;
+    }
+}
+</style>

+ 88 - 0
src/components/popup/diagnosisReportPopup.vue

@@ -0,0 +1,88 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        teleport="body"
+        class="diagnosis-report-popup"
+        :close-on-click-overlay="true"
+    >
+        <img class="diagnosis-report-popup__image" src="@/assets/img/agricultural/report.png" alt="" />
+        <div class="diagnosis-report-popup__title">
+            <div>{{ t("agriFile.plantingDiagnosisReport") }}</div>
+            <div>{{ t("agriFile.reportGenerated") }}</div>
+        </div>
+        <div class="diagnosis-report-popup__btn" @click="handleView">{{ t("agriFile.clickToView") }}</div>
+        <div class="diagnosis-report-popup__hint">{{ t("agriFile.viewInDiagnosisHint") }}</div>
+    </popup>
+</template>
+
+<script setup>
+import { computed } from "vue";
+import { Popup } from "vant";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const emit = defineEmits(["update:show", "confirm"]);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+const handleView = () => {
+    emit("confirm");
+    showValue.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.diagnosis-report-popup {
+    width: 78%;
+    padding: 90px 20px 22px;
+    text-align: center;
+    overflow: visible;
+    background: linear-gradient(360deg, #FFFFFF 0%, #D3EBFF 100%);
+
+    &__image {
+        display: block;
+        width: 138px;
+        height: 138px;
+        margin: 0 auto 16px;
+        position: absolute;
+        top: -54px;
+        left: 50%;
+        transform: translateX(-50%);
+    }
+
+    &__title {
+        font-size: 30px;
+        color: #010101;
+        line-height: 32px;
+        div{
+            font-family: "pangmenzhengdao";
+        }
+    }
+
+    &__btn {
+        width: 162px;
+        margin: 18px auto 10px;
+        padding: 8px 0;
+        border-radius: 25px;
+        background: linear-gradient(180deg, #96D1FF 0%, #2199F8 100%);
+        color: #fff;
+        font-size: 16px;
+    }
+
+    &__hint {
+        color: #1D1919;
+    }
+}
+</style>

+ 145 - 0
src/components/popup/farmCalendarPopup.vue

@@ -0,0 +1,145 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        teleport="body"
+        class="farm-calendar-popup"
+        :close-on-click-overlay="true"
+    >
+        <div class="farm-calendar-popup__title">{{ t("agriFile.completeFarmInfo") }}</div>
+        <div class="farm-calendar-popup__subtitle">
+            {{ t("agriFile.getImmediately") }}
+            <span>{{ t("workExecute.exclusiveFarmCalendar") }}</span>
+        </div>
+        <div class="farm-calendar-popup__days">
+            <div
+                v-for="(day, index) in weekDays"
+                :key="day"
+                class="farm-calendar-popup__day"
+                :class="{ 'is-active': activeDay === index }"
+                @click="activeDay = index"
+            >
+                {{ t(day) }}
+            </div>
+        </div>
+        <div class="farm-calendar-popup__image">
+            <img v-if="currentImage" :src="currentImage" alt="" />
+            <slot v-else name="image" :day="activeDay"></slot>
+        </div>
+        <div class="farm-calendar-popup__btn" @click="handleOpen">{{ t("agriFile.goEnable") }}</div>
+    </popup>
+</template>
+
+<script setup>
+import { computed, ref } from "vue";
+import { Popup } from "vant";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+    images: {
+        type: Array,
+        default: () => [],
+    },
+});
+
+const emit = defineEmits(["update:show", "confirm"]);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+const weekDays = ["week.1", "week.2", "week.3", "week.4", "week.5"];
+const activeDay = ref(1);
+
+const currentImage = computed(() => props.images[activeDay.value] || "");
+
+const handleOpen = () => {
+    emit("confirm");
+    showValue.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.farm-calendar-popup {
+    width: 82%;
+    padding: 20px 10px;
+    text-align: center;
+    background: linear-gradient(360deg, #FFFFFF 1.28%, #D7EEFF 55.76%, #FFFFFF 98.67%);
+
+    &__title,
+    &__subtitle {
+        font-size: 24px;
+        font-family: "pangmenzhengdao";
+        line-height: 28px;
+    }
+
+    &__subtitle {
+        span {
+            color: #2199f8;
+        }
+    }
+
+    &__days {
+        display: flex;
+        justify-content: space-between;
+        gap: 12px;
+        margin-top: 14px;
+    }
+
+    &__day {
+        position: relative;
+        flex: 1;
+        border-radius: 4px 4px 0 0;
+        background: #fff;
+        padding: 4px 0;
+
+        &.is-active {
+            background: linear-gradient(149.44deg, #9FD5FF 2.43%, #2199F8 91.84%);
+            color: #fff;
+            border-radius: 4px;
+
+            &::after {
+                content: "";
+                position: absolute;
+                left: 50%;
+                bottom: -6px;
+                transform: translateX(-50%);
+                border-left: 6px solid transparent;
+                border-right: 6px solid transparent;
+                border-top: 6px solid #2199f8;
+            }
+        }
+    }
+
+    &__image {
+        width: 100%;
+        height: 142px;
+        margin: 12px 0 14px;
+        border-radius: 5px;
+        background: #f2f4f7;
+
+        img {
+            display: block;
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    &__btn {
+        width: 162px;
+        margin: auto;
+        padding: 8px 0;
+        border-radius: 25px;
+        background: #2199f8;
+        color: #fff;
+        font-size: 16px;
+    }
+}
+</style>

+ 94 - 0
src/components/popup/leaveConfirmPopup.vue

@@ -0,0 +1,94 @@
+<template>
+    <popup
+        v-model:show="showValue"
+        round
+        teleport="body"
+        class="leave-confirm-popup"
+        :close-on-click-overlay="true"
+    >
+        <img class="leave-confirm-popup__icon" src="@/assets/img/agricultural/notice.png" alt="" />
+        <div class="leave-confirm-popup__title">{{ t("agriFile.confirmLeaveTitle") }}</div>
+        <div class="leave-confirm-popup__desc">
+            <div>{{ t("agriFile.confirmLeaveDesc1") }}</div>
+            <div>{{ t("agriFile.confirmLeaveDesc2") }}</div>
+        </div>
+        <div class="leave-confirm-popup__btn" @click="handleContinue">{{ t("agriFile.continueRecord") }}</div>
+        <div class="leave-confirm-popup__leave" @click="handleLeave">{{ t("agriFile.confirmLeave") }}</div>
+    </popup>
+</template>
+
+<script setup>
+import { computed } from "vue";
+import { Popup } from "vant";
+import { useI18n } from "@/i18n";
+
+const { t } = useI18n();
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const emit = defineEmits(["update:show", "confirm", "cancel"]);
+
+const showValue = computed({
+    get: () => props.show,
+    set: (val) => emit("update:show", val),
+});
+
+const handleContinue = () => {
+    emit("cancel");
+    showValue.value = false;
+};
+
+const handleLeave = () => {
+    emit("confirm");
+    showValue.value = false;
+};
+</script>
+
+<style scoped lang="scss">
+.leave-confirm-popup {
+    width: 78%;
+    padding: 72px 20px 22px;
+    text-align: center;
+    overflow: visible;
+    background: linear-gradient(360deg, #FFFFFF 0%, #D3EBFF 100%);
+
+    &__icon {
+        position: absolute;
+        top: -32px;
+        left: 50%;
+        display: block;
+        width: 102px;
+        height: 102px;
+        transform: translateX(-50%);
+    }
+
+    &__title {
+        font-size: 26px;
+        color: #010101;
+        font-family: "pangmenzhengdao";
+    }
+
+    &__desc {
+        margin-top: 8px;
+        color: #1F1F1F;
+    }
+
+    &__btn {
+        margin: 20px auto 10px;
+        padding: 8px;
+        border-radius: 25px;
+        background: linear-gradient(180deg, #96D1FF 0%, #2199F8 100%);
+        color: #fff;
+        font-size: 16px;
+    }
+
+    &__leave {
+        color: #AEAEAE;
+    }
+}
+</style>

+ 28 - 2
src/i18n/messages.js

@@ -6,7 +6,6 @@ export default {
             situationAssessment: "农情研判",
             agriFile: "农事档案",
             agriRecord: "农事规划",
-            trace: "有味溯源",
             schedule: "农事列表",
         },
         common: {
@@ -170,6 +169,19 @@ export default {
             confirmPlot: "确认地块",
             pleaseSelectPlot: "请先选择地块",
             cancel: "取消",
+            completeFarmInfo: "完善农场信息",
+            getImmediately: "马上获取",
+            patrolDiagnosisService: "巡田诊断服务",
+            goEnable: "去开启",
+            plantingDiagnosisReport: "种植诊断报告",
+            reportGenerated: "已生成",
+            clickToView: "点击查看",
+            viewInDiagnosisHint: "还可在 农事档案-诊断报告 中查看",
+            confirmLeaveTitle: "确认要离开?",
+            confirmLeaveDesc1: "长势巡园比例还没录入,确定要走吗?",
+            confirmLeaveDesc2: "少了这一步,估产数据就不准了哦~",
+            continueRecord: "继续记录",
+            confirmLeave: "确认离开",
             agriAlbum: "农情相册",
             diagnosisReport: "诊断报告",
             reportUpdate: "更新",
@@ -314,6 +326,7 @@ export default {
             forward: "转发",
             viewDetail: "查看详情",
             forwardTip: "转发功能即将开放",
+            exclusiveFarmCalendar: "专属农事日历",
             redispatch: "重新派发",
             remindAccept: "提醒接受",
             modifyInfo: "修改信息",
@@ -393,7 +406,6 @@ export default {
             situationAssessment: "Situation Assessment",
             agriFile: "Crop Archives",
             agriRecord: "Farm Planning",
-            trace: "Traceability",
             schedule: "Schedule",
         },
         common: {
@@ -557,6 +569,19 @@ export default {
             confirmPlot: "Confirm plot",
             pleaseSelectPlot: "Please select a plot first",
             cancel: "Cancel",
+            completeFarmInfo: "Complete farm info",
+            getImmediately: "Get",
+            patrolDiagnosisService: "field diagnosis now",
+            goEnable: "Get started",
+            plantingDiagnosisReport: "Planting diagnosis report",
+            reportGenerated: "Generated",
+            clickToView: "View now",
+            viewInDiagnosisHint: "Also available in Crop Archives - Diagnosis report",
+            confirmLeaveTitle: "Leave this page?",
+            confirmLeaveDesc1: "Growth patrol ratio is not entered yet. Leave anyway?",
+            confirmLeaveDesc2: "Without this step, yield estimates will be less accurate~",
+            continueRecord: "Keep recording",
+            confirmLeave: "Leave",
             agriAlbum: "Agri album",
             diagnosisReport: "Diagnosis report",
             reportUpdate: "Update",
@@ -701,6 +726,7 @@ export default {
             forward: "Forward",
             viewDetail: "View details",
             forwardTip: "Forward feature coming soon",
+            exclusiveFarmCalendar: "exclusive farm calendar",
             redispatch: "Redispatch",
             remindAccept: "Remind to accept",
             modifyInfo: "Edit info",

+ 0 - 40
src/router/globalRoutes.js

@@ -33,20 +33,6 @@ export default [
         meta: { keepAlive: true },
         component: () => import("@/views/old_mini/create_farm/editMap.vue"),
     },
-    // 聊天
-    {
-        path: "/chat_frame",
-        name: "ChatFrame",
-        meta: { keepAlive: true },
-        component: () => import("@/views/old_mini/chat_frame/index.vue"),
-    },
-    // 成果报告
-    {
-        path: "/achievement_report",
-        name: "AchievementReport",
-        meta: { keepAlive: true },
-        component: () => import("@/views/old_mini/achievement_report/index.vue"),
-    },
     // 长势报告
     {
         path: "/growth_report",
@@ -54,18 +40,6 @@ export default [
         meta: { showTabbar: true, keepAlive: true },
         component: () => import("@/views/old_mini/growth_report/index.vue"),
     },
-    // 历史风险报告
-    {
-        path: "/history_risk_report",
-        name: "HistoryRiskReport",
-        component: () => import("@/views/old_mini/growth_report/historyRiskReport.vue"),
-    },
-    // 咨询专家
-    {
-        path: "/consult",
-        name: "Consult",
-        component: () => import("@/views/old_mini/chat_frame/consult.vue"),
-    },
     // 农事互动列表
     {
         path: "/interaction_list",
@@ -80,20 +54,6 @@ export default [
         meta: { keepAlive: true },
         component: () => import("@/views/old_mini/interactionList/drawRegion.vue"),
     },
-    //确认区域
-    {
-        path: "/confirm_area",
-        name: "ConfirmArea",
-        meta: { keepAlive: true },
-        component: () => import("@/views/old_mini/interactionList/confirmArea.vue"),
-    },
-    //有味溯源
-    {
-        path: "/youwei_trace",
-        name: "YouweiTrace",
-        component: () => import("@/views/old_mini/youwei_trace/index.vue"),
-        meta: { showTabbar: true, keepAlive: true },
-    },
     // 农事详情
     {
         path: "/work_detail",

+ 0 - 784
src/views/old_mini/achievement_report/index.vue

@@ -1,784 +0,0 @@
-<template>
-    <div class="achievement-report-page">
-        <custom-header :name="$t('生成成果报告')"></custom-header>
-        <div class="report-content-wrap" v-loading="loading">
-            <div class="report-content" ref="reportDom">
-                <img src="@/assets/img/home/qrcode.png" alt="" class="code-icon" />
-                <div class="report-header">
-                    <img class="header-book" src="@/assets/img/home/book.png" alt="" />
-                    <div class="time-tag">{{ workItem?.executeDate }}</div>
-                    <div class="report-title">{{ t('成果报告') }}</div>
-                    <div class="report-info">
-                        <div class="info-item">
-                            <img class="info-icon" :src="workItem?.executorIcon || 'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'" alt="" />
-                            <span class="info-text">执行组织:{{ workItem?.executeMain }}</span>
-                        </div>
-                        <div class="info-item">
-                            <img class="info-icon" src="@/assets/img/home/farm.png" alt="" />
-                            <span class="info-text">服务农场:{{ workItem?.farmName }}</span>
-                        </div>
-                    </div>
-                </div>
-
-                <div class="report-box">
-                    <div class="report-box-item">
-                        <div class="item-content">{{ workItem?.farmWorkName || "--" }}</div>
-                        <div class="item-title">{{ t('作业农事') }}</div>
-                    </div>
-                    <div class="report-box-item">
-                        <div class="item-content">{{ formatArea(workItem?.area) || "--" }}</div>
-                        <div class="item-title">{{ t('作业面积') }}</div>
-                    </div>
-                    <div class="report-box-item">
-                        <div class="item-content">{{ reportData?.workCycle ? (reportData?.workCycle + '天') : "--" }}</div>
-                        <div class="item-title">{{ t('作业周期') }}</div>
-                    </div>
-                    <div class="report-box-item">
-                        <div class="item-content">{{ reportData?.deviceName || "--" }}</div>
-                        <div class="item-title">{{ t('使用设备') }}</div>
-                    </div>
-                </div>
-
-                <!-- <div class="report-box">
-                    <div class="box-title">{{ t('精准施治,智慧护航') }}</div>
-                    <div class="box-text">
-                        {{ reportData?.resultInfo || "--" }}
-                    </div>
-                </div> -->
-
-                <div class="report-excute" v-for="(item, index) in workItem?.executeEvidence" :key="index">
-                    <div class="tag-label">{{ t('执行照片') }}</div>
-                    <album-draw-box :key="paramsPage.id" :isShowNum="0" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.executeEvidence?.length"></album-draw-box>
-                </div>
-                <div class="report-excute" v-for="(item, index) in workItem?.reviewImage" :key="index">
-                    <div class="tag-label">{{ t('复核照片') }}</div>
-                    <album-draw-box :key="paramsPage.id+'复核'" :isShowNum="0" :isAchievementImgs="true" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.reviewImage?.length"></album-draw-box>
-                </div>
-                <!-- <div class="report-excute" v-for="(item, index) in workItem?.reviewImage" :key="index">
-                    <div class="tag-label">{{ t('复核照片') }}</div>
-                    <album-draw-box :isShowNum="0" :photo="item" :current="index" :index="'review'+index" :length="workItem?.reviewImage?.length"></album-draw-box>
-                </div> -->
-                <!-- <div class="report-excute">
-                    <album-carousel
-                        :key="93"
-                        :isAchievementImgs="true"
-                        :images="combinedReviewImages"
-                    ></album-carousel>
-                </div> -->
-            </div>
-
-            <div class="bottom-btn">
-                <div class="btn-item second" @click="handleDownload">{{ t('保存图片') }}</div>
-                <div class="btn-item primay" @click="handleDownload">{{ t('转发') }}</div>
-            </div>
-        </div>
-
-        <!-- 组合照片(用于生成合成图片) -->
-        <div class="review-hide-box">
-            <div class="review-image" ref="reviewComboRef">
-                <div class="review-mask">
-                    <div class="review-text">{{ t('复核成效') }}</div>
-                    <div class="review-content">
-                        {{ workItem?.reCheckText }}
-                    </div>
-                </div>
-                <div class="vs-wrap" v-if="workItem?.reviewImage && workItem?.reviewImage?.length">
-                    <img src="@/assets/img/home/vs.png" alt="" />
-                </div>
-                <div class="review-image-item" v-if="workItem?.executeEvidence?.length">
-                    <div class="review-image-item-title">{{ t('复核照片') }}</div>
-                    <img
-                        class="review-image-item-img left-img"
-                        :src="leftCoverImg"
-                        style="width: 100%; height: 255px; display: block; image-rendering: auto"
-                    />
-                </div>
-                <div class="review-image-item" v-if="workItem?.reviewImage?.length">
-                    <img
-                        class="review-image-item-img right-img"
-                        :src="rightCoverImg"
-                        style="width: 100%; height: 255px; display: block; image-rendering: auto"
-                    />
-                </div>
-            </div>
-        </div>
-    </div>
-
-    <popup class="cavans-popup" v-model:show="showPopup">
-        <div class="cavans-content">
-            <img class="current-img" :src="pageImg" alt="" />
-        </div>
-        <!-- 底部操作按钮 -->
-        <div class="bottom-actions" @click.stop="showPopup = false">
-            <div class="action-buttons">
-                
-                <div class="action-btn text-btn">
-                    &lt;&lt;长按图片保存或转发&gt;&gt;
-                </div>
-            </div>
-            <div class="cancel-btn" @click="handleCancel">{{ t('取消') }}</div>
-        </div>
-    </popup>
-</template>
-
-<script setup>
-import { useI18n } from "@/i18n";
-const { t } = useI18n();
-import CustomHeader from "@/components/customHeader.vue";
-import AlbumCarousel from "@/components/album_compoents/albumCarousel";
-import { ref, onActivated, onDeactivated, onUnmounted, nextTick, watch } from "vue";
-import html2canvas from "html2canvas";
-import { uploadBase64 } from "@/common/uploadImg";
-import { detectRuntimeEnvironment } from "@/common/commonFun";
-import { useRoute } from "vue-router";
-import { Popup } from "vant";
-import { base_img_url2 } from "@/api/config";
-import wx from "weixin-js-sdk";
-import AlbumDrawBox from "@/components/album_compoents/albumDrawBox.vue";
-
-const route = useRoute();
-const loading = ref(false);
-const workItem = ref({});
-const showPopup = ref(false);
-function formatArea(val) {
-    const num = typeof val === "number" ? val : parseFloat(val);
-    if (Number.isNaN(num)) return val;
-    return Number.isInteger(num) ? num : num.toFixed(2) + "亩";
-}
-const paramsPage = ref({});
-onActivated(() => {
-    window.scrollTo(0, 0);
-    paramsPage.value = route.query.miniJson ? JSON.parse(route.query.miniJson) : {};
-    getDetail();
-    getResultReport();
-});
-
-const reportData = ref({});
-const getResultReport = () => {
-    VE_API.z_farm_work_record.resultReport({ recordId: paramsPage.value.id }).then((res) => {
-        if (res.code === 0) {
-            reportData.value = res.data;
-        }
-    });
-};
-
-const getDetail = () => {
-    if (!paramsPage.value.id) return;
-    loading.value = true;
-    VE_API.z_farm_work_record
-        .getDetail({ id: paramsPage.value.id })
-        .then(({ data }) => {
-            workItem.value = data[0];
-        })
-        .finally(() => {
-            loading.value = false;
-        });
-};
-
-const isDowload = ref(true);
-const reportDom = ref(null);
-const pageImg = ref(null);
-
-async function handleDownload() {
-    isDowload.value = false;
-    setTimeout(async () => {
-        // 获取要截图的DOM元素
-        const element = reportDom.value;
-
-        try {
-            const canvas = await html2canvas(element, {
-                scrollY: -window.scrollY, // 处理滚动条位置
-                allowTaint: true, // 允许跨域图片
-                useCORS: true, // 使用CORS
-                scale: 2, // 提高分辨率(2倍)
-                height: element.scrollHeight, // 设置完整高度
-                width: element.scrollWidth, // 设置完整宽度
-                logging: true, // 开启日志(调试用)
-            });
-
-            // 转换为图片并下载
-            const image = canvas.toDataURL("image/png");
-            pageImg.value = image;
-            nextTick(() => {
-                showPopup.value = true;
-            })
-        } catch (error) {
-            isDowload.value = true;
-        }
-    });
-}
-
-const handleCancel = () => {
-    showPopup.value = false;
-};
-
-function downloadImage(dataUrl, filename) {
-    const link = document.createElement("a");
-    link.href = dataUrl;
-    link.download = filename;
-    document.body.appendChild(link);
-    link.click();
-    document.body.removeChild(link);
-}
-
-const reviewComboRef = ref(null);
-const combinedReviewImages = ref([]);
-// 生成组合照片,传给相册组件
-const generateCombinedReviewImage = async () => {
-    try {
-        await prepareCoverImages();
-        await nextTick();
-
-        const canvas = await html2canvas(reviewComboRef.value, {
-            backgroundColor: null,
-            useCORS: true,
-            allowTaint: true,
-            scale: window.devicePixelRatio || 2,
-        });
-
-        combinedReviewImages.value = [canvas.toDataURL("image/png")];
-    } catch (e) {
-        console.error("生成组合照片失败", e);
-    }
-};
-
-const prepareCoverImages = async () => {
-    await nextTick();
-
-    const itemEl = reviewComboRef.value.querySelector(".review-image-item");
-
-    const cssWidth = itemEl.offsetWidth;
-    const cssHeight = 255;
-
-    if (workItem.value?.executeEvidence?.length) {
-        leftCoverImg.value = await coverImageToBase64HD(
-            base_img_url2 + workItem.value.executeEvidence.at(-1),
-            cssWidth,
-            cssHeight
-        );
-    }
-
-    if (workItem.value?.reviewImage?.length) {
-        rightCoverImg.value = await coverImageToBase64HD(
-            base_img_url2 + workItem.value.reviewImage.at(-1),
-            cssWidth,
-            cssHeight
-        );
-    }
-};
-
-const leftCoverImg = ref("");
-const rightCoverImg = ref("");
-
-function coverImageToBase64HD(imgUrl, cssWidth, cssHeight) {
-    return new Promise((resolve, reject) => {
-        const dpr = window.devicePixelRatio || 2;
-
-        const img = new Image();
-        img.crossOrigin = "anonymous";
-        img.src = imgUrl;
-
-        img.onload = () => {
-            // ⚠️ 用“物理像素”创建 canvas
-            const canvas = document.createElement("canvas");
-            canvas.width = cssWidth * dpr;
-            canvas.height = cssHeight * dpr;
-
-            const ctx = canvas.getContext("2d");
-            ctx.scale(dpr, dpr);
-
-            const imgRatio = img.width / img.height;
-            const targetRatio = cssWidth / cssHeight;
-
-            let sx = 0,
-                sy = 0,
-                sw = img.width,
-                sh = img.height;
-
-            if (imgRatio > targetRatio) {
-                sw = img.height * targetRatio;
-                sx = (img.width - sw) / 2;
-            } else {
-                sh = img.width / targetRatio;
-                sy = (img.height - sh) / 2;
-            }
-
-            ctx.drawImage(img, sx, sy, sw, sh, 0, 0, cssWidth, cssHeight);
-
-            resolve(canvas.toDataURL("image/png"));
-        };
-
-        img.onerror = reject;
-    });
-}
-
-
-const userInfoStr = localStorage.getItem("localUserInfo");
-const userInfo = userInfoStr ? JSON.parse(userInfoStr) : {};
-
-async function handleForward() {
-    setTimeout(async () => {
-        // 获取要截图的DOM元素
-        const element = reportDom.value;
-
-        try {
-            const canvas = await html2canvas(element, {
-                scrollY: -window.scrollY, // 处理滚动条位置
-                allowTaint: true, // 允许跨域图片
-                useCORS: true, // 使用CORS
-                scale: 2, // 提高分辨率(2倍)
-                height: element.scrollHeight, // 设置完整高度
-                width: element.scrollWidth, // 设置完整宽度
-                logging: true, // 开启日志(调试用)
-            });
-
-            // 转换为图片并下载
-            const image = canvas.toDataURL("image/png");
-            const process = detectRuntimeEnvironment();
-            // if (process === "wechat-webview") {
-                const imgUrl = await uploadBase64(image, false);
-                const params = {
-                    val: 'share',
-                    key: "report",
-                };
-                VE_API.mine.saveSessionStore({ ...params, text: imgUrl }).then((res) => {
-                    if (res.success) {
-                        wx.miniProgram.navigateTo({
-                            url: `/pages/subPages/report_page/index`,
-                        });
-                    }
-                });
-            // }
-            isDowload.value = true;
-        } catch (error) {
-            isDowload.value = true;
-        }
-    });
-
-
-    // const image = await handleDownload(true);
-    // const imgUrl = await uploadBase64(image, false);
-    // const params = {
-    //     miniUserId: 766,
-    //     key: "report",
-    // };
-    // VE_API.mine.saveSessionStore({ ...params, text: imgUrl }).then((res) => {
-    //     if (res.success) {
-    //         wx.miniProgram.navigateTo({
-    //             url: `/pages/subPages/report_page/index`,
-    //         });
-    //     }
-    // });
-}
-
-watch(
-    () => [workItem.value.executeEvidence, workItem.value.reviewImage],
-    ([preImgs, reviewImgs]) => {
-        if (preImgs && preImgs.length && reviewImgs && reviewImgs.length) {
-            generateCombinedReviewImage();
-        }
-    },
-    { deep: true }
-);
-
-// 清理数据的函数
-const clearData = () => {
-    workItem.value = {};
-    reportData.value = {};
-    paramsPage.value = {};
-    loading.value = false;
-    isDowload.value = true;
-    combinedReviewImages.value = [];
-    leftCoverImg.value = "";
-    rightCoverImg.value = "";
-};
-
-onDeactivated(() => {
-    clearData();
-});
-
-onUnmounted(() => {
-    clearData();
-});
-</script>
-
-<style lang="scss" scoped>
-.achievement-report-page {
-    width: 100%;
-    height: 100vh;
-    background: linear-gradient(195.35deg, #d4e4ff 16.34%, rgba(93, 189, 255, 0) 50.3%),
-        linear-gradient(156.64deg, rgba(255, 255, 255, 0.16) 27.7%, rgba(255, 255, 255, 0) 72.82%);
-
-    .report-content-wrap {
-        height: calc(100% - 40px);
-        padding-bottom: 60px;
-        overflow: auto;
-        box-sizing: border-box;
-        position: relative;
-        .bottom-btn {
-            z-index: 2;
-            position: fixed;
-            bottom: 0;
-            left: 0;
-            width: 100%;
-            background: #fff;
-            height: 60px;
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            padding: 0 12px;
-            box-sizing: border-box;
-            box-shadow: 2px 2px 4.5px 0px rgba(0, 0, 0, 0.4);
-            .btn-item {
-                height: 40px;
-                line-height: 40px;
-                padding: 0 24px;
-                border-radius: 20px;
-                font-size: 14px;
-                &.second {
-                    color: #666666;
-                    border: 1px solid rgba(153, 153, 153, 0.5);
-                }
-                &.primay {
-                    padding: 0 34px;
-                    background: linear-gradient(180deg, #76c3ff, #2199f8);
-                    color: #fff;
-                }
-            }
-        }
-    }
-    
-    .code-icon {
-        position: absolute;
-        right: 10px;
-        top: 12px;
-        width: 48px;
-    }
-    .report-content {
-        background: url("@/assets/img/home/report_bg.png") no-repeat center center;
-        background-size: 100% auto;
-        background-position: top center;
-        padding: 24px 16px 16px;
-        box-sizing: border-box;
-        position: relative;
-        .report-header {
-            position: relative;
-            .header-book {
-                position: absolute;
-                right: 0;
-                bottom: -6px;
-                height: 88px;
-                z-index: 10;
-            }
-            .time-tag {
-                background: linear-gradient(137.86deg, #9fd5ff 5.87%, #2199f8 82.98%);
-                border-radius: 5px 0 5px 0;
-                height: 23px;
-                line-height: 23px;
-                font-size: 13px;
-                font-weight: 500;
-                color: #fff;
-                padding: 0 9px;
-                width: fit-content;
-            }
-            .report-title {
-                font-family: "PangMenZhengDao";
-                font-size: 34px;
-                line-height: 38px;
-                color: #000000;
-            }
-            .report-info {
-                padding: 10px 0 16px 0;
-                .info-item {
-                    width: fit-content;
-                    display: flex;
-                    height: 33px;
-                    align-items: center;
-                    padding: 0 6px 0 3px;
-                    background: linear-gradient(90deg, rgba(255, 255, 255, 0.58) 0%, rgba(255, 255, 255, 0.0696) 100%);
-                    border-radius: 20px;
-                    border: 0.5px solid rgba(33, 153, 248, 0.35);
-                    gap: 3px;
-                    .info-icon {
-                        width: 26px;
-                        height: 26px;
-                        object-fit: cover;
-                        border-radius: 50%;
-                    }
-                    .info-text {
-                        font-size: 14px;
-                        color: #2199f8;
-                    }
-                }
-                .info-item + .info-item {
-                    margin-top: 5px;
-                }
-            }
-        }
-
-        .report-box {
-            display: flex;
-            align-items: center;
-            padding: 8px;
-            background: linear-gradient(0deg, #ffffff 86.32%, #2199f8 136.87%);
-            border: 1px solid #ffffff;
-            border-radius: 8px;
-            gap: 5px;
-            position: relative;
-            .report-box-item {
-                flex: 1;
-                background: rgba(33, 153, 248, 0.1);
-                border-radius: 8px;
-                min-height: 62px;
-                box-sizing: border-box;
-                padding: 2px 4px;
-                display: flex;
-                flex-direction: column;
-                justify-content: center;
-                .item-content {
-                    color: #2199f8;
-                    font-size: 14px;
-                    text-align: center;
-                }
-                .item-title {
-                    color: #000000;
-                    font-size: 10px;
-                    text-align: center;
-                    padding-top: 5px;
-                }
-            }
-
-            .box-title {
-                position: absolute;
-                top: -8px;
-                left: 0;
-                height: 32px;
-                line-height: 26px;
-                padding: 0 10px;
-                color: #ffffff;
-                background: url("@/assets/img/home/title-bg.png") no-repeat center center / 100% 100%;
-            }
-            .box-text {
-                padding: 22px 0 12px 0;
-            }
-        }
-        .report-box + .report-box {
-            margin-top: 20px;
-        }
-        .report-excute {
-            position: relative;
-            margin-top: 12px;
-            .tag-label {
-                position: absolute;
-                top: 0;
-                left: 0;
-                padding: 4px 10px;
-                background: rgba(54, 52, 52, 0.8);
-                color: #fff;
-                font-size: 12px;
-                border-radius: 8px 0 8px 0;
-                z-index: 1;
-            }
-            ::v-deep {
-                .carousel-container .carousel-wrapper .carousel-img {
-                    min-width: calc(100vw - 32px);
-                    width: calc(100vw - 32px);
-                }
-            }
-        }
-    }
-    .download-btn {
-        position: fixed;
-        bottom: 20px;
-        left: 50%;
-        // background: #fff;
-        // box-shadow: 2px 2px 4.5px 0px #00000066;
-        // width: 100%;
-        transform: translateX(-50%);
-    }
-
-    .review-hide-box {
-        position: absolute;
-        left: 0;
-        width: 100%;
-        height: 100%;
-        z-index: -1;
-        bottom: 0;
-    }
-
-    .review-image {
-        position: relative;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: 8px;
-        margin: 12px;
-        background: #fff;
-        border-radius: 8px;
-        .review-mask {
-            z-index: 1;
-            pointer-events: none;
-            position: absolute;
-            left: 0;
-            top: 0;
-            width: 100%;
-            height: 100%;
-            border-radius: 8px;
-            background: linear-gradient(
-                360deg,
-                rgba(0, 0, 0, 0.78) 0%,
-                rgba(0, 0, 0, 0.437208) 19.87%,
-                rgba(0, 0, 0, 0) 33.99%
-            );
-            display: flex;
-            flex-direction: column;
-            align-items: baseline;
-            justify-content: end;
-            padding: 12px;
-            box-sizing: border-box;
-            color: #fff;
-            .review-text {
-                font-family: "PangMenZhengDao";
-                font-size: 16px;
-                margin-bottom: 1px;
-            }
-            .review-content {
-                font-size: 10px;
-                line-height: 15px;
-            }
-        }
-        .vs-wrap {
-            position: absolute;
-            left: 50%;
-            top: 50%;
-            transform: translate(-50%, -50%);
-            width: 40px;
-            height: 40px;
-            z-index: 10;
-            img {
-                width: 100%;
-                height: 100%;
-                object-fit: cover;
-            }
-        }
-        .review-image-item {
-            position: relative;
-            flex: 1;
-            .review-image-item-title {
-                position: absolute;
-                top: 0;
-                left: 0;
-                background: rgba(54, 52, 52, 0.6);
-                padding: 4px 10px;
-                border-radius: 8px 0 8px 0;
-                backdrop-filter: 4px;
-                font-size: 12px;
-                color: #fff;
-            }
-            // .review-image-item-img {
-            //     width: 100%;
-            //     height: 250px;
-            //     object-fit: cover;
-            // }
-            .review-image-item-img {
-                width: 100%;
-                height: 100%;
-                object-fit: cover;
-                object-position: center;
-            }
-            .left-img {
-                border-radius: 8px 0 0 8px;
-            }
-            .right-img {
-                border-radius: 0 8px 8px 0;
-            }
-        }
-    }
-}
-
-
-.cavans-popup {
-    width: 100%;
-    max-width: 100%;
-    max-height: 92vh;
-    background: none;
-    border-radius: 12px;
-    overflow: auto;
-    display: flex;
-    flex-direction: column;
-    backdrop-filter: 4px;
-    .cavans-content {
-        text-align: center;
-        padding: 0 12px;
-        height: fit-content;
-        overflow: auto;
-        .current-img {
-            width: 100%;
-        }
-    }
-
-    // 底部操作按钮
-    .bottom-actions {
-        flex-shrink: 0;
-
-        .action-buttons {
-            padding: 12px 0 4px 0;
-            display: flex;
-            justify-content: space-around;
-
-            .action-btn {
-                display: flex;
-                flex-direction: column;
-                align-items: center;
-                cursor: pointer;
-                &.text-btn {
-                    font-size: 12px;
-                    color: rgba(255, 255, 255, 0.7);
-                }
-
-                .icon-circle {
-                    width: 48px;
-                    height: 48px;
-                    border-radius: 50%;
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                    color: #fff;
-                    margin-bottom: 4px;
-
-                    .el-icon {
-                        color: #fff;
-                    }
-                    img {
-                        width: 50px;
-                    }
-                }
-
-                &.blue-btn .icon-circle {
-                    background: #2199f8;
-                }
-
-                &.green-btn .icon-circle {
-                    background: #07c160;
-                }
-
-                &.orange-btn .icon-circle {
-                    background: #ff790b;
-                }
-
-                .btn-label {
-                    font-size: 12px;
-                    color: #fff;
-                }
-            }
-        }
-
-        .cancel-btn {
-            text-align: center;
-            font-size: 18px;
-            color: #fff;
-            cursor: pointer;
-        }
-    }
-}
-</style>

+ 0 - 6
src/views/old_mini/agri_file/index copy.vue

@@ -144,12 +144,6 @@ const trackingList = ref([]);
 // ]);
 
 const handleReportTabClick = (item) => {
-    if (item.key === "historyRisk") {
-        router.push(
-            `/history_risk_report?farmVariety=${currentFarmVariety.value ?? ""}&currentFarmName=${currentFarmName.value ?? ""}`
-        );
-        return;
-    }
     panelViewType.value = "plot";
 };
 

+ 36 - 5
src/views/old_mini/agri_file/index.vue

@@ -78,11 +78,12 @@
                         </el-icon>
                         <span>{{ t("agriFile.addAlbum") }}</span>
                     </div>
-                    <div v-for="item in albumList" :key="item.id" class="album-card">
+                    <div v-for="item in albumList" :key="item.id" class="album-card" :class="{ 'is-empty': !item.count }">
                         <div class="album-card__cover">
-                            <img :src="item.cover" alt="" />
+                            <img v-if="item.count && item.cover" :src="item.cover" alt="" />
+                            <img v-else class="album-card__empty-icon" src="@/assets/img/agricultural/photo.png" alt="" />
                             <span class="album-card__count">{{ t("agriFile.photoCount", { count: item.count }) }}</span>
-                            <div class="album-card__add">
+                            <div class="album-card__add" @click.stop="openUploadPopup">
                                 <el-icon>
                                     <Plus />
                                 </el-icon>
@@ -110,6 +111,8 @@
             </div>
         </div>
         <album-upload-popup v-model:show="showUploadPopup" />
+        <complete-farm-info-popup v-model:show="showCompleteFarmPopup" @confirm="goCompleteFarmInfo" />
+        <diagnosis-report-popup v-model:show="showDiagnosisReportPopup" @confirm="goDiagnosisReport" />
     </div>
 </template>
 
@@ -124,6 +127,8 @@ import FileMap from "./fileMap";
 import * as util from "@/common/ol_common.js";
 import farmHeader from "@/components/farmHeader.vue";
 import albumUploadPopup from "./components/albumUploadPopup.vue";
+import completeFarmInfoPopup from "@/components/popup/completeFarmInfoPopup.vue";
+import diagnosisReportPopup from "@/components/popup/diagnosisReportPopup.vue";
 import PhenologyTrackTimelineItem from "@/components/pageComponents/PhenologyTrackTimelineItem.vue";
 
 const { t } = useI18n();
@@ -166,7 +171,7 @@ const zoneList = ref([
 ]);
 
 const albumList = ref([
-    { id: 1, name: "", count: 125, cover: defaultCover },
+    { id: 1, name: "", count: 0, cover: "" },
     { id: 2, name: "", count: 125, cover: defaultCover },
     { id: 3, name: "", count: 125, cover: defaultCover },
 ]);
@@ -179,6 +184,8 @@ const reportList = ref([
 ]);
 
 const showUploadPopup = ref(false);
+const showCompleteFarmPopup = ref(false);
+const showDiagnosisReportPopup = ref(false);
 const patrolList = ref([
     {
         id: 1,
@@ -307,6 +314,14 @@ const openUploadPopup = () => {
     showUploadPopup.value = true;
 };
 
+const goCompleteFarmInfo = () => {
+    router.push("/entry_information");
+};
+
+const goDiagnosisReport = () => {
+    activeNav.value = "report";
+};
+
 onMounted(() => {
     fillMockLabels();
     initAlbumMap();
@@ -507,12 +522,23 @@ watch(activeNav, (key) => {
             .album-card {
                 width: 100%;
 
+                &.is-empty {
+                    .album-card__cover {
+                        display: flex;
+                        align-items: center;
+                        justify-content: center;
+                        overflow: hidden;
+                        border-radius: 8px;
+                        background: linear-gradient(180deg, #E0F1FF 0%, #C2E4FF 100%);
+                    }
+                }
+
                 &__cover {
                     position: relative;
                     width: 100%;
                     aspect-ratio: 1;
 
-                    img {
+                    img:not(.album-card__empty-icon) {
                         width: 100%;
                         height: 100%;
                         object-fit: cover;
@@ -520,6 +546,11 @@ watch(activeNav, (key) => {
                     }
                 }
 
+                &__empty-icon {
+                    width: 50px;
+                    height: 50px;
+                }
+
                 &__count {
                     position: absolute;
                     top: 1px;

+ 21 - 1
src/views/old_mini/agri_file/pages/growthTrack.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="growth-track-page">
-        <custom-header :name="t('agriFile.workName')" />
+        <custom-header :name="t('agriFile.workName')" :isGoBack="true" @goback="handleBack" />
         <div class="growth-track-body">
             <div class="growth-hero">
                 <div class="growth-hero__top">
@@ -79,6 +79,10 @@
             <span>{{ t("agriFile.uploadPhoto") }}</span>
         </div>
         <album-upload-popup v-model:show="showUploadPopup" />
+        <leave-confirm-popup
+            v-model:show="showLeavePopup"
+            @confirm="confirmLeave"
+        />
     </div>
 </template>
 
@@ -89,6 +93,7 @@ import { Icon } from 'vant';
 import { ElMessage } from "element-plus";
 import customHeader from "@/components/customHeader.vue";
 import albumUploadPopup from "../components/albumUploadPopup.vue";
+import leaveConfirmPopup from "@/components/popup/leaveConfirmPopup.vue";
 import { useI18n } from "@/i18n";
 
 const { t } = useI18n();
@@ -102,6 +107,7 @@ const reason = computed(() => t("agriFile.interactReason"));
 const zoneName = computed(() => t("agriFile.zoneOne"));
 const ratio = ref("");
 const showUploadPopup = ref(false);
+const showLeavePopup = ref(false);
 const situationList = [
     { nameKey: "agriFile.growthAbnormal" },
     { nameKey: "agriFile.rootAbnormal" },
@@ -122,6 +128,20 @@ const handleUploadClick = () => {
     }
     showUploadPopup.value = true;
 };
+
+const hasRatio = () => ratio.value !== "" && ratio.value != null;
+
+const handleBack = () => {
+    // if (!isAbnormal.value && !hasRatio()) {
+    //     showLeavePopup.value = true;
+    //     return;
+    // }
+    router.back();
+};
+
+const confirmLeave = () => {
+    router.back();
+};
 </script>
 
 <style lang="scss" scoped>

+ 2 - 16
src/views/old_mini/agri_record/index.vue

@@ -7,7 +7,7 @@
         <div class="agri-record-header">
             <weather-info ref="weatherInfoRef" :hasWeather="false" from="agri_record" class="weather-info"
                 @weatherExpanded="weatherExpanded" @changeGarden="changeGarden" @changeGardenTab="changeGardenTab"
-                @reportTabClick="handleReportTabClick" :isGarden="true"
+                :isGarden="true"
                 :gardenId="defaultGardenId" />
         </div>
         <!-- 农场列表 -->
@@ -45,12 +45,11 @@ import customHeader from "@/components/customHeader.vue";
 import { ref, computed, onActivated, onDeactivated, watch, nextTick } from "vue";
 import { useStore } from "vuex";
 import weatherInfo from "@/components/weatherInfo.vue";
-import { useRoute, useRouter, onBeforeRouteLeave } from "vue-router";
+import { useRoute, onBeforeRouteLeave } from "vue-router";
 import ArchivesFarmTimeLine from "@/components/pageComponents/ArchivesFarmTimeLine.vue";
 import gardenList from "@/components/gardenList.vue";
 
 const route = useRoute();
-const router = useRouter();
 
 const archivesScrollAreaRef = ref(null);
 const getArchivesOuterScrollKey = () =>
@@ -99,17 +98,6 @@ const changeGardenTab = (tab) => {
     activeGardenTab.value = tab;
 };
 
-const currentFarmName = ref("");
-const currentFarmVariety = ref(null);
-
-const handleReportTabClick = (item) => {
-    if (item.key === "historyRisk") {
-        router.push(
-            `/history_risk_report?farmVariety=${currentFarmVariety.value ?? ""}&currentFarmName=${currentFarmName.value ?? ""}`
-        );
-    }
-};
-
 const handleGardenLoaded = ({ hasFarm }) => {
     weatherInfoRef.value?.setGardenLoaded?.(hasFarm);
 };
@@ -180,8 +168,6 @@ const changeGarden = (data) => {
     if (!data?.id) return;
     gardenId.value = data.id;
     selectedGardenId.value = data.id;
-    currentFarmName.value = data.name ?? "";
-    currentFarmVariety.value = data.farm_variety ?? null;
     store.commit("home/SET_GARDEN_ID", data.id);
 };
 </script>

+ 0 - 690
src/views/old_mini/chat_frame/consult.vue

@@ -1,690 +0,0 @@
-<template>
-    <div class="consult">
-        <custom-header :name="$t('咨询专家')"></custom-header>
-        <div class="consult-content">
-            <!-- 聊天消息区域 -->
-            <div class="chat-messages" ref="messagesContainer">
-                <div v-for="(msg, index) in messages" :key="index" class="message" :class="msg.sender">
-                    <!-- 对方消息 -->
-                    <template v-if="msg.sender === 'received'">
-                        <!-- <div class="avatar">{{ msg.receiverName.charAt(0) }}</div> -->
-                        <el-avatar class="avatar" :size="40" :src="msg.receiverIcon ||
-                            'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'
-                            " />
-                        <div class="bubble"
-                            :class="{ 'no-bubble': msg.messageType === 'image', 'card-bubble': msg.messageType === 'card' }">
-                            <!-- 文本消息 -->
-                            <div v-if="msg.messageType === 'text'" class="content">{{ msg.content }}</div>
-
-                            <!-- 图片消息 -->
-                            <div v-if="msg.messageType === 'image'" class="image-message">
-                                <img :src="msg.content + resize" @click="showImagePreview(msg.content)"
-                                    @load="handleImageLoad" alt="图片" />
-                            </div>
-
-                            <!-- 对话样式消息 -->
-                            <div v-if="msg.messageType === 'report'" class="dialog-message"
-                                @click="handleReportClick(msg)">
-                                <template v-if="(msg.reportType || msg.content.reportType) === 'farm_report'">
-                                    <div class="report-title">{{ msg.title || msg.content.title }}</div>
-                                    <div class="dialog-title">这是{{ curRole == 2 ? '该农场' : '我' }}的果园情况,请查看~</div>
-                                    <img src="https://birdseye-img.sysuimars.com/birdseye-look-mini/share-report-bg.png"
-                                        alt="" class="monitor-image" />
-                                </template>
-                                <template v-else>
-                                    <div class="dialog-title">{{ msg.title || msg.content.title }}</div>
-                                    <img src="@/assets/img/monitor/image.png" alt="" class="monitor-image" />
-                                </template>
-                            </div>
-
-                            <!-- 对话样式消息 -->
-                            <div v-if="msg.messageType === 'card'" class="question-message">
-                                <div class="question-title">{{ msg.title || msg.content.title }}</div>
-                                <div class="image-wrap">
-                                    <!-- coverUrl 为 JSON 字符串的数组时,解析后遍历 -->
-                                    <template
-                                        v-if="msg.content && msg.content.coverUrl && Array.isArray(msg.content.coverUrl)">
-                                        <img v-for="item in msg.content.coverUrl" :key="item" :src="handleImgUrl(item)"
-                                            alt="" />
-                                    </template>
-                                    <!-- 否则当作普通字符串 URL 使用 -->
-                                    <img v-else :src="handleImgUrl(msg.content.coverUrl)" alt="">
-                                </div>
-                                <div class="btn-detail" @click="handleDetailClick">{{ $t('查看详情') }}</div>
-                            </div>
-                        </div>
-                    </template>
-
-                    <!-- 我方消息 -->
-                    <template v-else>
-                        <div class="bubble"
-                            :class="{ 'no-bubble': msg.messageType === 'image', 'card-bubble': msg.messageType === 'card' || msg.messageType === 'report' }">
-                            <!-- 文本消息 -->
-                            <div v-if="msg.messageType === 'text'" class="content">{{ msg.content }}</div>
-
-                            <!-- 图片消息 -->
-                            <div v-if="msg.messageType === 'image'" class="image-message">
-                                <img :src="msg.content + resize" @click="showImagePreview(msg.content)"
-                                    @load="handleImageLoad" alt="图片" />
-                            </div>
-
-                            <!-- 对话样式消息 -->
-                            <div v-if="msg.messageType === 'report'" class="dialog-message"
-                                @click="handleReportClick(msg)">
-                                <template v-if="(msg.reportType || msg.content.reportType) === 'farm_report'">
-                                    <div class="report-title">{{ msg.title || msg.content.title }}</div>
-                                    <div class="dialog-title">这是{{ curRole == 2 ? '该农场' : '我' }}果园情况,请查看~</div>
-                                    <img src="https://birdseye-img.sysuimars.com/birdseye-look-mini/share-report-bg.png"
-                                        alt="" class="monitor-image" />
-                                </template>
-                                <template v-else>
-                                    <div class="dialog-title">{{ msg.title || msg.content.title }}</div>
-                                    <img src="@/assets/img/monitor/image.png" alt="" class="monitor-image" />
-                                </template>
-                            </div>
-
-                            <!-- 对话样式消息 -->
-                            <div v-if="msg.messageType === 'card'" class="question-message">
-                                <div class="question-title">{{ msg.title || msg.content.title }}</div>
-                                <div class="image-wrap">
-                                    <!-- coverUrl 为 JSON 字符串的数组时,解析后遍历 -->
-                                    <template
-                                        v-if="msg.content && msg.content.coverUrl && Array.isArray(msg.content.coverUrl)">
-                                        <img v-for="item in msg.content.coverUrl" :key="item" :src="handleImgUrl(item)"
-                                            alt="" />
-                                    </template>
-                                    <!-- 否则当作普通字符串 URL 使用 -->
-                                    <img v-else :src="handleImgUrl(msg.content.coverUrl)" alt="">
-                                </div>
-                                <div class="btn-detail" @click="handleDetailClick">{{ $t('查看详情') }}</div>
-                            </div>
-                        </div>
-                        <!-- <div class="avatar avatar-r">{{ msg.senderName.charAt(0) }}</div> -->
-                        <el-avatar class="avatar avatar-r" :size="40" :src="msg.senderIcon ||
-                            'https://birdseye-img.sysuimars.com/dinggou-mini/defalut-icon.png'
-                            " />
-                    </template>
-                </div>
-            </div>
-
-            <!-- 输入框区域 -->
-            <div class="input-area">
-                <!-- <div class="toolbar">
-                    <el-icon class="link" @click="startImageUpload"><Link /></el-icon>
-                    <input type="file" ref="fileInput" accept="image/*" style="display: none" @change="handleImageUpload" />
-                </div> -->
-
-                <input type="text" v-model="inputMessage" :placeholder="$t('请输入你想说的话~')" @keyup.enter="sendTextMessage" />
-                <div class="send" @click="sendTextMessage">{{ $t('发送') }}</div>
-            </div>
-
-            <!-- 图片预览模态框 -->
-            <div v-if="previewImage" class="image-preview" @click="previewImage = null">
-                <img :src="previewImage" alt="预览" />
-            </div>
-        </div>
-    </div>
-</template>
-
-<script setup>
-import { ref, nextTick, onDeactivated, onMounted } from "vue";
-import { useRouter, useRoute } from "vue-router";
-import { base_img_url2 } from "@/api/config";
-import { getFileExt } from "@/utils/util";
-import UploadFile from "@/utils/upliadFile";
-import MqttClient from "@/plugins/MqttClient";
-import customHeader from "@/components/customHeader.vue";
-import { useStore } from "vuex";
-const store = useStore();
-const resize = "?x-oss-process=image/resize,p_120/format,webp/quality,q_100";
-const router = useRouter();
-const route = useRoute();
-
-
-const curUserId = Number(localStorage.getItem("MINI_USER_ID"));
-const storeFarmId = ref(store.state.home.gardenId)
-const senderIcon = ref("");
-const receiverIcon = ref("");
-
-// 本地用户头像
-const localUserInfoIcon = (() => {
-    try {
-        const info = JSON.parse(localStorage.getItem("localUserInfo") || "{}");
-        return info?.icon || "";
-    } catch (e) {
-        return "";
-    }
-})();
-
-// mqtt 连接
-const mqttClient = ref(null);
-const messagesContainer = ref(null);
-
-// 初始化 mqtt
-const initMqtt = () => {
-    const topics = [`user/chat/message/${storeFarmId.value}/${curUserId}`]; // 订阅的主题数组
-    mqttClient.value = new MqttClient(topics, (topic, message) => {
-        if (message && message.length > 10) {
-            const obj = JSON.parse(message);
-            console.log("message有值", obj);
-            if (obj.senderId === curUserId) {
-                return;
-            }
-            if (obj.senderId === Number(route.query.userId)) {
-                // 检查是否已存在相同 id 的消息,避免重复添加
-                if (obj.id && messages.value.some(msg => msg.id === obj.id)) {
-                    return;
-                }
-                if (obj.messageType === "image") {
-                    if (obj.image && (obj.image.url || obj.image.originUrl)) {
-                        obj.content = obj.image.url || obj.image.originUrl;
-                    } else if (obj.content) {
-                        try {
-                            const img = JSON.parse(obj.content);
-                            obj.content = img.url || img.originUrl;
-                        } catch (e) {
-                            console.error(e, "e");
-                        }
-                    }
-                } else if (obj.messageType !== 'text') {
-                    obj.content = JSON.parse(obj.content);
-                    // card 类型的 content 中,如果 coverUrl 是数组,则转成字符串(兼容旧格式)
-                    if (obj.messageType === 'card' && obj.content && Array.isArray(obj.content.coverUrl)) {
-                        obj.content.coverUrl = JSON.stringify(obj.content.coverUrl);
-                    }
-                }
-                obj.receiverId = curUserId;
-                (obj.sender = obj.senderId === curUserId ? "sent" : "received"), (obj.senderIcon = senderIcon.value);
-                obj.receiverIcon = receiverIcon.value;
-                messages.value.push(obj);
-
-                scrollToBottom();
-            }
-        }
-    });
-
-    mqttClient.value.connect();
-};
-
-// 消息数据
-const messages = ref([]);
-
-// 输入相关
-const inputMessage = ref("");
-const fileInput = ref(null);
-
-function handleImageLoad() {
-    scrollToBottom();
-}
-
-const handleDetailClick = () => {
-    router.push(`/interaction_list?expertMiniUserId=${route.query.userId}`);
-}
-
-// 图片预览
-const previewImage = ref(null);
-
-const handleCardClick = (msg) => {
-    router.push(msg.linkUrl || msg.content.linkUrl);
-}
-
-const handleImgUrl = (url) => {
-    if (url && url.includes('https://')) {
-        return url;
-    } else {
-        return base_img_url2 + url + resize;
-    }
-}
-
-// 图片处理
-const startImageUpload = () => {
-    fileInput.value.click();
-};
-
-const uploadFileObj = new UploadFile();
-
-const handleImageUpload = (event) => {
-    const file = event.target.files[0];
-    if (file) {
-        // 实际项目中应该上传到服务器,这里使用本地URL模拟
-        const miniUserId = localStorage.getItem("MINI_USER_ID");
-        let ext = getFileExt(file.name);
-        let key = `birdseye-look-mini/${miniUserId}/${new Date().getTime()}.${ext}`;
-        let imageUrl = "";
-        uploadFileObj.put(key, file).then((resFilename) => {
-            imageUrl = base_img_url2 + resFilename;
-            sendImageMessage(imageUrl);
-        });
-    }
-};
-
-const showImagePreview = (imageUrl) => {
-    previewImage.value = imageUrl;
-};
-
-// 发送图片消息
-const sendImageMessage = (thumbnailUrl) => {
-    const message = {
-        sender: "sent",
-        messageType: "image",
-        senderIcon: senderIcon.value,
-        content: thumbnailUrl,
-    };
-    sendMessage(message);
-};
-
-//发送消息接口
-//类型 text ,file,image
-const sendMsg = (messageType = "text", content = "", obj = {}) => {
-    const params = {
-        farmId: storeFarmId.value,
-        receiverId: route.query.userId,
-        content,
-        [messageType]: obj,
-        messageType,
-    };
-    VE_API.bbs.sendMsg(params);
-};
-
-// 发送消息
-const sendMessage = (message) => {
-    if (message.messageType === "text") {
-        sendMsg("text", message.content);
-    } else if (message.messageType === "image") {
-        // 按新协议:不传 content,传 image 对象
-        sendMsg("image", "", { url: message.content, thumbnailUrl: message.content + resize });
-    } else if (message.messageType === "report") {
-        // 对话样式消息不发送到服务器,只显示在本地
-        console.log("发送对话样式消息:", message);
-        if (message.reportType === 'farm_report') {
-            sendMsg('report', '', {
-                title: message.title,
-                reportId: message.reportId,
-                reportType: message.reportType,
-            });
-        } else {
-            sendMsg('report', '', {
-                title: message.title,
-                reportId: message.reportId,
-                reportType: message.reportType,
-            });
-            console.log('其他文件1');
-        }
-    } else {
-        sendMsg('card', '', {
-            title: message.title,
-            coverUrl: message.coverUrl,
-            cardType: message.cardType,
-            linkUrl: message.linkUrl
-        });
-    }
-    messages.value.push(message);
-    scrollToBottom();
-};
-
-// 发送文本消息
-const sendTextMessage = () => {
-    if (inputMessage.value.trim()) {
-        const message = {
-            sender: "sent",
-            messageType: "text",
-            senderIcon: senderIcon.value,
-            content: inputMessage.value,
-        };
-        sendMessage(message);
-        inputMessage.value = "";
-    }
-};
-
-const scrollToBottom = () => {
-    nextTick(() => {
-        setTimeout(() => {
-            if (messagesContainer.value) {
-                messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
-            }
-        }, 300);
-    });
-};
-
-const curRole = ref(null);
-
-// 点击农场报告对话框
-const handleReportClick = (msg) => {
-    if (msg.reportType === 'farm_report' || msg.messageType === 'report') {
-        const params = {
-            farmId: msg.reportId || msg.content?.reportId,
-            showFilter: true,
-        }
-        router.push(`/farm_report?miniJson=${JSON.stringify(params)}`);
-    } else {
-        console.log('其他文件');
-    }
-}
-
-// 页面加载时自动添加欢迎消息
-onMounted(() => {
-    createSession(route.query.userId);
-    scrollToBottom();
-});
-
-//聊天会话
-const createSession = (targetUserId, callback) => {
-    VE_API.bbs.createSession({ farmId: storeFarmId.value, targetUserId }).then(({ data, code }) => {
-        if (code === 0) {
-            senderIcon.value = localUserInfoIcon;
-            receiverIcon.value = data.session.targetUserAvatar;
-            messages.value = data.messages.map((item) => {
-                let content = item.content;
-                if (item.messageType === "image") {
-                    // 优先读取后端的 image 字段,其次兼容旧的 content(JSON)
-                    if (item.image && (item.image.url || item.image.originUrl)) {
-                        content = item.image.url || item.image.originUrl;
-                    } else if (item.content) {
-                        try {
-                            const imgObj = JSON.parse(item.content);
-                            content = imgObj.url || imgObj.originUrl;
-                        } catch (e) {
-                            console.error(e, "e");
-                        }
-                    }
-                } else if (item.messageType !== 'text') {
-                    content = JSON.parse(item.content);
-                }
-                return {
-                    ...item,
-                    content,
-                    sender: item.senderId === curUserId ? "sent" : "received",
-                    senderIcon: item.senderId === curUserId ? localUserInfoIcon : data.session.targetUserAvatar,
-                    receiverIcon: data.session.targetUserAvatar,
-                };
-            });
-
-            setTimeout(() => {
-                scrollToBottom();
-            }, 300);
-            callback && callback();
-
-            // 初始化mqtt
-            initMqtt();
-        }
-    });
-};
-
-onDeactivated(() => {
-    mqttClient.value && mqttClient.value.client.end(true);
-});
-</script>
-
-<style scoped lang="scss">
-.consult {
-    width: 100%;
-    height: calc(100vh - 40px);
-    box-sizing: border-box;
-
-    .consult-content {
-        width: 100%;
-        height: 100%;
-        display: flex;
-        flex-direction: column;
-        position: relative;
-    }
-}
-
-/* 聊天消息区域样式 */
-.chat-messages {
-    flex: 1;
-    padding: 12px;
-    overflow-y: auto;
-    background-color: #fff;
-    box-sizing: border-box;
-
-    .message {
-        display: flex;
-        margin-bottom: 15px;
-    }
-
-    .received {
-        justify-content: flex-start;
-
-        .bubble {
-            background-color: #F4F5F8;
-            border-radius: 0 10px 10px 10px;
-            padding: 10px 12px;
-            box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
-
-            .content {
-                font-size: 16px;
-                color: #666666;
-            }
-        }
-    }
-
-    .sent {
-        justify-content: flex-end;
-
-        .bubble {
-            background-color: #07c160;
-            border-radius: 10px 0 10px 10px;
-            padding: 10px 15px;
-            box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
-
-            .content {
-                font-size: 16px;
-                color: #000;
-            }
-        }
-        .question-message {
-            .question-title{
-                color: #000;
-            }
-        }
-    }
-
-    .avatar {
-        width: 40px;
-        height: 40px;
-        border-radius: 50%;
-        background-color: #07c160;
-        color: white;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        margin-right: 10px;
-        font-weight: bold;
-    }
-
-    .avatar-r {
-        margin: 0 0 0 10px;
-    }
-
-    .bubble {
-        max-width: 70%;
-    }
-}
-
-
-
-.input-area {
-    display: flex;
-    align-items: center;
-    padding: 15px 10px;
-    border-top: 1px solid #e6e6e6;
-    background-color: white;
-    position: relative;
-    width: 100%;
-    box-sizing: border-box;
-
-    input {
-        flex: 1;
-        padding: 10px;
-        border: 1px solid #e6e6e6;
-        border-radius: 20px;
-        outline: none;
-    }
-
-    .send {
-        margin-left: 10px;
-        padding: 8px 20px;
-        background-color: #07c160;
-        color: white;
-        border: none;
-        border-radius: 20px;
-        cursor: pointer;
-    }
-}
-
-/* 新增的多媒体消息样式 */
-.image-message {
-    img {
-        max-width: 200px;
-        max-height: 200px;
-        border-radius: 8px;
-    }
-}
-
-/* 图片消息不使用对话气泡样式 */
-.no-bubble {
-    background: transparent !important;
-    border-radius: 0 !important;
-    padding: 0 !important;
-    box-shadow: none !important;
-    color: inherit !important;
-}
-
-/* 工具栏样式 */
-.toolbar {
-    display: flex;
-    align-items: center;
-
-    button {
-        background: none;
-        border: none;
-        font-size: 20px;
-        margin-right: 10px;
-        cursor: pointer;
-        padding: 5px;
-    }
-
-    .link {
-        font-size: 24px;
-        margin-right: 10px;
-    }
-}
-
-/* 图片预览 */
-.image-preview {
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: rgba(0, 0, 0, 0.8);
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    z-index: 1000;
-}
-
-.image-preview img {
-    max-width: 90%;
-    max-height: 90%;
-    object-fit: contain;
-}
-
-/* 对话样式消息 */
-.dialog-message {
-    max-width: 100%;
-    background: #fff !important;
-    border-radius: 10px;
-
-    .report-title {
-        font-size: 16px;
-        font-weight: 600;
-        color: #000;
-        margin-bottom: 5px;
-    }
-
-    .dialog-title {
-        font-size: 12px;
-        color: rgba(0, 0, 0, 0.6);
-        margin-bottom: 10px;
-    }
-
-    .monitor-image {
-        width: 222px;
-        height: 180px;
-        object-fit: cover;
-    }
-
-    .farm-report-content,
-    .farm-work-content {
-
-        .report-details,
-        .work-details {
-            background: #f8f9fa;
-            border-radius: 8px;
-            padding: 12px;
-            margin-top: 10px;
-
-            .detail-item {
-                display: flex;
-                margin-bottom: 6px;
-                font-size: 13px;
-
-                &:last-child {
-                    margin-bottom: 0;
-                }
-
-                .detail-label {
-                    color: #666;
-                    min-width: 80px;
-                }
-
-                .detail-value {
-                    color: #333;
-                    flex: 1;
-                }
-            }
-        }
-    }
-}
-
-.question-message {
-    .question-title {
-        font-size: 16px;
-        color: #666666;
-        margin-bottom: 6px;
-    }
-
-    .image-wrap {
-        display: flex;
-        // flex-wrap: wrap;
-        gap: 10px;
-
-        img {
-            width: 75px;
-            height: 70px;
-            border-radius: 8px;
-            object-fit: cover;
-        }
-    }
-
-    .btn-detail {
-        font-size: 14px;
-        margin-top: 8px;
-        background: #FFFFFF;
-        border-radius: 6px;
-        text-align: center;
-        padding: 6px;
-    }
-}
-
-/* 我方消息中的对话样式 */
-.message.sent .dialog-message {
-    background: #e3f2fd;
-
-    .work-details {
-        background: #f0f8ff;
-    }
-}
-</style>

+ 0 - 66
src/views/old_mini/chat_frame/index.vue

@@ -1,66 +0,0 @@
-<template>
-    <div class="dialogue">
-        <custom-header :name="nameVal || '飞鸟种植助手'" bgColor="#f2f3f5" isGoBack @goback="headerCallBack"></custom-header>
-        <chat-window :text="desc" :userId="userIdVal" :img="imgVal" @update:name="nameVal = $event"></chat-window>
-    </div>
-</template>
-
-<script setup>
-import customHeader from "@/components/customHeader.vue";
-import { onActivated, ref } from "vue";
-import chatWindow from "@/components/chatWindow";
-import { useRoute, useRouter } from "vue-router";
-
-const route = useRoute();
-const router = useRouter();
-const desc = ref("");
-const nameVal = ref("");
-const userIdVal = ref(null);
-const imgVal = ref("");
-const formPageVal = ref("");
-onActivated(() => {
-    userIdVal.value = null;
-    setTimeout(() => {
-        const { text, formPage, userId, img } = route.query;
-        desc.value = text;
-        userIdVal.value = userId;
-        imgVal.value = img;
-        formPageVal.value = formPage;
-        if(formPage) {
-            sessionStorage.setItem('chat_frame_page', formPage);
-        }
-    }, 100);
-});
-
-function headerCallBack() {
-    const page = sessionStorage.getItem('chat_frame_page');
-    if(page === 'messageList' || page === 'monitor') {
-        VE_API.bbs.readUpdate({ targetUserId: userIdVal.value, farmId: route.query.farmId }).then((res) => {
-            if (res.code === 0) {
-                sessionStorage.removeItem('chat_frame_page');
-                router.replace({ path: '/message_list', query: { farmId: route.query.farmId, from: 'monitor' } });
-            }
-        });
-    } else {
-        router.go(-1);
-    }
-}
-</script>
-
-<style lang="scss" scoped>
-.dialogue {
-    width: 100%;
-    height: calc(100vh - 40px);
-    background: #f2f3f5;
-    box-sizing: border-box;
-    .chat-container {
-        display: flex;
-        flex-direction: column;
-        height: 100%;
-        width: 100%;
-        margin: 0 auto;
-        border: 1px solid #e6e6e6;
-        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
-    }
-}
-</style>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác