张诺
6 小时以前 cf7143ce23bda4368634cbee9464796c032d6201
src/views/equipmentManagement/ledger/index.vue
@@ -70,8 +70,8 @@
    </div>
    <Modal ref="modalRef" @success="getTableData"></Modal>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px" draggable>
      <div style="text-align:center;">
        <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
      <div class="qr-dialog-content">
        <img :src="qrCodeUrl" alt="二维码" style="width:220px;height:auto;" />
        <div style="margin:10px 0;">
          <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
        </div>
@@ -247,10 +247,83 @@
    });
};
const loadImage = (src) =>
  new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = (err) => reject(err);
    img.src = src;
  });
const wrapTextLines = (ctx, text, maxWidth) => {
  const safeText = String(text || "").trim();
  if (!safeText) return [];
  const lines = [];
  let currentLine = "";
  for (const char of safeText) {
    const nextLine = currentLine + char;
    if (ctx.measureText(nextLine).width > maxWidth && currentLine) {
      lines.push(currentLine);
      currentLine = char;
    } else {
      currentLine = nextLine;
    }
  }
  if (currentLine) {
    lines.push(currentLine);
  }
  return lines;
};
const buildQrImageWithTitle = async (qrContent, deviceName) => {
  const baseQrDataUrl = await QRCode.toDataURL(qrContent, {
    width: 220,
    margin: 2,
  });
  const qrImage = await loadImage(baseQrDataUrl);
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  if (!ctx) return baseQrDataUrl;
  const titleText = deviceName || "未命名设备";
  const fontSize = 30;
  const sidePadding = 16;
  const topPadding = 14;
  const titleBottomGap = 10;
  const bottomPadding = 14;
  ctx.font = `${fontSize}px sans-serif`;
  const textMaxWidth = qrImage.width + 40;
  const titleLines = wrapTextLines(ctx, titleText, textMaxWidth);
  const lineHeight = 22;
  const titleHeight = titleLines.length * lineHeight;
  canvas.width = Math.max(qrImage.width + sidePadding * 2, textMaxWidth + sidePadding * 2);
  canvas.height = topPadding + titleHeight + titleBottomGap + qrImage.height + bottomPadding;
  ctx.fillStyle = "#ffffff";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.font = `${fontSize}px sans-serif`;
  ctx.fillStyle = "#303133";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  titleLines.forEach((line, index) => {
    const y = topPadding + lineHeight * index + lineHeight / 2;
    ctx.fillText(line, canvas.width / 2, y);
  });
  const qrX = (canvas.width - qrImage.width) / 2;
  const qrY = topPadding + titleHeight + titleBottomGap;
  ctx.drawImage(qrImage, qrX, qrY, qrImage.width, qrImage.height);
  return canvas.toDataURL("image/png");
};
const showQRCode = async (row) => {
  // 直接使用URL,不要用JSON.stringify包装
  const qrContent = proxy.javaApi + '/device-info?deviceId=' + row.id;
  qrCodeUrl.value = await QRCode.toDataURL(qrContent);
  qrCodeUrl.value = await buildQrImageWithTitle(qrContent, row.deviceName);
  qrRowData.value = row;
  qrDialogVisible.value = true;
};
@@ -276,4 +349,16 @@
  justify-content: space-between;
  margin-bottom: 10px;
}
.qr-dialog-content {
  text-align: center;
}
.qr-device-name {
  margin-bottom: 8px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  word-break: break-all;
}
</style>