浏览代码

fix: 农事成效照片

lxf 2 天之前
父节点
当前提交
b0a0554044

+ 5 - 0
src/api/modules/z_farm_work_record.js

@@ -27,4 +27,9 @@ module.exports = {
         url: url + "/addExecuteImg",
         type: "post",
     },
+    // 获取触发照片
+    getTriggerImg: {
+        url: config.base_dev_url + "farm-work-manual-trigger/getImagesByRecordId",
+        type: "get",
+    },
 }

+ 2 - 2
src/components/detailDialog.vue

@@ -343,8 +343,8 @@ function toPage() {
                 align-items: center;
             }
             img {
-                width: 62px;
-                height: 62px;
+                width: 48px;
+                height: 48px;
                 border-radius: 50%;
                 object-fit: cover;
             }

+ 25 - 8
src/components/taskItem.vue

@@ -78,12 +78,15 @@
                     </div>
                 </div>
                 <div class="review-image" v-if="!expiredDay">
-                    <div class="review-image-item">
+                    <div class="review-image-item" v-if="triggerImg.length">
                         <div class="review-image-item-title">农事前</div>
-                        <img
-                            src="@/assets/img/monitor/aaa.png"
-                            alt=""
-                        />
+                        <photo-provider :photo-closable="true">
+                                <photo-consumer v-for="src in [triggerImg[triggerImg.length - 1].cloudFilename]" intro="执行照片" :key="src" :src="base_img_url2 + src">
+                                    <div class="img-item">
+                                        <img :src="base_img_url2 + src" class="view-box">
+                                    </div>
+                                </photo-consumer>
+                        </photo-provider>
                     </div>
                     <div class="review-image-item" v-if="detailItem?.executeEvidence?.length">
                         <div class="review-image-item-title">农事后</div>
@@ -135,6 +138,9 @@
     
     <!-- 处方卡片 -->
     <detail-dialog ref="detailDialogRef" />
+
+    <!-- 分享农事成效弹窗 -->
+    <review-popup ref="reviewPopupRef" />
 </template>
 
 <script setup>
@@ -143,6 +149,7 @@ import { useRouter } from "vue-router";
 import { base_img_url2 } from "@/api/config";
 import wx from 'weixin-js-sdk';
 import detailDialog from "@/components/detailDialog.vue";
+import reviewPopup from "@/views/old_mini/task_condition/components/reviewPopup.vue";
 
 const props = defineProps({
     status: {
@@ -163,15 +170,16 @@ const expiredDay = ref(0) // 过期天数
 
 const router = useRouter();
 const detailDialogRef = ref(null);
+const reviewPopupRef = ref(null);
 const toPage = () => {
     router.push("/report_detail")
 }
 
 const shareResult = () => {
     if (detailItem.value?.executeEvidence?.length) {
-        wx.miniProgram.navigateTo({
-            url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify({ shareText: "向您分享了农事执行成果", id: detailItem.value.id })}&type=reviewWork`,
-        });
+        const preImg = triggerImg.value.length ? base_img_url2 + triggerImg.value[triggerImg.value.length - 1].cloudFilename : '';
+        const resImg = detailItem.value?.executeEvidence?.length ? base_img_url2 + detailItem.value.executeEvidence[detailItem.value.executeEvidence.length - 1] : '';
+        reviewPopupRef.value.handleShowPopup(detailItem.value.id, preImg, resImg);
     }
 }
 
@@ -184,9 +192,18 @@ onMounted(async () => {
         if (Array.isArray(detailItem.value?.prescriptionList)) {
             prescriptionText.value = buildPrescriptionText(detailItem.value.prescriptionList);
         }
+
+        getTriggerImg();
     }
 })
 
+const triggerImg = ref([]);
+const getTriggerImg = async () => {
+    console.log('detailItem', detailItem.value)
+    const { data } = await VE_API.z_farm_work_record.getTriggerImg({ farmWorkRecordId: detailItem.value.id });
+    console.log('triggerImg', data)
+    triggerImg.value = data || [];
+}
 const detailItem = ref({});
 const prescriptionText = ref('');
 function buildPrescriptionText(list) {

+ 6 - 1
src/views/old_mini/modify_work/completedWork.vue

@@ -82,7 +82,7 @@
                                             <div class="price-info">
                                                 <div class="info-l">执行方式<span class="main-text">{{ quotationData.executionMethodName || '--' }}</span></div>
                                                 <div class="info-c">亩单价<span class="main-text">{{ quotationData.farmWorkServiceCost ? (quotationData.farmWorkServiceCost + '元/亩') : '--' }}</span></div>
-                                                <div class="info-r">亩数<span class="main-text">{{ quotationData.area ? (quotationData.area + '亩') : '--' }}</span></div>
+                                                <div class="info-r">亩数<span class="main-text">{{ quotationData.area ? formatArea(quotationData.area) + '亩' : '--' }}</span></div>
                                             </div>
                                             <div class="price-total">
                                                 报价合计:<span class="main-val">{{ quotationData.totalCost || '--' }}</span>元
@@ -560,6 +560,11 @@ onActivated(async () => {
     // }
 });
 
+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 detailData = ref({});
 const getDetail = async (id) => {
     const { data } = await VE_API.z_farm_work_record.getDetail({ id });

+ 590 - 0
src/views/old_mini/task_condition/components/reviewPopup.vue

@@ -0,0 +1,590 @@
+<template>
+    <popup class="price-sheet-popup" v-model:show="showPopup">
+        <div class="price-sheet-content">
+            <div class="price-sheet-content-inner">
+                <div class="sheet-content">
+                    <div class="review-image">
+                        <div class="review-image-item">
+                            <div class="review-image-item-title">农事前</div>
+                            <img class="review-image-item-img" :src="preImg" alt="" />
+                        </div>
+                        <div class="review-image-item">
+                            <div class="review-image-item-title">农事后</div>
+                            <img class="review-image-item-img" :src="resImg" alt="" />
+                        </div>
+                    </div>
+                    <!-- 报价详情区域 -->
+                    <div class="quotation-info">
+                        <div class="info-item">
+                            <span class="info-label">报价组织</span>
+                            <span class="info-value">{{ quotationData.serviceMain || '--' }}</span>
+                        </div>
+                        <div class="info-item">
+                            <span class="info-label">报价农事</span>
+                            <span class="info-value">{{ quotationData?.farmWorkName || '--' }}</span>
+                        </div>
+                        <div class="info-item">
+                            <span class="info-label">执行时间</span>
+                            <span class="info-value">{{ quotationData?.executeDate || '--' }}</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 底部操作按钮 -->
+            <div class="bottom-actions" @click.stop="showPopup = false">
+                <div class="action-buttons">
+                    <div class="action-btn blue-btn" @click.stop="handleShare">
+                        <div class="icon-circle">
+                            <img src="@/assets/img/home/bird.png" alt="" />
+                        </div>
+                        <span class="btn-label">飞鸟用户</span>
+                    </div>
+                    <div class="action-btn green-btn" @click.stop="handleWechat">
+                        <div class="icon-circle">
+                            <img src="@/assets/img/home/wechat.png" alt="" />
+                        </div>
+                        <span class="btn-label">微信</span>
+                    </div>
+                    <div class="action-btn orange-btn">
+                        <div class="icon-circle">
+                            <el-icon :size="24"><Download /></el-icon>
+                        </div>
+                        <span class="btn-label">保存图片</span>
+                    </div>
+                </div>
+                <div class="cancel-btn" @click="handleCancel">取消</div>
+            </div>
+        </div>
+    </popup>
+</template>
+
+<script setup>
+import { Popup } from "vant";
+import { ref, computed, onActivated } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+import wx from "weixin-js-sdk";
+import html2canvas from "html2canvas";
+
+const router = useRouter();
+const showPopup = ref(false);
+const contentEl = ref(null);
+const preImg = ref('');
+const resImg = ref('');
+// 报价数据
+const quotationData = ref({});
+
+onActivated(() => {
+})
+const recordId = ref('');
+const handleShowPopup = async (id, preImgVal, resImgVal) => {
+    recordId.value = id;
+    await getDetail();
+    preImg.value = preImgVal;
+    resImg.value = resImgVal;
+    showPopup.value = true;
+};
+
+async function getDetail() {
+    const { data } = await VE_API.z_farm_work_record.getDetail({ id: recordId.value });
+    quotationData.value = data[0];
+}
+
+const handleShare = () => {
+    const userId = quotationData.value.users[0]?.userId;
+    const parmasPage = {
+        farmWorkOrderId:quotationData.value.orderId,
+        farmMiniUserId:userId,
+        farmMiniUserName:quotationData.value.expertUserName,
+        farmId:quotationData.value.farmId,
+        farmWorkName:quotationData.value.farmWorkName,
+        id:quotationData.value.id,
+        type:'quotation'
+    }
+    if(userId){
+        router.push(`/chat_frame?userId=${userId}&name=${parmasPage.farmMiniUserName}&farmId=${parmasPage.farmId}&pageParams=${JSON.stringify(parmasPage)}`);
+    }else{
+        ElMessage.warning('尚未绑定用户,暂时无法分享')
+    }   
+};
+
+const handleWechat = () => {
+    console.log("handleWechat");
+    // router.push({
+    //     path: "/completed_work",
+    //     query: { id: quotationData.value.id, farmWorkOrderId: quotationData.value.orderId, isAssign: true },
+    // });
+
+    wx.miniProgram.navigateTo({
+            url: `/pages/subPages/share_page/index?pageParams=${JSON.stringify({ shareText: "向您分享了农事执行成果", id: recordId.value })}&type=reviewWork`,
+        });
+};
+
+const handleSaveImage = async () => {
+    try {
+        if (!contentEl.value) return;
+        const element = contentEl.value;
+        const scroller = element.querySelector('.sheet-content');
+
+        // 记录原样式
+        const prev = {
+            elementOverflow: element.style.overflow,
+            elementMaxHeight: element.style.maxHeight,
+            elementHeight: element.style.height,
+            scrollerOverflow: scroller ? scroller.style.overflow : undefined,
+            scrollerMaxHeight: scroller ? scroller.style.maxHeight : undefined,
+            scrollerHeight: scroller ? scroller.style.height : undefined,
+        };
+
+        // 展开内容,去除滚动限制,确保截图包含全部内容
+        element.style.overflow = 'visible';
+        element.style.maxHeight = 'none';
+        element.style.height = 'auto';
+        if (scroller) {
+            scroller.style.overflow = 'visible';
+            scroller.style.maxHeight = 'none';
+            scroller.style.height = 'auto';
+        }
+
+        // 计算完整尺寸
+        const width = element.scrollWidth;
+        const height = element.scrollHeight;
+
+        const canvas = await html2canvas(element, {
+            backgroundColor: '#ffffff',
+            useCORS: true,
+            allowTaint: true,
+            scale: Math.min(2, window.devicePixelRatio || 2),
+            width,
+            height,
+            windowWidth: width,
+            windowHeight: height,
+            scrollX: 0,
+            scrollY: 0,
+        });
+        const dataUrl = canvas.toDataURL('image/png');
+        const link = document.createElement('a');
+        link.href = dataUrl;
+        link.download = '服务报价单.png';
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+
+        // 还原样式
+        element.style.overflow = prev.elementOverflow;
+        element.style.maxHeight = prev.elementMaxHeight;
+        element.style.height = prev.elementHeight;
+        if (scroller) {
+            scroller.style.overflow = prev.scrollerOverflow;
+            scroller.style.maxHeight = prev.scrollerMaxHeight;
+            scroller.style.height = prev.scrollerHeight;
+        }
+    } catch (e) {
+        console.error('保存图片失败', e);
+    }
+};
+
+const handleCancel = () => {
+    showPopup.value = false;
+};
+
+defineExpose({
+    handleShowPopup,
+});
+</script>
+
+<style lang="scss" scoped>
+.price-sheet-popup {
+    width: 90%;
+    max-height: 82vh;
+    background: none;
+    border-radius: 12px;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+    backdrop-filter: 4px;
+
+    ::v-deep {
+        .van-popup__close-icon {
+            color: #000;
+            font-size: 18px;
+            top: 12px;
+            right: 12px;
+        }
+    }
+}
+
+.price-sheet-content {
+    display: flex;
+    flex-direction: column;
+    max-height: 82vh;
+    // height: 95vh;
+    .price-sheet-content-inner {
+        background: #fff;
+        border-radius: 12px;
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+        overflow: hidden;
+    }
+}
+
+.sheet-content {
+    padding: 16px;
+    flex: 1;
+    overflow-y: auto;
+    overflow-x: hidden;
+    position: relative;
+}
+.review-image {
+    position: relative;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 8px;
+    .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;
+        }
+    }
+}
+
+// 报价详情区域
+.quotation-info {
+    margin-bottom: 12px;
+
+    .info-item {
+        font-size: 16px;
+        color: #000;
+        margin-bottom: 8px;
+
+        .info-label {
+            padding-right: 8px;
+            color: rgba(0, 0, 0, 0.5);
+        }
+
+        .info-value {
+            color: #000;
+        }
+
+        &.catalog-label {
+            font-weight: bold;
+            margin-top: 10px;
+            margin-bottom: 10px;
+            position: relative;
+
+            .info-label {
+                color: #000;
+            }
+        }
+    }
+
+    .total-bar {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        background: rgba(33, 153, 248, 0.1);
+        border: 1px solid rgba(33, 153, 248, 0.5);
+        height: 38px;
+        border-radius: 4px;
+
+        .total-label {
+            font-size: 14px;
+            color: #000000;
+        }
+
+        .total-value {
+            font-size: 22px;
+            font-weight: bold;
+            color: #2199F8;
+        }
+
+        .total-unit {
+            font-size: 14px;
+            color: #000;
+            margin-left: 4px;
+        }
+    }
+}
+
+// 肥药费用区域
+.fertilizer-cost-section {
+    margin-bottom: 10px;
+
+    .section-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 8px;
+
+        .section-title {
+            font-size: 14px;
+            color: #000;
+        }
+
+        .section-total {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000;
+
+            .unit-text {
+                padding-left: 2px;
+                font-size: 12px;
+                font-weight: normal;
+            }
+        }
+    }
+
+    .cost-table {
+        border: 1px solid rgba(225, 225, 225, 0.5);
+        border-radius: 5px;
+        overflow: hidden;
+
+        .table-header {
+            display: flex;
+            background: rgba(241, 241, 241, 0.4);
+            padding: 8px 6px;
+            font-size: 12px;
+            color: #767676;
+            border-bottom: 1px solid rgba(225, 225, 225, 0.5);
+
+            .col-1 {
+                width: 40px;
+                text-align: center;
+            }
+
+            .col-2 {
+                flex: 1;
+                text-align: center;
+            }
+
+            .col-3 {
+                width: 52px;
+                text-align: center;
+            }
+
+            .col-4 {
+                width: 56px;
+                text-align: center;
+            }
+
+            .col-5 {
+                width: 52px;
+                text-align: center;
+            }
+
+            .col-6 {
+                width: 52px;
+                text-align: center;
+            }
+        }
+
+        .table-row {
+            display: flex;
+            padding: 8px 6px;
+            font-size: 11px;
+            color: rgba(0, 0, 0, 0.6);
+            background: #fff;
+            border-bottom: 1px solid rgba(225, 225, 225, 0.3);
+
+            &:last-child {
+                border-bottom: none;
+            }
+
+            .col-1,
+            .col-2,
+            .col-3,
+            .col-4,
+            .col-5,
+            .col-6 {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
+
+            .col-1 {
+                width: 40px;
+            }
+
+            .col-2 {
+                flex: 1;
+            }
+
+            .col-3 {
+                width: 52px;
+            }
+
+            .col-4 {
+                width: 56px;
+            }
+
+            .col-5 {
+                width: 52px;
+            }
+
+            .col-6 {
+                width: 52px;
+            }
+        }
+    }
+}
+
+// 服务费用区域
+.service-cost-section {
+    position: relative;
+
+    .section-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 12px;
+
+        .section-title {
+            font-size: 14px;
+            color: #000;
+        }
+
+        .section-total {
+            font-size: 16px;
+            font-weight: bold;
+            color: #000;
+
+            .unit-text {
+                padding-left: 2px;
+                font-size: 12px;
+                font-weight: normal;
+            }
+        }
+    }
+
+    .service-details {
+        display: flex;
+        align-items: center;
+        border: 1px solid rgba(206, 206, 206, 0.5);
+        padding: 8px 0;
+        border-radius: 4px;
+        margin-bottom: 10px;
+
+        .detail-item {
+            font-size: 14px;
+            flex: 1;
+            text-align: center;
+
+            .detail-label {
+                color: rgba(0, 0, 0, 0.2);
+                margin-top: 6px;
+            }
+
+            .detail-value {
+                color: rgba(0, 0, 0, 0.8);
+            }
+        }
+        .detail-item + .detail-item {
+            position: relative;
+            &::before {
+                content: '';
+                position: absolute;
+                left: 0;
+                top: 50%;
+                transform: translateY(-50%);
+                width: 1px;
+                height: 20px;
+                background: rgba(0, 0, 0, 0.1);
+            }
+        }
+    }
+}
+.edit-btn-box {
+    display: flex;
+    justify-content: end;
+    position: absolute;
+    right: 0;
+    top: -8px;
+    z-index: 10;
+}
+.edit-btn {
+    background: rgba(33, 153, 248, 0.1);
+    color: #2199F8;
+    padding: 6px 16px;
+    border-radius: 20px;
+    font-size: 14px;
+    width: fit-content;
+    cursor: pointer;
+}
+
+// 底部操作按钮
+.bottom-actions {
+    flex-shrink: 0;
+
+    .action-buttons {
+        padding: 16px;
+        display: flex;
+        justify-content: space-around;
+
+        .action-btn {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            cursor: pointer;
+
+            .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>

+ 4 - 1
src/views/old_mini/task_condition/components/uploadExecute.vue

@@ -66,7 +66,10 @@ function handleConfirm() {
         if (res.code === 0) {
             ElMessage.success('请求确认成功');
             show.value = false;
-            showShare.value = true;
+            VE_API.z_farm_work_record.getDetail({ id: farmData.value.id }).then(({ data }) => {
+                farmData.value = data[0];
+                showShare.value = true;
+            });
         }
     });
 }