<template>
  <div class="key-dm-leave-wrapper">
    <div v-if="pendingCount > 0" class="pushCls">
      <div class="pushIconCls">！</div>
    </div>
    <Tabs v-model="activeTab" @on-click="handleTabChange" class="tabsCls">
      <TabPane label="请假申请" name="apply">
        <div class="search-div">
          <Row type="flex" :gutter="16" class="mt8">
            <Col span="18">
              <span>开始：</span>
              <DatePicker v-model="filters.apply.start_time" type="date" placeholder="开始日期" style="width: 200px" class="mr10" />
              <span>结束：</span>
              <DatePicker v-model="filters.apply.end_time" type="date" placeholder="结束日期" style="width: 200px" class="mr10" />
              <span>状态：</span>
              <Select v-model="filters.apply.status" style="width: 200px">
                <Option v-for="opt in statusOptionsFor('apply')" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
              </Select>
            </Col>
            <Col span="6" class="text-right">
              <Button type="primary" class="mr10" @click="handleSearch('apply')">搜索</Button>
              <Button type="primary" class="mr10" @click="handleReset('apply')">重置</Button>
              <Button type="primary" @click="openApplyModal">申请</Button>
            </Col>
          </Row>
        </div>
        <Table border :loading="loading.apply" :columns="applyColumns" :data="tables.apply">
          <template slot="action" slot-scope="{ row }">
            <Button size="small" class="mr10" @click="openDetail(row)">详细</Button>
            <Button size="small" type="primary" class="mr10" @click="openEdit(row)" v-if="canEdit(row)">修改</Button>
            <Button size="small" type="success" class="mr10" @click="submit(row)" v-if="canSubmit(row)">提交</Button>
            <Poptip confirm title="确认删除？" transfer @on-ok="deleteApply(row)" v-if="canDelete(row)">
              <Button size="small" type="error" class="mr10">删除</Button>
            </Poptip>
            <Button size="small" type="primary" class="mr10" @click="refillApplyDirect(row)" v-if="row.status === -1">重新填报</Button>
            <Poptip confirm title="确认撤回？" transfer @on-ok="revoke(row)" v-if="canRevoke(row)">
              <Button size="small" type="warning" class="mr10">撤回</Button>
            </Poptip>
          </template>
        </Table>
        <Page class="page_style" :total="pagers.apply.totalRecord" :current="pagers.apply.pageNo" :page-size="pagers.apply.pageSize"
          show-total show-sizer @on-change="pageChange('apply', $event)" @on-page-size-change="sizeChange('apply', $event)" />
      </TabPane>

      <TabPane :label="'请假待审核'" name="pending">
        <Table border :loading="loading.pending" :columns="pendingColumns" :data="tables.pending">
          <template slot="action" slot-scope="{ row }">
            <div class="action-buttons">
              <Button size="small" type="primary" class="mr10" @click="openApproveModal(row)">处理</Button>
            </div>
          </template>
        </Table>
        <Page class="page_style" :total="pagers.pending.totalRecord" :current="pagers.pending.pageNo" :page-size="pagers.pending.pageSize"
          show-total show-sizer @on-change="pageChange('pending', $event)" @on-page-size-change="sizeChange('pending', $event)" />
      </TabPane>

      <TabPane label="审核历史查询" name="history">
        <div class="search-div">
          <Row type="flex" :gutter="16" align="middle">
            <Col span="18">
              <span>开始：</span>
              <DatePicker v-model="filters.history.start_time" type="date" placeholder="开始日期" style="width: 200px" class="mr10" />
              <span>结束：</span>
              <DatePicker v-model="filters.history.end_time" type="date" placeholder="结束日期" style="width: 200px" class="mr10" />
              <span>状态：</span>
              <Select v-model="filters.history.status" style="width: 200px">
                <Option v-for="opt in statusOptionsFor('history')" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
              </Select>
            </Col>
            <Col span="6" class="text-right">
              <Button type="primary" class="mr10" @click="handleSearch('history')">搜索</Button>
              <Button type="primary" class="mr10" @click="handleReset('history')">重置</Button>
            </Col>
          </Row>
        </div>
        <Table border :loading="loading.history" :columns="historyColumns" :data="tables.history">
          <template slot="action" slot-scope="{ row }">
            <Button size="small" class="mr10" @click="openHistoryDetail(row)">详细</Button>
          </template>
        </Table>
        <Page class="page_style" :total="pagers.history.totalRecord" :current="pagers.history.pageNo" :page-size="pagers.history.pageSize"
          show-total show-sizer @on-change="pageChange('history', $event)" @on-page-size-change="sizeChange('history', $event)" />
      </TabPane>

      <TabPane label="请假申请查询" name="query">
        <div class="search-div">
          <Row type="flex" :gutter="16" align="middle">
            <Col span="18">
              <span>开始：</span>
              <DatePicker v-model="filters.query.start_time" type="date" placeholder="开始日期" style="width: 200px" class="mr10" />
              <span>结束：</span>
              <DatePicker v-model="filters.query.end_time" type="date" placeholder="结束日期" style="width: 200px" class="mr10" />
              <span>状态：</span>
              <Select v-model="filters.query.status" style="width: 200px">
                <Option v-for="opt in statusOptionsFor('query')" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
              </Select>
            </Col>
            <Col span="6" class="text-right">
              <Button type="primary" class="mr10" @click="handleSearch('query')">搜索</Button>
              <Button type="primary" class="mr10" @click="handleReset('query')">重置</Button>
            </Col>
          </Row>
        </div>
        <Table border :loading="loading.query" :columns="queryColumns" :data="tables.query">
          <template slot="action" slot-scope="{ row }">
            <Button size="small" class="mr10" @click="openDetail(row)">详细</Button>
          </template>
        </Table>
        <Page class="page_style" :total="pagers.query.totalRecord" :current="pagers.query.pageNo" :page-size="pagers.query.pageSize"
          show-total show-sizer @on-change="pageChange('query', $event)" @on-page-size-change="sizeChange('query', $event)" />
      </TabPane>

      <TabPane label="请假统计" name="stats">
        <div class="search-div">
          <Row type="flex" :gutter="16" align="middle">
            <Col span="18">
              <span>开始：</span>
              <DatePicker v-model="statsStart" type="date" placeholder="开始日期" style="width: 200px" class="mr10" />
              <span>结束：</span>
              <DatePicker v-model="statsEnd" type="date" placeholder="结束日期" style="width: 200px" class="mr10" />
            </Col>
            <Col span="6" class="text-right">
              <Button type="primary" class="mr10" @click="loadStats">统计</Button>
              <Button type="primary" class="mr10" @click="resetStats">重置</Button>
            </Col>
          </Row>
        </div>
        <Table border :loading="loading.stats" :columns="statsColumns" :data="tables.stats" />
      </TabPane>
    </Tabs>

    <!-- 申请弹窗 -->
    <Modal v-model="applyModal.visible" :title="applyModal.isEdit ? '修改请假' : '新增请假'" width="700">
      <Form :model="applyModal.form" :rules="applyRules" :label-width="120" ref="applyForm">
        <FormItem label="请假类型" prop="leave_type_id">
          <Select v-model="applyModal.form.leave_type_id" style="width: 60%">
            <Option v-for="opt in leaveTypes" :key="opt.id" :value="opt.id">{{ opt.type_name }}</Option>
          </Select>
        </FormItem>
        <FormItem label="开始时间" prop="start_time">
          <DatePicker v-model="applyModal.form.start_time" type="datetime" placeholder="请选择开始时间" />
        </FormItem>
        <FormItem label="结束时间" prop="end_time">
          <DatePicker v-model="applyModal.form.end_time" type="datetime" placeholder="请选择结束时间" />
        </FormItem>
        <FormItem label="请假时长(天)" prop="duration">
          <Input v-model="applyModal.form.duration" placeholder="可手动录入或自动计算" />
        </FormItem>
        <FormItem label="请假事由" prop="reason">
          <Input type="textarea" v-model="applyModal.form.reason" :rows="3" />
        </FormItem>
        <FormItem label="紧急联系人">
          <Input v-model="applyModal.form.emergency_contact" />
        </FormItem>
        <FormItem label="紧急电话" prop="emergency_phone">
          <Input v-model="applyModal.form.emergency_phone" />
        </FormItem>
        <FormItem label="审批人" prop="approver_id">
          <Select v-if="leaveApprovalOptions.length > 0" v-model="applyModal.form.approver_id" style="width: 60%">
            <Option v-for="opt in leaveApprovalOptions" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
          </Select>
          <span v-else style="color: #999; font-style: italic;">请先进行审批人配置</span>
        </FormItem>
      </Form>
      <div slot="footer">
        <Button @click="applyModal.visible = false">取消</Button>
        <Button type="primary" :loading="applyModal.saving" @click="saveApply">保存</Button>
      </div>
    </Modal>

    <!-- 审核弹窗 -->
    <Modal v-model="approveModal.visible" title="请假审批" width="700">
      <Form :model="approveModal.record" :label-width="120">
        <FormItem label="申请人">
          <span>{{ approveModal.record.user_name }}</span>
        </FormItem>
        <FormItem label="请假类型">
          <span>{{ approveModal.record.leave_type_name }}</span>
        </FormItem>
        <FormItem label="起止时间">
          <span>{{ formatDatetime(approveModal.record.start_time) }} - {{ formatDatetime(approveModal.record.end_time) }}</span>
        </FormItem>
        <FormItem label="请假事由">
          <span>{{ approveModal.record.reason }}</span>
        </FormItem>

        <!-- 审批流程 -->
        <FormItem v-if="approveModal.approvalFlow && approveModal.approvalFlow.length > 0" label="审批流程">
          <div class="approval-flow-section">
            <div v-for="(flow, index) in approveModal.approvalFlow" :key="index" class="flow-item">
              <Row class="flow-row">
                <Col span="12" class="flow-col"><span class="label">审核人：</span><span class="value no-ellipsis" :title="flow.approver_name">{{ flow.approver_name || '-' }}</span></Col>
                <Col span="12" class="flow-col"><span class="label">时间：</span><span  class="value no-ellipsis"  :title="flow.create_time">{{ flow.create_time || '-' }}</span></Col>
              </Row>
              <Row class="flow-row">
                 <Col span="24" class="flow-col"><span class="label">转审核意见：</span><span class="value" :title="flow.transfer_reason">{{ flow.transfer_reason || '-' }}</span></Col>
              </Row>
            </div>
          </div>
        </FormItem>

        <FormItem>
          <Checkbox v-model="approveModal.transferEnabled">转审核</Checkbox>
        </FormItem>
        <FormItem v-if="approveModal.transferEnabled" label="选择审批人" prop="transferApprover">
          <Select v-if="leaveApprovalOptions.length > 0" v-model="approveModal.transferApprover" style="width: 60%" placeholder="请选择审批人">
            <Option v-for="opt in leaveApprovalOptions" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
          </Select>
          <span v-else style="color: #999; font-style: italic;">请先进行审批人配置</span>
        </FormItem>

        <FormItem :label="approveModal.transferEnabled ? '转审原因' : '审批意见'" prop="comment">
          <Input type="textarea"
            v-model="approveModal.comment"
            :rows="3"
            :placeholder="approveModal.transferEnabled ? '请输入转审原因（选填）' : '请输入审批意见（驳回时必填）'" />
        </FormItem>
      </Form>
      <div slot="footer">
        <Button @click="approveModal.visible = false">取消</Button>
        <Button type="info" @click="refillApply" v-if="approveModal.record.status === -1">重新填报</Button>
        <Button type="success" :loading="approveModal.submitting" @click="confirmApprove" v-if="!approveModal.transferEnabled">通过</Button>
        <Button type="primary" :loading="approveModal.submitting" @click="confirmTransferApprove" v-if="approveModal.transferEnabled">确认</Button>
        <Button type="error" :loading="approveModal.submitting" @click="confirmReject">驳回</Button>
      </div>
    </Modal>

    <!-- 详情弹窗（申请 + 审核记录） -->
    <Modal v-model="detailModal.visible" title="请假详情" width="720">
      <Spin fix v-if="detailModal.loading"></Spin>
      <div v-else>
        <Row :gutter="16">
          <Col span="12"><p><strong>申请人：</strong>{{ detailModal.data.user_name || '-' }}</p></Col>
          <Col span="12"><p><strong>部门：</strong>{{ detailModal.data.department_name || '-' }}</p></Col>
        </Row>
        <Row class="mt8"><Col span="24"><p><strong>请假类型：</strong>{{ detailModal.data.leave_type_name || '-' }}</p></Col></Row>
        <Row class="mt8"><Col span="24"><p><strong>起止时间：</strong>{{ formatDatetime(detailModal.data.start_time) }} - {{ formatDatetime(detailModal.data.end_time) }}</p></Col></Row>
        <Row class="mt8"><Col span="24"><p><strong>请假事由：</strong>{{ detailModal.data.reason || '-' }}</p></Col></Row>
        <Row class="mt8"><Col span="24"><p><strong>状态：</strong>{{ mapStatusText(detailModal.data.status) }}</p></Col></Row>
        <Row v-if="detailModal.data.status === -1 || String(detailModal.data.status) === '-1'" class="mt8"><Col span="24"><p><strong>驳回理由：</strong>{{ detailModal.data.back_reason || detailModal.data.backReason || '-' }}</p></Col></Row>
        <Row class="mt8"><Col span="24"><h4>审批记录</h4></Col></Row>
        <div>
          <!-- 详情表格：使用预先定义的列配置渲染 approvals -->
          <Table :data="detailModal.approvals" :columns="detailApprovalColumns" size="small" border />
        </div>
      </div>
      <div slot="footer"><Button type="primary" @click="detailModal.visible=false">关闭</Button></div>
    </Modal>

    <!-- 转审核弹窗 -->
    <Modal v-model="transferModal.visible" title="转审核" width="520">
      <Form :model="transferModal" :label-width="120">
        <FormItem label="选择审批人" prop="approver_id">
          <Select v-model="transferModal.selectedApprover" style="width: 60%">
            <Option v-for="opt in leaveApprovalOptions" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
          </Select>
        </FormItem>
        <FormItem label="备注（可选）">
          <Input type="textarea" v-model="transferModal.comment" :rows="3" placeholder="转交备注（选填）" />
        </FormItem>
      </Form>
      <div slot="footer">
        <Button @click="transferModal.visible = false">取消</Button>
        <Button type="primary" :loading="transferModal.processing" @click="confirmTransfer">确定转交</Button>
      </div>
    </Modal>
  </div>
</template>

<script>
import {
  getLeaveList,
  getLeaveListByUserId,
  saveLeaveApplication,
  submitLeaveApplication,
  revokeLeaveApplication,
  getPendingLeaveList,
  approveLeave,
  rejectLeave,
  getLeaveApprovalHistory,
  getLeaveStats,
  getLeaveById,
  getLeaveTypeList,
  deleteLeaveById,
  selectTransferApprovalList,
  transferLeaveApproval,
  getLeaveApprovalPermission
} from '@/api/key-dm'
// 注意：不单独调用后台的 isPending 接口，改为复用 getPendingLeaveList 获取 totalRecord 判定是否存在待办

export default {
  name: 'key-dm-leave-index',
  data () {
    return {
      activeTab: 'apply',
      filters: { apply: { start_time: null, end_time: null, status: '' }, history: { start_time: null, end_time: null, status: '' }, query: { start_time: null, end_time: null, status: '' } },
      // 下拉：请假审核权限可选人员
      leaveApprovalOptions: [],
      // 下拉：转审核审批人列表
      transferApprovalOptions: [],
      selectedApprovalUser: '',
      tables: { apply: [], pending: [], history: [], query: [], stats: [] },
      pagers: { apply: { pageNo: 1, pageSize: 10, totalRecord: 0 }, pending: { pageNo: 1, pageSize: 10, totalRecord: 0 }, history: { pageNo: 1, pageSize: 10, totalRecord: 0 }, query: { pageNo: 1, pageSize: 10, totalRecord: 0 } },
      loading: { apply: false, pending: false, history: false, query: false, stats: false },
      // 待审批数量徽章
      pendingCount: 0,
      leaveTypes: [],
      // 状态下拉
      statusOptions: [
        { id: '', name: '全部' },
        { id: '0', name: '未提交' },
        { id: '1', name: '待审批' },
        { id: '2', name: '审批中' },
        { id: '9', name: '审核通过' },
        { id: '-1', name: '已驳回' }
      ],
      applyColumns: [
        { type: 'index', title: '序号', width: 60, align: 'center' },
        { title: '部门', key: 'department_name', align: 'center' },
        { title: '申请人', key: 'user_name', align: 'center' },
        { title: '请假类型', key: 'leave_type_name', align: 'center' },
        { title: '起止时间', key: 'start_time', width: 300, align: 'center', render: (h, { row }) => h('span', `${row.start_time || '-'} ~ ${row.end_time || '-'}`) },
        { title: '时长', key: 'duration', align: 'center' },
        { title: '状态', key: 'status', align: 'center', render: (h, { row }) => h('span', this.mapStatusText(row.status)) },
        { title: '操作', slot: 'action', align: 'center', width: 250 }
      ],
      pendingColumns: [
        { type: 'index', title: '序号', width: 60, align: 'center' },
        { title: '申请人', key: 'user_name', align: 'center' },
        { title: '请假类型', key: 'leave_type_name', align: 'center' },
        { title: '起止时间', key: 'start_time', align: 'center' },
        { title: '提交时间', key: 'submit_time', align: 'center' },
        { title: '操作', slot: 'action', align: 'center', width: 120 }
      ],
      historyColumns: [
        { type: 'index', title: '序号', width: 60, align: 'center' },
        { title: '申请人', key: 'user_name', align: 'center' },
        { title: '请假类型', key: 'leave_type_name', align: 'center' },
        { title: '起止时间', key: 'start_time', align: 'center', render: (h, { row }) => h('span', `${row.start_time || '-'} ~ ${row.end_time || '-'}`) },
        { title: '审批完成时间', key: 'approval_complete_time', align: 'center' },
        { title: '操作', slot: 'action', align: 'center', width: 100 }
      ],
      queryColumns: [
        { type: 'index', title: '序号', width: 60, align: 'center' },
        { title: '申请人', key: 'user_name', align: 'center' },
        { title: '状态', key: 'status', align: 'center', render: (h, { row }) => h('span', this.mapStatusText(row.status)) },
        { title: '提交时间', key: 'submit_time', align: 'center' },
        { title: '操作', slot: 'action', align: 'center', width: 100 }
      ],
      statsColumns: [
        { type: 'index', title: '序号', width: 60, align: 'center' },
        { title: '部门', key: 'department_name', align: 'center' },
        { title: '人员', key: 'user_name', align: 'center' },
        { title: '已通过天数', key: 'passed_days', align: 'center' }
      ],
      detailApprovalColumns: [
        {
          title: '审批类型',
          key: 'node_type',
          align: 'center',
          render: (h, { row }) => {
            const v = row && (row.node_type || row.nodeType)
            const text = (v === 1 || String(v) === '1') ? '流程' : ((v === 2 || String(v) === '2') ? '转审' : (v || '-'))
            return h('span', text)
          }
        },
        { title: '审批人', key: 'approver_name', align: 'center' },
        { title: '时间', key: 'approver_time', align: 'center' },
        {
          title: '进度',
          key: 'status',
          align: 'center',
          render: (h, { row }) => {
            const s = row && (row.status === 0 || row.status === '0' ? 0 : (row.status || row.approval_status || ''))
            const text = (s === 1 || String(s) === '1') ? '处理中' : ((s === 2 || String(s) === '2') ? '已处理' : ((s === 0 || String(s) === '0') ? '未处理' : '-'))
            return h('span', text)
          }
        }
      ],
      applyModal: { visible: false, isEdit: false, saving: false, form: {} },
      // 控制是否在赋值时触发自动计算（true = 允许计算；false = 赋值时不计算）
      suppressDurationCalc: false,
      approveModal: { visible: false, record: {}, comment: '', submitting: false, transferEnabled: false, transferApprover: '', approvalFlow: [] },
      transferModal: { visible: false, selectedApprover: '', comment: '', processing: false, record: {} },
      detailModal: { visible: false, loading: false, data: {}, approvals: [] },
      statsStart: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
      statsEnd: new Date(),
      statsDept: '',
      // 表单验证规则
      applyRules: {
        leave_type_id: [
          { required: true, message: '请选择请假类型', trigger: 'change' }
        ],
        start_time: [
          { required: true, message: '请选择开始时间', trigger: 'change', type: 'date' }
        ],
        end_time: [
          { required: true, message: '请选择结束时间', trigger: 'change', type: 'date' }
        ],
        duration: [
          {
            validator: function (rule, value, callback) {
              const form = (this && this.applyModal && this.applyModal.form) ? this.applyModal.form : {}
              const parseToDateLocal = (v) => {
                if (v === null || v === undefined || v === '') return null
                if (typeof v === 'number') return new Date(v)
                if (typeof v === 'string') {
                  if (/^\d+$/.test(v)) return new Date(Number(v))
                  const s = v.includes('T') ? v : v.replace(' ', 'T')
                  const parsed = new Date(s)
                  return isNaN(parsed.getTime()) ? null : parsed
                }
                if (v instanceof Date) return isNaN(v.getTime()) ? null : v
                const parsed = new Date(v)
                return isNaN(parsed.getTime()) ? null : parsed
              }

              // 解析起止时间的整天差
              const sd = parseToDateLocal(form.start_time)
              const ed = parseToDateLocal(form.end_time)
              let fullDays = 0
              if (sd && ed) {
                const diff = ed.getTime() - sd.getTime()
                fullDays = Math.floor(diff / (1000 * 60 * 60 * 24))
              }

              // 如果不足整天，不校验（允许用户填写任意时长）
              if (fullDays < 1) { callback(); return }

              // 整天或以上时，必须填写且整数部分需与计算天数一致
              if (value === null || value === undefined || String(value).trim() === '') {
                callback(new Error('请填写请假时长')); return
              }

              const num = Number(value)
              if (isNaN(num) || num <= 0) { callback(new Error('请假天数必须为大于 0 的数字')); return }

              const integerPart = Math.floor(num)
              if (integerPart !== fullDays) {
                callback(new Error(`请假整数天应为 ${fullDays} 天，请确认`)); return
              }

              callback()
            },
            trigger: 'blur'
          }
        ],
        approver_id: [
          { required: true, message: '请选择审批人', trigger: 'change' }
        ],
        reason: [
          { required: true, message: '请填写请假事由', trigger: 'change' }
        ],
        emergency_phone: [
          { message: '请输入紧急电话', trigger: 'blur' },
          {
            pattern: /^(1[3-9]\d{9})|((0\d{2,3}-?)?\d{7,8})$/,
            message: '请输入有效的电话号码',
            trigger: 'blur'
          }
        ],
        // （必填性与具体数值由上面的 validator 决定）
      }
    }
  },
  created () {
    this.loadLeaveTypes()
    this.fetchList('apply')
    this.loadLeaveApprovalOptions()
    // 加载待审批计数，用于页面顶部/选项卡徽章显示
    this.loadPendingCount()
    // 将 duration 的 validator 绑定到组件实例的方法，确保内部能访问 this.applyModal.form
    this.applyRules.duration = [{ validator: this.validateDuration, trigger: 'blur' }]
  },
  watch: {
    // 监听开始时间和结束时间变化，自动计算天数
    'applyModal.form.start_time' (newVal) {
      if (this.suppressDurationCalc) return
      // 用户修改起始时间时，先清空已有时长（避免与新时间不符），再进行自动计算
      if (this.applyModal && this.applyModal.form) this.applyModal.form.duration = ''
      this.calculateDuration()
    },
    'applyModal.form.end_time' (newVal) {
      if (this.suppressDurationCalc) return
      // 用户修改结束时间时，先清空已有时长（避免与新时间不符），再进行自动计算
      if (this.applyModal && this.applyModal.form) this.applyModal.form.duration = ''
      this.calculateDuration()
    },
    // 当弹窗关闭时清理选择的审批人，确保下次打开时下拉框能正确刷新显示
    'applyModal.visible' (val) {
      if (!val && this.applyModal && this.applyModal.form) {
        // 仅在新增模式（isEdit 为 false）时清空审批人，编辑模式保留选择
        if (!this.applyModal.isEdit) {
          this.applyModal.form.approver_id = ''
        }
      }
    },
    'transferModal.visible' (val) {
      if (!val && this.transferModal) {
        this.transferModal.selectedApprover = ''
        this.transferModal.comment = ''
      }
    },
    'approveModal.visible' (val) {
      if (!val && this.approveModal) {
        // 关闭审批弹窗时清空审批流程和转审核相关字段，避免残留数据影响下次打开
        this.approveModal.approvalFlow = []
        this.approveModal.transferEnabled = false
        this.approveModal.transferApprover = ''
        this.approveModal.comment = ''
        this.approveModal.record = {}
      }
    }
  },
  methods: {
    // validator 方法：确保能通过 this 访问组件数据
    validateDuration (rule, value, callback) {
      const form = this.applyModal && this.applyModal.form ? this.applyModal.form : {}
      const parseToDateLocal = (v) => {
        if (v === null || v === undefined || v === '') return null
        if (typeof v === 'number') return new Date(v)
        if (typeof v === 'string') {
          if (/^\d+$/.test(v)) return new Date(Number(v))
          const s = v.includes('T') ? v : v.replace(' ', 'T')
          const parsed = new Date(s)
          return isNaN(parsed.getTime()) ? null : parsed
        }
        if (v instanceof Date) return isNaN(v.getTime()) ? null : v
        const parsed = new Date(v)
        return isNaN(parsed.getTime()) ? null : parsed
      }

      const sd = parseToDateLocal(form.start_time)
      const ed = parseToDateLocal(form.end_time)
      let fullDays = 0
      if (sd && ed) {
        const diff = ed.getTime() - sd.getTime()
        fullDays = Math.floor(diff / (1000 * 60 * 60 * 24))
      }

      // 如果不足整天，不校验
      if (fullDays < 1) { callback(); return }

      if (value === null || value === undefined || String(value).trim() === '') {
        callback(new Error('请填写请假时长')); return
      }

      const num = Number(value)
      if (isNaN(num) || num <= 0) { callback(new Error('请假天数必须为大于 0 的数字')); return }

      const integerPart = Math.floor(num)
      if (integerPart !== fullDays) {
        callback(new Error(`请假整数天应为 ${fullDays} 天，请确认`)); return
      }

      callback()
    },
    // 返回给定 tab 的状态下拉选项：只有 apply tab 显示“未提交(0)”，其他 tab 隐藏该选项
    statusOptionsFor (tab) {
      if (tab === 'apply') return this.statusOptions
      // 过滤掉 id 为 '0' 或 0 的未提交选项
      return (this.statusOptions || []).filter(opt => String(opt.id) !== '0')
    },
    // 计算请假天数（只计算整天，忽略零点几部分）
    calculateDuration () {
      const form = this.applyModal.form
      if (!form || !form.start_time || !form.end_time) return

      const startDate = new Date(form.start_time)
      const endDate = new Date(form.end_time)

      if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) return
      if (endDate.getTime() <= startDate.getTime()) return

      // 计算整天数（向下取整），只要整天 >=1 则自动填充为整数天
      const diffTime = endDate.getTime() - startDate.getTime()
      const fullDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))

      const existing = form.duration !== null && form.duration !== undefined && String(form.duration).trim() !== ''
      if (fullDays >= 1) {
        // 只有当表单中尚未填写时才自动填充，避免覆盖后端/用户已有值
        if (!existing) form.duration = String(fullDays)
      } else {
        // 少于整天时不自动计算，交由用户手动填写（例如半天、几小时）
        // 不覆盖已有用户填写的值
        if (!existing) form.duration = ''
      }
    },
    handleTabChange (name) {
      this.activeTab = name
      if (name === 'pending') this.fetchList('pending')
      if (name === 'history') this.fetchList('history')
      if (name === 'query') this.fetchList('query')
    },
    fetchList (tab) {
      const apiMap = { apply: getLeaveListByUserId, pending: getPendingLeaveList, history: getLeaveApprovalHistory, query: getLeaveList }
      const api = apiMap[tab]
      if (!api) return
      this.loading[tab] = true
      // 在发送请求前把 start_time/end_time 格式化为 DB 可接受的字符串（并设置为当天边界）
      // Send formatted query params directly under `params`.
      // Some HTTP wrapper will place this object into the server-side `map.params`,
      // so avoid adding an extra `params` nesting here.
      const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.prepareQueryParams(tab) })
      api(payload).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          const data = ret.data.data || {}
          // 后端可能返回时间戳（毫秒）或不同格式，统一转换为可读字符串用于前端展示
          const normalizedResults = (data.results || []).map(item => {
            if (!item) return item
            if (Object.prototype.hasOwnProperty.call(item, 'start_time')) {
              item.start_time = this.formatDisplayDatetime(item.start_time)
            }
            if (Object.prototype.hasOwnProperty.call(item, 'end_time')) {
              item.end_time = this.formatDisplayDatetime(item.end_time)
            }
            if (Object.prototype.hasOwnProperty.call(item, 'submit_time')) {
              item.submit_time = this.formatDisplayDatetime(item.submit_time)
            }
            if (Object.prototype.hasOwnProperty.call(item, 'approval_complete_time')) {
              item.approval_complete_time = this.formatDisplayDatetime(item.approval_complete_time)
            }
            // Normalize approver id/name from various backend key possibilities so the edit modal can reflect the selected approver
            item.approver_id = item.approver_id || item.approverId || item.transfer_to_id || item.transferToId || item.deal_user_id || item.dealUserId || ''
            // 仅以 approver_name / approverName 为准，不使用 transfer_to_name 回退，避免混淆审批人来源
            item.approver_name = item.approver_name || item.approverName || item.deal_user_name || item.dealUserName || item.update_name || item.update_by_name || ''
            // Normalize transfer reason from various backend key possibilities
            item.transfer_reason = item.transfer_reason || item.transferReason || item.reason || ''
            // Normalize leave_id (主表id) from various backend key possibilities
            item.leave_id = item.leave_id || item.leaveId || item.main_id || item.mainId || ''
            return item
          })
          if (tab === 'history') this.tables.history = normalizedResults
          else if (tab === 'pending') this.tables.pending = normalizedResults
          else if (tab === 'query') this.tables.query = normalizedResults
          else this.tables.apply = normalizedResults
          if (data.totalRecord !== undefined) (this.pagers[tab] || {}).totalRecord = data.totalRecord
        } else {
          this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
        }
      }).finally(() => { this.loading[tab] = false })
    },

    // 加载待审批数量（用于显示“！”）
    loadPendingCount () {
      // 复用 getPendingLeaveList，一页一条以从返回中读取 totalRecord
      // 兼容后端可能返回多种结构：{errcode:0, data:{totalRecord,...}}, {data:{results:[]}}, 直接数组，或 page 对象等
      getPendingLeaveList({ pageNo: 1, pageSize: 1, params: {} }).then(ret => {
        try {
          // 常见包装结构：{ errcode:0, data: {...} }
          if (ret && ret.data) {
            const root = ret.data
            // 如果有 errcode 且为 0，取 data 字段优先
            const payload = (root.errcode === 0 && (root.data !== undefined)) ? root.data : root
            let count = 0
            if (payload == null) {
              count = 0
            } else if (typeof payload.totalRecord === 'number') {
              count = payload.totalRecord
            } else if (typeof payload.total === 'number') {
              count = payload.total
            } else if (Array.isArray(payload.results)) {
              count = payload.results.length
            } else if (Array.isArray(payload.data)) {
              count = payload.data.length
            } else if (Array.isArray(root)) {
              count = root.length
            } else if (Array.isArray(ret.data.results)) {
              count = ret.data.results.length
            } else {
              // 兜底：如果 payload 有 totalRecord 字符串
              const tr = payload.totalRecord || payload.total
              count = tr ? Number(tr) : 0
            }
            // 只要存在 >0 即视为有待办，设置为 1 以便用 v-if 判断
            this.pendingCount = count > 0 ? 1 : 0
          } else {
            this.pendingCount = 0
          }
        } catch (e) {
          console.error('loadPendingCount parse error', e)
          this.pendingCount = 0
        }
      }).catch(err => {
        console.error('loadPendingCount error', err)
        this.pendingCount = 0
      })
    },
    /**
     * Prepare query params: normalize start_time/end_time into DB datetime strings.
     * - If start_time exists, set to 00:00:00 of that day.
     * - If end_time exists, set to 23:59:59 of that day.
     * - If only end_time is selected, set start_time to a very early date (e.g., 1970-01-01)
     *   to ensure backend queries records before the end_time.
     * Returns a shallow copy of filters[tab] with formatted strings.
     */
    prepareQueryParams (tab) {
      const raw = (this.filters && this.filters[tab]) ? Object.assign({}, this.filters[tab]) : {}
      const parseToDate = (v) => {
        if (v === null || v === undefined || v === '') return null
        if (v instanceof Date) return isNaN(v.getTime()) ? null : new Date(v)
        try {
          // Handles ISO strings and numeric timestamps as well
          const d = new Date(v)
          return isNaN(d.getTime()) ? null : d
        } catch (e) { return null }
      }

      // Handle start_time
      if (raw.start_time) {
        const sd = parseToDate(raw.start_time)
        if (sd) {
          sd.setHours(0, 0, 0, 0)
          raw.start_time = this.formatForDbDatetime(sd)
        }
      } else if (raw.end_time && !raw.start_time) {
        // If only end_time is selected, set start_time to a very early date
        // to ensure backend queries all records before the end_time
        raw.start_time = '1970-01-01 00:00:00'
      }

      // Handle end_time
      if (raw.end_time) {
        const ed = parseToDate(raw.end_time)
        if (ed) {
          ed.setHours(23, 59, 59, 0)
          raw.end_time = this.formatForDbDatetime(ed)
        }
      } else {
        // 如果用户没有选择结束时间，删除 end_time 属性，确保后端不按结束时间过滤
        delete raw.end_time
      }
      return raw
    },
    /**
     * Map numeric approval status codes to human-readable text.
     * 0 => 未提交
     * 1 => 待审批
     * 2 => 审批中
     * 9 => 审核通过
     * other => 显示原始值或通用占位
     */
    mapStatusText (status) {
      if (status === 0 || String(status) === '0') return '未提交'
      if (status === 1 || String(status) === '1') return '待审批'
      if (status === 2 || String(status) === '2') return '审批中'
      if (status === 9 || String(status) === '9') return '审核通过'
      if (status === -1 || String(status) === '-1') return '已驳回'
      if (status === null || status === undefined || status === '') return '-'
      return String(status)
    },
    handleSearch (tab) { (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
    handleReset (tab) { this.filters[tab] = {}; (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
    pageChange (tab, pageNo) { (this.pagers[tab] || {}).pageNo = pageNo; this.fetchList(tab) },
    sizeChange (tab, size) { (this.pagers[tab] || {}).pageSize = size; (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
    loadLeaveTypes () {
      getLeaveTypeList().then(res => {
        const data = res && res.data
        let results = []

        if (Array.isArray(data)) {
          results = data
        } else if (data) {
          if (Array.isArray(data.results)) results = data.results
          else if (Array.isArray(data.data)) results = data.data
          else if (data.data && Array.isArray(data.data.results)) results = data.data.results
        }

        // Map results into the shape used by the select
        this.leaveTypes = results.map(it => ({ id: it.id, type_name: it.type_name }))
        // 如果没有数据，添加默认选项
        if (this.leaveTypes.length === 0) {
          this.leaveTypes = [{ id: '', type_name: '暂无请假类型' }]
        }
      }).catch(err => {
        // 保持简洁的错误输出，避免抛出未捕获异常
        console.error('loadLeaveTypes error', err)
      })
    },
    loadLeaveApprovalOptions () {
      // 从后端获取有请假审批权限的机构/人员列表，并映射为 { id, name } 供 Select 使用
      // 如果返回数据中存在 leader===1 的条目，则默认将 selectedApprovalUser 设为该 id（优先使用 leader 标记的审批人）
      getLeaveApprovalPermission().then(res => {
        const d = res && res.data
        let results = []
        if (Array.isArray(d)) results = d
        else if (d) {
          if (Array.isArray(d.results)) results = d.results
          else if (Array.isArray(d.data)) results = d.data
          else if (d.data && Array.isArray(d.data.results)) results = d.data.results
        }

        // 先尝试找到 leader === 1 的条目（支持数字或字符串）
        let leaderId = ''
        for (let it of (results || [])) {
          if (!it) continue
          if (it.leader === 1 || it.leader === '1') {
            const rawId = it.user_id || it.id || it.userId || ''
            leaderId = rawId === null || rawId === undefined ? '' : String(rawId)
            break
          }
        }

        // 统一将 id 转为字符串，避免类型不一致导致 Select 无法正确匹配显示
        this.leaveApprovalOptions = (results || []).map(it => {
          const rawId = it.user_id || it.id || it.userId || ''
          return {
            id: rawId === null || rawId === undefined ? '' : String(rawId),
            name: it.user_name || it.name || it.userName || it.nick || ''
          }
        })

        // 如果存在 leader，默认选中该用户（不覆盖已有已选值，除非为空）
        if (leaderId) {
          // 仅在当前未有已选审批人时设置默认值，避免影响编辑场景
          if (!this.selectedApprovalUser || String(this.selectedApprovalUser).trim() === '') {
            this.selectedApprovalUser = leaderId
          }
        }
      }).catch(err => {
        console.error('loadLeaveApprovalOptions error', err)
      })
    },
    formatForDbDatetime (val) {
      if (val === null || val === undefined || val === '') return val
      let d = val
      if (typeof val === 'string') {
        // 处理带 T 的 ISO 字符串或其他可解析字符串
        d = new Date(val)
      }
      if (!(d instanceof Date) || isNaN(d.getTime())) {
        // 如果无法解析，直接返回原值（后端可进一步校验）
        return val
      }
      const pad = (n) => (n < 10 ? '0' + n : String(n))
      return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' +
        pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds())
    },
    formatDisplayDatetime (val) {
      if (val === null || val === undefined || val === '') return '-'
      // 数字（毫秒）
      if (typeof val === 'number') return this.formatForDbDatetime(new Date(val))
      // 纯数字字符串（可能是时间戳）
      if (typeof val === 'string' && /^\d+$/.test(val)) {
        try { return this.formatForDbDatetime(new Date(Number(val))) } catch (e) { return val }
      }
      // ISO 字符串带 T
      if (typeof val === 'string' && val.includes('T')) return this.formatDatetime(val)
      // 尝试解析为 Date
      const parsed = new Date(val)
      if (!isNaN(parsed.getTime())) return this.formatForDbDatetime(parsed)
      return val
    },
    openApplyModal () {
      this.applyModal.isEdit = false
      // 默认开始时间为当天（时分秒置为 0），精确到天
      const today = new Date()
      today.setHours(0, 0, 0, 0)
      // 在赋值期间禁止自动计算，直到下一个 tick 再恢复，避免渲染期间 watcher 覆盖表单值
      this.suppressDurationCalc = true
      this.applyModal.form = { user_id: '', user_name: '', leave_type_id: '', start_time: today, end_time: '', duration_unit: 2, duration: '', reason: '', emergency_contact: '', emergency_phone: '', approver_id: this.selectedApprovalUser || '' }
      this.$nextTick(() => { this.suppressDurationCalc = false })
      this.applyModal.visible = true
    },
    openEdit (row) {
      this.applyModal.isEdit = true
      // 在赋值期间禁止自动计算，直到渲染稳定后再恢复，避免覆盖已有 duration
      this.suppressDurationCalc = true
      this.applyModal.form = Object.assign({}, row)
      this.$nextTick(() => { this.suppressDurationCalc = false })
      // ensure approver_id exists on form when editing
      if (!this.applyModal.form.approver_id) this.applyModal.form.approver_id = row.approver_id || row.approverId || row.transfer_to_id || ''
      // 保证与 leaveApprovalOptions 中 id 的类型一致（字符串）
      if (this.applyModal.form.approver_id !== null && this.applyModal.form.approver_id !== undefined) {
        this.applyModal.form.approver_id = String(this.applyModal.form.approver_id)
      }
      // 编辑时需要设置leave_id为主表id，如果没有则使用id作为回退
      this.applyModal.form.leave_id = row.leave_id || row.id
      this.applyModal.visible = true
    },
    saveApply () {
      this.$refs.applyForm.validate((valid) => {
        if (!valid) {
          return false
        }

        // 验证开始/结束时间：结束时间必须大于开始时间
        const form = this.applyModal.form || {}
        const parseToDate = (v) => {
          if (v === null || v === undefined || v === '') return null
          if (typeof v === 'number') return new Date(v)
          if (typeof v === 'string') {
            if (/^\d+$/.test(v)) return new Date(Number(v))
            // 把 "YYYY-MM-DD HH:mm:ss" 之类的字符串转换为可解析的 ISO 风格（替换第一个空格为'T'）
            const s = v.includes('T') ? v : v.replace(' ', 'T')
            const parsed = new Date(s)
            return isNaN(parsed.getTime()) ? null : parsed
          }
          if (v instanceof Date) return isNaN(v.getTime()) ? null : v
          const parsed = new Date(v)
          return isNaN(parsed.getTime()) ? null : parsed
        }
        const startDate = parseToDate(form.start_time)
        const endDate = parseToDate(form.end_time)
        if (startDate && endDate) {
          if (endDate.getTime() <= startDate.getTime()) {
            this.applyModal.saving = false
            this.$Message.warning('结束时间必须大于开始时间')
            return
          }
        }

        // 拷贝表单并格式化开始/结束时间为数据库 datetime 字符串
        const payload = Object.assign({}, this.applyModal.form)
        // 同时把审批人姓名一并传回后端（优先使用表单中的 approver_id，否则使用页面选中的 selectedApprovalUser）
        const approverId = payload.approver_id || this.selectedApprovalUser || ''
        const approverOption = (this.leaveApprovalOptions || []).find(opt => opt.id === approverId)
        payload.approver_name = approverOption ? approverOption.name : (payload.approver_name || '')
        if (payload.start_time) payload.start_time = this.formatForDbDatetime(payload.start_time)
        if (payload.end_time) payload.end_time = this.formatForDbDatetime(payload.end_time)

        this.applyModal.saving = true
        saveLeaveApplication(payload).then(ret => {
          if (ret.data && ret.data.errcode === 0) {
            this.$Message.success('保存成功')
            this.applyModal.visible = false
            this.fetchList('apply')
          } else this.$Notice.error({ title: '保存失败', desc: ret.data && ret.data.errmsg })
        }).finally(() => { this.applyModal.saving = false })
      })
    },
    canEdit (row) {
      const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
      return status === 0
    },
    canSubmit (row) {
      const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
      return status === 0
    },
    canRevoke (row) {
      const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
      return status === 1
    },
    canDelete (row) {
      const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
      return status === 0
    },
    submit (row) {
      this.$Modal.confirm({ title: '确认提交',
        content: '确认提交此请假申请进入审批流程？',
        onOk: () => {
          submitLeaveApplication({ id: row.id, leave_id: row.leave_id }).then(ret => {
            if (ret.data && ret.data.errcode === 0) { this.$Message.success('提交成功'); this.fetchList('apply') } else this.$Notice.error({ title: '提交失败', desc: ret.data && ret.data.errmsg })
          })
        } })
    },
    revoke (row) {
      revokeLeaveApplication({ id: row.id, leave_id: row.leave_id }).then(ret => {
        if (ret.data && ret.data.errcode === 0) { this.$Message.success('撤回成功'); this.fetchList('apply') } else this.$Notice.error({ title: '撤回失败', desc: ret.data && ret.data.errmsg })
      })
    },
    openApproveModal (row) {
      this.approveModal.record = Object.assign({}, row)
      this.approveModal.comment = ''
      this.approveModal.transferEnabled = false
      this.approveModal.transferApprover = ''

      // 仅使用 selectTransferApprovalList 接口来获取审批流程与转审核选项（移除对 getLeaveById 的调用）
      selectTransferApprovalList({ id: row.id }).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          const data = ret.data.data || []
          // 填充转审核下拉选项
          this.transferApprovalOptions = (data || []).map(it => {
            const rawId = it.user_id || it.id || it.userId || ''
            return {
              id: rawId === null || rawId === undefined ? '' : String(rawId),
              name: it.user_name || it.name || it.userName || it.nick || ''
            }
          })
          // 直接使用该接口返回的数据作为审批流程展示（包含转审核记录）
          this.approveModal.approvalFlow = (data || []).map(item => ({
            // 仅显示后端返回的 approver_name（或 approverName），不再使用 transfer_to_name 回退
            approver_name: item.approver_name || item.approverName || '',
            transfer_reason: item.transfer_reason || '',
            create_time: this.formatDisplayDatetime(item.create_time),
            status: item.status,
            node_index: item.node_index || '1'
          }))
        } else {
          // 接口返回错误，清空数据并记录日志
          this.transferApprovalOptions = []
          this.approveModal.approvalFlow = []
          console.error('获取转审核审批人列表失败', ret.data && ret.data.errmsg)
        }
      }).catch(err => {
        // 出错时提供默认占位并清空展示
        this.transferApprovalOptions = [{ id: '', name: '请先进行审批人配置' }]
        this.approveModal.approvalFlow = []
        console.error('selectTransferApprovalList error', err)
      }).finally(() => {
        // 如果转审核已启用且未选择任何审批人，尝试选择第一个有效 id（非空字符串）
        if (this.approveModal.transferEnabled) {
          const firstValid = (this.transferApprovalOptions || []).find(opt => opt && opt.id !== null && opt.id !== undefined && String(opt.id).trim() !== '')
          if (!this.approveModal.transferApprover) {
            this.approveModal.transferApprover = firstValid ? firstValid.id : ''
          }
        }
        this.approveModal.visible = true
      })
    },
    canTransfer (row) {
      // 仅当当前为审批中（status === 2）时允许转交；可根据需求调整条件
      return row && (row.status === 2 || String(row.status) === '2')
    },
    openTransferModal (row) {
      this.transferModal.record = Object.assign({}, row)
      // 默认选择当前审批人（如果有）
      this.transferModal.selectedApprover = row.approver_id || row.approverId || row.transfer_to_id || ''
      if (this.transferModal.selectedApprover !== null && this.transferModal.selectedApprover !== undefined) {
        this.transferModal.selectedApprover = String(this.transferModal.selectedApprover)
      }
      this.transferModal.comment = ''
      this.transferModal.visible = true
    },
    confirmTransfer () {
      if (!this.transferModal.selectedApprover) {
        this.$Message.warning('请选择要转交的审批人')
        return
      }
      this.transferModal.processing = true
      const approverOption = (this.leaveApprovalOptions || []).find(opt => opt.id === this.transferModal.selectedApprover)
      console.log('this.transferModal.record', this.transferModal.record)
      const payload = {
        id: this.transferModal.record.id,
        leave_id: this.transferModal.record.leave_id,
        approver_id: this.transferModal.selectedApprover,
        approver_name: approverOption ? approverOption.name : '',
        comment: this.transferModal.comment || ''
      }
      transferLeaveApproval(payload).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          this.$Message.success('转交成功')
          this.transferModal.visible = false
          // 刷新待办列表并更新待审批计数徽章
          this.fetchList('pending')
          this.loadPendingCount()
        } else {
          this.$Notice.error({ title: '转交失败', desc: ret.data && ret.data.errmsg })
        }
      }).finally(() => { this.transferModal.processing = false })
    },
    confirmApprove () {
      this.approveModal.submitting = true
      approveLeave({ id: this.approveModal.record.id, leave_id: this.approveModal.record.leave_id || this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
        if (ret.data && ret.data.errcode === 0) { this.$Message.success('审批通过'); this.approveModal.visible = false; this.fetchList('pending'); this.loadPendingCount() } else this.$Notice.error({ title: '审批失败', desc: ret.data && ret.data.errmsg })
      }).finally(() => { this.approveModal.submitting = false })
    },
    confirmTransferApprove () {
      if (!this.approveModal.transferApprover) {
        this.$Message.warning('请选择要转交的审批人')
        return
      }
      this.approveModal.submitting = true
      const approverOption = (this.leaveApprovalOptions || []).find(opt => opt.id === this.approveModal.transferApprover)
      const payload = {
        id: this.approveModal.record.id,
        leave_id: this.approveModal.record.leave_id || this.approveModal.record.id,
        approver_id: this.approveModal.transferApprover,
        approver_name: approverOption ? approverOption.name : '',
        comment: this.approveModal.comment || ''
      }
      transferLeaveApproval(payload).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          this.$Message.success('转交成功')
          this.approveModal.visible = false
          this.fetchList('pending')
          this.loadPendingCount()
        } else {
          this.$Notice.error({ title: '转交失败', desc: ret.data && ret.data.errmsg })
        }
      }).finally(() => { this.approveModal.submitting = false })
    },
    confirmReject () {
      if (!this.approveModal.comment || !this.approveModal.comment.trim()) { this.$Message.warning('请输入驳回原因'); return }
      this.approveModal.submitting = true
      rejectLeave({ id: this.approveModal.record.id, leave_id: this.approveModal.record.leave_id || this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
        if (ret.data && ret.data.errcode === 0) { this.$Message.success('已驳回'); this.approveModal.visible = false; this.fetchList('pending'); this.loadPendingCount() } else this.$Notice.error({ title: '驳回失败', desc: ret.data && ret.data.errmsg })
      }).finally(() => { this.approveModal.submitting = false })
    },
    refillApply () {
      // 将批准记录数据反显到申请表单，清除 id 和 leave_id
      this.applyModal.isEdit = false
      // 在赋值期间禁止自动计算，直到渲染稳定后再恢复
      this.suppressDurationCalc = true
      this.applyModal.form = {
        leave_type_id: this.approveModal.record.leave_type_id || '',
        start_time: this.approveModal.record.start_time || '',
        end_time: this.approveModal.record.end_time || '',
        reason: this.approveModal.record.reason || '',
        emergency_contact: this.approveModal.record.emergency_contact || '',
        emergency_phone: this.approveModal.record.emergency_phone || '',
        approver_id: this.approveModal.record.approver_id || this.selectedApprovalUser || ''
      }
      // 不保留 id 和 leave_id，保持为空
      this.$nextTick(() => { this.suppressDurationCalc = false })
      this.applyModal.visible = true
      // 关闭审批弹窗
      this.approveModal.visible = false
    },
    refillApplyDirect (row) {
      // 直接从列表行数据进行重新填报，清除 id 和 leave_id
      this.applyModal.isEdit = false
      // 在赋值期间禁止自动计算，直到渲染稳定后再恢复
      this.suppressDurationCalc = true
      this.applyModal.form = {
        leave_type_id: row.leave_type_id || '',
        start_time: row.start_time || '',
        end_time: row.end_time || '',
        reason: row.reason || '',
        emergency_contact: row.emergency_contact || '',
        emergency_phone: row.emergency_phone || '',
        approver_id: row.approver_id || this.selectedApprovalUser || ''
      }
      // 不保留 id 和 leave_id，保持为空
      this.$nextTick(() => { this.suppressDurationCalc = false })
      this.applyModal.visible = true
    },
    openDetail (row) {
      this.detailModal.visible = true
      this.detailModal.loading = true
      getLeaveById({ id: row.leave_id || row.id }).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          const respData = ret.data.data || {}
          this.detailModal.data = respData
          // 格式化主记录时间字段（后端可能返回时间戳或字符串）
          this.detailModal.data.start_time = this.formatDisplayDatetime(respData.start_time)
          this.detailModal.data.end_time = this.formatDisplayDatetime(respData.end_time)
          this.detailModal.data.submit_time = this.formatDisplayDatetime(respData.submit_time)
          this.detailModal.data.approval_complete_time = this.formatDisplayDatetime(respData.approval_complete_time)
          // 优先使用 subTables 中的 approvalRecords（如果存在），否则使用 respData.approvals
          let approvals = []
          if (respData.subTables && Array.isArray(respData.subTables.approvalRecords)) {
            approvals = respData.subTables.approvalRecords.slice()
          } else {
            approvals = respData.approvals || []
          }
          if (!Array.isArray(approvals)) approvals = []
          approvals = approvals.map(item => {
            const nodeIndex = Number(item && (item.node_index || item.nodeIndex))
            return Object.assign({}, item, {
              node_index: isNaN(nodeIndex) ? 0 : nodeIndex,
              approver_time: this.formatDisplayDatetime(item ? item.approver_time : null),
              // 详情中也只使用 approver_name / approverName，不再回退到 transfer_to_name
              approver_name: item ? (item.approver_name || item.approverName || '') : '',
              // 保留并统一转审备注字段，方便前端在详情表格中直接展示
              transfer_reason: item ? (item.transfer_reason || item.transferReason || '') : '',
              // 将 comment 设为 comment 或 transfer_reason（优先显示 comment）
              comment: item ? (item.comment || '') : ''
            })
          })
          approvals.sort((a, b) => (a.node_index || 0) - (b.node_index || 0))
          this.detailModal.approvals = approvals
        } else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
      }).finally(() => { this.detailModal.loading = false })
    },
    openHistoryDetail (row) { this.openDetail(row) },
    renderApproveResult (h, { row }) { return row.approver_result === 1 ? '通过' : (row.approver_result === -1 ? '驳回' : '未处理') },
    formatDatetime (val) { if (!val) return '-'; return String(val).replace('T', ' ') },
    loadStats () {
      this.loading.stats = true
      const params = {
        start: this.statsStart,
        end: this.statsEnd,
        department: this.statsDept
      }
      getLeaveStats({ params }).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          console.log('ret.data.data', ret.data.data)
          this.tables.stats = ret.data.data || []
        } else {
          this.$Notice.error({ title: '统计失败', desc: ret.data && ret.data.errmsg })
        }
      }).finally(() => { this.loading.stats = false })
    },
    resetStats () {
      this.statsStart = null
      this.statsEnd = null
      this.statsDept = ''
      this.tables.stats = []
    },
    deleteApply (row) {

      deleteLeaveById({ id: row.id, leave_id: row.leave_id }).then(ret => {
        if (ret.data && ret.data.errcode === 0) {
          this.$Message.success('删除成功')
          this.fetchList('apply')
        } else {
          this.$Notice.error({ title: '删除失败', desc: ret.data && ret.data.errmsg })
        }
      })
    }
  }
}
</script>

<style scoped>
.key-dm-leave-wrapper { padding: 10px; min-height: 380px; position: relative; }
.tabsCls { min-height: 380px;}
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.text-right { text-align: right; }
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
.action-buttons { display: flex; align-items: center; gap: 8px; justify-content: center; }

/* 审批流程样式：居中显示、固定高度、超出滚动 */
.approval-flow-section { max-height: 200px; overflow-y: auto; padding-right: 8px; margin-bottom: 8px; }
.flow-item { margin-bottom: 6px; padding: 8px 12px; background: transparent; border-radius: 0; border: none; text-align: left; }
.flow-row { align-items: center; display:flex; gap:0; }
.flow-row > [class*="ivu-col"] { padding-left: 0; padding-right: 0; }
.flow-col { display: inline-flex; align-items: center; gap: 4px; color: #6b6f76; font-size: 14px; line-height: 22px; }
.flow-col .label { color: #6b7785; font-weight: 400; min-width: 0; margin-right: 2px; padding-right: 0; }
.flow-col .value { display: inline-block; max-width: 100%; overflow: visible; text-overflow: unset; white-space: normal; color: #333; }
.flow-col .value.no-ellipsis { white-space: normal; overflow: visible; text-overflow: unset; }
.flow-reason { margin: 6px 0 0; color: #333; font-size: 13px; }
.exclaim-badge {
  background: #ff4d4f !important;
  color: #fff !important;
  border-radius: 10px !important;
  padding: 0 6px !important;
  font-weight: 700 !important;
  display: inline-block !important;
  line-height: 18px !important;
  margin-left: 6px;
  min-width: 14px;
  text-align: center;
}
.tab-label { display: inline-flex; align-items: center; gap: 6px; }

/* badge positioning (like key-person-dual) */
.pushCls {
  position: absolute;
  z-index: 10;
  left: 202px;
  top: 10px;
}
.pushIconCls {
  position: relative;
  z-index: 11;
  cursor: pointer;
  color: red;
  font-weight: bold;
  font-size: 16px;
  line-height: 16px;
}
</style>
<style>
/* ensure tab label content from slot (rendered by Tabs component) is visible */
.tab-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #333 !important;
  font-weight: 500;
}
.exclaim-badge {
  background: #ff4d4f !important;
  color: #fff !important;
  border-radius: 10px !important;
  padding: 0 6px !important;
  font-weight: 700 !important;
  display: inline-block !important;
  line-height: 18px !important;
  margin-left: 6px;
  min-width: 14px;
  text-align: center;
  font-size: 12px;
}
</style>
