浏览代码

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

wangsisi 4 天之前
父节点
当前提交
5915b505e5

+ 15 - 9
src/components/album_compoents/albumCarouselItem.vue

@@ -27,14 +27,14 @@
                         <div class="mask-line line-middle">
                             <span class="work-name">{{ imgData?.farmWorkName }}</span>
                             <span class="line-separator">|</span>
-                            <span class="prescription-text">药物处方:{{ prescriptionText }}</span>
-                        </div>
-                        <div class="mask-line line-bottom">
                             <span class="location-text">
                                 <img src="@/assets/watermark/address.png" alt="location" class="location-icon" />
-                                {{ imgData?.farmName }}({{ imgData?.serviceRegion }})
+                                {{ imgData?.farmName }}
                             </span>
                         </div>
+                        <div class="mask-line line-bottom">
+                            <span class="prescription-text">药物处方:{{ prescriptionText }}</span>
+                        </div>
                     </div>
                 </div>
                 <div class="carousel-img-mask" v-if="isAchievementImgs && imgData?.reCheckText">
@@ -70,13 +70,14 @@
                         <div class="mask-line line-middle">
                             <span class="work-name">{{ imgData?.farmWorkName }}</span>
                             <span class="line-separator">|</span>
-                            <span class="prescription-text">药物处方:{{ prescriptionText }}</span>
-                        </div>
-                        <div class="mask-line line-bottom">
                             <span class="location-text">
                                 <img src="@/assets/watermark/address.png" alt="location" class="location-icon" />
-                                {{ imgData?.farmName }}({{ imgData?.serviceRegion }})
+                                {{ imgData?.farmName }}
                             </span>
+                            
+                        </div>
+                        <div class="mask-line line-bottom">
+                            <span class="prescription-text">药物处方:{{ prescriptionText }}</span>
                         </div>
                     </div>
                 </div>
@@ -381,6 +382,12 @@ const getPhotoSrc = (photo) => {
             align-items: center;
             flex-wrap: wrap;
         }
+        .prescription-text {
+            display: flex;
+            .prescription-text-label {
+                flex: none;
+            }
+        }
         .line-middle {
             margin-top: 4px;
             .work-name {
@@ -398,7 +405,6 @@ const getPhotoSrc = (photo) => {
         }
         .date-text,
         .executor-text,
-        .prescription-text,
         .location-text {
             white-space: nowrap;
         }

+ 136 - 64
src/components/album_compoents/albumDrawBox.vue

@@ -185,7 +185,11 @@ async function generateImageWithQRCode() {
                 drawReviewEffectText(tempCtx, w, h);
             }
         } else {
-            drawBottomMask(tempCtx, w, h);
+            // 先计算文字区域,获取第一行文字的Y坐标
+            const topLineY = calculateTextOverlayTopY(tempCtx, w, h);
+            // 先绘制遮罩(作为背景)
+            drawBottomMask(tempCtx, w, h, topLineY);
+            // 再绘制文字(显示在遮罩上方)
             drawBottomTextOverlay(tempCtx, w, h);
         }
         
@@ -276,7 +280,11 @@ function drawWatermark2(sourceImg, displayImg) {
             drawReviewEffectText(ctx, w, h);
         }
     } else {
-        drawBottomMask(ctx, w, h);
+        // 先计算文字区域,获取第一行文字的Y坐标
+        const topLineY = calculateTextOverlayTopY(ctx, w, h);
+        // 先绘制遮罩(作为背景)
+        drawBottomMask(ctx, w, h, topLineY);
+        // 再绘制文字(显示在遮罩上方)
         drawBottomTextOverlay(ctx, w, h);
     }
 
@@ -323,6 +331,59 @@ function drawImageCover(ctx, img, w, h) {
 
     ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h);
 }
+
+// 计算文字区域的顶部Y坐标(不实际绘制)
+function calculateTextOverlayTopY(ctx, w, h) {
+    const paddingX = 12;
+    const paddingBottom = 12;
+    const lineHeight = 16;
+
+    // ⬇️ 从底部开始,一行一行往上
+    let y = h - paddingBottom;
+
+    // 第三行(最底):药物处方(支持自动换行)
+    ctx.font = "10px sans-serif";
+    const prescriptionFull =
+        "药物处方:" + (buildPrescriptionText(props.imgData?.prescriptionList) || "");
+    
+    // 计算文本最大宽度
+    const maxWidth = w - paddingX * 2;
+    
+    // 自动换行处理
+    const prescriptionLines = [];
+    let line = "";
+    for (let i = 0; i < prescriptionFull.length; i++) {
+        const testLine = line + prescriptionFull[i];
+        const testWidth = ctx.measureText(testLine).width;
+        if (testWidth > maxWidth && line !== "") {
+            prescriptionLines.push(line);
+            line = prescriptionFull[i];
+        } else {
+            line = testLine;
+        }
+    }
+    if (line) {
+        prescriptionLines.push(line);
+    }
+    
+    // 计算药物处方第一行的实际位置(最上面一行的 baseline)
+    const prescriptionFirstLineY = prescriptionLines.length > 1 
+        ? y - (prescriptionLines.length - 1) * lineHeight 
+        : y;
+    
+    // 第二行:农事名称 + 农场名称
+    const spacing = 16;
+    const singleLineY = prescriptionFirstLineY - spacing;
+
+    const workNameText = props.imgData?.farmWorkName || "";
+    let firstLineY = singleLineY;
+
+    // 第一行(最上)的 baseline
+    const topLineY = firstLineY - 20;
+    
+    return topLineY;
+}
+
 function drawBottomTextOverlay(ctx, w, h) {
     const paddingX = 12;
     const paddingBottom = 12;
@@ -336,75 +397,73 @@ function drawBottomTextOverlay(ctx, w, h) {
     // ⬇️ 从底部开始,一行一行往上
     let y = h - paddingBottom;
 
-    // 第三行(最底)
-    ctx.font = "10px sans-serif";
-    ctx.drawImage(imageCache.get("address"), paddingX, y - 9, 9, 10);
-    ctx.fillText("荔博园(广东省广州市从化区)", paddingX + 12, y);
-
-    // 第二行:农事名称 + 药物处方(处方过长时向下换行,整体块仍贴近底部)
-    // 单行基准位置(仅一行时的 baseline)
-    const singleLineY = y - 15;
-
-    // 计算处方文本(长度大于 30 时才自动换行)
+    // 第三行(最底):药物处方(支持自动换行)
     ctx.font = "10px sans-serif";
-    const workNameText = props.imgData?.farmWorkName || "";
     const prescriptionFull =
         "药物处方:" + (buildPrescriptionText(props.imgData?.prescriptionList) || "");
-
+    
+    // 计算文本最大宽度
     const maxWidth = w - paddingX * 2;
-
-    let firstLineY = singleLineY;
-
-    if (prescriptionFull.length <= 30) {
-        // 不足 30 个字符,保持一行:名称 + 处方在同一 baseline
-        firstLineY = singleLineY;
-
-        // 农事名称
-        ctx.font = "16px PangMenZhengDao";
-        ctx.fillText(workNameText, paddingX, firstLineY);
-
-        // 处方文本紧跟在名称后面
-        ctx.font = "10px sans-serif";
-        const nameWidth = ctx.measureText(workNameText).width;
-        const gap = 8;
-        ctx.fillText(prescriptionFull, paddingX + nameWidth + gap + 28, firstLineY);
-    } else {
-        // 超过 30 个字符,自动换行显示(处方整体从左对齐)
-        const prescriptionLines = [];
-        let line = "";
-        for (let i = 0; i < prescriptionFull.length; i++) {
-            const testLine = line + prescriptionFull[i];
-            const testWidth = ctx.measureText(testLine).width;
-            if (testWidth > maxWidth && line !== "") {
-                prescriptionLines.push(line);
-                line = prescriptionFull[i];
-            } else {
-                line = testLine;
-            }
-        }
-        if (line) {
+    
+    // 自动换行处理
+    const prescriptionLines = [];
+    let line = "";
+    for (let i = 0; i < prescriptionFull.length; i++) {
+        const testLine = line + prescriptionFull[i];
+        const testWidth = ctx.measureText(testLine).width;
+        if (testWidth > maxWidth && line !== "") {
             prescriptionLines.push(line);
+            line = prescriptionFull[i];
+        } else {
+            line = testLine;
+        }
+    }
+    if (line) {
+        prescriptionLines.push(line);
+    }
+    
+    // 从底部向上绘制多行文本
+    let textY = y;
+    for (let i = prescriptionLines.length - 1; i >= 0; i--) {
+        ctx.fillText(prescriptionLines[i], paddingX, textY);
+        if (i > 0) {
+            textY -= lineHeight; // 向上换行
         }
+    }
+    
+    // 计算药物处方第一行的实际位置(最上面一行的 baseline)
+    // 单行时:第一行位置 = y(底部位置)
+    // 多行时:第一行位置 = y - (行数 - 1) * lineHeight
+    const prescriptionFirstLineY = prescriptionLines.length > 1 
+        ? y - (prescriptionLines.length - 1) * lineHeight 
+        : y;
+    
+    // 第二行:农事名称 + 农场名称
+    // 在药物处方第一行上方留出足够间距,确保不重叠
+    // 需要考虑字体大小差异:药物处方是 10px,农事名称是 16px
+    // 单行时使用较小间距,多行时使用较大间距
+    const spacing = 16;
+    const singleLineY = prescriptionFirstLineY - spacing;
+
+    ctx.font = "10px sans-serif";
+    const workNameText = props.imgData?.farmWorkName || "";
+    const farmNameText = props.imgData?.farmName || "";
 
-        const blockLines = 1 + prescriptionLines.length; // 1 行名称 + N 行处方
-        // 整个“第二行块”的第一行 baseline,使最后一行仍然靠近 singleLineY
-        firstLineY = singleLineY - (blockLines - 1) * lineHeight;
+    let firstLineY = singleLineY;
 
-        // 农事名称(第一行,靠左)
-        ctx.font = "16px PangMenZhengDao";
-        ctx.fillText(workNameText, paddingX, firstLineY);
+    // 农事名称
+    // ctx.font = "16px PangMenZhengDao";
+    ctx.font = "16px PangMenZhengDao";
+    ctx.fillText(workNameText, paddingX, firstLineY);
 
-        // 处方文本,从下一行开始,全部从左侧对齐
-        ctx.font = "10px sans-serif";
-        let textY = firstLineY + lineHeight;
-        prescriptionLines.forEach((text) => {
-            ctx.fillText(text, paddingX, textY);
-            textY += lineHeight;
-        });
-    }
+    // 农场名称紧跟在名称后面
+    const nameWidth = ctx.measureText(workNameText).width;
+    ctx.font = "10px sans-serif";
+    const gap = 8;
+    ctx.fillText(farmNameText, paddingX + nameWidth + gap, firstLineY);
 
-    // 第一行(最上)的 baseline 在整个“第二行块”之上 17px
-    y = firstLineY - 17;
+    // 第一行(最上)的 baseline 在整个"第二行块"之上 17px
+    y = firstLineY - 20;
     ctx.font = "12px PangMenZhengDao";
     const timeText = props.imgData?.executeDate;
     ctx.fillText(timeText, paddingX, y);
@@ -475,13 +534,26 @@ function drawReviewEffectText(ctx, w, h) {
     ctx.shadowBlur = 0;
 }
 
-function drawBottomMask(ctx, w, h) {
-    const maskHeight = 66; // 和 3 行文字 + padding 精确匹配
+function drawBottomMask(ctx, w, h, topY) {
+    let maskHeight;
+    let maskTop;
+    
+    if (topY !== undefined) {
+        // 遮罩从第一行文字上方一点开始,到图片底部
+        // 第一行文字是 12px 字体,实际高度约 12-14px,留出一些空间
+        const textHeight = 14; // 第一行文字的实际高度
+        maskTop = topY - textHeight; // 从文字上方开始
+        maskHeight = h - maskTop; // 从 maskTop 到图片底部
+    } else {
+        // 默认高度(用于 drawReviewEffectText 等情况)
+        maskHeight = 66;
+        maskTop = h - maskHeight;
+    }
 
     ctx.fillStyle = "rgba(0,0,0,0.45)";
     ctx.fillRect(
         0,
-        h - maskHeight, // ✅ 绝对贴底
+        maskTop,
         w,
         maskHeight
     );

+ 6 - 4
src/components/popup/priceSheetPopup.vue

@@ -45,11 +45,12 @@
     
                     <!-- 药肥费用区域 -->
                     <div class="fertilizer-cost-section">
-                        <div class="section-header">
+                        <div class="section-header" v-if="priceData?.prescription?.pesticideFertilizerList?.length">
                             <div class="section-title">药肥费用</div>
                             <div class="section-total">{{ pesticideCostTotal ? formatArea(pesticideCostTotal) : "--" }}<span class="unit-text">元</span></div>
                         </div>
-                        <div class="cost-table">
+                        <price-table :prescription-data="priceData?.prescription" :area="priceData?.farm?.mianji" />
+                        <!-- <div class="cost-table">
                             <div class="table-header">
                                 <div class="col-1">类型</div>
                                 <div class="col-2">名称</div>
@@ -68,9 +69,9 @@
                                 <div class="col-3">{{ item.brand || '--' }}</div>
                                 <div class="col-4">{{ item.price || '--' }}</div>
                                 <div class="col-5">{{ item.dosage ? item.dosage + item.unit : '--' }}</div>
-                                <div class="col-6">{{ getTotal(item) }}</div>
+                                <div class="col-6">{{ getTotal(item) }}</div>
                             </div>
-                        </div>
+                        </div> -->
                     </div>
     
                     <!-- 服务费用区域 -->
@@ -133,6 +134,7 @@ import { ElMessage } from "element-plus";
 import wx from "weixin-js-sdk";
 import html2canvas from "html2canvas";
 import { formatArea } from "@/common/commonFun";
+import PriceTable from "@/views/old_mini/agri_work/components/priceTable.vue";
 
 const router = useRouter();
 const route = useRoute();

+ 24 - 3
src/views/old_mini/achievement_report/index.vue

@@ -48,11 +48,11 @@
 
                 <div class="report-excute" v-for="(item, index) in workItem?.executeEvidence" :key="index">
                     <div class="tag-label">执行照片</div>
-                    <album-draw-box :isShowNum="0" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.executeEvidence?.length"></album-draw-box>
+                    <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">复核照片</div>
-                    <album-draw-box :isShowNum="0" :isAchievementImgs="true" :imgData="workItem" :photo="item" :current="index" :index="index" :length="workItem?.reviewImage?.length"></album-draw-box>
+                    <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">复核照片</div>
@@ -108,7 +108,7 @@
 <script setup>
 import CustomHeader from "@/components/customHeader.vue";
 import AlbumCarousel from "@/components/album_compoents/albumCarousel";
-import { ref, onActivated, nextTick, watch } from "vue";
+import { ref, onActivated, onDeactivated, onUnmounted, nextTick, watch } from "vue";
 import html2canvas from "html2canvas";
 import { uploadBase64 } from "@/common/uploadImg";
 import { detectRuntimeEnvironment } from "@/common/commonFun";
@@ -129,6 +129,7 @@ const paramsPage = ref({});
 onActivated(() => {
     window.scrollTo(0, 0);
     paramsPage.value = route.query.miniJson ? JSON.parse(route.query.miniJson) : {};
+    console.log('paramsPage', paramsPage.value);
     getDetail();
     getResultReport();
 });
@@ -312,6 +313,26 @@ watch(
     },
     { 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>

+ 5 - 2
src/views/old_mini/agri_work/components/priceTable.vue

@@ -6,7 +6,7 @@
                 <div class="title-3"><div class="table-name">药肥品牌</div></div>
                 <div class="title-4"><div class="table-name">药肥单价</div></div>
                 <div class="title-5"><div class="table-name">单亩用量</div></div>
-                <div class="title-6" v-if="area"><div class="table-name">总价</div></div>
+                <div class="title-6" v-if="area"><div class="table-name line-center">总价</div></div>
             </div>
             
             <template v-if="isArrList && prescriptionData?.length">
@@ -149,6 +149,9 @@ const getTotal = (item) => {
             font-size: 12px;
             margin: 0 auto;
         }
+        .line-center {
+            line-height: 34px;
+        }
     }
 
     .title-1 {
@@ -167,7 +170,7 @@ const getTotal = (item) => {
         width: 52px;
     }
     .title-6 {
-        width: 52px;
+        width: 60px;
     }
     .new-table-wrap {
         padding: 5px;

+ 1 - 1
src/views/old_mini/modify_work/detailWork.vue

@@ -197,7 +197,7 @@
                             >
                                 <div class="new-table">
                                     <div class="line-l">
-                                        <div class="line-1 title-1">{{ subP.typeName }}</div>
+                                        <div class="line-1 title-1">{{ subP.typeName || '--' }}</div>
                                         <div class="line-2">{{ subP.name }}</div>
                                     </div>
                                     <div class="line-r">

+ 2 - 2
src/views/old_mini/modify_work/modify.vue

@@ -401,7 +401,7 @@
                                 > -->
                                     <div class="new-table">
                                         <div class="line-l">
-                                            <div class="line-1 title-1">{{ subP.typeName }}</div>
+                                            <div class="line-1 title-1">{{ subP.typeName || '--' }}</div>
                                             <div class="line-2">
                                                 {{ subP.name || subP.pesticideFertilizerName }}
                                             </div>
@@ -684,7 +684,7 @@ const getDetail = async () => {
             };
             // 按照 interactPopup.vue 的方式获取数据
             await getPhenologyList(data.containerSpaceTimeId || route.query.containerSpaceTimeId);
-            await getFarmWorkArrangeDetail(route.query.id);
+            await getFarmWorkArrangeDetail(route.query.id || route.query.arrangeId);
         }
     }
 };