<template>
  <div class="pdf-stamper">
    <div class="toolbar">
      <el-button size="mini" @click="prevPage" :disabled="pageNo <= 1">上一页</el-button>
      <span class="page">第 {{ pageNo }} / {{ pageCount }} 页</span>
      <el-button size="mini" @click="nextPage" :disabled="pageNo >= pageCount">下一页</el-button>
      <span class="split"></span>
      <el-input-number size="mini" v-model="stampWidth" :min="10" :max="600" :step="5" />
      <span class="wh">宽</span>
      <el-input-number size="mini" v-model="stampHeight" :min="10" :max="600" :step="5" />
      <span class="wh">高</span>
      <span class="hint">在 PDF 上单击选择盖章位置（以页面左下为原点）</span>
    </div>

    <div class="canvas-wrap" ref="wrap" @click="onClick">
      <canvas ref="canvas"></canvas>
      <div
        v-if="hasStamp"
        class="stamp-box"
        :style="stampBoxStyle"
        title="盖章位置预览"
      ></div>
    </div>
  </div>
</template>

<script>
import * as pdfjsLib from 'pdfjs-dist'
// worker
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.js'

pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker

export default {
  name: 'PdfStamper',
  props: {
    /** PDF 的可访问 URL（OSS url） */
    pdfUrl: {
      type: String,
      required: true
    },
    /** 初始页 */
    initialPage: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      loading: false,
      pdf: null,
      pageCount: 1,
      pageNo: 1,
      // 渲染缩放
      scale: 1,
      // pdf 页面像素尺寸(渲染后)
      viewportWidth: 0,
      viewportHeight: 0,
      // 盖章尺寸（像素，基于当前 scale 渲染像素）
      stampWidth: 160,
      stampHeight: 160,
      // 盖章位置（以 canvas 左上为原点的像素）
      stampXTopLeft: 0,
      stampYTopLeft: 0,
      hasStamp: false
    }
  },
  watch: {
    pdfUrl: {
      immediate: true,
      handler() {
        this.init()
      }
    },
    pageNo() {
      this.renderPage()
    },
    stampWidth() {
      this.emitStamp()
    },
    stampHeight() {
      this.emitStamp()
    }
  },
  methods: {
    async init() {
      if (!this.pdfUrl) return
      this.loading = true
      this.hasStamp = false
      try {
        this.pdf = await pdfjsLib.getDocument({ url: this.pdfUrl }).promise
        this.pageCount = this.pdf.numPages || 1
        this.pageNo = Math.min(Math.max(this.initialPage, 1), this.pageCount)
        await this.$nextTick()
        await this.renderPage()
      } catch (e) {
        console.error('PDF加载失败', e)
        this.$emit('error', e)
      } finally {
        this.loading = false
      }
    },

    async renderPage() {
      if (!this.pdf) return
      const page = await this.pdf.getPage(this.pageNo)
      const wrap = this.$refs.wrap
      const canvas = this.$refs.canvas
      const ctx = canvas.getContext('2d')

      // 自适应容器宽度
      const unscaledViewport = page.getViewport({ scale: 1 })
      const maxWidth = (wrap && wrap.clientWidth) ? wrap.clientWidth : 900
      this.scale = maxWidth / unscaledViewport.width
      const viewport = page.getViewport({ scale: this.scale })

      canvas.width = viewport.width
      canvas.height = viewport.height
      this.viewportWidth = viewport.width
      this.viewportHeight = viewport.height

      // 清空
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      await page.render({ canvasContext: ctx, viewport }).promise

      // 切页后清空盖章框（避免误用上一页坐标）
      this.hasStamp = false
      this.emitStamp()
    },

    prevPage() {
      if (this.pageNo > 1) this.pageNo -= 1
    },
    nextPage() {
      if (this.pageNo < this.pageCount) this.pageNo += 1
    },

    onClick(e) {
      const rect = this.$refs.canvas.getBoundingClientRect()
      const x = e.clientX - rect.left
      const y = e.clientY - rect.top

      // 让点击点成为盖章框中心
      this.stampXTopLeft = Math.max(0, Math.min(x - this.stampWidth / 2, this.viewportWidth - this.stampWidth))
      this.stampYTopLeft = Math.max(0, Math.min(y - this.stampHeight / 2, this.viewportHeight - this.stampHeight))
      this.hasStamp = true
      this.emitStamp()
    },

    emitStamp() {
      // 输出给父组件的坐标必须是 PDFBox 期望的坐标：左下为原点
      // 当前 stampXTopLeft / stampYTopLeft 是左上原点
      const xPx = this.stampXTopLeft
      const yPx = this.viewportHeight - this.stampYTopLeft - this.stampHeight

      this.$emit('change', {
        pageNo: this.pageNo,
        xPx: Math.round(xPx),
        yPx: Math.round(yPx),
        // 位置换算需要：当前渲染后的 viewport 尺寸（像素）
        viewportWidth: Math.round(this.viewportWidth),
        viewportHeight: Math.round(this.viewportHeight),
        // 方案B：后端严格用原图大小，这里不再强依赖 widthPx/heightPx（可留给预览）
        widthPx: Math.round(this.stampWidth),
        heightPx: Math.round(this.stampHeight),
        ready: this.hasStamp
      })
    }
  },
  computed: {
    stampBoxStyle() {
      return {
        width: this.stampWidth + 'px',
        height: this.stampHeight + 'px',
        left: this.stampXTopLeft + 'px',
        top: this.stampYTopLeft + 'px'
      }
    }
  }
}
</script>

<style scoped>
.pdf-stamper {
  width: 100%;
}
.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.page {
  font-weight: 700;
  color: #2b2f36;
}
.split {
  width: 1px;
  height: 16px;
  background: #e6e8ed;
  margin: 0 6px;
}
.wh {
  font-size: 12px;
  color: #606266;
}
.hint {
  font-size: 12px;
  color: #8a8f99;
}
.canvas-wrap {
  position: relative;
  border: 1px solid #e6e8ed;
  border-radius: 8px;
  overflow: auto;
  background: #fff;
}
canvas {
  display: block;
}
.stamp-box {
  position: absolute;
  border: 2px dashed #e6a23c;
  box-sizing: border-box;
  pointer-events: none;
  background: rgba(230, 162, 60, 0.08);
}
</style>

