zouyu
2026-05-26 b8da78824e4c67632abb65302f01ccf74d5a1096
src/views/safetyManagement/trainingManage/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" type="border-card">
    <el-tabs v-model="activeTab" type="border-card" @tab-change="handleTabChange">
      <el-tab-pane label="培训资料" name="materials">
        <el-form :model="materialFilters" :inline="true">
          <el-form-item label="资料名称">
@@ -8,10 +8,17 @@
          </el-form-item>
          <el-form-item label="资料类型">
            <el-select v-model="materialFilters.type" placeholder="请选择" clearable style="width: 150px">
              <el-option label="制度" value="system" />
              <el-option label="课件" value="courseware" />
              <el-option label="视频" value="video" />
              <el-option label="案例" value="case" />
              <el-option label="PDF" value="PDF" />
              <el-option label="制度" value="制度" />
              <el-option label="课件" value="课件" />
              <el-option label="视频" value="视频" />
              <el-option label="案例" value="案例" />
            </el-select>
          </el-form-item>
          <el-form-item label="状态">
            <el-select v-model="materialFilters.status" placeholder="请选择" clearable style="width: 150px">
              <el-option label="启用" :value="1" />
              <el-option label="停用" :value="0" />
            </el-select>
          </el-form-item>
          <el-form-item>
@@ -21,9 +28,9 @@
        </el-form>
        <div class="table_list">
          <div class="actions">
            <el-button type="primary" @click="uploadMaterial" icon="Upload">上传资料</el-button>
            <el-button type="primary" @click="openMaterialDialog" icon="Upload">上传资料</el-button>
          </div>
          <PIMTable :column="materialColumns" :tableData="materialList" :page="materialPage" @pagination="changeMaterialPage" />
          <PIMTable :column="materialColumns" :tableData="materialList" :page="materialPage" @pagination="changeMaterialPage" :tableLoading="materialLoading" />
        </div>
      </el-tab-pane>
@@ -35,6 +42,20 @@
          <el-form-item label="岗位">
            <el-input v-model="planFilters.post" placeholder="请输入岗位" clearable style="width: 200px" />
          </el-form-item>
          <el-form-item label="培训等级">
            <el-select v-model="planFilters.level" placeholder="请选择" clearable style="width: 150px">
              <el-option label="一级" value="一级" />
              <el-option label="二级" value="二级" />
              <el-option label="三级" value="三级" />
            </el-select>
          </el-form-item>
          <el-form-item label="状态">
            <el-select v-model="planFilters.status" placeholder="请选择" clearable style="width: 150px">
              <el-option label="待执行" :value="0" />
              <el-option label="执行中" :value="1" />
              <el-option label="已完成" :value="2" />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="getPlanData">搜索</el-button>
            <el-button @click="resetPlanFilters">重置</el-button>
@@ -42,9 +63,9 @@
        </el-form>
        <div class="table_list">
          <div class="actions">
            <el-button type="primary" @click="addPlan" icon="Plus">制定计划</el-button>
            <el-button type="primary" @click="openPlanDialog" icon="Plus">制定计划</el-button>
          </div>
          <PIMTable :column="planColumns" :tableData="planList" :page="planPage" @pagination="changePlanPage" />
          <PIMTable :column="planColumns" :tableData="planList" :page="planPage" @pagination="changePlanPage" :tableLoading="planLoading" />
        </div>
      </el-tab-pane>
@@ -53,22 +74,232 @@
          <el-form-item label="员工姓名">
            <el-input v-model="recordFilters.employeeName" placeholder="请输入员工姓名" clearable style="width: 200px" />
          </el-form-item>
          <el-form-item label="状态">
            <el-select v-model="recordFilters.status" placeholder="请选择" clearable style="width: 150px">
              <el-option label="已完成" :value="1" />
              <el-option label="未完成" :value="0" />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="getRecordData">搜索</el-button>
            <el-button @click="resetRecordFilters">重置</el-button>
            <el-button type="primary" @click="openRecordDialog" icon="Plus">新增</el-button>
            <el-button type="success" @click="exportRecords" icon="Download">导出</el-button>
          </el-form-item>
        </el-form>
        <div class="table_list">
          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" />
          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" :tableLoading="recordLoading" />
        </div>
      </el-tab-pane>
    </el-tabs>
    <!-- 培训资料弹窗 -->
    <el-dialog :title="materialDialog.title" v-model="materialDialog.visible" width="600px" append-to-body>
      <el-form ref="materialFormRef" :model="materialForm" :rules="materialRules" label-width="100px">
        <el-form-item label="资料名称" prop="name">
          <el-input v-model="materialForm.name" placeholder="请输入资料名称" />
        </el-form-item>
        <el-form-item label="资料类型" prop="type">
          <el-select v-model="materialForm.type" placeholder="请选择资料类型" style="width: 100%">
            <el-option label="PDF" value="PDF" />
            <el-option label="制度" value="制度" />
            <el-option label="课件" value="课件" />
            <el-option label="视频" value="视频" />
            <el-option label="案例" value="案例" />
          </el-select>
        </el-form-item>
        <el-form-item label="文件上传" prop="fileUrl" v-if="!materialForm.id">
          <el-upload
            ref="uploadRef"
            action="/dev-api/common/upload"
            :headers="uploadHeaders"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :before-upload="beforeUpload"
            :limit="1"
          >
            <el-button type="primary">选择文件</el-button>
            <template #tip>
              <div class="el-upload__tip">支持 PDF、Word、视频等格式</div>
            </template>
          </el-upload>
        </el-form-item>
        <el-form-item label="文件大小" prop="fileSize" v-if="materialForm.fileSize">
          <el-input v-model="materialForm.fileSize" disabled />
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="materialForm.status">
            <el-radio :value="1">启用</el-radio>
            <el-radio :value="0">停用</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="materialForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="materialDialog.visible = false">取 消</el-button>
        <el-button type="primary" @click="submitMaterialForm" :loading="materialDialog.loading">确 定</el-button>
      </template>
    </el-dialog>
    <!-- 培训计划弹窗 -->
    <el-dialog :title="planDialog.title" v-model="planDialog.visible" width="700px" append-to-body>
      <el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="计划年度" prop="year">
              <el-date-picker v-model="planForm.year" type="year" placeholder="选择年度" value-format="YYYY" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="适用岗位" prop="post">
              <el-input v-model="planForm.post" placeholder="请输入适用岗位" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="培训等级" prop="level">
              <el-select v-model="planForm.level" placeholder="请选择培训等级" style="width: 100%">
                <el-option label="一级" value="一级" />
                <el-option label="二级" value="二级" />
                <el-option label="三级" value="三级" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="计划课时" prop="hours">
              <el-input-number v-model="planForm.hours" :min="1" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="培训内容" prop="content">
          <el-input v-model="planForm.content" type="textarea" :rows="3" placeholder="请输入培训内容" />
        </el-form-item>
        <el-form-item label="培训资料" prop="materialIds">
          <el-select v-model="planForm.materialIds" multiple placeholder="请选择培训资料" style="width: 100%">
            <el-option v-for="item in materialOptions" :key="item.id" :label="item.name" :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="planForm.status">
            <el-radio :value="0">待执行</el-radio>
            <el-radio :value="1">执行中</el-radio>
            <el-radio :value="2">已完成</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="planForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="planDialog.visible = false">取 消</el-button>
        <el-button type="primary" @click="submitPlanForm" :loading="planDialog.loading">确 定</el-button>
      </template>
    </el-dialog>
    <!-- 完成记录弹窗 -->
    <el-dialog :title="recordDialog.title" v-model="recordDialog.visible" width="700px" append-to-body>
      <el-form ref="recordFormRef" :model="recordForm" :rules="recordRules" label-width="100px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="培训计划" prop="planId">
              <el-select v-model="recordForm.planId" placeholder="请选择培训计划" style="width: 100%" @change="handlePlanChange">
                <el-option v-for="item in planOptions" :key="item.id" :label="item.content" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="员工姓名" prop="employeeId">
              <el-select v-model="recordForm.employeeId" placeholder="请选择员工" style="width: 100%" @change="handleEmployeeChange">
                <el-option v-for="item in employeeOptions" :key="item.userId" :label="item.userName" :value="item.userId" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="完成时间" prop="completeTime">
              <el-date-picker v-model="recordForm.completeTime" type="datetime" placeholder="选择完成时间" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="学习时长" prop="duration">
              <el-input-number v-model="recordForm.duration" :min="0" :precision="1" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="培训方式" prop="method">
              <el-select v-model="recordForm.method" placeholder="请选择培训方式" style="width: 100%">
                <el-option label="线下授课" value="线下授课" />
                <el-option label="线上学习" value="线上学习" />
                <el-option label="实操演练" value="实操演练" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="考核分数" prop="score">
              <el-input-number v-model="recordForm.score" :min="0" :max="100" style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="考核结果" prop="result">
              <el-select v-model="recordForm.result" placeholder="请选择考核结果" style="width: 100%">
                <el-option label="通过" value="通过" />
                <el-option label="未通过" value="未通过" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="状态" prop="status">
              <el-radio-group v-model="recordForm.status">
                <el-radio :value="1">已完成</el-radio>
                <el-radio :value="0">未完成</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="recordForm.remark" type="textarea" :rows="3" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="recordDialog.visible = false">取 消</el-button>
        <el-button type="primary" @click="submitRecordForm" :loading="recordDialog.loading">确 定</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ref, reactive, onMounted } from "vue";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import {
  getMaterialList,
  uploadMaterial as uploadMaterialApi,
  updateMaterial,
  deleteMaterial,
  getMaterialDetail,
  getPlanList,
  addPlan as addPlanApi,
  updatePlan,
  deletePlan,
  getPlanDetail,
  getRecordList,
  addRecord as addRecordApi,
  updateRecord,
  deleteRecord,
  getRecordDetail,
  exportRecord
} from "@/api/safetyManagement/trainingManage.js";
import { listUser } from "@/api/system/user";
import { getToken } from "@/utils/auth";
import { ElMessage, ElMessageBox } from "element-plus";
defineOptions({
  name: "培训管理",
@@ -76,54 +307,653 @@
const activeTab = ref("materials");
// 培训资料
const materialFilters = reactive({ name: "", type: "" });
// ==================== 培训资料 ====================
const materialFilters = reactive({ name: "", type: "", status: null });
const materialList = ref([]);
const materialPage = reactive({ current: 1, size: 10, total: 0 });
const materialLoading = ref(false);
const materialColumns = [
  { label: "资料名称", prop: "name", align: "center" },
  { label: "类型", prop: "type", align: "center" },
  {
    label: "类型",
    prop: "type",
    align: "center",
    dataType: "tag",
    formatType: () => "primary",
    formatData: (val) => val || '-'
  },
  { label: "上传人", prop: "uploader", align: "center" },
  { label: "上传时间", prop: "uploadTime", align: "center" },
  { label: "文件大小", prop: "fileSize", align: "center" },
  {
    label: "状态",
    prop: "status",
    align: "center",
    dataType: "tag",
    formatType: (val) => (val === 1 ? "success" : "info"),
    formatData: (val) => (val === 1 ? "启用" : "停用")
  },
  {
    label: "操作",
    prop: "action",
    align: "center",
    dataType: "action",
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => handleEditMaterial(row) },
      { name: "下载", type: "text", clickFun: (row) => handleDownload(row) },
      { name: "删除", type: "text", clickFun: (row) => handleDeleteMaterial(row) }
    ]
  }
];
// 培训计划
const planFilters = reactive({ year: "", post: "" });
const materialDialog = reactive({ visible: false, title: "", loading: false });
const materialFormRef = ref(null);
const uploadRef = ref(null);
const materialForm = reactive({
  id: null,
  name: "",
  type: "",
  fileUrl: "",
  fileSize: "",
  status: 1,
  remark: ""
});
const materialRules = {
  name: [{ required: true, message: "请输入资料名称", trigger: "blur" }],
  type: [{ required: true, message: "请选择资料类型", trigger: "change" }]
};
const uploadHeaders = reactive({
  Authorization: "Bearer " + getToken()
});
// ==================== 培训计划 ====================
const planFilters = reactive({ year: "", post: "", level: "", status: null });
const planList = ref([]);
const planPage = reactive({ current: 1, size: 10, total: 0 });
const planLoading = ref(false);
const planColumns = [
  { label: "计划年度", prop: "year", align: "center" },
  { label: "岗位", prop: "post", align: "center" },
  { label: "层级", prop: "level", align: "center" },
  { label: "培训等级", prop: "level", align: "center" },
  { label: "培训内容", prop: "content", align: "center" },
  { label: "计划课时", prop: "hours", align: "center" },
  {
    label: "状态",
    prop: "status",
    align: "center",
    dataType: "tag",
    formatType: (val) => {
      if (val === 0) return 'info';
      if (val === 1) return 'warning';
      return 'success';
    },
    formatData: (val) => {
      const statusMap = { 0: '待执行', 1: '执行中', 2: '已完成' };
      return statusMap[val] || val;
    }
  },
  {
    label: "操作",
    prop: "action",
    align: "center",
    dataType: "action",
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => handleEditPlan(row) },
      { name: "删除", type: "text", clickFun: (row) => handleDeletePlan(row) }
    ]
  }
];
// 完成记录
const recordFilters = reactive({ employeeName: "" });
const planDialog = reactive({ visible: false, title: "", loading: false });
const planFormRef = ref(null);
const planForm = reactive({
  id: null,
  year: "",
  post: "",
  level: "",
  content: "",
  hours: 1,
  materialIds: [],
  status: 0,
  remark: ""
});
const planRules = {
  year: [{ required: true, message: "请选择计划年度", trigger: "change" }],
  post: [{ required: true, message: "请输入适用岗位", trigger: "blur" }],
  level: [{ required: true, message: "请选择培训等级", trigger: "change" }],
  content: [{ required: true, message: "请输入培训内容", trigger: "blur" }],
  hours: [{ required: true, message: "请输入计划课时", trigger: "blur" }]
};
const materialOptions = ref([]);
// ==================== 完成记录 ====================
const recordFilters = reactive({ employeeName: "", status: null });
const recordList = ref([]);
const recordPage = reactive({ current: 1, size: 10, total: 0 });
const recordLoading = ref(false);
const recordColumns = [
  { label: "员工姓名", prop: "employeeName", align: "center" },
  { label: "培训内容", prop: "content", align: "center" },
  { label: "完成时间", prop: "completeTime", align: "center" },
  { label: "考核结果", prop: "result", align: "center" },
  { label: "学习时长", prop: "duration", align: "center" },
  { label: "培训方式", prop: "method", align: "center" },
  { label: "分数", prop: "score", align: "center" },
  {
    label: "考核结果",
    prop: "result",
    align: "center",
    dataType: "tag",
    formatType: (val) => {
      if (val === '优秀' || val === '通过') return 'success';
      if (val === '良好') return 'primary';
      if (val === '合格') return 'warning';
      return 'danger';
    },
    formatData: (val) => val || '-'
  },
  {
    label: "操作",
    prop: "action",
    align: "center",
    dataType: "action",
    operation: [
      { name: "编辑", type: "text", clickFun: (row) => handleEditRecord(row) },
      { name: "删除", type: "text", clickFun: (row) => handleDeleteRecord(row) }
    ]
  }
];
const getMaterialData = () => {};
const resetMaterialFilters = () => { materialFilters.name = ""; materialFilters.type = ""; };
const uploadMaterial = () => {};
const changeMaterialPage = ({ page, limit }) => { materialPage.current = page; materialPage.size = limit; };
const recordDialog = reactive({ visible: false, title: "", loading: false });
const recordFormRef = ref(null);
const recordForm = reactive({
  id: null,
  planId: null,
  employeeId: null,
  employeeName: "",
  content: "",
  completeTime: "",
  duration: 0,
  method: "",
  score: 0,
  result: "",
  status: 1,
  remark: ""
});
const recordRules = {
  planId: [{ required: true, message: "请选择培训计划", trigger: "change" }],
  employeeId: [{ required: true, message: "请选择员工", trigger: "change" }],
  completeTime: [{ required: true, message: "请选择完成时间", trigger: "change" }]
};
const planOptions = ref([]);
const employeeOptions = ref([]);
const getPlanData = () => {};
const resetPlanFilters = () => { planFilters.year = ""; planFilters.post = ""; };
const addPlan = () => {};
const changePlanPage = ({ page, limit }) => { planPage.current = page; planPage.size = limit; };
// ==================== 通用方法 ====================
const loadData = () => {
  switch (activeTab.value) {
    case 'materials':
      getMaterialData();
      break;
    case 'plans':
      getPlanData();
      break;
    case 'records':
      getRecordData();
      break;
  }
};
const getRecordData = () => {};
const resetRecordFilters = () => { recordFilters.employeeName = ""; };
const changeRecordPage = ({ page, limit }) => { recordPage.current = page; recordPage.size = limit; };
const handleTabChange = () => {
  loadData();
};
// ==================== 培训资料方法 ====================
const getMaterialData = async () => {
  materialLoading.value = true;
  try {
    const res = await getMaterialList({
      pageNum: materialPage.current,
      pageSize: materialPage.size,
      ...materialFilters
    });
    if (res.code === 200) {
      materialList.value = res.data.records || res.data.rows || [];
      materialPage.total = res.data.total || 0;
    }
  } catch (error) {
    ElMessage.error('获取培训资料失败');
  } finally {
    materialLoading.value = false;
  }
};
const resetMaterialFilters = () => {
  materialFilters.name = "";
  materialFilters.type = "";
  materialFilters.status = null;
  materialPage.current = 1;
  getMaterialData();
};
const changeMaterialPage = ({ page, limit }) => {
  materialPage.current = page;
  materialPage.size = limit;
  getMaterialData();
};
const resetMaterialForm = () => {
  materialForm.id = null;
  materialForm.name = "";
  materialForm.type = "";
  materialForm.fileUrl = "";
  materialForm.fileSize = "";
  materialForm.status = 1;
  materialForm.remark = "";
  if (uploadRef.value) {
    uploadRef.value.clearFiles();
  }
};
const openMaterialDialog = () => {
  resetMaterialForm();
  materialDialog.title = "上传培训资料";
  materialDialog.visible = true;
};
const handleEditMaterial = async (row) => {
  resetMaterialForm();
  try {
    const res = await getMaterialDetail(row.id);
    if (res.code === 200) {
      Object.assign(materialForm, res.data);
      materialDialog.title = "编辑培训资料";
      materialDialog.visible = true;
    }
  } catch (error) {
    ElMessage.error('获取资料详情失败');
  }
};
const handleUploadSuccess = (response) => {
  if (response.code === 200) {
    materialForm.fileUrl = response.url;
    materialForm.fileSize = formatFileSize(response.file?.size || 0);
    ElMessage.success('文件上传成功');
  } else {
    ElMessage.error(response.msg || '上传失败');
  }
};
const handleUploadError = () => {
  ElMessage.error('文件上传失败');
};
const beforeUpload = (file) => {
  const maxSize = 50 * 1024 * 1024; // 50MB
  if (file.size > maxSize) {
    ElMessage.error('文件大小不能超过 50MB');
    return false;
  }
  return true;
};
const formatFileSize = (size) => {
  if (size < 1024) return size + ' B';
  if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB';
  if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB';
  return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
};
const submitMaterialForm = async () => {
  const valid = await materialFormRef.value.validate().catch(() => false);
  if (!valid) return;
  if (!materialForm.id && !materialForm.fileUrl) {
    ElMessage.error('请上传文件');
    return;
  }
  materialDialog.loading = true;
  try {
    const api = materialForm.id ? updateMaterial : uploadMaterialApi;
    const res = await api(materialForm);
    if (res.code === 200) {
      ElMessage.success(materialForm.id ? '修改成功' : '上传成功');
      materialDialog.visible = false;
      getMaterialData();
    }
  } catch (error) {
    ElMessage.error(materialForm.id ? '修改失败' : '上传失败');
  } finally {
    materialDialog.loading = false;
  }
};
const handleDownload = (row) => {
  if (row.fileUrl) {
    // 处理URL格式问题
    let url = row.fileUrl;
    // 修复类似 http://host:portupload/ 的错误格式
    url = url.replace(/^(http:\/\/[^/]+:\d+)(upload\/)/, '$1/$2');
    // 如果不是完整URL,添加前缀
    if (!url.startsWith('http')) {
      url = '/dev-api/' + url.replace(/^\//, '');
    }
    window.open(url, '_blank');
  } else {
    ElMessage.warning('文件地址不存在');
  }
};
const handleDeleteMaterial = (row) => {
  ElMessageBox.confirm(`确认删除资料 "${row.name}" 吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning"
  }).then(async () => {
    try {
      const res = await deleteMaterial(row.id);
      if (res.code === 200) {
        ElMessage.success("删除成功");
        getMaterialData();
      }
    } catch (error) {
      ElMessage.error("删除失败");
    }
  });
};
// ==================== 培训计划方法 ====================
const getPlanData = async () => {
  planLoading.value = true;
  try {
    const res = await getPlanList({
      pageNum: planPage.current,
      pageSize: planPage.size,
      ...planFilters
    });
    if (res.code === 200) {
      planList.value = res.data.records || res.data.rows || [];
      planPage.total = res.data.total || 0;
    }
  } catch (error) {
    ElMessage.error('获取培训计划失败');
  } finally {
    planLoading.value = false;
  }
};
const resetPlanFilters = () => {
  planFilters.year = "";
  planFilters.post = "";
  planFilters.level = "";
  planFilters.status = null;
  planPage.current = 1;
  getPlanData();
};
const changePlanPage = ({ page, limit }) => {
  planPage.current = page;
  planPage.size = limit;
  getPlanData();
};
const loadMaterialOptions = async () => {
  try {
    const res = await getMaterialList({ pageNum: 1, pageSize: 1000 });
    if (res.code === 200) {
      materialOptions.value = res.data.records || res.data.rows || [];
    }
  } catch (error) {
    console.error('加载培训资料选项失败', error);
  }
};
const resetPlanForm = () => {
  planForm.id = null;
  planForm.year = "";
  planForm.post = "";
  planForm.level = "";
  planForm.content = "";
  planForm.hours = 1;
  planForm.materialIds = [];
  planForm.status = 0;
  planForm.remark = "";
};
const openPlanDialog = () => {
  resetPlanForm();
  loadMaterialOptions();
  planDialog.title = "制定培训计划";
  planDialog.visible = true;
};
const handleEditPlan = async (row) => {
  resetPlanForm();
  loadMaterialOptions();
  try {
    const res = await getPlanDetail(row.id);
    if (res.code === 200) {
      Object.assign(planForm, res.data);
      // 将 materialIds 字符串转换为数组
      if (planForm.materialIds && typeof planForm.materialIds === 'string') {
        planForm.materialIds = planForm.materialIds.split(',').map(id => parseInt(id));
      }
      planDialog.title = "编辑培训计划";
      planDialog.visible = true;
    }
  } catch (error) {
    ElMessage.error('获取计划详情失败');
  }
};
const submitPlanForm = async () => {
  const valid = await planFormRef.value.validate().catch(() => false);
  if (!valid) return;
  planDialog.loading = true;
  try {
    const submitData = { ...planForm };
    // 将 materialIds 数组转换为逗号分隔的字符串
    if (Array.isArray(submitData.materialIds)) {
      submitData.materialIds = submitData.materialIds.join(',');
    }
    const api = planForm.id ? updatePlan : addPlanApi;
    const res = await api(submitData);
    if (res.code === 200) {
      ElMessage.success(planForm.id ? '修改成功' : '新增成功');
      planDialog.visible = false;
      getPlanData();
    }
  } catch (error) {
    ElMessage.error(planForm.id ? '修改失败' : '新增失败');
  } finally {
    planDialog.loading = false;
  }
};
const handleDeletePlan = (row) => {
  ElMessageBox.confirm(`确认删除该培训计划吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning"
  }).then(async () => {
    try {
      const res = await deletePlan(row.id);
      if (res.code === 200) {
        ElMessage.success("删除成功");
        getPlanData();
      }
    } catch (error) {
      ElMessage.error("删除失败");
    }
  });
};
// ==================== 完成记录方法 ====================
const getRecordData = async () => {
  recordLoading.value = true;
  try {
    const res = await getRecordList({
      pageNum: recordPage.current,
      pageSize: recordPage.size,
      ...recordFilters
    });
    if (res.code === 200) {
      recordList.value = res.data.records || res.data.rows || [];
      recordPage.total = res.data.total || 0;
    }
  } catch (error) {
    ElMessage.error('获取完成记录失败');
  } finally {
    recordLoading.value = false;
  }
};
const resetRecordFilters = () => {
  recordFilters.employeeName = "";
  recordFilters.status = null;
  recordPage.current = 1;
  getRecordData();
};
const changeRecordPage = ({ page, limit }) => {
  recordPage.current = page;
  recordPage.size = limit;
  getRecordData();
};
const exportRecords = async () => {
  try {
    const res = await exportRecord(recordFilters);
    // 处理文件下载
    const blob = new Blob([res]);
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = '培训完成记录.xlsx';
    link.click();
    ElMessage.success('导出成功');
  } catch (error) {
    ElMessage.error('导出失败');
  }
};
// 完成记录表单方法
const resetRecordForm = () => {
  recordForm.id = null;
  recordForm.planId = null;
  recordForm.employeeId = null;
  recordForm.employeeName = "";
  recordForm.content = "";
  recordForm.completeTime = "";
  recordForm.duration = 0;
  recordForm.method = "";
  recordForm.score = 0;
  recordForm.result = "";
  recordForm.status = 1;
  recordForm.remark = "";
};
// 加载培训计划选项
const loadPlanOptions = async () => {
  try {
    const res = await getPlanList({ pageNum: 1, pageSize: 1000 });
    if (res.code === 200) {
      planOptions.value = res.data.records || res.data.rows || [];
    }
  } catch (error) {
    console.error('加载培训计划失败', error);
  }
};
// 加载员工选项(从用户管理获取)
const loadEmployeeOptions = async () => {
  try {
    const res = await listUser({ pageNum: 1, pageSize: 1000 });
    // 用户管理接口直接返回 rows,没有 code 字段
    employeeOptions.value = res.rows || [];
  } catch (error) {
    console.error('加载员工列表失败', error);
  }
};
const handlePlanChange = (val) => {
  const selectedPlan = planOptions.value.find(item => item.id === val);
  if (selectedPlan) {
    recordForm.content = selectedPlan.content;
  }
};
const handleEmployeeChange = (val) => {
  const selectedEmployee = employeeOptions.value.find(item => item.userId === val);
  if (selectedEmployee) {
    recordForm.employeeName = selectedEmployee.userName;
  }
};
const openRecordDialog = () => {
  resetRecordForm();
  loadPlanOptions();
  loadEmployeeOptions();
  recordDialog.title = "新增完成记录";
  recordDialog.visible = true;
};
const handleEditRecord = async (row) => {
  resetRecordForm();
  try {
    const res = await getRecordDetail(row.id);
    if (res.code === 200) {
      Object.assign(recordForm, res.data);
      recordDialog.title = "编辑完成记录";
      recordDialog.visible = true;
    }
  } catch (error) {
    ElMessage.error('获取记录详情失败');
  }
};
const submitRecordForm = async () => {
  const valid = await recordFormRef.value.validate().catch(() => false);
  if (!valid) return;
  recordDialog.loading = true;
  try {
    const api = recordForm.id ? updateRecord : addRecordApi;
    const res = await api(recordForm);
    if (res.code === 200) {
      ElMessage.success(recordForm.id ? '修改成功' : '新增成功');
      recordDialog.visible = false;
      getRecordData();
    }
  } catch (error) {
    ElMessage.error(recordForm.id ? '修改失败' : '新增失败');
  } finally {
    recordDialog.loading = false;
  }
};
const handleDeleteRecord = (row) => {
  ElMessageBox.confirm(`确认删除该完成记录吗?`, "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning"
  }).then(async () => {
    try {
      const res = await deleteRecord(row.id);
      if (res.code === 200) {
        ElMessage.success("删除成功");
        getRecordData();
      }
    } catch (error) {
      ElMessage.error("删除失败");
    }
  });
};
onMounted(() => {
  getMaterialData();
});
</script>
<style lang="scss" scoped>