|  | @@ -0,0 +1,434 @@
 | 
	
		
			
				|  |  | +<template>
 | 
	
		
			
				|  |  | +  <view class="w-full h-screen relative">
 | 
	
		
			
				|  |  | +    <camera
 | 
	
		
			
				|  |  | +      :device-position="position"
 | 
	
		
			
				|  |  | +      resolution="high"
 | 
	
		
			
				|  |  | +      :flash="flashMode"
 | 
	
		
			
				|  |  | +      frame-size="large"
 | 
	
		
			
				|  |  | +      @initdone="initdone"
 | 
	
		
			
				|  |  | +      @ready="ready"
 | 
	
		
			
				|  |  | +      class="w-full h-85%"
 | 
	
		
			
				|  |  | +    ></camera>
 | 
	
		
			
				|  |  | +    <view class="absolute left-20rpx bottom-25% text-white">
 | 
	
		
			
				|  |  | +      <view class="mb24rpx max-w-180rpx">
 | 
	
		
			
				|  |  | +        <view
 | 
	
		
			
				|  |  | +          class="font-semibold bg-white h45rpx rounded-8rpx p8rpx flex items-center"
 | 
	
		
			
				|  |  | +          ><view class="bg-amber p-4rpx text-black rounded-8rpx">打卡</view>
 | 
	
		
			
				|  |  | +          <view class="time-text ml8rpx">{{ dayjs().format("HH:mm") }}</view>
 | 
	
		
			
				|  |  | +        </view>
 | 
	
		
			
				|  |  | +      </view>
 | 
	
		
			
				|  |  | +      <view class="px8rpx border-l border-l-solid border-l-amber">
 | 
	
		
			
				|  |  | +        <view class=""
 | 
	
		
			
				|  |  | +          >贵州省贵阳市观山湖区世纪城街道北京西路8号观山湖区办公大楼13层观山湖区文体广电旅游局</view
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +        <view class="mt2">
 | 
	
		
			
				|  |  | +          {{ dayjs().format("YYYY.MM.DD") }} {{ dayjs().format("dddd") }}
 | 
	
		
			
				|  |  | +        </view>
 | 
	
		
			
				|  |  | +      </view>
 | 
	
		
			
				|  |  | +    </view>
 | 
	
		
			
				|  |  | +    <canvas
 | 
	
		
			
				|  |  | +      id="watermarkCanvas"
 | 
	
		
			
				|  |  | +      canvas-id="watermarkCanvas"
 | 
	
		
			
				|  |  | +      style="position: fixed; top: -10000px"
 | 
	
		
			
				|  |  | +      :style="{
 | 
	
		
			
				|  |  | +        width: cavansInfo?.width + 'px',
 | 
	
		
			
				|  |  | +        height: cavansInfo?.height + 'px',
 | 
	
		
			
				|  |  | +      }"
 | 
	
		
			
				|  |  | +    ></canvas>
 | 
	
		
			
				|  |  | +    <view
 | 
	
		
			
				|  |  | +      class="bg-black h15% bottom-safe-area px120rpx box-border pb20rpx flex items-center w-full justify-between w-full"
 | 
	
		
			
				|  |  | +    >
 | 
	
		
			
				|  |  | +      <view @click="changeFlashMode">
 | 
	
		
			
				|  |  | +        <image
 | 
	
		
			
				|  |  | +          src="@/subPack/static/close.png"
 | 
	
		
			
				|  |  | +          class="w64rpx h64rpx"
 | 
	
		
			
				|  |  | +          v-if="flashMode == 'off'"
 | 
	
		
			
				|  |  | +        />
 | 
	
		
			
				|  |  | +        <image src="@/subPack/static/open.png" class="w64rpx h64rpx" v-else />
 | 
	
		
			
				|  |  | +      </view>
 | 
	
		
			
				|  |  | +      <view @click="takePhoto">
 | 
	
		
			
				|  |  | +        <image src="@/subPack/static/pic.png" class="w128rpx h128rpx" />
 | 
	
		
			
				|  |  | +      </view>
 | 
	
		
			
				|  |  | +      <view @click="changePosition">
 | 
	
		
			
				|  |  | +        <image src="@/subPack/static/back.png" class="w64rpx h64rpx" />
 | 
	
		
			
				|  |  | +      </view>
 | 
	
		
			
				|  |  | +    </view>
 | 
	
		
			
				|  |  | +  </view>
 | 
	
		
			
				|  |  | +  <page-container
 | 
	
		
			
				|  |  | +    :show="uploading"
 | 
	
		
			
				|  |  | +    :duration="false"
 | 
	
		
			
				|  |  | +    :overlay="false"
 | 
	
		
			
				|  |  | +    @beforeleave="beforeleave"
 | 
	
		
			
				|  |  | +  >
 | 
	
		
			
				|  |  | +    <wd-message-box></wd-message-box>
 | 
	
		
			
				|  |  | +  </page-container>
 | 
	
		
			
				|  |  | +</template>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<script setup lang="ts">
 | 
	
		
			
				|  |  | +import { BASE_URL } from "@/config";
 | 
	
		
			
				|  |  | +import router from "@/router";
 | 
	
		
			
				|  |  | +import type { CameraDevicePosition, CameraFlash } from "@uni-helper/uni-types";
 | 
	
		
			
				|  |  | +import "dayjs/locale/zh-cn";
 | 
	
		
			
				|  |  | +import dayjs from "dayjs";
 | 
	
		
			
				|  |  | +const flashMode = ref<CameraFlash>("off");
 | 
	
		
			
				|  |  | +const position = ref<CameraDevicePosition>("back");
 | 
	
		
			
				|  |  | +dayjs.locale("zh-cn");
 | 
	
		
			
				|  |  | +const cavansInfo = ref<UniApp.GetImageInfoSuccessData>();
 | 
	
		
			
				|  |  | +const isShow = ref(false);
 | 
	
		
			
				|  |  | +const messageBox = useMessage();
 | 
	
		
			
				|  |  | +const FilePath = ref();
 | 
	
		
			
				|  |  | +const { send, abort, uploading, data } = useRequest(
 | 
	
		
			
				|  |  | +  (FormData) =>
 | 
	
		
			
				|  |  | +    Apis.sys.upload({
 | 
	
		
			
				|  |  | +      data: FormData,
 | 
	
		
			
				|  |  | +      requestType: "upload",
 | 
	
		
			
				|  |  | +      fileType: "image",
 | 
	
		
			
				|  |  | +    }),
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    immediate: false,
 | 
	
		
			
				|  |  | +  },
 | 
	
		
			
				|  |  | +);
 | 
	
		
			
				|  |  | +async function initdone() {
 | 
	
		
			
				|  |  | +  console.log("相机初始化完成!!!");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +function ready() {
 | 
	
		
			
				|  |  | +  console.log("相机初始化成功!!!");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +function changeFlashMode() {
 | 
	
		
			
				|  |  | +  flashMode.value = flashMode.value == "off" ? "on" : "off";
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +function changePosition() {
 | 
	
		
			
				|  |  | +  position.value = position.value == "back" ? "front" : "back";
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +function takePhoto() {
 | 
	
		
			
				|  |  | +  const ctx = wx.createCameraContext();
 | 
	
		
			
				|  |  | +  isShow.value = true;
 | 
	
		
			
				|  |  | +  uni.showLoading({ mask: true });
 | 
	
		
			
				|  |  | +  ctx.takePhoto({
 | 
	
		
			
				|  |  | +    quality: "high",
 | 
	
		
			
				|  |  | +    success: async (res) => {
 | 
	
		
			
				|  |  | +      if (res.tempImagePath) {
 | 
	
		
			
				|  |  | +        const img = await addWatermarkToImage(res.tempImagePath);
 | 
	
		
			
				|  |  | +        FilePath.value = img;
 | 
	
		
			
				|  |  | +        await send({ filePath: img, name: "file" });
 | 
	
		
			
				|  |  | +        console.log(data.value);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // const path = `${BASE_URL}/sys/common/static/${resData.message}`;
 | 
	
		
			
				|  |  | +        // console.log(path, "上传文件");
 | 
	
		
			
				|  |  | +        // useCameraStore().setImg(path);
 | 
	
		
			
				|  |  | +        // isShow.value = false;
 | 
	
		
			
				|  |  | +        // router.back();
 | 
	
		
			
				|  |  | +        // uni.hideLoading();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    fail(err) {
 | 
	
		
			
				|  |  | +      isShow.value = false;
 | 
	
		
			
				|  |  | +      uni.hideLoading();
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function beforeleave() {
 | 
	
		
			
				|  |  | +  messageBox
 | 
	
		
			
				|  |  | +    .confirm({
 | 
	
		
			
				|  |  | +      title: "正在上传图片,确认退出吗?",
 | 
	
		
			
				|  |  | +      cancelButtonText: "退出",
 | 
	
		
			
				|  |  | +      confirmButtonText: "继续上传",
 | 
	
		
			
				|  |  | +      closeOnClickModal: false,
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    .then(async () => {
 | 
	
		
			
				|  |  | +      console.log("点击了确定按钮");
 | 
	
		
			
				|  |  | +    })
 | 
	
		
			
				|  |  | +    .catch(() => {
 | 
	
		
			
				|  |  | +      abort();
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +function addWatermarkToImage(imagePath: string): Promise<string> {
 | 
	
		
			
				|  |  | +  return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  | +    const ctx = uni.createCanvasContext("watermarkCanvas");
 | 
	
		
			
				|  |  | +    console.log("addWatermarkToImage", imagePath);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    uni.getImageInfo({
 | 
	
		
			
				|  |  | +      src: imagePath,
 | 
	
		
			
				|  |  | +      success: (imageInfo) => {
 | 
	
		
			
				|  |  | +        const canvasWidth = imageInfo.width;
 | 
	
		
			
				|  |  | +        const canvasHeight = imageInfo.height;
 | 
	
		
			
				|  |  | +        cavansInfo.value = imageInfo;
 | 
	
		
			
				|  |  | +        ctx.drawImage(imagePath, 0, 0, canvasWidth, canvasHeight);
 | 
	
		
			
				|  |  | +        // 字体大小统一为45px
 | 
	
		
			
				|  |  | +        const fontSize = 45;
 | 
	
		
			
				|  |  | +        const lineHeight = fontSize * 1.2;
 | 
	
		
			
				|  |  | +        const padding = 15;
 | 
	
		
			
				|  |  | +        const cornerRadius = 10;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 计算水印总高度
 | 
	
		
			
				|  |  | +        const watermarkHeight = lineHeight * 6 + padding * 3;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 计算文本宽度
 | 
	
		
			
				|  |  | +        ctx.setFontSize(fontSize);
 | 
	
		
			
				|  |  | +        const timeText = dayjs().format("HH:mm");
 | 
	
		
			
				|  |  | +        const checkInText = "打卡";
 | 
	
		
			
				|  |  | +        const addressText =
 | 
	
		
			
				|  |  | +          "贵州省贵阳市观山湖区世纪城街道北京西路8号观山湖区办公大楼13层观山湖区文体广电旅游局";
 | 
	
		
			
				|  |  | +        const dateText = `${dayjs().format("YYYY.MM.DD")} ${dayjs().format("dddd")}`;
 | 
	
		
			
				|  |  | +        const timeWidth = ctx.measureText(timeText).width;
 | 
	
		
			
				|  |  | +        const checkInWidth = ctx.measureText(checkInText).width + padding * 4; // 增加padding确保文字不溢出
 | 
	
		
			
				|  |  | +        const firstLineWidth = checkInWidth + timeWidth + padding * 2;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 水印位置:左下角,留出边距
 | 
	
		
			
				|  |  | +        const margin = 30;
 | 
	
		
			
				|  |  | +        const startX = margin;
 | 
	
		
			
				|  |  | +        const startY = canvasHeight - watermarkHeight - margin;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 1. 绘制第一行白色背景圆角矩形
 | 
	
		
			
				|  |  | +        ctx.setFillStyle("#ffffff");
 | 
	
		
			
				|  |  | +        ctx.beginPath();
 | 
	
		
			
				|  |  | +        ctx.moveTo(startX + cornerRadius, startY);
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + firstLineWidth,
 | 
	
		
			
				|  |  | +          startY,
 | 
	
		
			
				|  |  | +          startX + firstLineWidth,
 | 
	
		
			
				|  |  | +          startY + lineHeight + padding * 2,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + firstLineWidth,
 | 
	
		
			
				|  |  | +          startY + lineHeight + padding * 2,
 | 
	
		
			
				|  |  | +          startX,
 | 
	
		
			
				|  |  | +          startY + lineHeight + padding * 2,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX,
 | 
	
		
			
				|  |  | +          startY + lineHeight + padding * 2,
 | 
	
		
			
				|  |  | +          startX,
 | 
	
		
			
				|  |  | +          startY,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX,
 | 
	
		
			
				|  |  | +          startY,
 | 
	
		
			
				|  |  | +          startX + firstLineWidth,
 | 
	
		
			
				|  |  | +          startY,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.closePath();
 | 
	
		
			
				|  |  | +        ctx.fill();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 2. 绘制"打卡"黄色背景圆角矩形
 | 
	
		
			
				|  |  | +        const checkInY = startY + padding;
 | 
	
		
			
				|  |  | +        ctx.setFillStyle("#ffcc00");
 | 
	
		
			
				|  |  | +        ctx.beginPath();
 | 
	
		
			
				|  |  | +        ctx.moveTo(startX + padding + cornerRadius, checkInY);
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + checkInWidth - padding,
 | 
	
		
			
				|  |  | +          checkInY,
 | 
	
		
			
				|  |  | +          startX + checkInWidth - padding,
 | 
	
		
			
				|  |  | +          checkInY + lineHeight,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + checkInWidth - padding,
 | 
	
		
			
				|  |  | +          checkInY + lineHeight,
 | 
	
		
			
				|  |  | +          startX + padding,
 | 
	
		
			
				|  |  | +          checkInY + lineHeight,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + padding,
 | 
	
		
			
				|  |  | +          checkInY + lineHeight,
 | 
	
		
			
				|  |  | +          startX + padding,
 | 
	
		
			
				|  |  | +          checkInY,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.arcTo(
 | 
	
		
			
				|  |  | +          startX + padding,
 | 
	
		
			
				|  |  | +          checkInY,
 | 
	
		
			
				|  |  | +          startX + checkInWidth - padding,
 | 
	
		
			
				|  |  | +          checkInY,
 | 
	
		
			
				|  |  | +          cornerRadius,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        ctx.closePath();
 | 
	
		
			
				|  |  | +        ctx.fill();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 3. 绘制"打卡"文字
 | 
	
		
			
				|  |  | +        ctx.setFillStyle("#000000");
 | 
	
		
			
				|  |  | +        ctx.setFontSize(fontSize);
 | 
	
		
			
				|  |  | +        ctx.font = `bold ${fontSize}px sans-serif`; // 设置字体加粗
 | 
	
		
			
				|  |  | +        ctx.fillText(
 | 
	
		
			
				|  |  | +          checkInText,
 | 
	
		
			
				|  |  | +          startX + padding * 2,
 | 
	
		
			
				|  |  | +          checkInY + lineHeight - 5,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 4. 绘制渐变时间文字
 | 
	
		
			
				|  |  | +        const timeX = startX + checkInWidth + padding;
 | 
	
		
			
				|  |  | +        const timeY = checkInY + lineHeight - 5;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 创建渐变
 | 
	
		
			
				|  |  | +        const gradient = ctx.createLinearGradient(
 | 
	
		
			
				|  |  | +          0,
 | 
	
		
			
				|  |  | +          timeY,
 | 
	
		
			
				|  |  | +          0,
 | 
	
		
			
				|  |  | +          timeY + lineHeight,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        gradient.addColorStop(0, "#ff6b00"); // 顶部颜色
 | 
	
		
			
				|  |  | +        gradient.addColorStop(1, "#ff3c00"); // 底部颜色
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        ctx.setFillStyle(gradient);
 | 
	
		
			
				|  |  | +        ctx.font = `bold ${fontSize}px sans-serif`; // 设置字体加粗
 | 
	
		
			
				|  |  | +        ctx.fillText(timeText, timeX, timeY);
 | 
	
		
			
				|  |  | +        // 5. 绘制地址信息(带左边框,支持自动换行)
 | 
	
		
			
				|  |  | +        const maxWidth = canvasWidth - startX * 2 - padding * 6; // 最大宽度
 | 
	
		
			
				|  |  | +        const addressStartY = startY + lineHeight + padding * 3 + 60;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 绘制地址文本(支持换行)
 | 
	
		
			
				|  |  | +        function drawTextWithWrap(
 | 
	
		
			
				|  |  | +          text: string,
 | 
	
		
			
				|  |  | +          x: number,
 | 
	
		
			
				|  |  | +          y: number,
 | 
	
		
			
				|  |  | +          maxWidth: number,
 | 
	
		
			
				|  |  | +          lineHeight: number,
 | 
	
		
			
				|  |  | +        ) {
 | 
	
		
			
				|  |  | +          let line = "";
 | 
	
		
			
				|  |  | +          let currentY = y;
 | 
	
		
			
				|  |  | +          for (let i = 0; i < text.length; i++) {
 | 
	
		
			
				|  |  | +            const testLine = line + text[i];
 | 
	
		
			
				|  |  | +            const metrics = ctx.measureText(testLine);
 | 
	
		
			
				|  |  | +            const testWidth = metrics.width;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (testWidth > maxWidth && i > 0) {
 | 
	
		
			
				|  |  | +              // 绘制当前行并换行
 | 
	
		
			
				|  |  | +              ctx.fillText(line, x, currentY);
 | 
	
		
			
				|  |  | +              line = text[i];
 | 
	
		
			
				|  |  | +              currentY += lineHeight;
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +              line = testLine;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          // 绘制最后一行
 | 
	
		
			
				|  |  | +          ctx.fillText(line, x, currentY);
 | 
	
		
			
				|  |  | +          return currentY; // 返回最后一行的Y坐标
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 绘制地址左边框(支持多行)
 | 
	
		
			
				|  |  | +        ctx.setStrokeStyle("#ffcc00");
 | 
	
		
			
				|  |  | +        ctx.setLineWidth(6);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 先测量地址文本需要多少行
 | 
	
		
			
				|  |  | +        const tempCtx = uni.createCanvasContext("watermarkCanvas");
 | 
	
		
			
				|  |  | +        tempCtx.setFontSize(fontSize);
 | 
	
		
			
				|  |  | +        let addressLines = [];
 | 
	
		
			
				|  |  | +        let addressLine = "";
 | 
	
		
			
				|  |  | +        for (let i = 0; i < addressText.length; i++) {
 | 
	
		
			
				|  |  | +          const testLine = addressLine + addressText[i];
 | 
	
		
			
				|  |  | +          const metrics = tempCtx.measureText(testLine);
 | 
	
		
			
				|  |  | +          const testWidth = metrics.width;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          if (testWidth > maxWidth && i > 0) {
 | 
	
		
			
				|  |  | +            addressLines.push(addressLine);
 | 
	
		
			
				|  |  | +            addressLine = addressText[i];
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            addressLine = testLine;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        addressLines.push(addressLine);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 绘制地址左边框(每行一个)
 | 
	
		
			
				|  |  | +        for (let i = 0; i < addressLines.length; i++) {
 | 
	
		
			
				|  |  | +          const lineY = addressStartY + i * lineHeight;
 | 
	
		
			
				|  |  | +          ctx.beginPath();
 | 
	
		
			
				|  |  | +          ctx.moveTo(startX + padding, lineY - lineHeight + 5);
 | 
	
		
			
				|  |  | +          ctx.lineTo(startX + padding, lineY + 5);
 | 
	
		
			
				|  |  | +          ctx.stroke();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        ctx.setFillStyle("#ffffff");
 | 
	
		
			
				|  |  | +        // 绘制地址文本(自动换行)
 | 
	
		
			
				|  |  | +        const lastAddressLineY = drawTextWithWrap(
 | 
	
		
			
				|  |  | +          addressText,
 | 
	
		
			
				|  |  | +          startX + padding * 3,
 | 
	
		
			
				|  |  | +          addressStartY,
 | 
	
		
			
				|  |  | +          maxWidth,
 | 
	
		
			
				|  |  | +          lineHeight,
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 6. 绘制日期信息(带左边框)
 | 
	
		
			
				|  |  | +        const dateY = lastAddressLineY + lineHeight;
 | 
	
		
			
				|  |  | +        // 绘制黄色左边框
 | 
	
		
			
				|  |  | +        ctx.setStrokeStyle("#ffcc00");
 | 
	
		
			
				|  |  | +        ctx.setLineWidth(6);
 | 
	
		
			
				|  |  | +        ctx.beginPath();
 | 
	
		
			
				|  |  | +        ctx.moveTo(startX + padding, dateY - lineHeight + 5);
 | 
	
		
			
				|  |  | +        ctx.lineTo(startX + padding, dateY + 5);
 | 
	
		
			
				|  |  | +        ctx.stroke();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        ctx.setFillStyle("#ffffff");
 | 
	
		
			
				|  |  | +        // 增加与左边框的距离
 | 
	
		
			
				|  |  | +        ctx.fillText(dateText, startX + padding * 3, dateY);
 | 
	
		
			
				|  |  | +        ctx.draw(false, () => {
 | 
	
		
			
				|  |  | +          setTimeout(() => {
 | 
	
		
			
				|  |  | +            uni.canvasToTempFilePath({
 | 
	
		
			
				|  |  | +              canvasId: "watermarkCanvas",
 | 
	
		
			
				|  |  | +              x: 0,
 | 
	
		
			
				|  |  | +              y: 0,
 | 
	
		
			
				|  |  | +              width: canvasWidth,
 | 
	
		
			
				|  |  | +              height: canvasHeight,
 | 
	
		
			
				|  |  | +              destWidth: canvasWidth,
 | 
	
		
			
				|  |  | +              destHeight: canvasHeight,
 | 
	
		
			
				|  |  | +              quality: 1,
 | 
	
		
			
				|  |  | +              success: (res) => {
 | 
	
		
			
				|  |  | +                console.log("水印图片生成成功:", res.tempFilePath);
 | 
	
		
			
				|  |  | +                resolve(res.tempFilePath);
 | 
	
		
			
				|  |  | +              },
 | 
	
		
			
				|  |  | +              fail: (err) => {
 | 
	
		
			
				|  |  | +                console.error("导出水印图片失败:", err);
 | 
	
		
			
				|  |  | +                reject(err);
 | 
	
		
			
				|  |  | +              },
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +          }, 100);
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      fail: (err) => {
 | 
	
		
			
				|  |  | +        console.error("获取图片信息失败:", err);
 | 
	
		
			
				|  |  | +        reject(err);
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +function getArryBuffer(filePath: string) {
 | 
	
		
			
				|  |  | +  return new Promise<ArrayBuffer>((resolve, reject) => {
 | 
	
		
			
				|  |  | +    uni.getFileSystemManager().readFile({
 | 
	
		
			
				|  |  | +      filePath: filePath,
 | 
	
		
			
				|  |  | +      encoding: "binary",
 | 
	
		
			
				|  |  | +      success(res) {
 | 
	
		
			
				|  |  | +        resolve(res.data as ArrayBuffer);
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +      fail(err) {
 | 
	
		
			
				|  |  | +        reject(err);
 | 
	
		
			
				|  |  | +      },
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +  });
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +</script>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<style scoped>
 | 
	
		
			
				|  |  | +.time-text {
 | 
	
		
			
				|  |  | +  background: linear-gradient(to bottom, #02459f, #000);
 | 
	
		
			
				|  |  | +  -webkit-background-clip: text;
 | 
	
		
			
				|  |  | +  -webkit-text-fill-color: transparent;
 | 
	
		
			
				|  |  | +  background-clip: text;
 | 
	
		
			
				|  |  | +  text-fill-color: transparent;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +</style>
 | 
	
		
			
				|  |  | +<route lang="json">
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  "name": "Camera",
 | 
	
		
			
				|  |  | +  "style": {
 | 
	
		
			
				|  |  | +    "navigationBarTitleText": "",
 | 
	
		
			
				|  |  | +    "navigationStyle": "custom",
 | 
	
		
			
				|  |  | +    "disableScroll": true
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +</route>
 |