Browse Source

fix: 成果报告单张图片

lxf 2 hours ago
parent
commit
a819fd69c8

+ 4 - 0
src/api/modules/container_farm_work_arrange.js

@@ -10,4 +10,8 @@ module.exports = {
         url: url + "/saveComposite",
         type: "post",
     },
+    toggleFollow: {
+        url: url + "/toggleFollow",
+        type: "get",
+    },
 }

+ 0 - 1
src/components/album_compoents/albumCarouselItem.vue

@@ -217,7 +217,6 @@ const clickPhoto = (photo) => {
 };
 
 const handleSaveImage = () => {
-    console.log('save');
     downloadImage(previewCanvas.value, '执行照片');
 };
 

+ 494 - 285
src/components/album_compoents/albumDrawBox.vue

@@ -1,377 +1,586 @@
 <template>
-  <photo-consumer
-      class="carousel-item"
-      :src="watermark || getPhotoSrc(photo)"
-  >
-    <img
-      v-if="Math.abs(current - index) < 3"
-      crossorigin="anonymous"
-      loading="lazy"
-      @load="drawWatermark($event)"
-      :src="watermark || getPhotoSrc(photo)"
-      style="width: 100%; height: 255px; object-fit: cover; display: block; border-radius: 8px;"
-    />
-    <canvas
-      ref="canvasRef"
-      style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none;border-radius: 8px;"
-    ></canvas>
-    <div class="tag-box right" v-if="isShowNum" :class="{'leftTop': 'leftTop'}">{{ index+1 }}/{{ length }}</div>
-<!--    <div class="center-mark">mark</div>-->
-  </photo-consumer>
-
+    <photo-consumer class="carousel-item" :src="watermark || getPhotoSrc(photo)">
+        <img
+            v-if="Math.abs(current - index) < 3"
+            crossorigin="anonymous"
+            loading="lazy"
+            @load="drawWatermark($event)"
+            :src="watermark || getPhotoSrc(photo)"
+            style="width: 100%; height: 255px; object-fit: cover; display: block; border-radius: 8px"
+        />
+        <canvas
+            ref="canvasRef"
+            @click="handleClick"
+            style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; border-radius: 8px"
+        ></canvas>
+        <div class="tag-box right" v-if="isShowNum" :class="{ leftTop: 'leftTop' }">{{ index + 1 }}/{{ length }}</div>
+        <!--    <div class="center-mark">mark</div>-->
+    </photo-consumer>
+
+    <popup class="cavans-popup" v-model:show="showPopup">
+        <div class="cavans-content">
+            <img class="current-img" :src="watermarkWithQRCode || watermark" alt="" />
+        </div>
+        <!-- 底部操作按钮 -->
+        <div class="bottom-actions" @click.stop="showPopup = false">
+            <div class="action-buttons">
+                <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" @click.stop="handleSaveImage">
+                    <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>
+    </popup>
 </template>
 
 <script setup>
+import { Popup } from "vant";
 import { ref, onMounted, onBeforeUnmount, defineProps } from "vue";
 import { base_img_url2 } from "@/api/config";
-import {imageCache,loadImage} from "./cacheImg.js"
-import {dateFormat} from "@/utils/date_util.js"
-
-import {drawTextInRect, drawBorderImageInRect, drawImageInRect, drawRectInRect, drawHorizontalTextList} from "./utils"
+import { imageCache, loadImage } from "./cacheImg.js";
+import { dateFormat } from "@/utils/date_util.js";
+
+import {
+    drawTextInRect,
+    drawBorderImageInRect,
+    drawImageInRect,
+    drawRectInRect,
+    drawHorizontalTextList,
+} from "./utils";
 // const resize = "?x-oss-process=image/resize,p_30/format,webp/quality,q_40";
 const resize = "";
 
 const canvasRef = ref(null);
-const watermark = ref(null)
-const baseMapBig = ref(false)
+const watermark = ref(null);
+const baseMapBig = ref(false);
 
 const props = defineProps({
-  photo:{
-    required: true
-  },
-  index:{
-    type: Number,
-    required: true
-  },
-  length:{
-    type: Number,
-    required: true
-  },
-  current:{
-    type: Number,
-    required: true
-  },
-  isShowNum:{
-    type: Number,
-    required: true
-  }
-})
+    photo: {
+        required: true,
+    },
+    index: {
+        type: Number,
+        required: true,
+    },
+    length: {
+        type: Number,
+        required: true,
+    },
+    current: {
+        type: Number,
+        required: true,
+    },
+    isShowNum: {
+        type: Number,
+        required: true,
+    },
+});
 let img = null;
 let ctx = null;
+// 保存原始图片引用,用于重新绘制
+let cachedSourceImg = null;
+let cachedDisplayImg = null;
 
 function getWatermarkKey(photo) {
-  return photo?.resFilename || photo?.cloudFilename || photo
+    return photo?.resFilename || photo?.cloudFilename || photo;
 }
 
 function getPhotoSrc(photo) {
-  const key = photo?.resFilename || photo?.cloudFilename || photo
-  if (typeof key === 'string' && (key.startsWith('http') || key.startsWith('data:'))) {
-    return key + resize
-  }
-  return base_img_url2 + key + resize
+    const key = photo?.resFilename || photo?.cloudFilename || photo;
+    if (typeof key === "string" && (key.startsWith("http") || key.startsWith("data:"))) {
+        return key + resize;
+    }
+    return base_img_url2 + key + resize;
 }
 
+const showPopup = ref(false);
+const watermarkWithQRCode = ref(null); // 带二维码的图片,用于弹窗显示
 
-async function drawWatermark(event) {
-  const displayImg = event.target
+async function handleClick() {
+    // 点击时生成带二维码的图片用于弹窗显示
+    await generateImageWithQRCode();
+    showPopup.value = true;
+}
+
+// 加载二维码图片
+async function loadQRCodeImage() {
+    // 先检查缓存
+    if (imageCache.has("qrcode")) {
+        return imageCache.get("qrcode");
+    }
+    // 使用 loadImage 加载
+    try {
+        await loadImage(require("@/assets/img/home/qrcode.png"), "qrcode");
+        return imageCache.get("qrcode");
+    } catch (error) {
+        console.error("加载二维码失败:", error);
+        // 如果 require 失败,尝试直接创建图片
+        return new Promise((resolve, reject) => {
+            const img = new Image();
+            img.crossOrigin = "anonymous";
+            img.onload = () => {
+                imageCache.set("qrcode", img);
+                resolve(img);
+            };
+            img.onerror = reject;
+            img.src = require("@/assets/img/home/qrcode.png");
+        });
+    }
+}
+
+// 生成带二维码的图片(用于弹窗显示,不影响原图)
+async function generateImageWithQRCode() {
+    if (!cachedSourceImg || !cachedDisplayImg) return;
+    
+    try {
+        // 加载二维码图片
+        const qrCodeImg = await loadQRCodeImage();
+        
+        // 获取显示图片的尺寸
+        const rect = cachedDisplayImg.getBoundingClientRect();
+        const w = rect.width;
+        const h = rect.height;
+        
+        // 创建一个临时的 canvas 用于生成带二维码的图片
+        const tempCanvas = document.createElement("canvas");
+        const tempCtx = tempCanvas.getContext("2d");
+        
+        const dpr = window.devicePixelRatio || 1;
+        
+        // 设置 canvas 尺寸
+        tempCanvas.width = w * dpr;
+        tempCanvas.height = h * dpr;
+        
+        tempCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
+        tempCtx.imageSmoothingEnabled = true;
+        tempCtx.imageSmoothingQuality = "high";
+        
+        // 清空 canvas
+        tempCtx.clearRect(0, 0, w, h);
+        
+        // 重新绘制图片
+        drawImageCoverByNatural(tempCtx, cachedSourceImg, w, h);
+        
+        // 绘制底部遮罩和文字
+        drawBottomMask(tempCtx, w, h);
+        drawBottomTextOverlay(tempCtx, w, h);
+        
+        // 二维码尺寸和位置(参考 albumCarouselItem 的样式)
+        const qrSize = 40;
+        const qrX = w - qrSize - 12; // 距离右边 12px
+        const qrY = 12; // 距离顶部 12px
+        
+        // 绘制二维码到右上角
+        tempCtx.drawImage(qrCodeImg, qrX, qrY, qrSize, qrSize);
+        
+        // 保存为带二维码的图片(不影响原来的 watermark)
+        watermarkWithQRCode.value = tempCanvas.toDataURL("image/jpeg", 0.85);
+    } catch (error) {
+        console.error("生成带二维码的图片失败:", error);
+        // 如果失败,使用原来的 watermark
+        watermarkWithQRCode.value = watermark.value;
+    }
+}
 
-  const key = getWatermarkKey(props.photo)
-  if (watermarkCache.has(key)) {
-    watermark.value = watermarkCache.get(key)
-    return
-  }
+async function drawWatermark(event) {
+    const displayImg = event.target;
+
+    const key = getWatermarkKey(props.photo);
+    if (watermarkCache.has(key)) {
+        watermark.value = watermarkCache.get(key);
+        // 从缓存中恢复图片引用
+        const cachedData = watermarkCache.get(key + "_refs");
+        if (cachedData) {
+            cachedSourceImg = cachedData.sourceImg;
+            cachedDisplayImg = cachedData.displayImg;
+        }
+        return;
+    }
 
-  // ✅ 用原始图片重新创建一个 Image
-  const sourceImg = await loadOriginalImage(displayImg.src)
+    // ✅ 用原始图片重新创建一个 Image
+    const sourceImg = await loadOriginalImage(displayImg.src);
+    
+    // 保存引用
+    cachedSourceImg = sourceImg;
+    cachedDisplayImg = displayImg;
 
-  drawWatermark2(sourceImg, displayImg)
+    drawWatermark2(sourceImg, displayImg);
 
-  watermarkCache.set(key, watermark.value)
+    watermarkCache.set(key, watermark.value);
+    // 同时保存图片引用
+    watermarkCache.set(key + "_refs", { sourceImg, displayImg });
 }
 
-const watermarkCache = new Map()
-
+const watermarkCache = new Map();
 
 function loadOriginalImage(src) {
-  return new Promise((resolve) => {
-    const img = new Image()
-    img.crossOrigin = 'anonymous'
-    img.onload = () => resolve(img)
-    img.src = src
-  })
+    return new Promise((resolve) => {
+        const img = new Image();
+        img.crossOrigin = "anonymous";
+        img.onload = () => resolve(img);
+        img.src = src;
+    });
 }
 
 function drawWatermark2(sourceImg, displayImg) {
-  const canvas = canvasRef.value
-  const ctx = canvas.getContext('2d')
+    const canvas = canvasRef.value;
+    const ctx = canvas.getContext("2d");
 
-  const rect = displayImg.getBoundingClientRect()
-  const w = rect.width
-  const h = rect.height
+    const rect = displayImg.getBoundingClientRect();
+    const w = rect.width;
+    const h = rect.height;
 
-  const dpr = window.devicePixelRatio || 1
+    const dpr = window.devicePixelRatio || 1;
 
-  canvas.width = w * dpr
-  canvas.height = h * dpr
-  canvas.style.width = w + 'px'
-  canvas.style.height = h + 'px'
+    canvas.width = w * dpr;
+    canvas.height = h * dpr;
+    canvas.style.width = w + "px";
+    canvas.style.height = h + "px";
 
-  ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
-  ctx.imageSmoothingEnabled = true
-  ctx.imageSmoothingQuality = 'high'
+    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+    ctx.imageSmoothingEnabled = true;
+    ctx.imageSmoothingQuality = "high";
 
-  ctx.clearRect(0, 0, w, h)
+    ctx.clearRect(0, 0, w, h);
 
-  // ✅ 用「原始像素图」做 cover
-  drawImageCoverByNatural(ctx, sourceImg, w, h)
+    // ✅ 用「原始像素图」做 cover
+    drawImageCoverByNatural(ctx, sourceImg, w, h);
 
-  drawBottomMask(ctx, w, h)
-  drawBottomTextOverlay(ctx, w, h)
+    drawBottomMask(ctx, w, h);
+    drawBottomTextOverlay(ctx, w, h);
 
-  watermark.value = canvas.toDataURL('image/jpeg', 0.85)
+    watermark.value = canvas.toDataURL("image/jpeg", 0.85);
 }
 function drawImageCoverByNatural(ctx, img, w, h) {
-  const imgRatio = img.naturalWidth / img.naturalHeight
-  const canvasRatio = w / h
-
-  let sx, sy, sw, sh
-
-  if (imgRatio > canvasRatio) {
-    sh = img.naturalHeight
-    sw = sh * canvasRatio
-    sx = (img.naturalWidth - sw) / 2
-    sy = 0
-  } else {
-    sw = img.naturalWidth
-    sh = sw / canvasRatio
-    sx = 0
-    sy = (img.naturalHeight - sh) / 2
-  }
-
-  ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h)
+    const imgRatio = img.naturalWidth / img.naturalHeight;
+    const canvasRatio = w / h;
+
+    let sx, sy, sw, sh;
+
+    if (imgRatio > canvasRatio) {
+        sh = img.naturalHeight;
+        sw = sh * canvasRatio;
+        sx = (img.naturalWidth - sw) / 2;
+        sy = 0;
+    } else {
+        sw = img.naturalWidth;
+        sh = sw / canvasRatio;
+        sx = 0;
+        sy = (img.naturalHeight - sh) / 2;
+    }
+
+    ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h);
 }
 
 function drawImageCover(ctx, img, w, h) {
-  const imgRatio = img.naturalWidth / img.naturalHeight
-  const canvasRatio = w / h
-
-  let sx, sy, sw, sh
-
-  if (imgRatio > canvasRatio) {
-    sh = img.naturalHeight
-    sw = sh * canvasRatio
-    sx = (img.naturalWidth - sw) / 2
-    sy = 0
-  } else {
-    sw = img.naturalWidth
-    sh = sw / canvasRatio
-    sx = 0
-    sy = (img.naturalHeight - sh) / 2
-  }
-
-  ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h)
+    const imgRatio = img.naturalWidth / img.naturalHeight;
+    const canvasRatio = w / h;
+
+    let sx, sy, sw, sh;
+
+    if (imgRatio > canvasRatio) {
+        sh = img.naturalHeight;
+        sw = sh * canvasRatio;
+        sx = (img.naturalWidth - sw) / 2;
+        sy = 0;
+    } else {
+        sw = img.naturalWidth;
+        sh = sw / canvasRatio;
+        sx = 0;
+        sy = (img.naturalHeight - sh) / 2;
+    }
+
+    ctx.drawImage(img, sx, sy, sw, sh, 0, 0, w, h);
 }
 function drawBottomTextOverlay(ctx, w, h) {
-  const paddingX = 12
-  const paddingBottom = 8
-  const lineHeight = 16
-
-  ctx.textBaseline = 'alphabetic'
-  ctx.fillStyle = '#fff'
-  ctx.shadowColor = 'rgba(0,0,0,0.6)'
-  ctx.shadowBlur = 2
-
-  // ⬇️ 从底部开始,一行一行往上
-  let y = h - paddingBottom
-
-  // 第三行(最底)
-  ctx.font = '10px sans-serif'
-  console.log('paddingX', paddingX, y)
-  ctx.drawImage(imageCache.get("address"), paddingX, y - 9, 9, 10);
-  ctx.fillText(
-    '荔博园(广东省广州市从化区)',
-    paddingX + 12,
-    y
-  )
-
-  // 第二行
-  y -= 15
-  ctx.font = '16px PangMenZhengDao'
-  const workNameText = '梢期杀虫'
-  const prescriptionText = '药物处方:乙烯利'
-  ctx.fillText(
-    workNameText,
-    paddingX,
-    y
-  )
-  ctx.font = '10px sans-serif'
-  ctx.fillText(
-    prescriptionText,
-    paddingX + workNameText.length * 20,
-    y
-  )
-
-  // 第一行(最上)
-  y -= 17
-  ctx.font = '12px PangMenZhengDao'
-  const timeText = '2025.12.25'
-  ctx.fillText(timeText, paddingX, y)
-  const executorText = '执行人:张三李四'
-  ctx.font = '10px sans-serif'
-  ctx.fillText(executorText, paddingX + 80, y)
-
-  ctx.shadowBlur = 0
+    const paddingX = 12;
+    const paddingBottom = 8;
+    const lineHeight = 16;
+
+    ctx.textBaseline = "alphabetic";
+    ctx.fillStyle = "#fff";
+    ctx.shadowColor = "rgba(0,0,0,0.6)";
+    ctx.shadowBlur = 2;
+
+    // ⬇️ 从底部开始,一行一行往上
+    let y = h - paddingBottom;
+
+    // 第三行(最底)
+    ctx.font = "10px sans-serif";
+    console.log("paddingX", paddingX, y);
+    ctx.drawImage(imageCache.get("address"), paddingX, y - 9, 9, 10);
+    ctx.fillText("荔博园(广东省广州市从化区)", paddingX + 12, y);
+
+    // 第二行
+    y -= 15;
+    ctx.font = "16px PangMenZhengDao";
+    const workNameText = "梢期杀虫";
+    const prescriptionText = "药物处方:乙烯利";
+    ctx.fillText(workNameText, paddingX, y);
+    ctx.font = "10px sans-serif";
+    ctx.fillText(prescriptionText, paddingX + workNameText.length * 20, y);
+
+    // 第一行(最上)
+    y -= 17;
+    ctx.font = "12px PangMenZhengDao";
+    const timeText = "2025.12.25";
+    ctx.fillText(timeText, paddingX, y);
+    const executorText = "执行人:张三李四";
+    ctx.font = "10px sans-serif";
+    ctx.fillText(executorText, paddingX + 80, y);
+
+    ctx.shadowBlur = 0;
 }
 
-
 function drawBottomMask(ctx, w, h) {
-  const maskHeight = 60  // 和 3 行文字 + padding 精确匹配
-
-  ctx.fillStyle = 'rgba(0,0,0,0.45)'
-  ctx.fillRect(
-    0,
-    h - maskHeight, // ✅ 绝对贴底
-    w,
-    maskHeight
-  )
+    const maskHeight = 60; // 和 3 行文字 + padding 精确匹配
+
+    ctx.fillStyle = "rgba(0,0,0,0.45)";
+    ctx.fillRect(
+        0,
+        h - maskHeight, // ✅ 绝对贴底
+        w,
+        maskHeight
+    );
 }
 
-
-
-
 const showTagBox = ref(true); // 控制 tag-box 的显示状态
 const hideTagBox = (event) => {
-  event.stopPropagation();
-  showTagBox.value = false; // 隐藏 tag-box
+    event.stopPropagation();
+    showTagBox.value = false; // 隐藏 tag-box
 };
 
 const formatDate = (date) => {
-  const year = date.getFullYear();
-  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
-  const day = String(date.getDate()).padStart(2, '0');
-  return `${(year+"").substring(2)}${month}${day}`;
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始,需要加1
+    const day = String(date.getDate()).padStart(2, "0");
+    return `${(year + "").substring(2)}${month}${day}`;
 };
 
+const handleSaveImage = () => {
+    // 保存带二维码的图片
+    downloadImage(watermarkWithQRCode.value || watermark.value, "执行照片");
+};
 
+const handleWechat = () => {
+    // 微信分享功能(可以后续实现)
+    console.log("微信分享");
+};
 
+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);
+}
 </script>
 
 <style lang="scss" scoped>
 .canvas-container {
-  width: 100%;
-  height: 100%;
-  display: flex;
-  justify-content: center;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
 }
 .carousel-item {
-  min-width: 100%;
-  max-height: 100%;
-  flex-shrink: 0;
-  width: 100%;
-  pointer-events: auto;
-  position: relative;
-  .tag-box {
-    position: absolute;
-    bottom: 30%;
-    left: 50%;
-    transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
-    height: 18px;
-    padding: 0 6px;
-    background: rgba(108, 108, 108, 0.67);
-    border-radius: 10px;
-    display: flex;
-    align-items: center;
-    color: #FFFFFF;
-    font-size: 12px;
-
-    &.right {
-      left: auto;
-      right: 10px;
+    min-width: 100%;
+    max-height: 100%;
+    flex-shrink: 0;
+    width: 100%;
+    pointer-events: auto;
+    position: relative;
+    .tag-box {
+        position: absolute;
+        bottom: 30%;
+        left: 50%;
+        transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
+        height: 18px;
+        padding: 0 6px;
+        background: rgba(108, 108, 108, 0.67);
+        border-radius: 10px;
+        display: flex;
+        align-items: center;
+        color: #ffffff;
+        font-size: 12px;
+
+        &.right {
+            left: auto;
+            right: 10px;
+        }
+        &.leftTop {
+            height: 25px;
+            line-height: 26px;
+            padding: 0 8px;
+            border-radius: 16px;
+            background: rgba(0, 0, 0, 0.6);
+            bottom: auto;
+            top: 6px;
+        }
     }
-    &.leftTop {
-      height: 25px;
-      line-height: 26px;
-      padding: 0 8px;
-      border-radius: 16px;
-      background: rgba(0, 0, 0, 0.6);
-      bottom: auto;
-      top: 6px;
+    .tag-text {
+        position: absolute;
+        bottom: 31%;
+        left: 50%;
+        width: 80%;
+        transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
+        height: 24px;
+        padding: 10px 0px 10px 0px;
+        background: rgba(0, 0, 0, 0.67);
+        border-radius: 6px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        text-align: center;
+        color: #ffffff;
+        font-size: 12px;
     }
-  }
-  .tag-text {
-    position: absolute;
-    bottom: 31%;
-    left: 50%;
-    width: 80%;
-    transform: translate(-50%, 50%); // 确保在高二分之一的位置水平居中
-    height: 24px;
-    padding: 10px 0px 10px 0px;
-    background: rgba(0, 0, 0, 0.67);
-    border-radius: 6px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    text-align: center;
-    color: #FFFFFF;
-    font-size: 12px;
-  }
 
-  .center-mark {
-    position: absolute;
-    bottom: 10px;
-    left: 50%;
-    transform: translateX(-50%);
-    color: #36402c;
-    font-size: rpx(24);
-    font-weight: bold;
-    padding: rpx(14) rpx(30);
-    background: linear-gradient(
+    .center-mark {
+        position: absolute;
+        bottom: 10px;
+        left: 50%;
+        transform: translateX(-50%);
+        color: #36402c;
+        font-size: rpx(24);
+        font-weight: bold;
+        padding: rpx(14) rpx(30);
+        background: linear-gradient(
             90deg,
             rgba(255, 255, 255, 0) 0%,
             rgba(255, 255, 255, 0.6) 24%,
             rgba(255, 255, 255, 0.6) 76%,
             rgba(255, 255, 255, 0) 100%
-    );
-  }
+        );
+    }
 }
 .carousel-item img {
-  width: 100%;
-  display: block;
+    width: 100%;
+    display: block;
 }
 canvas {
-  position: absolute;
+    position: absolute;
 }
 .close-button {
-  background: transparent;
-  border: none;
-  color: #FFFFFF;
-  cursor: pointer;
-  font-size: 10px; // 可以根据需求调整大小
-  position: absolute;
-  top: -1px;
-  right: -9px; // 调整为合适的间距
-  transform: translateY(-50%);
+    background: transparent;
+    border: none;
+    color: #ffffff;
+    cursor: pointer;
+    font-size: 10px; // 可以根据需求调整大小
+    position: absolute;
+    top: -1px;
+    right: -9px; // 调整为合适的间距
+    transform: translateY(-50%);
 }
 
 .floating-img {
-  position: absolute;
-  bottom: 0;
-  right: 0;
-  width: auto !important;
-  height: 25% !important;
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    width: auto !important;
+    height: 25% !important;
 }
 .floating-img-big {
-  position: fixed !important;
-  z-index: 99999 !important;
-  top: 50% !important;
-  left: 50% !important;
-  width: auto !important;
-  height: 100% !important;
-  transform: translate(-50%, -50%) !important;
+    position: fixed !important;
+    z-index: 99999 !important;
+    top: 50% !important;
+    left: 50% !important;
+    width: auto !important;
+    height: 100% !important;
+    transform: translate(-50%, -50%) !important;
 }
 
+.cavans-popup {
+    width: 100%;
+    max-width: 100%;
+    max-height: 90vh;
+    background: none;
+    border-radius: 12px;
+    overflow: auto;
+    display: flex;
+    flex-direction: column;
+    backdrop-filter: 4px;
+    .cavans-content {
+        text-align: center;
+        padding: 16px;
+        .current-img {
+            border-radius: 8px;
+            width: 100%;
+        }
+    }
 
-
-
+    // 底部操作按钮
+    .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>