<template>
  <div class="hrm-page">
    <section class="panel-grid quad">
        <el-card v-for="item in requestBlocks" :key="item.key" class="metal-panel" shadow="hover">
          <div slot="header" class="panel-header">
            <span>{{ item.title }}</span>
            <div class="actions-inline">
              <el-button size="mini" type="primary" plain icon="el-icon-plus" @click="openCreate(item.key)">新增</el-button>
              <el-select
                v-if="item.statusField"
                v-model="item.query.status"
                size="mini"
                placeholder="状态"
                clearable
                style="width: 120px"
                @change="item.loader"
              >
              <el-option label="草稿" value="draft" />
              <el-option label="审批中" value="pending" />
              <el-option label="通过" value="approved" />
              <el-option label="驳回" value="rejected" />
            </el-select>
            <el-button size="mini" icon="el-icon-refresh" @click="item.loader">刷新</el-button>
          </div>
        </div>
        <el-table :data="item.list" v-loading="item.loading" height="300" stripe>
          <el-table-column label="员工" prop="empId" min-width="100" />
          <el-table-column label="类型/目的" :prop="item.typeField" min-width="120" />
          <el-table-column label="开始" prop="startTime" min-width="140">
            <template slot-scope="scope">{{ formatDate(scope.row.startTime) }}</template>
          </el-table-column>
          <el-table-column label="结束" prop="endTime" min-width="140">
            <template slot-scope="scope">{{ formatDate(scope.row.endTime) }}</template>
          </el-table-column>
          <el-table-column label="状态" prop="status" min-width="110">
            <template slot-scope="scope">
              <el-tag :type="statusType(scope.row.status)">{{ scope.row.status || '-' }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="附件" prop="accessoryApplyIds" min-width="150" show-overflow-tooltip />
          <el-table-column
            v-if="item.key === 'seal'"
            label="操作"
            min-width="220"
            fixed="right"
          >
            <template slot-scope="scope">
              <el-button size="mini" type="text" @click="approveSeal(scope.row)">通过</el-button>
              <el-button size="mini" type="text" @click="rejectSeal(scope.row)">驳回</el-button>
              <el-button size="mini" type="text" @click="cancelSeal(scope.row)">撤销</el-button>
              <el-button size="mini" type="text" @click="openStamp(scope.row)">盖章</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
    </section>

    <el-dialog
      title="盖章"
      :visible.sync="stampDialogVisible"
      width="720px"
      append-to-body
    >
      <el-row :gutter="12">
        <el-col :span="12">
          <el-form :model="stampForm" label-width="110px" size="small">
            <el-form-item label="待盖章文件" prop="targetFileUrl">
              <el-input v-model="stampForm.targetFileUrl" placeholder="PDF或图片 OSS 完整 URL" />
            </el-form-item>
            <el-form-item label="章图片" prop="stampImageUrl">
              <el-select
                v-model="stampForm.stampImageUrl"
                filterable
                clearable
                placeholder="选择章图（字典配置）"
                @change="preloadStampImage"
                style="width: 100%"
              >
                <el-option
                  v-for="opt in dict.type.hrm_stamp_image || []"
                  :key="opt.value"
                  :label="opt.label"
                  :value="opt.value"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="页码">
              <el-input-number v-model="stampForm.pageNo" :min="1" />
            </el-form-item>
            <el-form-item label="坐标/尺寸">
              <div class="readonly-row">
                <span>X: {{ stampForm.xPx || '-' }} px</span>
                <span>Y: {{ stampForm.yPx || '-' }} px</span>
              </div>
              <div class="readonly-row">
                <span>宽: {{ stampForm.widthPx || stampImageNatural.width || '-' }} px</span>
                <span>高: {{ stampForm.heightPx || stampImageNatural.height || '-' }} px</span>
              </div>
              <div class="hint-text">点击右侧预览即可定位，尺寸默认取章图原始大小</div>
            </el-form-item>
          </el-form>
        </el-col>
        <el-col :span="12">
          <div class="preview-card">
            <div class="preview-title">图形化定位（点击预览设置坐标）</div>
            <div
              class="preview-area"
              ref="previewArea"
              v-if="stampForm.targetFileUrl"
            >
              <img
                :src="stampForm.targetFileUrl"
                class="preview-img"
                @load="handlePreviewLoad"
                @click="handlePreviewClick"
                alt="preview"
              >
              <div
                v-if="marker.visible"
                class="stamp-marker"
                :style="markerStyle"
              ></div>
            </div>
            <div v-else class="preview-placeholder">请先填写待盖章文件URL（建议提供图片预览）</div>
          </div>
        </el-col>
      </el-row>
      <div slot="footer" class="dialog-footer">
        <el-button @click="stampDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="stampSubmitting" @click="submitStamp">盖章</el-button>
      </div>
    </el-dialog>

    <el-dialog
      title="新增申请"
      :visible.sync="createDialogVisible"
      width="520px"
      append-to-body
    >
      <div class="create-type-switch">
        <el-radio-group v-model="createType" size="small" @change="() => (createForm = defaultCreateForm(createType))">
          <el-radio-button label="leave">请假</el-radio-button>
          <el-radio-button label="overtime">加班</el-radio-button>
          <el-radio-button label="travel">出差</el-radio-button>
          <el-radio-button label="seal">用印</el-radio-button>
        </el-radio-group>
        <span class="hint-text" style="margin-left:8px">先选类型，再填写必填项</span>
      </div>
      <el-form ref="createFormRef" :model="createForm" :rules="currentCreateRules" label-width="110px" size="small">
        <el-form-item label="申请人">
          <el-select
            v-model="createForm.empId"
            filterable
            clearable
            placeholder="选择员工"
            style="width: 100%"
          >
            <el-option
              v-for="emp in employees"
              :key="emp.empId"
              :label="`${emp.empName || emp.empNo || emp.empId}`"
              :value="emp.empId"
            />
          </el-select>
        </el-form-item>
        <template v-if="createType === 'leave'">
          <el-form-item label="请假类型" prop="leaveType">
            <el-input v-model="createForm.leaveType" placeholder="年假/事假等" />
          </el-form-item>
          <el-form-item label="开始时间" prop="startTime">
            <el-date-picker v-model="createForm.startTime" type="datetime" placeholder="开始" style="width: 100%" />
          </el-form-item>
          <el-form-item label="结束时间" prop="endTime">
            <el-date-picker v-model="createForm.endTime" type="datetime" placeholder="结束" style="width: 100%" />
          </el-form-item>
          <el-form-item label="时长(小时)" prop="hours">
            <el-input-number v-model="createForm.hours" :min="0.5" :step="0.5" />
          </el-form-item>
          <el-form-item label="事由" prop="reason">
            <el-input v-model="createForm.reason" type="textarea" :rows="2" />
          </el-form-item>
        </template>
        <template v-else-if="createType === 'overtime'">
          <el-form-item label="加班类型" prop="otType">
            <el-input v-model="createForm.otType" placeholder="工作日/休息日等" />
          </el-form-item>
          <el-form-item label="开始时间" prop="startTime">
            <el-date-picker v-model="createForm.startTime" type="datetime" placeholder="开始" style="width: 100%" />
          </el-form-item>
          <el-form-item label="结束时间" prop="endTime">
            <el-date-picker v-model="createForm.endTime" type="datetime" placeholder="结束" style="width: 100%" />
          </el-form-item>
          <el-form-item label="时长(小时)" prop="hours">
            <el-input-number v-model="createForm.hours" :min="0.5" :step="0.5" />
          </el-form-item>
          <el-form-item label="事由" prop="reason">
            <el-input v-model="createForm.reason" type="textarea" :rows="2" />
          </el-form-item>
        </template>
        <template v-else-if="createType === 'travel'">
          <el-form-item label="开始时间" prop="startTime">
            <el-date-picker v-model="createForm.startTime" type="datetime" placeholder="开始" style="width: 100%" />
          </el-form-item>
          <el-form-item label="结束时间" prop="endTime">
            <el-date-picker v-model="createForm.endTime" type="datetime" placeholder="结束" style="width: 100%" />
          </el-form-item>
          <el-form-item label="目的地" prop="destination">
            <el-input v-model="createForm.destination" />
          </el-form-item>
          <el-form-item label="事由" prop="reason">
            <el-input v-model="createForm.reason" type="textarea" :rows="2" />
          </el-form-item>
        </template>
        <template v-else-if="createType === 'seal'">
          <el-form-item label="用印类型" prop="sealType">
            <el-input v-model="createForm.sealType" placeholder="合同章/法人章..." />
          </el-form-item>
          <el-form-item label="用途说明" prop="purpose">
            <el-input v-model="createForm.purpose" type="textarea" :rows="2" />
          </el-form-item>
          <el-form-item label="申请材料附件" prop="applyFileIds">
            <file-upload v-model="createForm.applyFileIds" :limit="5" :file-size="50" :file-type="['pdf']" />
          </el-form-item>
          <el-form-item label="需要回执">
            <el-switch v-model="createForm.receiptRequired" :active-value="1" :inactive-value="0" />
          </el-form-item>
        </template>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="createDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="createSubmitting" @click="submitCreate">提交</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import {
  listLeaveReq,
  listOvertimeReq,
  listTravelReq,
  listSealReq,
  listEmployee,
  addLeaveReq,
  addOvertimeReq,
  addTravelReq,
  addSealReq,
  approveSealReq,
  rejectSealReq,
  cancelSealReq,
  stampSealJava,
  stampSealPython
} from '@/api/hrm'
import FileUpload from '@/components/FileUpload'

export default {
  name: 'HrmRequests',
  dicts: ['hrm_stamp_image'],
  components: { FileUpload },
  data() {
    return {
      requestBlocks: [],
      stampDialogVisible: false,
      stampSubmitting: false,
      stampForm: {
        targetFileUrl: '',
        stampImageUrl: '',
        pageNo: 1,
        xPx: 0,
        yPx: 0,
        widthPx: undefined,
        heightPx: undefined
      },
      currentSeal: null,
      previewNatural: { width: 0, height: 0 },
      marker: { visible: false, x: 0, y: 0, width: 0, height: 0 },
      stampImageNatural: { width: 0, height: 0 },
      employees: [],
      createDialogVisible: false,
      createType: '',
      createSubmitting: false,
      createForm: {},
      createRules: {
        empId: [{ required: true, message: '请选择申请人', trigger: 'change' }],
        leaveType: [{ required: true, message: '请选择请假类型', trigger: 'blur' }],
        otType: [{ required: true, message: '请输入加班类型', trigger: 'blur' }],
        sealType: [{ required: true, message: '请输入用印类型', trigger: 'blur' }],
        startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
        endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
        hours: [{ required: true, message: '请输入时长', trigger: 'blur' }],
        destination: [{ required: true, message: '请输入目的地', trigger: 'blur' }],
        purpose: [{ required: true, message: '请输入用途说明', trigger: 'blur' }],
        applyFileIds: [{ required: true, message: '请上传附件', trigger: 'change' }],
        reason: [{ required: true, message: '请输入事由', trigger: 'blur' }]
      }
    }
  },
  created() {
    this.initRequests()
    this.loadEmployees()
  },
  computed: {
    applicantDisplay() {
      const user = this.$store?.state?.user || {}
      const name = user.nickName || user.userName || ''
      const id = user.userId || user.userId === 0 ? user.userId : ''
      return name ? `${name}${id ? ` (${id})` : ''}` : id || '当前登录人'
    },
    currentCreateRules() {
      // 动态裁剪规则，避免多余必填影响校验
      const keys = {
        leave: ['empId', 'leaveType', 'startTime', 'endTime', 'hours', 'reason'],
        overtime: ['empId', 'otType', 'startTime', 'endTime', 'hours', 'reason'],
        travel: ['empId', 'startTime', 'endTime', 'destination', 'reason'],
        seal: ['empId', 'sealType', 'purpose', 'applyFileIds']
      }[this.createType] || []
      const picked = {}
      keys.forEach(k => {
        if (this.createRules[k]) picked[k] = this.createRules[k]
      })
      return picked
    }
  },
  methods: {
    statusType(status) {
      if (!status) return 'info'
      const map = { pending: 'warning', draft: 'info', approved: 'success', rejected: 'danger' }
      return map[status] || 'info'
    },
    formatDate(val) {
      if (!val) return ''
      const d = new Date(val)
      const p = n => (n < 10 ? `0${n}` : n)
      return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
    },
    initRequests() {
      this.requestBlocks = [
        { key: 'leave', title: '请假单', typeField: 'leaveType', statusField: 'status', query: { pageNum: 1, pageSize: 10, status: undefined }, list: [], loading: false, loader: this.loadLeaveReq },
        { key: 'overtime', title: '加班单', typeField: 'overtimeType', statusField: 'status', query: { pageNum: 1, pageSize: 10, status: undefined }, list: [], loading: false, loader: this.loadOvertimeReq },
        { key: 'travel', title: '出差单', typeField: 'travelType', statusField: 'status', query: { pageNum: 1, pageSize: 10, status: undefined }, list: [], loading: false, loader: this.loadTravelReq },
        { key: 'seal', title: '用印申请', typeField: 'sealType', statusField: 'status', query: { pageNum: 1, pageSize: 10, status: undefined }, list: [], loading: false, loader: this.loadSealReq }
      ]
      this.requestBlocks.forEach(b => b.loader())
    },
    loadLeaveReq() {
      const block = this.requestBlocks.find(i => i.key === 'leave')
      block.loading = true
      listLeaveReq(block.query)
        .then(res => {
          block.list = res.rows || []
        })
        .finally(() => {
          block.loading = false
        })
    },
    loadOvertimeReq() {
      const block = this.requestBlocks.find(i => i.key === 'overtime')
      block.loading = true
      listOvertimeReq(block.query)
        .then(res => {
          block.list = res.rows || []
        })
        .finally(() => {
          block.loading = false
        })
    },
    loadTravelReq() {
      const block = this.requestBlocks.find(i => i.key === 'travel')
      block.loading = true
      listTravelReq(block.query)
        .then(res => {
          block.list = res.rows || []
        })
        .finally(() => {
          block.loading = false
        })
    },
    loadSealReq() {
      const block = this.requestBlocks.find(i => i.key === 'seal')
      block.loading = true
      listSealReq(block.query)
        .then(res => {
          block.list = res.rows || []
        })
        .finally(() => {
          block.loading = false
        })
    },
    approveSeal(row) {
      approveSealReq(row.bizId).then(() => {
        this.$message.success('已通过')
        this.loadSealReq()
      })
    },
    rejectSeal(row) {
      rejectSealReq(row.bizId).then(() => {
        this.$message.success('已驳回')
        this.loadSealReq()
      })
    },
    cancelSeal(row) {
      cancelSealReq(row.bizId).then(() => {
        this.$message.success('已撤销')
        this.loadSealReq()
      })
    },
    openStamp(row) {
      this.currentSeal = row
      this.stampDialogVisible = true
      this.marker.visible = false
    },
    submitStamp() {
      if (!this.currentSeal) return
      this.stampSubmitting = true
      stampSealJava(this.currentSeal.bizId, this.stampForm)
        .then(() => {
          this.$message.success('盖章指令已提交')
          this.stampDialogVisible = false
          this.loadSealReq()
        })
        .finally(() => {
          this.stampSubmitting = false
        })
    },
    handlePreviewLoad(e) {
      const img = e.target
      this.previewNatural = { width: img.naturalWidth, height: img.naturalHeight }
      this.updateMarkerStyle()
    },
    handlePreviewClick(event) {
      if (!this.previewNatural.width || !this.previewNatural.height) return
      const rect = this.$refs.previewArea.getBoundingClientRect()
      const displayWidth = rect.width
      const displayHeight = rect.height
      const clickX = event.clientX - rect.left
      const clickY = event.clientY - rect.top
      const xRatio = clickX / displayWidth
      const yRatio = clickY / displayHeight
      const xPx = Math.round(xRatio * this.previewNatural.width)
      // 注意 PDF 坐标原点左下，这里预览原点左上，需要转换
      const yPx = Math.round((1 - yRatio) * this.previewNatural.height)
      this.stampForm.xPx = xPx
      this.stampForm.yPx = yPx
      // 默认尺寸取章图天然尺寸
      if (this.stampImageNatural.width) {
        this.stampForm.widthPx = this.stampForm.widthPx || this.stampImageNatural.width
        this.stampForm.heightPx = this.stampForm.heightPx || this.stampImageNatural.height
      }
      this.updateMarkerStyle()
    },
    preloadStampImage() {
      if (!this.stampForm.stampImageUrl) return
      const img = new Image()
      img.onload = () => {
        this.stampImageNatural = { width: img.width, height: img.height }
      }
      img.src = this.stampForm.stampImageUrl
    },
    updateMarkerStyle() {
      if (!this.previewNatural.width || !this.previewNatural.height) return
      const rect = this.$refs.previewArea?.getBoundingClientRect?.()
      if (!rect) return
      const displayWidth = rect.width
      const displayHeight = rect.height
      const xRatio = this.stampForm.xPx / this.previewNatural.width
      const yRatio = 1 - this.stampForm.yPx / this.previewNatural.height
      const wRatio = (this.stampForm.widthPx || this.stampImageNatural.width || 0) / this.previewNatural.width
      const hRatio = (this.stampForm.heightPx || this.stampImageNatural.height || 0) / this.previewNatural.height
      this.marker = {
        visible: true,
        x: xRatio * displayWidth,
        y: yRatio * displayHeight,
        width: wRatio * displayWidth,
        height: hRatio * displayHeight
      }
    },
    openCreate(type) {
      this.createType = type
      this.createDialogVisible = true
      this.createForm = this.defaultCreateForm(type)
      this.$nextTick(() => this.$refs.createFormRef && this.$refs.createFormRef.clearValidate())
    },
    defaultCreateForm(type) {
      const common = { empId: '', remark: '' }
      if (type === 'leave') {
        return { ...common, leaveType: '', startTime: '', endTime: '', hours: 0, reason: '' }
      }
      if (type === 'overtime') {
        return { ...common, otType: '', startTime: '', endTime: '', hours: 0, reason: '' }
      }
      if (type === 'travel') {
        return { ...common, startTime: '', endTime: '', destination: '', reason: '' }
      }
      if (type === 'seal') {
        return { ...common, sealType: '', purpose: '', applyFileIds: '', receiptRequired: 0 }
      }
      return common
    },
    loadEmployees() {
      listEmployee({ pageNum: 1, pageSize: 500 }).then(res => {
        this.employees = res.rows || res.data || []
      })
    },
    submitCreate() {
      if (!this.createType) return
      const apiMap = {
        leave: addLeaveReq,
        overtime: addOvertimeReq,
        travel: addTravelReq,
        seal: addSealReq
      }
      const fn = apiMap[this.createType]
      if (!fn) return
      // 默认申请人取当前登录用户
      const payload = { ...this.createForm }
      const userId = this.$store?.state?.user?.userId
      if (userId) {
        payload.empId = userId
      } else {
        this.$message.error('未获取到当前登录用户，无法提交')
        return
      }
      this.$refs.createFormRef.validate(valid => {
        if (!valid) return
        this.createSubmitting = true
        fn(payload)
          .then(() => {
            this.$message.success('提交成功')
            this.createDialogVisible = false
            const block = this.requestBlocks.find(i => i.key === this.createType)
            block && block.loader()
          })
          .finally(() => {
            this.createSubmitting = false
          })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.hrm-page {
  padding: 16px 20px 32px;
  background: #f8f9fb;
}
.panel-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}
.metal-panel {
  border: 1px solid #d7d9df;
  border-radius: 10px;
  background: #fff;
}
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 600;
  color: #303133;
}
.actions-inline {
  display: flex;
  gap: 8px;
  align-items: center;
}
.coord-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.preview-card {
  border: 1px dashed #e6e8ed;
  border-radius: 8px;
  padding: 8px;
  min-height: 340px;
}
.preview-title {
  font-weight: 600;
  margin-bottom: 8px;
}
.preview-area {
  position: relative;
  width: 100%;
  height: 280px;
  overflow: hidden;
  background: #f5f7fa;
  border-radius: 6px;
  border: 1px solid #ebeef5;
}
.preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  cursor: crosshair;
}
.preview-placeholder {
  color: #a0a3ad;
  font-size: 13px;
  padding: 12px;
  background: #fafafa;
  border: 1px dashed #ebeef5;
  border-radius: 6px;
}
.stamp-marker {
  position: absolute;
  border: 2px dashed #409eff;
  background: rgba(64, 158, 255, 0.08);
  pointer-events: none;
  transform: translate(-50%, -50%);
}
@media (max-width: 1200px) {
  .panel-grid {
    grid-template-columns: 1fr;
  }
}
</style>
