张诺
昨天 195770f92f7d739ffba6447fdbf3a3d5b9e009fa
src/views/equipmentManagement/ledger/index.vue
@@ -7,7 +7,6 @@
          style="width: 240px"
          placeholder="请输入设备名称"
          clearable
          :prefix-icon="Search"
          @change="getTableData"
        />
      </el-form-item>
@@ -17,7 +16,6 @@
            style="width: 240px"
            placeholder="请输入规格型号"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -27,17 +25,6 @@
            style="width: 240px"
            placeholder="请输入供应商"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
      <el-form-item label="单位">
        <el-input
            v-model="filters.unit"
            style="width: 240px"
            placeholder="请输入单位"
            clearable
            :prefix-icon="Search"
            @change="getTableData"
        />
      </el-form-item>
@@ -79,22 +66,17 @@
        @selection-change="handleSelectionChange"
        @pagination="changePage"
      >
        <template #operation="{ row }">
          <el-button type="primary" text @click="edit(row.id)" icon="editPen">
            编辑
          </el-button>
          <el-button
            type="danger"
            text
            icon="delete"
            @click="deleteRow(row.id)"
          >
            删除
          </el-button>
        </template>
      </PIMTable>
    </div>
    <Modal ref="modalRef" @success="getTableData"></Modal>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px" draggable>
      <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>
      </div>
    </el-dialog>
  </div>
</template>
@@ -106,6 +88,8 @@
import Modal from "./Modal.vue";
import { ElMessageBox, ElMessage } from "element-plus";
import dayjs from "dayjs";
import QRCode from "qrcode";
import { ref } from "vue";
defineOptions({
  name: "设备台账",
@@ -115,6 +99,10 @@
const multipleList = ref([]);
const { proxy } = getCurrentInstance();
const modalRef = ref();
const qrDialogVisible = ref(false);
const qrCodeUrl = ref("");
const qrRowData = ref(null);
const {
  filters,
  columns,
@@ -126,72 +114,78 @@
} = usePaginationApi(
  getLedgerPage,
  {
    searchText: undefined,
    deviceName: undefined,
    deviceModel: undefined,
    supplierName: undefined,
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
  [
    {
      label: "设备名称",
      align: "center",
      prop: "deviceName",
    },
    {
      label: "规格型号",
      align: "center",
      prop: "deviceModel",
    },
    {
      label: "设备品牌",
      prop: "deviceBrand",
    },
    {
      label: "设备类型",
      prop: "type",
    },
    {
      label: "供应商",
      align: "center",
      prop: "supplierName",
    },
    {
      label: "单位",
      align: "center",
      prop: "unit",
      label: "存放位置",
      prop: "storageLocation",
    },
    {
      label: "数量",
      align: "center",
      prop: "number",
    },
    {
      label: "含税单价",
      align: "center",
      prop: "taxIncludingPriceUnit",
    },
    {
      label: "含税总价",
      align: "center",
      prop: "taxIncludingPriceTotal",
    },
    {
      label: "税率",
      align: "center",
      prop: "taxRate",
    },
    {
      label: "不含税总价",
      align: "center",
      prop: "unTaxIncludingPriceTotal",
    },
    {
      label: "录入人",
      align: "center",
      prop: "createUser",
    },
    {
      label: "录入日期",
      align: "center",
      prop: "createTime",
      formatData: (v) => {
        if (!v) return '';
        // 如果包含时分秒,只取日期部分
        if (v.includes(' ')) {
          return v.split(' ')[0];
        }
        return v;
      },
    },
    {
      fixed: "right",
      label: "操作",
      dataType: "slot",
      slot: "operation",
      align: "center",
      width: "200px",
    },
      {
         dataType: "action",
         label: "操作",
         align: "center",
         fixed: 'right',
         width: 150,
         operation: [
            {
               name: "编辑",
               clickFun: (row) => {
                  edit(row.id)
               },
            },
            {
               name: "生成二维码",
               clickFun: (row) => {
                  showQRCode(row)
               },
            },
         ],
      },
  ]
);
@@ -253,13 +247,95 @@
    });
};
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 buildQrImageWithTitle(qrContent, row.deviceName);
  qrRowData.value = row;
  qrDialogVisible.value = true;
};
const downloadQRCode = () => {
  const a = document.createElement("a");
  a.href = qrCodeUrl.value;
  a.download = `${qrRowData.value.deviceName || "二维码"}.png`;
  a.click();
};
onMounted(() => {
  filters.entryDate = [
    dayjs().format("YYYY-MM-DD"),
    dayjs().add(1, "day").format("YYYY-MM-DD"),
  ]
  filters.entryDateStart = dayjs().format("YYYY-MM-DD")
  filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD")
  getTableData();
});
</script>
@@ -273,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>